@pol-studios/powersync 1.0.6 → 1.0.7

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.
Files changed (118) hide show
  1. package/dist/CacheSettingsManager-1exbOC6S.d.ts +261 -0
  2. package/dist/attachments/index.d.ts +65 -355
  3. package/dist/attachments/index.js +24 -6
  4. package/dist/{types-Cd7RhNqf.d.ts → background-sync-ChCXW-EV.d.ts} +53 -2
  5. package/dist/chunk-4C3RY5SU.js +204 -0
  6. package/dist/chunk-4C3RY5SU.js.map +1 -0
  7. package/dist/{chunk-3AYXHQ4W.js → chunk-53WH2JJV.js} +111 -47
  8. package/dist/chunk-53WH2JJV.js.map +1 -0
  9. package/dist/chunk-A4IBBWGO.js +377 -0
  10. package/dist/chunk-A4IBBWGO.js.map +1 -0
  11. package/dist/chunk-BREGB4WL.js +1768 -0
  12. package/dist/chunk-BREGB4WL.js.map +1 -0
  13. package/dist/{chunk-EJ23MXPQ.js → chunk-CGL33PL4.js} +3 -1
  14. package/dist/chunk-CGL33PL4.js.map +1 -0
  15. package/dist/chunk-DGUM43GV.js +11 -0
  16. package/dist/chunk-DHYUBVP7.js +131 -0
  17. package/dist/chunk-DHYUBVP7.js.map +1 -0
  18. package/dist/chunk-FV2HXEIY.js +124 -0
  19. package/dist/chunk-FV2HXEIY.js.map +1 -0
  20. package/dist/chunk-GKF7TOMT.js +1 -0
  21. package/dist/{chunk-R4YFWQ3Q.js → chunk-H772V6XQ.js} +304 -51
  22. package/dist/chunk-H772V6XQ.js.map +1 -0
  23. package/dist/{chunk-62J2DPKX.js → chunk-HFOFLW5F.js} +396 -412
  24. package/dist/chunk-HFOFLW5F.js.map +1 -0
  25. package/dist/chunk-KGSFAE5B.js +1 -0
  26. package/dist/chunk-LNL64IJZ.js +1 -0
  27. package/dist/chunk-MKD2VCX3.js +32 -0
  28. package/dist/chunk-MKD2VCX3.js.map +1 -0
  29. package/dist/{chunk-7EMDVIZX.js → chunk-N75DEF5J.js} +19 -1
  30. package/dist/chunk-N75DEF5J.js.map +1 -0
  31. package/dist/chunk-P6WOZO7H.js +49 -0
  32. package/dist/chunk-P6WOZO7H.js.map +1 -0
  33. package/dist/chunk-TGBT5XBE.js +1 -0
  34. package/dist/chunk-TGBT5XBE.js.map +1 -0
  35. package/dist/chunk-UEYRTLKE.js +72 -0
  36. package/dist/chunk-UEYRTLKE.js.map +1 -0
  37. package/dist/chunk-WGHNIAF7.js +329 -0
  38. package/dist/chunk-WGHNIAF7.js.map +1 -0
  39. package/dist/chunk-WQ5MPAVC.js +449 -0
  40. package/dist/chunk-WQ5MPAVC.js.map +1 -0
  41. package/dist/{chunk-FPTDATY5.js → chunk-XQAJM2MW.js} +22 -11
  42. package/dist/chunk-XQAJM2MW.js.map +1 -0
  43. package/dist/chunk-YSTEESEG.js +676 -0
  44. package/dist/chunk-YSTEESEG.js.map +1 -0
  45. package/dist/chunk-ZEOKPWUC.js +1165 -0
  46. package/dist/chunk-ZEOKPWUC.js.map +1 -0
  47. package/dist/connector/index.d.ts +182 -3
  48. package/dist/connector/index.js +12 -4
  49. package/dist/core/index.d.ts +5 -3
  50. package/dist/core/index.js +5 -2
  51. package/dist/error/index.d.ts +54 -0
  52. package/dist/error/index.js +8 -0
  53. package/dist/error/index.js.map +1 -0
  54. package/dist/index.d.ts +100 -12
  55. package/dist/index.js +148 -38
  56. package/dist/index.native.d.ts +20 -10
  57. package/dist/index.native.js +148 -39
  58. package/dist/index.web.d.ts +20 -10
  59. package/dist/index.web.js +149 -39
  60. package/dist/maintenance/index.d.ts +118 -0
  61. package/dist/maintenance/index.js +17 -0
  62. package/dist/maintenance/index.js.map +1 -0
  63. package/dist/platform/index.d.ts +16 -1
  64. package/dist/platform/index.js +2 -0
  65. package/dist/platform/index.js.map +1 -1
  66. package/dist/platform/index.native.d.ts +2 -2
  67. package/dist/platform/index.native.js +2 -1
  68. package/dist/platform/index.web.d.ts +1 -1
  69. package/dist/platform/index.web.js +2 -1
  70. package/dist/pol-attachment-queue-C7YNXXhK.d.ts +676 -0
  71. package/dist/provider/index.d.ts +447 -21
  72. package/dist/provider/index.js +33 -13
  73. package/dist/storage/index.d.ts +6 -0
  74. package/dist/storage/index.js +28 -0
  75. package/dist/storage/index.js.map +1 -0
  76. package/dist/storage/index.native.d.ts +6 -0
  77. package/dist/storage/index.native.js +26 -0
  78. package/dist/storage/index.native.js.map +1 -0
  79. package/dist/storage/index.web.d.ts +6 -0
  80. package/dist/storage/index.web.js +26 -0
  81. package/dist/storage/index.web.js.map +1 -0
  82. package/dist/storage/upload/index.d.ts +55 -0
  83. package/dist/storage/upload/index.js +15 -0
  84. package/dist/storage/upload/index.js.map +1 -0
  85. package/dist/storage/upload/index.native.d.ts +57 -0
  86. package/dist/storage/upload/index.native.js +14 -0
  87. package/dist/storage/upload/index.native.js.map +1 -0
  88. package/dist/storage/upload/index.web.d.ts +5 -0
  89. package/dist/storage/upload/index.web.js +14 -0
  90. package/dist/storage/upload/index.web.js.map +1 -0
  91. package/dist/{index-l3iL9Jte.d.ts → supabase-connector-qLm-WHkM.d.ts} +90 -25
  92. package/dist/sync/index.d.ts +288 -23
  93. package/dist/sync/index.js +22 -10
  94. package/dist/types-BVacP54t.d.ts +52 -0
  95. package/dist/types-Bgvx7-E8.d.ts +187 -0
  96. package/dist/{types-afHtE1U_.d.ts → types-CDqWh56B.d.ts} +2 -0
  97. package/package.json +72 -2
  98. package/dist/chunk-32OLICZO.js +0 -1
  99. package/dist/chunk-3AYXHQ4W.js.map +0 -1
  100. package/dist/chunk-5FIMA26D.js +0 -1
  101. package/dist/chunk-62J2DPKX.js.map +0 -1
  102. package/dist/chunk-7EMDVIZX.js.map +0 -1
  103. package/dist/chunk-EJ23MXPQ.js.map +0 -1
  104. package/dist/chunk-FPTDATY5.js.map +0 -1
  105. package/dist/chunk-KCDG2MNP.js +0 -1431
  106. package/dist/chunk-KCDG2MNP.js.map +0 -1
  107. package/dist/chunk-OLHGI472.js +0 -1
  108. package/dist/chunk-PAFBKNL3.js +0 -99
  109. package/dist/chunk-PAFBKNL3.js.map +0 -1
  110. package/dist/chunk-R4YFWQ3Q.js.map +0 -1
  111. package/dist/chunk-V6LJ6MR2.js +0 -740
  112. package/dist/chunk-V6LJ6MR2.js.map +0 -1
  113. package/dist/chunk-VJCL2SWD.js +0 -1
  114. package/dist/failed-upload-store-C0cLxxPz.d.ts +0 -33
  115. /package/dist/{chunk-32OLICZO.js.map → chunk-DGUM43GV.js.map} +0 -0
  116. /package/dist/{chunk-5FIMA26D.js.map → chunk-GKF7TOMT.js.map} +0 -0
  117. /package/dist/{chunk-OLHGI472.js.map → chunk-KGSFAE5B.js.map} +0 -0
  118. /package/dist/{chunk-VJCL2SWD.js.map → chunk-LNL64IJZ.js.map} +0 -0
@@ -1,9 +1,9 @@
1
1
  import {
2
- failedUploadStore
3
- } from "./chunk-PAFBKNL3.js";
2
+ withExponentialBackoff
3
+ } from "./chunk-FV2HXEIY.js";
4
4
  import {
5
5
  classifySupabaseError
6
- } from "./chunk-FPTDATY5.js";
6
+ } from "./chunk-XQAJM2MW.js";
7
7
 
8
8
  // src/connector/types.ts
9
9
  var defaultSchemaRouter = () => "public";
@@ -11,18 +11,60 @@ var DEFAULT_RETRY_CONFIG = {
11
11
  transient: {
12
12
  maxRetries: 3,
13
13
  baseDelayMs: 1e3,
14
- maxDelayMs: 3e4,
14
+ // 1 second initial delay
15
+ maxDelayMs: 4e3,
16
+ // 4 second cap
15
17
  backoffMultiplier: 2
18
+ // 1s → 2s → 4s
16
19
  },
17
20
  permanent: {
18
- maxRetries: 2,
19
- baseDelayMs: 5e3,
20
- maxDelayMs: 3e5,
21
- backoffMultiplier: 3
21
+ maxRetries: 0,
22
+ // No retries - permanent errors won't succeed
23
+ baseDelayMs: 1e3,
24
+ // Irrelevant with 0 retries, but needed for type
25
+ maxDelayMs: 1e3,
26
+ // Irrelevant with 0 retries
27
+ backoffMultiplier: 1
28
+ // Irrelevant with 0 retries
22
29
  }
23
30
  };
24
31
 
25
32
  // src/conflicts/detect.ts
33
+ var ConflictDetectionError = class extends Error {
34
+ constructor(message, options) {
35
+ super(message);
36
+ this.name = "ConflictDetectionError";
37
+ this.cause = options?.cause;
38
+ }
39
+ };
40
+ function deepEqual(a, b, maxDepth = 10) {
41
+ if (a === b) return true;
42
+ if (typeof a === "number" && typeof b === "number") {
43
+ if (Number.isNaN(a) && Number.isNaN(b)) return true;
44
+ }
45
+ if (a === null || a === void 0 || b === null || b === void 0) return false;
46
+ if (typeof a !== typeof b) return false;
47
+ if (typeof a !== "object") return false;
48
+ if (maxDepth <= 0) {
49
+ console.warn("[deepEqual] Max depth reached, falling back to reference equality");
50
+ return a === b;
51
+ }
52
+ if (a instanceof Date && b instanceof Date) {
53
+ return a.getTime() === b.getTime();
54
+ }
55
+ if (a instanceof Date || b instanceof Date) return false;
56
+ if (Array.isArray(a) !== Array.isArray(b)) return false;
57
+ const keysA = Object.keys(a);
58
+ const keysB = Object.keys(b);
59
+ if (keysA.length !== keysB.length) return false;
60
+ for (const key of keysA) {
61
+ if (!keysB.includes(key)) return false;
62
+ if (!deepEqual(a[key], b[key], maxDepth - 1)) {
63
+ return false;
64
+ }
65
+ }
66
+ return true;
67
+ }
26
68
  var TABLE_NAME_REGEX = /^[a-zA-Z_][a-zA-Z0-9_]*$/;
27
69
  function validateTableName(table) {
28
70
  if (!TABLE_NAME_REGEX.test(table)) {
@@ -54,14 +96,9 @@ async function detectConflicts(table, recordId, localVersion, serverVersion, pen
54
96
  ascending: false
55
97
  }).limit(20);
56
98
  if (error) {
57
- console.warn("[detectConflicts] Failed to query AuditLog:", error);
58
- return {
59
- hasConflict: false,
60
- conflicts: [],
61
- nonConflictingChanges: Object.keys(filteredPendingChanges),
62
- table,
63
- recordId
64
- };
99
+ throw new ConflictDetectionError(`AuditLog query failed for ${table}/${recordId}`, {
100
+ cause: error
101
+ });
65
102
  }
66
103
  const serverChanges = /* @__PURE__ */ new Map();
67
104
  for (const log of auditLogs ?? []) {
@@ -70,7 +107,7 @@ async function detectConflicts(table, recordId, localVersion, serverVersion, pen
70
107
  if (!oldRec || !newRec) continue;
71
108
  for (const [field, newValue] of Object.entries(newRec)) {
72
109
  if (ignoredFields.has(field)) continue;
73
- if (oldRec[field] !== newValue && !serverChanges.has(field)) {
110
+ if (!deepEqual(oldRec[field], newValue) && !serverChanges.has(field)) {
74
111
  serverChanges.set(field, {
75
112
  newValue,
76
113
  changedBy: log.changeBy,
@@ -129,121 +166,63 @@ async function getLocalVersion(table, recordId, db) {
129
166
  return result?._version ?? null;
130
167
  }
131
168
 
132
- // src/utils/retry.ts
133
- var AbortError = class extends Error {
134
- constructor(message = "Operation aborted") {
169
+ // src/connector/supabase-connector.ts
170
+ var ValidationError = class extends Error {
171
+ constructor(message) {
135
172
  super(message);
136
- this.name = "AbortError";
137
- }
138
- };
139
- var RetryExhaustedError = class extends Error {
140
- /** The last error that caused the final retry to fail */
141
- cause;
142
- /** Total number of attempts made */
143
- attempts;
144
- constructor(cause, attempts) {
145
- super(`Retry exhausted after ${attempts} attempt(s): ${cause.message}`);
146
- this.name = "RetryExhaustedError";
147
- this.cause = cause;
148
- this.attempts = attempts;
173
+ this.name = "ValidationError";
149
174
  }
150
175
  };
151
- function calculateBackoffDelay(attempt, config) {
152
- const {
153
- baseDelayMs,
154
- maxDelayMs,
155
- backoffMultiplier
156
- } = config;
157
- const safeAttempt = Math.max(0, attempt);
158
- const exponentialDelay = baseDelayMs * Math.pow(backoffMultiplier, safeAttempt);
159
- return Math.min(exponentialDelay, maxDelayMs);
160
- }
161
- function addJitter(delay) {
162
- const jitterFactor = 0.9 + Math.random() * 0.2;
163
- return Math.round(delay * jitterFactor);
164
- }
165
- function sleep(ms, signal) {
166
- return new Promise((resolve, reject) => {
167
- if (signal?.aborted) {
168
- reject(new AbortError());
169
- return;
170
- }
171
- if (ms <= 0) {
172
- resolve();
173
- return;
176
+ function isAuthError(error) {
177
+ if (!error) return false;
178
+ if (typeof error === "object" && error !== null) {
179
+ const statusCode = error.status ?? error.statusCode;
180
+ if (statusCode === 401 || statusCode === 403) {
181
+ return true;
174
182
  }
175
- let timeoutId;
176
- const handleAbort = () => {
177
- if (timeoutId !== void 0) {
178
- clearTimeout(timeoutId);
179
- }
180
- reject(new AbortError());
181
- };
182
- if (signal) {
183
- signal.addEventListener("abort", handleAbort, {
184
- once: true
185
- });
183
+ const code = error.code;
184
+ if (code === "401" || code === "403" || code === "42501") {
185
+ return true;
186
186
  }
187
- timeoutId = setTimeout(() => {
188
- if (signal) {
189
- signal.removeEventListener("abort", handleAbort);
190
- }
191
- resolve();
192
- }, ms);
187
+ }
188
+ const message = error instanceof Error ? error.message.toLowerCase() : String(error).toLowerCase();
189
+ const authPatterns = [
190
+ /\bjwt\s+(expired|invalid|malformed)\b/,
191
+ /\btoken\s+(expired|invalid|revoked)\b/,
192
+ /\bsession\s+(expired|invalid)\b/,
193
+ /\bunauthorized\s*(access|request|error)?\b/,
194
+ /\baccess\s+(denied|forbidden)\b/,
195
+ /\bnot\s+authorized\b/,
196
+ /\bauth(entication)?\s+(failed|error|required)\b/,
197
+ /\bpermission\s+denied\b/,
198
+ /\binvalid\s+(credentials|api[_\s]?key)\b/,
199
+ /\brls\b.*\bpolicy\b/,
200
+ /\brow[_\s]?level[_\s]?security\b/,
201
+ /\b42501\b/
202
+ // PostgreSQL insufficient privilege error code
203
+ ];
204
+ return authPatterns.some((pattern) => pattern.test(message));
205
+ }
206
+ function withTimeout(promise, ms, message) {
207
+ let timeoutId;
208
+ const timeoutPromise = new Promise((_, reject) => {
209
+ timeoutId = setTimeout(() => reject(new Error(message)), ms);
193
210
  });
211
+ return Promise.race([promise, timeoutPromise]).finally(() => clearTimeout(timeoutId));
194
212
  }
195
- var DEFAULT_BACKOFF_CONFIG = {
196
- maxRetries: 3,
197
- baseDelayMs: 1e3,
198
- maxDelayMs: 3e4,
199
- backoffMultiplier: 2
200
- };
201
- async function withExponentialBackoff(fn, config, options) {
202
- const {
203
- maxRetries,
204
- baseDelayMs,
205
- maxDelayMs,
206
- backoffMultiplier
207
- } = config;
208
- const {
209
- signal,
210
- onRetry
211
- } = options ?? {};
212
- if (signal?.aborted) {
213
- throw new AbortError();
214
- }
215
- const safeMaxRetries = Math.max(0, Math.floor(maxRetries));
216
- const totalAttempts = safeMaxRetries + 1;
217
- let lastError;
218
- for (let attempt = 0; attempt < totalAttempts; attempt++) {
219
- if (signal?.aborted) {
220
- throw new AbortError();
221
- }
222
- try {
223
- return await fn();
224
- } catch (error) {
225
- lastError = error instanceof Error ? error : new Error(String(error));
226
- const isLastAttempt = attempt === totalAttempts - 1;
227
- if (isLastAttempt) {
228
- break;
229
- }
230
- if (signal?.aborted) {
231
- throw new AbortError();
232
- }
233
- const baseDelay = calculateBackoffDelay(attempt, {
234
- baseDelayMs,
235
- maxDelayMs,
236
- backoffMultiplier
237
- });
238
- const delayWithJitter = addJitter(baseDelay);
239
- onRetry?.(attempt + 1, delayWithJitter, lastError);
240
- await sleep(delayWithJitter, signal);
213
+ var MAX_PAYLOAD_SIZE = 900 * 1024;
214
+ function groupEntriesByTable(entries) {
215
+ const grouped = /* @__PURE__ */ new Map();
216
+ for (const entry of entries) {
217
+ const existing = grouped.get(entry.table);
218
+ if (existing) {
219
+ existing.push(entry);
220
+ } else {
221
+ grouped.set(entry.table, [entry]);
241
222
  }
242
223
  }
243
- throw new RetryExhaustedError(lastError, totalAttempts);
224
+ return grouped;
244
225
  }
245
-
246
- // src/connector/supabase-connector.ts
247
226
  var SupabaseConnector = class {
248
227
  supabase;
249
228
  powerSyncUrl;
@@ -267,6 +246,10 @@ var SupabaseConnector = class {
267
246
  resolvedConflicts = /* @__PURE__ */ new Map();
268
247
  // Cleanup function for resolution listener subscription
269
248
  unsubscribeResolution;
249
+ // Promise-based locking for version column checks to prevent duplicate queries
250
+ versionColumnPromises = /* @__PURE__ */ new Map();
251
+ // Flag to track if connector has been destroyed
252
+ isDestroyed = false;
270
253
  // Retry configuration
271
254
  retryConfig;
272
255
  autoRetryPaused = false;
@@ -313,11 +296,13 @@ var SupabaseConnector = class {
313
296
  * Call this when the connector is no longer needed.
314
297
  */
315
298
  destroy() {
299
+ this.isDestroyed = true;
316
300
  if (this.unsubscribeResolution) {
317
301
  this.unsubscribeResolution();
318
302
  this.unsubscribeResolution = void 0;
319
303
  }
320
304
  this.resolvedConflicts.clear();
305
+ this.versionColumnPromises.clear();
321
306
  }
322
307
  // ─── Retry Control Methods ─────────────────────────────────────────────────
323
308
  /**
@@ -339,113 +324,81 @@ var SupabaseConnector = class {
339
324
  console.log("[Connector] Auto-retry resumed");
340
325
  }
341
326
  }
342
- /**
343
- * Manually retry all failed uploads that are ready for retry.
344
- * This processes entries from the failed upload store.
345
- */
346
- async retryFailedUploads() {
347
- const retryableUploads = failedUploadStore.getRetryable();
348
- if (retryableUploads.length === 0) {
349
- if (__DEV__) {
350
- console.log("[Connector] No failed uploads ready for retry");
351
- }
352
- return;
353
- }
354
- if (__DEV__) {
355
- console.log("[Connector] Manually retrying failed uploads:", {
356
- count: retryableUploads.length,
357
- entries: retryableUploads.map((u) => ({
358
- table: u.table,
359
- id: u.id,
360
- operation: u.operation
361
- }))
362
- });
363
- }
364
- for (const upload of retryableUploads) {
365
- try {
366
- const entry = {
367
- table: upload.table,
368
- op: upload.operation,
369
- id: upload.id,
370
- clientId: Date.now(),
371
- // Synthetic clientId for retry
372
- opData: upload.data
373
- };
374
- await this.processWithRetry(entry);
375
- failedUploadStore.remove(upload.id);
376
- if (__DEV__) {
377
- console.log("[Connector] Retry succeeded for:", {
378
- table: upload.table,
379
- id: upload.id
380
- });
381
- }
382
- } catch (error) {
383
- if (__DEV__) {
384
- console.log("[Connector] Retry failed again for:", {
385
- table: upload.table,
386
- id: upload.id,
387
- error: error instanceof Error ? error.message : String(error)
388
- });
389
- }
390
- }
391
- }
392
- }
393
- /**
394
- * Clear all failed uploads from the store.
395
- * Use with caution - this discards all pending retries.
396
- */
397
- clearFailedUploads() {
398
- failedUploadStore.clear();
399
- if (__DEV__) {
400
- console.log("[Connector] Failed uploads cleared");
401
- }
402
- }
403
- /**
404
- * Get all failed uploads from the store.
405
- */
406
- getFailedUploads() {
407
- return failedUploadStore.getAll();
408
- }
409
327
  // ─── Private Retry Logic ───────────────────────────────────────────────────
410
328
  /**
411
329
  * Process a single CRUD entry with exponential backoff retry.
412
330
  *
331
+ * This method uses a two-phase approach:
332
+ * 1. First attempt - try the operation to classify the error type
333
+ * 2. Retry phase - use the appropriate config (transient vs permanent) based on classification
334
+ *
335
+ * This fixes the issue where reassigning selectedConfig inside withExponentialBackoff's
336
+ * callback had no effect because the config was already destructured at call time.
337
+ *
413
338
  * @param entry - The CRUD entry to process
414
339
  * @throws Error if all retries exhausted (for critical failures)
415
340
  */
416
341
  async processWithRetry(entry) {
342
+ if (this.isDestroyed) {
343
+ throw new Error("Connector destroyed");
344
+ }
417
345
  const classified = {
418
346
  isPermanent: false,
419
347
  pgCode: void 0,
420
348
  userMessage: ""
421
349
  };
422
- let selectedConfig = this.retryConfig.transient;
423
350
  let lastError;
424
351
  try {
425
- await withExponentialBackoff(async () => {
352
+ await this.processCrudEntry(entry);
353
+ return;
354
+ } catch (error) {
355
+ lastError = error instanceof Error ? error : new Error(String(error));
356
+ const classifiedError = classifySupabaseError(error);
357
+ classified.isPermanent = classifiedError.isPermanent;
358
+ classified.pgCode = classifiedError.pgCode;
359
+ classified.userMessage = classifiedError.userMessage;
360
+ if (isAuthError(error)) {
361
+ if (__DEV__) {
362
+ console.log("[Connector] Auth error detected, refreshing session before retry:", {
363
+ table: entry.table,
364
+ op: entry.op,
365
+ id: entry.id
366
+ });
367
+ }
426
368
  try {
369
+ await this.supabase.auth.refreshSession();
427
370
  await this.processCrudEntry(entry);
428
- } catch (error) {
429
- const classifiedError = classifySupabaseError(error);
430
- classified.isPermanent = classifiedError.isPermanent;
431
- classified.pgCode = classifiedError.pgCode;
432
- classified.userMessage = classifiedError.userMessage;
433
- if (classifiedError.isPermanent) {
434
- selectedConfig = this.retryConfig.permanent;
435
- }
436
- lastError = error instanceof Error ? error : new Error(String(error));
437
- if (__DEV__) {
438
- console.log("[Connector] CRUD operation failed, will retry:", {
439
- table: entry.table,
440
- op: entry.op,
441
- id: entry.id,
442
- isPermanent: classifiedError.isPermanent,
443
- pgCode: classifiedError.pgCode,
444
- userMessage: classifiedError.userMessage
445
- });
446
- }
447
- throw error;
371
+ return;
372
+ } catch (retryError) {
373
+ lastError = retryError instanceof Error ? retryError : new Error(String(retryError));
374
+ const retriedClassification = classifySupabaseError(retryError);
375
+ classified.isPermanent = retriedClassification.isPermanent;
376
+ classified.pgCode = retriedClassification.pgCode;
377
+ classified.userMessage = retriedClassification.userMessage;
448
378
  }
379
+ }
380
+ if (__DEV__) {
381
+ console.log("[Connector] Initial attempt failed, will retry with appropriate config:", {
382
+ table: entry.table,
383
+ op: entry.op,
384
+ id: entry.id,
385
+ isPermanent: classified.isPermanent,
386
+ pgCode: classified.pgCode,
387
+ userMessage: classified.userMessage
388
+ });
389
+ }
390
+ this.logger?.warn("[Connector] Initial attempt failed:", {
391
+ table: entry.table,
392
+ op: entry.op,
393
+ id: entry.id,
394
+ error: lastError.message,
395
+ isPermanent: classified.isPermanent
396
+ });
397
+ }
398
+ const selectedConfig = classified.isPermanent ? this.retryConfig.permanent : this.retryConfig.transient;
399
+ try {
400
+ await withExponentialBackoff(async () => {
401
+ await this.processCrudEntry(entry);
449
402
  }, selectedConfig, {
450
403
  onRetry: (attempt, delay, error) => {
451
404
  this.logger?.debug("[Connector] Retry attempt:", {
@@ -462,6 +415,7 @@ var SupabaseConnector = class {
462
415
  op: entry.op,
463
416
  id: entry.id,
464
417
  attempt,
418
+ maxRetries: selectedConfig.maxRetries,
465
419
  delayMs: delay,
466
420
  errorMessage: error.message
467
421
  });
@@ -469,31 +423,15 @@ var SupabaseConnector = class {
469
423
  }
470
424
  });
471
425
  } catch (error) {
472
- const finalError = lastError ?? (error instanceof Error ? error : new Error(String(error)));
426
+ const finalError = error instanceof Error ? error : new Error(String(error));
473
427
  const category = classified.isPermanent ? "permanent" : classified.pgCode ? "transient" : "unknown";
474
- const retryConfig = classified.isPermanent ? this.retryConfig.permanent : this.retryConfig.transient;
475
- const nextRetryDelay = calculateBackoffDelay(retryConfig.maxRetries, retryConfig);
476
- const nextRetryAt = Date.now() + nextRetryDelay;
477
- failedUploadStore.add({
478
- table: entry.table,
479
- operation: entry.op,
480
- data: entry.opData ?? {},
481
- error: {
482
- message: finalError.message,
483
- code: classified.pgCode,
484
- category
485
- },
486
- retryCount: retryConfig.maxRetries,
487
- lastAttempt: Date.now(),
488
- nextRetryAt
489
- });
490
428
  if (__DEV__) {
491
- console.log("[Connector] Entry added to failed upload store:", {
429
+ console.log("[Connector] CRUD entry failed after retries, leaving in ps_crud:", {
492
430
  table: entry.table,
493
431
  op: entry.op,
494
432
  id: entry.id,
495
433
  category,
496
- nextRetryAt: new Date(nextRetryAt).toISOString()
434
+ error: finalError.message
497
435
  });
498
436
  }
499
437
  this.logger?.error("[Connector] CRUD entry failed after retries:", {
@@ -501,12 +439,9 @@ var SupabaseConnector = class {
501
439
  op: entry.op,
502
440
  id: entry.id,
503
441
  error: finalError.message,
504
- category,
505
- nextRetryAt
442
+ category
506
443
  });
507
- if (classified.isPermanent) {
508
- throw finalError;
509
- }
444
+ throw finalError;
510
445
  }
511
446
  }
512
447
  /**
@@ -564,58 +499,33 @@ var SupabaseConnector = class {
564
499
  * 5. Applies resolution or skips entry based on handler response
565
500
  */
566
501
  async uploadData(database) {
502
+ if (this.isDestroyed) {
503
+ throw new Error("Connector destroyed - aborting upload");
504
+ }
567
505
  if (this.shouldUploadFn && !this.shouldUploadFn()) {
568
- if (__DEV__) {
569
- console.log("[Connector] Upload skipped - sync mode does not allow uploads");
570
- }
571
- this.logger?.debug("[Connector] Upload skipped - sync mode does not allow uploads");
572
- return;
506
+ this.logger?.debug("[Connector] Upload blocked - not currently permitted, will retry");
507
+ throw new Error("Upload not permitted - will retry on next sync cycle");
573
508
  }
574
- if (!this.autoRetryPaused) {
575
- const retryableUploads = failedUploadStore.getRetryable();
576
- if (retryableUploads.length > 0) {
577
- if (__DEV__) {
578
- console.log("[Connector] Processing retryable failed uploads:", {
579
- count: retryableUploads.length
580
- });
581
- }
582
- for (const upload of retryableUploads) {
583
- try {
584
- const entry = {
585
- table: upload.table,
586
- op: upload.operation,
587
- id: upload.id,
588
- clientId: Date.now(),
589
- // Synthetic clientId for retry
590
- opData: upload.data
591
- };
592
- await this.processWithRetry(entry);
593
- failedUploadStore.remove(upload.id);
594
- if (__DEV__) {
595
- console.log("[Connector] Retried upload succeeded:", {
596
- table: upload.table,
597
- id: upload.id
598
- });
599
- }
600
- } catch (error) {
601
- if (__DEV__) {
602
- console.log("[Connector] Retried upload failed again:", {
603
- table: upload.table,
604
- id: upload.id,
605
- error: error instanceof Error ? error.message : String(error)
606
- });
607
- }
608
- }
609
- }
509
+ const {
510
+ data: {
511
+ session
512
+ },
513
+ error: sessionError
514
+ } = await this.supabase.auth.getSession();
515
+ if (sessionError || !session) {
516
+ const noSessionError = new Error("No active session - cannot upload data");
517
+ if (__DEV__) {
518
+ console.error("[Connector] uploadData failed: no session");
610
519
  }
520
+ throw noSessionError;
611
521
  }
612
522
  if (__DEV__) {
613
523
  console.log("[Connector] uploadData called, fetching next CRUD transaction...");
614
524
  }
615
525
  const transaction = await database.getNextCrudTransaction();
616
- if (!transaction) {
526
+ if (!transaction || transaction.crud.length === 0) {
617
527
  if (__DEV__) {
618
- console.log("[Connector] No pending CRUD transaction found");
528
+ console.log("[Connector] No pending CRUD transaction found or transaction is empty");
619
529
  }
620
530
  return;
621
531
  }
@@ -672,6 +582,9 @@ var SupabaseConnector = class {
672
582
  entriesDiscarded.push(entry);
673
583
  break;
674
584
  case "partial":
585
+ if (!existingResolution.fields || existingResolution.fields.length === 0) {
586
+ throw new Error("Partial resolution requires at least one field");
587
+ }
675
588
  const partialEntry = {
676
589
  ...entry,
677
590
  opData: this.filterFields(entry.opData ?? {}, existingResolution.fields)
@@ -726,6 +639,9 @@ var SupabaseConnector = class {
726
639
  }
727
640
  break;
728
641
  case "partial":
642
+ if (!resolution.fields || resolution.fields.length === 0) {
643
+ throw new Error("Partial resolution requires at least one field");
644
+ }
729
645
  const partialEntry = {
730
646
  ...entry,
731
647
  opData: this.filterFields(entry.opData ?? {}, resolution.fields)
@@ -738,7 +654,7 @@ var SupabaseConnector = class {
738
654
  break;
739
655
  }
740
656
  } else {
741
- console.warn("[Connector] Conflict detected but no handler:", {
657
+ this.logger?.warn("[Connector] Conflict detected but no handler:", {
742
658
  table: entry.table,
743
659
  id: entry.id,
744
660
  conflicts: conflictResult.conflicts
@@ -755,7 +671,7 @@ var SupabaseConnector = class {
755
671
  });
756
672
  }
757
673
  this.onTransactionComplete?.(entriesQueuedForUI);
758
- return;
674
+ throw new Error("Entries queued for UI resolution - retry will occur on next sync cycle");
759
675
  }
760
676
  if (entriesToProcess.length === 0 && entriesDiscarded.length > 0) {
761
677
  if (__DEV__) {
@@ -772,7 +688,6 @@ var SupabaseConnector = class {
772
688
  }
773
689
  return;
774
690
  }
775
- const criticalFailures = [];
776
691
  const successfulEntries = [];
777
692
  for (const entry of entriesToProcess) {
778
693
  if (__DEV__) {
@@ -783,54 +698,52 @@ var SupabaseConnector = class {
783
698
  opData: entry.opData
784
699
  });
785
700
  }
786
- try {
787
- await this.processWithRetry(entry);
788
- successfulEntries.push(entry);
789
- } catch (error) {
790
- criticalFailures.push({
791
- entry,
792
- error: error instanceof Error ? error : new Error(String(error))
793
- });
794
- }
795
- }
796
- if (criticalFailures.length > 0) {
797
- const firstFailure = criticalFailures[0];
798
- const classified = classifySupabaseError(firstFailure.error);
799
- console.error("[PowerSync Connector] Critical upload failure:", {
800
- errorMessage: firstFailure.error.message,
801
- classified,
802
- isPermanent: classified.isPermanent,
803
- criticalCount: criticalFailures.length,
804
- successCount: successfulEntries.length,
805
- entries: criticalFailures.map((f) => ({
806
- table: f.entry.table,
807
- op: f.entry.op,
808
- id: f.entry.id
809
- }))
810
- });
811
- this.logger?.error("[Connector] Critical upload failure:", {
812
- error: firstFailure.error,
813
- classified,
814
- entries: criticalFailures.map((f) => ({
815
- table: f.entry.table,
816
- op: f.entry.op,
817
- id: f.entry.id
818
- }))
819
- });
820
- this.onTransactionFailure?.(criticalFailures.map((f) => f.entry), firstFailure.error, classified);
821
- throw firstFailure.error;
701
+ await this.processWithRetry(entry);
702
+ successfulEntries.push(entry);
822
703
  }
704
+ await this.finalizeTransaction({
705
+ transaction,
706
+ successfulEntries,
707
+ discardedEntries: entriesDiscarded,
708
+ partialResolutions
709
+ });
710
+ }
711
+ /**
712
+ * Finalize a transaction by completing it after all entries processed successfully.
713
+ * Extracted to eliminate duplication between uploadData and processTransaction.
714
+ *
715
+ * @param context - The finalization context containing results and transaction
716
+ */
717
+ async finalizeTransaction(context) {
718
+ const {
719
+ transaction,
720
+ successfulEntries,
721
+ discardedEntries = [],
722
+ partialResolutions = []
723
+ } = context;
823
724
  if (__DEV__) {
824
725
  console.log("[Connector] All CRUD entries processed, completing transaction...");
825
726
  }
826
727
  try {
827
- await transaction.complete();
728
+ await withTimeout(transaction.complete(), 3e4, "Transaction complete timeout");
828
729
  if (__DEV__) {
829
730
  console.log("[Connector] Transaction completed successfully:", {
830
731
  entriesCount: successfulEntries.length,
831
- discardedCount: entriesDiscarded.length
732
+ discardedCount: discardedEntries.length
832
733
  });
833
734
  }
735
+ for (const entry of successfulEntries) {
736
+ this.resolvedConflicts.delete(`${entry.table}:${entry.id}`);
737
+ }
738
+ for (const entry of discardedEntries) {
739
+ this.resolvedConflicts.delete(`${entry.table}:${entry.id}`);
740
+ }
741
+ if (this.resolvedConflicts.size > 100) {
742
+ const oldest = [...this.resolvedConflicts.keys()][0];
743
+ if (oldest) {
744
+ this.resolvedConflicts.delete(oldest);
745
+ }
746
+ }
834
747
  if (this.conflictBus && partialResolutions.length > 0) {
835
748
  for (const {
836
749
  originalConflict,
@@ -861,10 +774,8 @@ var SupabaseConnector = class {
861
774
  this.onTransactionComplete?.(successfulEntries);
862
775
  } catch (error) {
863
776
  const classified = classifySupabaseError(error);
864
- console.error("[PowerSync Connector] Transaction completion FAILED:", {
777
+ this.logger?.error("[Connector] Transaction completion FAILED:", {
865
778
  errorMessage: error instanceof Error ? error.message : String(error),
866
- errorKeys: error && typeof error === "object" ? Object.keys(error) : [],
867
- errorObject: JSON.stringify(error, null, 2),
868
779
  classified,
869
780
  isPermanent: classified.isPermanent,
870
781
  entries: successfulEntries.map((e) => ({
@@ -873,6 +784,12 @@ var SupabaseConnector = class {
873
784
  id: e.id
874
785
  }))
875
786
  });
787
+ if (__DEV__) {
788
+ console.error("[Connector] Transaction completion error details:", {
789
+ errorKeys: error && typeof error === "object" ? Object.keys(error) : [],
790
+ errorObject: JSON.stringify(error, null, 2)
791
+ });
792
+ }
876
793
  this.logger?.error("[Connector] Transaction completion error:", {
877
794
  error,
878
795
  classified,
@@ -888,106 +805,175 @@ var SupabaseConnector = class {
888
805
  }
889
806
  /**
890
807
  * Process a transaction without conflict detection.
891
- * Used when conflict detection is disabled.
808
+ * Uses batched operations for PUT and DELETE, individual processing for PATCH.
892
809
  */
893
810
  async processTransaction(transaction, _database) {
894
- const criticalFailures = [];
895
811
  const successfulEntries = [];
896
- for (const entry of transaction.crud) {
897
- if (__DEV__) {
898
- console.log("[Connector] Processing CRUD entry with retry:", {
899
- table: entry.table,
900
- op: entry.op,
901
- id: entry.id,
902
- opData: entry.opData
903
- });
812
+ const entriesByTable = groupEntriesByTable(transaction.crud);
813
+ if (__DEV__) {
814
+ console.log("[Connector] Processing transaction with batching:", {
815
+ totalEntries: transaction.crud.length,
816
+ tables: Array.from(entriesByTable.keys()),
817
+ entriesPerTable: Object.fromEntries(Array.from(entriesByTable.entries()).map(([table, entries]) => [table, {
818
+ total: entries.length,
819
+ put: entries.filter((e) => e.op === "PUT" /* PUT */).length,
820
+ patch: entries.filter((e) => e.op === "PATCH" /* PATCH */).length,
821
+ delete: entries.filter((e) => e.op === "DELETE" /* DELETE */).length
822
+ }]))
823
+ });
824
+ }
825
+ for (const [table, entries] of entriesByTable) {
826
+ const schema = this.schemaRouter(table);
827
+ const putEntries = entries.filter((e) => e.op === "PUT" /* PUT */);
828
+ const patchEntries = entries.filter((e) => e.op === "PATCH" /* PATCH */);
829
+ const deleteEntries = entries.filter((e) => e.op === "DELETE" /* DELETE */);
830
+ if (this.crudHandler) {
831
+ for (const entry of entries) {
832
+ await this.processWithRetry(entry);
833
+ successfulEntries.push(entry);
834
+ }
835
+ continue;
904
836
  }
905
- try {
837
+ if (putEntries.length > 0) {
838
+ const successful = await this.processBatchedPuts(table, schema, putEntries);
839
+ successfulEntries.push(...successful);
840
+ }
841
+ if (deleteEntries.length > 0) {
842
+ const successful = await this.processBatchedDeletes(table, schema, deleteEntries);
843
+ successfulEntries.push(...successful);
844
+ }
845
+ for (const entry of patchEntries) {
846
+ if (__DEV__) {
847
+ console.log("[Connector] Processing PATCH entry individually:", {
848
+ table: entry.table,
849
+ op: entry.op,
850
+ id: entry.id,
851
+ opData: entry.opData
852
+ });
853
+ }
906
854
  await this.processWithRetry(entry);
907
855
  successfulEntries.push(entry);
908
- } catch (error) {
909
- criticalFailures.push({
910
- entry,
911
- error: error instanceof Error ? error : new Error(String(error))
912
- });
913
856
  }
914
857
  }
915
- if (criticalFailures.length > 0) {
916
- const firstFailure = criticalFailures[0];
917
- const classified = classifySupabaseError(firstFailure.error);
918
- console.error("[PowerSync Connector] Critical upload failure:", {
919
- errorMessage: firstFailure.error.message,
920
- classified,
921
- isPermanent: classified.isPermanent,
922
- criticalCount: criticalFailures.length,
923
- successCount: successfulEntries.length,
924
- entries: criticalFailures.map((f) => ({
925
- table: f.entry.table,
926
- op: f.entry.op,
927
- id: f.entry.id
928
- }))
929
- });
930
- this.logger?.error("[Connector] Critical upload failure:", {
931
- error: firstFailure.error,
932
- classified,
933
- entries: criticalFailures.map((f) => ({
934
- table: f.entry.table,
935
- op: f.entry.op,
936
- id: f.entry.id
937
- }))
858
+ await this.finalizeTransaction({
859
+ transaction,
860
+ successfulEntries
861
+ });
862
+ }
863
+ /**
864
+ * Process batched PUT (upsert) operations for a single table.
865
+ * Falls back to individual processing if batch fails.
866
+ * CRITICAL: Throws on first failure to maintain transaction atomicity.
867
+ * @returns Array of successfully processed entries
868
+ * @throws Error on first failure - keeps entire transaction in ps_crud
869
+ */
870
+ async processBatchedPuts(table, schema, entries) {
871
+ const successful = [];
872
+ if (__DEV__) {
873
+ console.log("[Connector] Processing batched PUTs:", {
874
+ schema,
875
+ table,
876
+ count: entries.length
938
877
  });
939
- this.onTransactionFailure?.(criticalFailures.map((f) => f.entry), firstFailure.error, classified);
940
- throw firstFailure.error;
941
878
  }
879
+ const rows = entries.map((entry) => ({
880
+ id: entry.id,
881
+ ...entry.opData
882
+ }));
883
+ const query = schema === "public" ? this.supabase.from(table) : this.supabase.schema(schema).from(table);
884
+ const {
885
+ error
886
+ } = await query.upsert(rows, {
887
+ onConflict: "id"
888
+ });
889
+ if (error) {
890
+ if (__DEV__) {
891
+ console.warn("[Connector] Batched PUT failed, falling back to individual processing:", {
892
+ schema,
893
+ table,
894
+ error: error.message
895
+ });
896
+ }
897
+ for (const entry of entries) {
898
+ await this.processWithRetry(entry);
899
+ successful.push(entry);
900
+ }
901
+ } else {
902
+ if (__DEV__) {
903
+ console.log("[Connector] Batched PUT successful:", {
904
+ schema,
905
+ table,
906
+ count: entries.length
907
+ });
908
+ }
909
+ successful.push(...entries);
910
+ }
911
+ return successful;
912
+ }
913
+ /**
914
+ * Process batched DELETE operations for a single table.
915
+ * Falls back to individual processing if batch fails.
916
+ * CRITICAL: Throws on first failure to maintain transaction atomicity.
917
+ * @returns Array of successfully processed entries
918
+ * @throws Error on first failure - keeps entire transaction in ps_crud
919
+ */
920
+ async processBatchedDeletes(table, schema, entries) {
921
+ const successful = [];
942
922
  if (__DEV__) {
943
- console.log("[Connector] All CRUD entries processed, completing transaction...");
923
+ console.log("[Connector] Processing batched DELETEs:", {
924
+ schema,
925
+ table,
926
+ count: entries.length
927
+ });
944
928
  }
945
- try {
946
- await transaction.complete();
929
+ const ids = entries.map((entry) => entry.id);
930
+ const query = schema === "public" ? this.supabase.from(table) : this.supabase.schema(schema).from(table);
931
+ const {
932
+ error
933
+ } = await query.delete().in("id", ids);
934
+ if (error) {
947
935
  if (__DEV__) {
948
- console.log("[Connector] Transaction completed successfully:", {
949
- entriesCount: successfulEntries.length
936
+ console.warn("[Connector] Batched DELETE failed, falling back to individual processing:", {
937
+ schema,
938
+ table,
939
+ error: error.message
950
940
  });
951
941
  }
952
- this.onTransactionSuccess?.(successfulEntries);
953
- this.onTransactionComplete?.(successfulEntries);
954
- } catch (error) {
955
- const classified = classifySupabaseError(error);
956
- console.error("[PowerSync Connector] Transaction completion FAILED:", {
957
- errorMessage: error instanceof Error ? error.message : String(error),
958
- errorKeys: error && typeof error === "object" ? Object.keys(error) : [],
959
- errorObject: JSON.stringify(error, null, 2),
960
- classified,
961
- isPermanent: classified.isPermanent,
962
- entries: successfulEntries.map((e) => ({
963
- table: e.table,
964
- op: e.op,
965
- id: e.id
966
- }))
967
- });
968
- this.logger?.error("[Connector] Transaction completion error:", {
969
- error,
970
- classified,
971
- entries: successfulEntries.map((e) => ({
972
- table: e.table,
973
- op: e.op,
974
- id: e.id
975
- }))
976
- });
977
- this.onTransactionFailure?.(successfulEntries, error instanceof Error ? error : new Error(String(error)), classified);
978
- throw error;
942
+ for (const entry of entries) {
943
+ await this.processWithRetry(entry);
944
+ successful.push(entry);
945
+ }
946
+ } else {
947
+ if (__DEV__) {
948
+ console.log("[Connector] Batched DELETE successful:", {
949
+ schema,
950
+ table,
951
+ count: entries.length
952
+ });
953
+ }
954
+ successful.push(...entries);
979
955
  }
956
+ return successful;
980
957
  }
981
958
  /**
982
959
  * Check if a table has a _version column (cached).
960
+ * P4.1: Uses Promise-based locking to prevent duplicate concurrent queries.
983
961
  */
984
962
  async checkVersionColumn(table, db) {
985
963
  if (this.versionColumnCache.has(table)) {
986
964
  return this.versionColumnCache.get(table);
987
965
  }
988
- const hasVersion = await hasVersionColumn(table, db);
989
- this.versionColumnCache.set(table, hasVersion);
990
- return hasVersion;
966
+ if (!this.versionColumnPromises.has(table)) {
967
+ this.versionColumnPromises.set(table, hasVersionColumn(table, db).then((result) => {
968
+ this.versionColumnCache.set(table, result);
969
+ this.versionColumnPromises.delete(table);
970
+ return result;
971
+ }).catch((error) => {
972
+ this.versionColumnPromises.delete(table);
973
+ throw error;
974
+ }));
975
+ }
976
+ return this.versionColumnPromises.get(table);
991
977
  }
992
978
  /**
993
979
  * Filter opData to only include specified fields.
@@ -1003,12 +989,6 @@ var SupabaseConnector = class {
1003
989
  }
1004
990
  return filtered;
1005
991
  }
1006
- /**
1007
- * Process a single CRUD operation.
1008
- *
1009
- * UUID-native tables (public schema, post-migration) use `id` as the UUID column.
1010
- * Core schema tables (Profile, Comment, CommentSection) still use a separate `uuid` column.
1011
- */
1012
992
  /**
1013
993
  * Process a single CRUD operation.
1014
994
  *
@@ -1018,6 +998,16 @@ var SupabaseConnector = class {
1018
998
  const table = entry.table;
1019
999
  const id = entry.id;
1020
1000
  const schema = this.schemaRouter(table);
1001
+ if (entry.op === "PATCH" /* PATCH */ && Object.keys(entry.opData ?? {}).length === 0) {
1002
+ this.logger?.debug(`[Connector] Skipping empty PATCH for ${entry.table}:${entry.id}`);
1003
+ return;
1004
+ }
1005
+ if (entry.opData) {
1006
+ const payloadSize = JSON.stringify(entry.opData).length;
1007
+ if (payloadSize > MAX_PAYLOAD_SIZE) {
1008
+ throw new ValidationError(`Payload too large (${(payloadSize / 1024).toFixed(0)}KB > 900KB) for ${schema}.${table}`);
1009
+ }
1010
+ }
1021
1011
  if (this.crudHandler) {
1022
1012
  let handled = false;
1023
1013
  switch (entry.op) {
@@ -1164,17 +1154,11 @@ var SupabaseConnector = class {
1164
1154
  export {
1165
1155
  defaultSchemaRouter,
1166
1156
  DEFAULT_RETRY_CONFIG,
1157
+ ConflictDetectionError,
1167
1158
  detectConflicts,
1168
1159
  hasVersionColumn,
1169
1160
  fetchServerVersion,
1170
1161
  getLocalVersion,
1171
- AbortError,
1172
- RetryExhaustedError,
1173
- calculateBackoffDelay,
1174
- addJitter,
1175
- sleep,
1176
- DEFAULT_BACKOFF_CONFIG,
1177
- withExponentialBackoff,
1178
1162
  SupabaseConnector
1179
1163
  };
1180
- //# sourceMappingURL=chunk-62J2DPKX.js.map
1164
+ //# sourceMappingURL=chunk-HFOFLW5F.js.map