@pol-studios/db 1.0.51 → 1.0.53
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/{DataLayerContext--jqUiEMX.d.ts → DataLayerContext-DND_nO5a.d.ts} +3 -3
- package/dist/{chunk-G6E25CWV.js → chunk-D55QGM4P.js} +3 -3
- package/dist/{chunk-PGTRDT5K.js → chunk-FZJX2FZS.js} +685 -698
- package/dist/chunk-FZJX2FZS.js.map +1 -0
- package/dist/{chunk-QJZUIAHA.js → chunk-HZIVE5AZ.js} +14 -6
- package/dist/{chunk-QJZUIAHA.js.map → chunk-HZIVE5AZ.js.map} +1 -1
- package/dist/hooks/index.d.ts +2 -2
- package/dist/{index-CJ8GAb_-.d.ts → index-bMBDRkMF.d.ts} +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.js +3 -3
- package/dist/index.native.d.ts +5 -5
- package/dist/index.native.js +3 -3
- package/dist/index.web.d.ts +6 -6
- package/dist/index.web.js +2 -2
- package/dist/query/index.js +1 -1
- package/dist/types/index.d.ts +2 -2
- package/dist/{useDbCount-DeXtNH0E.d.ts → useDbCount-BGSlyv-F.d.ts} +1 -1
- package/dist/{useResolveFeedback-HTdDM4AQ.d.ts → useResolveFeedback-DLvQD0Wl.d.ts} +3 -3
- package/package.json +3 -3
- package/dist/chunk-PGTRDT5K.js.map +0 -1
- /package/dist/{chunk-G6E25CWV.js.map → chunk-D55QGM4P.js.map} +0 -0
|
@@ -4143,334 +4143,6 @@ var ADAPTER_STRATEGIES = {
|
|
|
4143
4143
|
AUTO: "auto"
|
|
4144
4144
|
};
|
|
4145
4145
|
|
|
4146
|
-
// src/adapters/auto-detector.ts
|
|
4147
|
-
var BackendStatus = /* @__PURE__ */ ((BackendStatus2) => {
|
|
4148
|
-
BackendStatus2["AVAILABLE"] = "available";
|
|
4149
|
-
BackendStatus2["INITIALIZING"] = "initializing";
|
|
4150
|
-
BackendStatus2["UNAVAILABLE"] = "unavailable";
|
|
4151
|
-
return BackendStatus2;
|
|
4152
|
-
})(BackendStatus || {});
|
|
4153
|
-
var AdapterAutoDetector = class {
|
|
4154
|
-
constructor(powerSyncDb, supabase, options = {}) {
|
|
4155
|
-
this.powerSyncDb = powerSyncDb;
|
|
4156
|
-
this.supabase = supabase;
|
|
4157
|
-
this.options = {
|
|
4158
|
-
preferPowerSync: options.preferPowerSync ?? true,
|
|
4159
|
-
statusCheckTimeout: options.statusCheckTimeout ?? 1e3,
|
|
4160
|
-
useOnlineUntilSynced: options.useOnlineUntilSynced ?? true
|
|
4161
|
-
};
|
|
4162
|
-
}
|
|
4163
|
-
options;
|
|
4164
|
-
listeners = /* @__PURE__ */ new Set();
|
|
4165
|
-
lastResult = null;
|
|
4166
|
-
syncStatus = null;
|
|
4167
|
-
/**
|
|
4168
|
-
* Update the PowerSync database reference.
|
|
4169
|
-
* Called when PowerSync becomes available after initial construction.
|
|
4170
|
-
*
|
|
4171
|
-
* @param db - PowerSync database instance or null
|
|
4172
|
-
*/
|
|
4173
|
-
setPowerSyncDb(db) {
|
|
4174
|
-
this.powerSyncDb = db;
|
|
4175
|
-
if (db) {
|
|
4176
|
-
this.detect();
|
|
4177
|
-
}
|
|
4178
|
-
}
|
|
4179
|
-
/**
|
|
4180
|
-
* Update the sync status from PowerSync.
|
|
4181
|
-
* Called when sync status changes to re-evaluate backend recommendation.
|
|
4182
|
-
*
|
|
4183
|
-
* @param status - Current sync status or null if not available
|
|
4184
|
-
*/
|
|
4185
|
-
updateSyncStatus(status) {
|
|
4186
|
-
const hadSynced = this.syncStatus?.hasSynced;
|
|
4187
|
-
const hasSyncedNow = status?.hasSynced;
|
|
4188
|
-
const changed = hadSynced !== hasSyncedNow;
|
|
4189
|
-
this.syncStatus = status;
|
|
4190
|
-
if (changed) {
|
|
4191
|
-
this.detect();
|
|
4192
|
-
}
|
|
4193
|
-
}
|
|
4194
|
-
/**
|
|
4195
|
-
* Get current sync status.
|
|
4196
|
-
* @returns Current sync status or null
|
|
4197
|
-
*/
|
|
4198
|
-
getSyncStatus() {
|
|
4199
|
-
return this.syncStatus;
|
|
4200
|
-
}
|
|
4201
|
-
// ===========================================================================
|
|
4202
|
-
// Main Detection Methods
|
|
4203
|
-
// ===========================================================================
|
|
4204
|
-
/**
|
|
4205
|
-
* Detect backend availability and recommend best option.
|
|
4206
|
-
*
|
|
4207
|
-
* The detection logic follows this priority:
|
|
4208
|
-
* 1. If preferPowerSync is true and PowerSync is available, use PowerSync
|
|
4209
|
-
* 2. If PowerSync is initializing and online with Supabase available, use Supabase temporarily
|
|
4210
|
-
* 3. If online with Supabase available, use Supabase
|
|
4211
|
-
* 4. If offline but PowerSync available, use PowerSync (offline mode)
|
|
4212
|
-
* 5. If offline and PowerSync exists (even if initializing), use PowerSync local data
|
|
4213
|
-
* 6. Default to Supabase as fallback
|
|
4214
|
-
*
|
|
4215
|
-
* @returns Detection result with recommendation and reasoning
|
|
4216
|
-
*/
|
|
4217
|
-
detect() {
|
|
4218
|
-
const result = this.performDetection();
|
|
4219
|
-
if (this.hasResultChanged(result)) {
|
|
4220
|
-
this.lastResult = result;
|
|
4221
|
-
this.notifyListeners(result);
|
|
4222
|
-
}
|
|
4223
|
-
return result;
|
|
4224
|
-
}
|
|
4225
|
-
/**
|
|
4226
|
-
* Detect backend availability WITHOUT notifying listeners.
|
|
4227
|
-
*
|
|
4228
|
-
* This method is safe to call during React's render phase because it does not
|
|
4229
|
-
* trigger state updates in other components. Use this method when you need to
|
|
4230
|
-
* check backend status synchronously during render (e.g., in getAdapter()).
|
|
4231
|
-
*
|
|
4232
|
-
* The regular detect() method with listener notifications should only be called
|
|
4233
|
-
* from useEffect or event handlers to avoid the React warning:
|
|
4234
|
-
* "Cannot update a component while rendering a different component"
|
|
4235
|
-
*
|
|
4236
|
-
* @returns Detection result with recommendation and reasoning
|
|
4237
|
-
*/
|
|
4238
|
-
detectSilent() {
|
|
4239
|
-
const result = this.performDetection();
|
|
4240
|
-
this.lastResult = result;
|
|
4241
|
-
return result;
|
|
4242
|
-
}
|
|
4243
|
-
/**
|
|
4244
|
-
* Internal detection logic shared by detect() and detectSilent().
|
|
4245
|
-
* @private
|
|
4246
|
-
*/
|
|
4247
|
-
performDetection() {
|
|
4248
|
-
const powerSyncStatus = this.detectPowerSyncStatus();
|
|
4249
|
-
const supabaseStatus = this.detectSupabaseStatus();
|
|
4250
|
-
const isOnline = this.checkOnlineStatus();
|
|
4251
|
-
let recommendedBackend;
|
|
4252
|
-
let reason;
|
|
4253
|
-
if (this.options.preferPowerSync && powerSyncStatus === "available" /* AVAILABLE */) {
|
|
4254
|
-
recommendedBackend = "powersync";
|
|
4255
|
-
reason = "PowerSync is available and preferred for offline-first experience";
|
|
4256
|
-
} else if (powerSyncStatus === "initializing" /* INITIALIZING */ && isOnline && supabaseStatus === "available" /* AVAILABLE */) {
|
|
4257
|
-
recommendedBackend = "supabase";
|
|
4258
|
-
reason = "PowerSync initial sync in progress; using Supabase for fresh data";
|
|
4259
|
-
} else if (supabaseStatus === "available" /* AVAILABLE */ && isOnline) {
|
|
4260
|
-
recommendedBackend = "supabase";
|
|
4261
|
-
reason = "Using Supabase direct connection";
|
|
4262
|
-
} else if (powerSyncStatus === "available" /* AVAILABLE */) {
|
|
4263
|
-
recommendedBackend = "powersync";
|
|
4264
|
-
reason = "Offline mode using PowerSync local data";
|
|
4265
|
-
} else if (!isOnline && this.powerSyncDb) {
|
|
4266
|
-
recommendedBackend = "powersync";
|
|
4267
|
-
reason = "Offline mode - using PowerSync local data (initial sync may be incomplete)";
|
|
4268
|
-
} else {
|
|
4269
|
-
recommendedBackend = "supabase";
|
|
4270
|
-
reason = "No confirmed available backend; defaulting to Supabase";
|
|
4271
|
-
}
|
|
4272
|
-
return {
|
|
4273
|
-
powerSyncStatus,
|
|
4274
|
-
supabaseStatus,
|
|
4275
|
-
recommendedBackend,
|
|
4276
|
-
isOnline,
|
|
4277
|
-
reason
|
|
4278
|
-
};
|
|
4279
|
-
}
|
|
4280
|
-
/**
|
|
4281
|
-
* Check if PowerSync is available.
|
|
4282
|
-
*
|
|
4283
|
-
* PowerSync status depends on:
|
|
4284
|
-
* 1. Database instance exists
|
|
4285
|
-
* 2. If useOnlineUntilSynced is true, also checks if initial sync completed
|
|
4286
|
-
*
|
|
4287
|
-
* @returns PowerSync backend status
|
|
4288
|
-
*/
|
|
4289
|
-
detectPowerSyncStatus() {
|
|
4290
|
-
if (!this.powerSyncDb) {
|
|
4291
|
-
return "unavailable" /* UNAVAILABLE */;
|
|
4292
|
-
}
|
|
4293
|
-
try {
|
|
4294
|
-
if (typeof this.powerSyncDb.getAll !== "function") {
|
|
4295
|
-
return "initializing" /* INITIALIZING */;
|
|
4296
|
-
}
|
|
4297
|
-
if (this.options.useOnlineUntilSynced) {
|
|
4298
|
-
if (!this.syncStatus || !this.syncStatus.hasSynced) {
|
|
4299
|
-
return "initializing" /* INITIALIZING */;
|
|
4300
|
-
}
|
|
4301
|
-
}
|
|
4302
|
-
return "available" /* AVAILABLE */;
|
|
4303
|
-
} catch {
|
|
4304
|
-
return "initializing" /* INITIALIZING */;
|
|
4305
|
-
}
|
|
4306
|
-
}
|
|
4307
|
-
/**
|
|
4308
|
-
* Check if Supabase is available.
|
|
4309
|
-
*
|
|
4310
|
-
* Supabase is considered available if we have a client instance.
|
|
4311
|
-
* The actual network connectivity is checked separately via checkOnlineStatus().
|
|
4312
|
-
*
|
|
4313
|
-
* @returns Supabase backend status
|
|
4314
|
-
*/
|
|
4315
|
-
detectSupabaseStatus() {
|
|
4316
|
-
if (!this.supabase) {
|
|
4317
|
-
return "unavailable" /* UNAVAILABLE */;
|
|
4318
|
-
}
|
|
4319
|
-
try {
|
|
4320
|
-
if (typeof this.supabase.from === "function") {
|
|
4321
|
-
return "available" /* AVAILABLE */;
|
|
4322
|
-
}
|
|
4323
|
-
return "unavailable" /* UNAVAILABLE */;
|
|
4324
|
-
} catch {
|
|
4325
|
-
return "unavailable" /* UNAVAILABLE */;
|
|
4326
|
-
}
|
|
4327
|
-
}
|
|
4328
|
-
/**
|
|
4329
|
-
* Check if device is online.
|
|
4330
|
-
*
|
|
4331
|
-
* Prefers the sync status's isOnline property if available (from React Native NetInfo).
|
|
4332
|
-
* Falls back to navigator.onLine in browser environments.
|
|
4333
|
-
* Returns false by default for non-browser environments (React Native, Node.js, etc.)
|
|
4334
|
-
* to ensure offline-first behavior works correctly.
|
|
4335
|
-
*
|
|
4336
|
-
* @returns Whether the device has network connectivity
|
|
4337
|
-
*/
|
|
4338
|
-
checkOnlineStatus() {
|
|
4339
|
-
if (this.syncStatus?.isOnline !== void 0) {
|
|
4340
|
-
return this.syncStatus.isOnline;
|
|
4341
|
-
}
|
|
4342
|
-
if (typeof window !== "undefined" && typeof navigator !== "undefined") {
|
|
4343
|
-
return navigator.onLine;
|
|
4344
|
-
}
|
|
4345
|
-
return false;
|
|
4346
|
-
}
|
|
4347
|
-
// ===========================================================================
|
|
4348
|
-
// Instance Management Methods
|
|
4349
|
-
// ===========================================================================
|
|
4350
|
-
/**
|
|
4351
|
-
* Update PowerSync instance (e.g., when it becomes available).
|
|
4352
|
-
*
|
|
4353
|
-
* @param db - New PowerSync database instance or null
|
|
4354
|
-
*/
|
|
4355
|
-
setPowerSync(db) {
|
|
4356
|
-
this.powerSyncDb = db;
|
|
4357
|
-
}
|
|
4358
|
-
/**
|
|
4359
|
-
* Update Supabase instance.
|
|
4360
|
-
*
|
|
4361
|
-
* @param supabase - New Supabase client instance or null
|
|
4362
|
-
*/
|
|
4363
|
-
setSupabase(supabase) {
|
|
4364
|
-
this.supabase = supabase;
|
|
4365
|
-
}
|
|
4366
|
-
/**
|
|
4367
|
-
* Get current PowerSync instance.
|
|
4368
|
-
*
|
|
4369
|
-
* @returns Current PowerSync database instance or null
|
|
4370
|
-
*/
|
|
4371
|
-
getPowerSync() {
|
|
4372
|
-
return this.powerSyncDb;
|
|
4373
|
-
}
|
|
4374
|
-
/**
|
|
4375
|
-
* Get current Supabase instance.
|
|
4376
|
-
*
|
|
4377
|
-
* @returns Current Supabase client instance or null
|
|
4378
|
-
*/
|
|
4379
|
-
getSupabase() {
|
|
4380
|
-
return this.supabase;
|
|
4381
|
-
}
|
|
4382
|
-
// ===========================================================================
|
|
4383
|
-
// Options Management
|
|
4384
|
-
// ===========================================================================
|
|
4385
|
-
/**
|
|
4386
|
-
* Update detector options.
|
|
4387
|
-
*
|
|
4388
|
-
* @param options - New options to merge with existing
|
|
4389
|
-
*/
|
|
4390
|
-
setOptions(options) {
|
|
4391
|
-
this.options = {
|
|
4392
|
-
...this.options,
|
|
4393
|
-
...options
|
|
4394
|
-
};
|
|
4395
|
-
}
|
|
4396
|
-
/**
|
|
4397
|
-
* Get current detector options.
|
|
4398
|
-
*
|
|
4399
|
-
* @returns Current detector options
|
|
4400
|
-
*/
|
|
4401
|
-
getOptions() {
|
|
4402
|
-
return {
|
|
4403
|
-
...this.options
|
|
4404
|
-
};
|
|
4405
|
-
}
|
|
4406
|
-
// ===========================================================================
|
|
4407
|
-
// Listener Management
|
|
4408
|
-
// ===========================================================================
|
|
4409
|
-
/**
|
|
4410
|
-
* Add a listener for backend change events.
|
|
4411
|
-
*
|
|
4412
|
-
* @param listener - Callback to invoke when detection result changes
|
|
4413
|
-
* @returns Function to remove the listener
|
|
4414
|
-
*/
|
|
4415
|
-
addListener(listener) {
|
|
4416
|
-
this.listeners.add(listener);
|
|
4417
|
-
return () => this.listeners.delete(listener);
|
|
4418
|
-
}
|
|
4419
|
-
/**
|
|
4420
|
-
* Remove a listener for backend change events.
|
|
4421
|
-
*
|
|
4422
|
-
* @param listener - Listener to remove
|
|
4423
|
-
*/
|
|
4424
|
-
removeListener(listener) {
|
|
4425
|
-
this.listeners.delete(listener);
|
|
4426
|
-
}
|
|
4427
|
-
/**
|
|
4428
|
-
* Get the last detection result.
|
|
4429
|
-
*
|
|
4430
|
-
* @returns Last detection result or null if never detected
|
|
4431
|
-
*/
|
|
4432
|
-
getLastResult() {
|
|
4433
|
-
return this.lastResult;
|
|
4434
|
-
}
|
|
4435
|
-
// ===========================================================================
|
|
4436
|
-
// Private Helper Methods
|
|
4437
|
-
// ===========================================================================
|
|
4438
|
-
/**
|
|
4439
|
-
* Check if the detection result has changed from the last result.
|
|
4440
|
-
*/
|
|
4441
|
-
hasResultChanged(result) {
|
|
4442
|
-
if (!this.lastResult) {
|
|
4443
|
-
return true;
|
|
4444
|
-
}
|
|
4445
|
-
return this.lastResult.powerSyncStatus !== result.powerSyncStatus || this.lastResult.supabaseStatus !== result.supabaseStatus || this.lastResult.recommendedBackend !== result.recommendedBackend || this.lastResult.isOnline !== result.isOnline;
|
|
4446
|
-
}
|
|
4447
|
-
/**
|
|
4448
|
-
* Notify all listeners of a detection result change.
|
|
4449
|
-
*/
|
|
4450
|
-
notifyListeners(result) {
|
|
4451
|
-
const isDev = typeof __DEV__ !== "undefined" ? __DEV__ : process.env.NODE_ENV !== "production";
|
|
4452
|
-
if (isDev) {
|
|
4453
|
-
const prevBackend = this.lastResult?.recommendedBackend;
|
|
4454
|
-
if (prevBackend && prevBackend !== result.recommendedBackend) {
|
|
4455
|
-
console.log(`[DataLayer] Backend switched: ${prevBackend} \u2192 ${result.recommendedBackend}`, `| Reason: ${result.reason}`);
|
|
4456
|
-
}
|
|
4457
|
-
if (result.recommendedBackend === "supabase" && this.options.preferPowerSync) {
|
|
4458
|
-
console.log(`[DataLayer] Using online fallback (Supabase)`, `| PowerSync: ${result.powerSyncStatus}`, `| Online: ${result.isOnline}`, `| Reason: ${result.reason}`);
|
|
4459
|
-
}
|
|
4460
|
-
}
|
|
4461
|
-
Array.from(this.listeners).forEach((listener) => {
|
|
4462
|
-
try {
|
|
4463
|
-
listener(result);
|
|
4464
|
-
} catch (error) {
|
|
4465
|
-
console.error("Error in backend change listener:", error);
|
|
4466
|
-
}
|
|
4467
|
-
});
|
|
4468
|
-
}
|
|
4469
|
-
};
|
|
4470
|
-
function createAdapterAutoDetector(powerSyncDb, supabase, options) {
|
|
4471
|
-
return new AdapterAutoDetector(powerSyncDb, supabase, options);
|
|
4472
|
-
}
|
|
4473
|
-
|
|
4474
4146
|
// src/adapters/sync-tracking-adapter.ts
|
|
4475
4147
|
var DEBUG_SYNC_TRACKING_VERBOSE = false;
|
|
4476
4148
|
function debugLog(message, ...args) {
|
|
@@ -4619,500 +4291,815 @@ var AdapterRegistry = class {
|
|
|
4619
4291
|
this.config = config;
|
|
4620
4292
|
}
|
|
4621
4293
|
/**
|
|
4622
|
-
* Cache of created adapters by table name
|
|
4294
|
+
* Cache of created adapters by table name
|
|
4295
|
+
*/
|
|
4296
|
+
adapters = /* @__PURE__ */ new Map();
|
|
4297
|
+
/**
|
|
4298
|
+
* PowerSync adapter instance (set during initialization or lazily via getter)
|
|
4299
|
+
*/
|
|
4300
|
+
powerSyncAdapter = null;
|
|
4301
|
+
/**
|
|
4302
|
+
* Getter function to retrieve PowerSync database instance.
|
|
4303
|
+
* Used for lazy initialization when PowerSync becomes available after registry creation.
|
|
4304
|
+
*/
|
|
4305
|
+
powerSyncGetter = null;
|
|
4306
|
+
/**
|
|
4307
|
+
* Factory function to create PowerSync adapter from database instance.
|
|
4308
|
+
* Set via setPowerSyncGetter along with the getter.
|
|
4309
|
+
*/
|
|
4310
|
+
powerSyncAdapterFactory = null;
|
|
4311
|
+
/**
|
|
4312
|
+
* Supabase adapter instance (set during initialization)
|
|
4313
|
+
*/
|
|
4314
|
+
supabaseAdapter = null;
|
|
4315
|
+
/**
|
|
4316
|
+
* Cached adapter instance (wraps Supabase with TanStack Query)
|
|
4317
|
+
*/
|
|
4318
|
+
cachedAdapter = null;
|
|
4319
|
+
/**
|
|
4320
|
+
* Dependencies for creating adapters
|
|
4321
|
+
*/
|
|
4322
|
+
deps = null;
|
|
4323
|
+
/**
|
|
4324
|
+
* Whether the registry has been initialized with adapters
|
|
4325
|
+
*/
|
|
4326
|
+
_isInitialized = false;
|
|
4327
|
+
/**
|
|
4328
|
+
* Auto-detector instance for automatic backend selection
|
|
4329
|
+
*/
|
|
4330
|
+
autoDetector = null;
|
|
4331
|
+
/**
|
|
4332
|
+
* Sync tracker for mutation tracking
|
|
4333
|
+
*/
|
|
4334
|
+
syncTracker = null;
|
|
4335
|
+
/**
|
|
4336
|
+
* Sync tracking adapter that wraps PowerSync adapter
|
|
4337
|
+
*/
|
|
4338
|
+
syncTrackingAdapter = null;
|
|
4339
|
+
/**
|
|
4340
|
+
* Listeners for backend change events
|
|
4341
|
+
*/
|
|
4342
|
+
backendChangeListeners = /* @__PURE__ */ new Set();
|
|
4343
|
+
/**
|
|
4344
|
+
* Last auto-detection result for debugging and status
|
|
4345
|
+
*/
|
|
4346
|
+
lastDetectionResult = null;
|
|
4347
|
+
// ===========================================================================
|
|
4348
|
+
// Initialization
|
|
4349
|
+
// ===========================================================================
|
|
4350
|
+
/**
|
|
4351
|
+
* Check if the registry has been initialized
|
|
4352
|
+
*/
|
|
4353
|
+
get isInitialized() {
|
|
4354
|
+
return this._isInitialized;
|
|
4355
|
+
}
|
|
4356
|
+
/**
|
|
4357
|
+
* Initialize the registry with dependencies.
|
|
4358
|
+
* Called by DataLayerProvider when PowerSync and Supabase are ready.
|
|
4359
|
+
*
|
|
4360
|
+
* @param deps - Dependencies needed to create adapters
|
|
4361
|
+
*/
|
|
4362
|
+
initialize(deps) {
|
|
4363
|
+
this.deps = deps;
|
|
4364
|
+
this._isInitialized = true;
|
|
4365
|
+
}
|
|
4366
|
+
/**
|
|
4367
|
+
* Set the PowerSync adapter instance
|
|
4368
|
+
*
|
|
4369
|
+
* @param adapter - PowerSync adapter implementation
|
|
4370
|
+
*/
|
|
4371
|
+
setPowerSyncAdapter(adapter) {
|
|
4372
|
+
this.powerSyncAdapter = adapter;
|
|
4373
|
+
if (this.syncTracker) {
|
|
4374
|
+
this.syncTrackingAdapter = new SyncTrackingAdapter(adapter, this.syncTracker);
|
|
4375
|
+
} else {
|
|
4376
|
+
this.syncTrackingAdapter = null;
|
|
4377
|
+
}
|
|
4378
|
+
this.adapters.clear();
|
|
4379
|
+
}
|
|
4380
|
+
/**
|
|
4381
|
+
* Set a getter function for lazy PowerSync adapter initialization.
|
|
4382
|
+
*
|
|
4383
|
+
* This allows the registry to lazily create the PowerSync adapter when
|
|
4384
|
+
* PowerSync becomes available, rather than requiring it at initialization time.
|
|
4385
|
+
* Solves race conditions where queries/mutations run before PowerSync is ready.
|
|
4386
|
+
*
|
|
4387
|
+
* @param getter - Function that returns the PowerSync database instance (or null if not ready)
|
|
4388
|
+
* @param factory - Function that creates a TableDataAdapter from the database instance
|
|
4389
|
+
*/
|
|
4390
|
+
setPowerSyncGetter(getter, factory) {
|
|
4391
|
+
this.powerSyncGetter = getter;
|
|
4392
|
+
this.powerSyncAdapterFactory = factory;
|
|
4393
|
+
}
|
|
4394
|
+
/**
|
|
4395
|
+
* Ensure PowerSync adapter is initialized if available.
|
|
4396
|
+
*
|
|
4397
|
+
* Checks the getter (if set) and lazily creates the adapter if PowerSync
|
|
4398
|
+
* is now available. Also updates the auto-detector with the new reference.
|
|
4399
|
+
*
|
|
4400
|
+
* @returns true if PowerSync adapter is available, false otherwise
|
|
4401
|
+
*/
|
|
4402
|
+
ensurePowerSyncAdapter() {
|
|
4403
|
+
if (this.powerSyncAdapter) {
|
|
4404
|
+
return true;
|
|
4405
|
+
}
|
|
4406
|
+
if (!this.powerSyncGetter || !this.powerSyncAdapterFactory) {
|
|
4407
|
+
return false;
|
|
4408
|
+
}
|
|
4409
|
+
const db = this.powerSyncGetter();
|
|
4410
|
+
if (!db) {
|
|
4411
|
+
return false;
|
|
4412
|
+
}
|
|
4413
|
+
const adapter = this.powerSyncAdapterFactory(db);
|
|
4414
|
+
this.setPowerSyncAdapter(adapter);
|
|
4415
|
+
if (this.autoDetector) {
|
|
4416
|
+
this.autoDetector.setPowerSyncDb(db);
|
|
4417
|
+
}
|
|
4418
|
+
return true;
|
|
4419
|
+
}
|
|
4420
|
+
/**
|
|
4421
|
+
* Set the sync tracker for mutation tracking.
|
|
4422
|
+
* When set, write operations on PowerSync tables will
|
|
4423
|
+
* automatically track pending mutations.
|
|
4424
|
+
*
|
|
4425
|
+
* NOTE: This also clears the adapter cache to ensure any
|
|
4426
|
+
* previously cached adapters (which may have been created
|
|
4427
|
+
* before the sync tracker was available) are recreated
|
|
4428
|
+
* with the new sync tracker on next access.
|
|
4429
|
+
*/
|
|
4430
|
+
setSyncTracker(tracker) {
|
|
4431
|
+
this.syncTracker = tracker;
|
|
4432
|
+
if (this.powerSyncAdapter) {
|
|
4433
|
+
this.syncTrackingAdapter = new SyncTrackingAdapter(this.powerSyncAdapter, tracker);
|
|
4434
|
+
}
|
|
4435
|
+
this.adapters.clear();
|
|
4436
|
+
}
|
|
4437
|
+
/**
|
|
4438
|
+
* Set the Supabase adapter instance
|
|
4439
|
+
*
|
|
4440
|
+
* @param adapter - Supabase adapter implementation
|
|
4441
|
+
*/
|
|
4442
|
+
setSupabaseAdapter(adapter) {
|
|
4443
|
+
this.supabaseAdapter = adapter;
|
|
4444
|
+
}
|
|
4445
|
+
/**
|
|
4446
|
+
* Set the Cached adapter instance
|
|
4447
|
+
*
|
|
4448
|
+
* @param adapter - Cached adapter implementation
|
|
4449
|
+
*/
|
|
4450
|
+
setCachedAdapter(adapter) {
|
|
4451
|
+
this.cachedAdapter = adapter;
|
|
4452
|
+
}
|
|
4453
|
+
/**
|
|
4454
|
+
* Initialize auto-detection with a detector instance
|
|
4455
|
+
*
|
|
4456
|
+
* @param detector - The auto-detector to use
|
|
4457
|
+
*/
|
|
4458
|
+
initializeAutoDetection(detector) {
|
|
4459
|
+
this.autoDetector = detector;
|
|
4460
|
+
detector.addListener((result) => {
|
|
4461
|
+
this.lastDetectionResult = result;
|
|
4462
|
+
this.notifyBackendChange(result.recommendedBackend);
|
|
4463
|
+
});
|
|
4464
|
+
}
|
|
4465
|
+
// ===========================================================================
|
|
4466
|
+
// Adapter Access
|
|
4467
|
+
// ===========================================================================
|
|
4468
|
+
/**
|
|
4469
|
+
* Get the appropriate adapter for a table based on configuration.
|
|
4470
|
+
*
|
|
4471
|
+
* The adapter is selected based on the table's strategy in config.tables:
|
|
4472
|
+
* - "powersync": Returns PowerSyncAdapter
|
|
4473
|
+
* - "supabase": Returns SupabaseAdapter
|
|
4474
|
+
* - "cached": Returns CachedAdapter (wrapping Supabase)
|
|
4475
|
+
* - "hybrid": Returns HybridAdapter (combining PowerSync + Cached)
|
|
4476
|
+
* - "auto": Uses auto-detection to select the best backend
|
|
4477
|
+
*
|
|
4478
|
+
* For tables not in config, defaults to auto-detection if available,
|
|
4479
|
+
* otherwise falls back to SupabaseAdapter.
|
|
4480
|
+
*
|
|
4481
|
+
* Supports schema-qualified table names:
|
|
4482
|
+
* - "core.Profile" looks up config["core.Profile"] first, then config["Profile"]
|
|
4483
|
+
* - Auto-generates PowerSync alias as "CoreProfile" if not explicitly set
|
|
4484
|
+
*
|
|
4485
|
+
* @param table - The table name (may be schema-qualified like "core.Profile")
|
|
4486
|
+
* @param operation - The operation type: 'read' uses fallback logic during init, 'write' always uses PowerSync for powersync tables
|
|
4487
|
+
* @returns The appropriate adapter for the table
|
|
4488
|
+
* @throws Error if adapters are not initialized
|
|
4489
|
+
*/
|
|
4490
|
+
getAdapter(table, _operation = "read") {
|
|
4491
|
+
this.ensurePowerSyncAdapter();
|
|
4492
|
+
const tableWithoutSchema = table.includes(".") ? table.split(".")[1] : table;
|
|
4493
|
+
const strategy = this.config.tables[table] ?? this.config.tables[tableWithoutSchema];
|
|
4494
|
+
if (!strategy || strategy.strategy === "auto") {
|
|
4495
|
+
if (this.powerSyncAdapter) {
|
|
4496
|
+
const powerSyncTableKeys = this.getPowerSyncTableKeys();
|
|
4497
|
+
const isConfigured = powerSyncTableKeys.some((key) => key === table || key === tableWithoutSchema || key.includes(".") && key.split(".")[1] === tableWithoutSchema);
|
|
4498
|
+
if (!isConfigured) {
|
|
4499
|
+
if (this.supabaseAdapter) {
|
|
4500
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
4501
|
+
console.warn(`[AdapterRegistry] Table "${table}" is not configured for PowerSync sync. Using Supabase fallback.`);
|
|
4502
|
+
}
|
|
4503
|
+
return this.supabaseAdapter;
|
|
4504
|
+
}
|
|
4505
|
+
throw new Error(`Table "${table}" is not configured for PowerSync sync and Supabase adapter is not available. Either add this table to the PowerSync schema or initialize the Supabase adapter.`);
|
|
4506
|
+
}
|
|
4507
|
+
}
|
|
4508
|
+
return this.getAutoAdapter(strategy, table);
|
|
4509
|
+
}
|
|
4510
|
+
if (strategy.strategy === "powersync") {
|
|
4511
|
+
if (this.powerSyncAdapter) {
|
|
4512
|
+
return this.syncTrackingAdapter ?? this.powerSyncAdapter;
|
|
4513
|
+
}
|
|
4514
|
+
if (this.supabaseAdapter) {
|
|
4515
|
+
const isOnline = this.autoDetector?.getSyncStatus()?.isOnline ?? (typeof navigator !== "undefined" ? navigator.onLine : false);
|
|
4516
|
+
if (isOnline) {
|
|
4517
|
+
return this.supabaseAdapter;
|
|
4518
|
+
}
|
|
4519
|
+
throw new Error(`Offline and PowerSync not ready for table "${table}". Wait for initialization.`);
|
|
4520
|
+
}
|
|
4521
|
+
}
|
|
4522
|
+
const existing = this.adapters.get(table);
|
|
4523
|
+
if (existing) {
|
|
4524
|
+
return existing;
|
|
4525
|
+
}
|
|
4526
|
+
const adapter = this.createAdapter(strategy);
|
|
4527
|
+
this.adapters.set(table, adapter);
|
|
4528
|
+
return adapter;
|
|
4529
|
+
}
|
|
4530
|
+
/**
|
|
4531
|
+
* Get the PowerSync adapter directly
|
|
4532
|
+
*
|
|
4533
|
+
* @returns PowerSync adapter or null if not initialized
|
|
4534
|
+
*/
|
|
4535
|
+
getPowerSyncAdapter() {
|
|
4536
|
+
return this.powerSyncAdapter;
|
|
4537
|
+
}
|
|
4538
|
+
/**
|
|
4539
|
+
* Get the Supabase adapter directly
|
|
4540
|
+
*
|
|
4541
|
+
* @returns Supabase adapter or null if not initialized
|
|
4542
|
+
*/
|
|
4543
|
+
getSupabaseAdapter() {
|
|
4544
|
+
return this.supabaseAdapter;
|
|
4545
|
+
}
|
|
4546
|
+
/**
|
|
4547
|
+
* Get the Cached adapter directly
|
|
4548
|
+
*
|
|
4549
|
+
* @returns Cached adapter or null if not initialized
|
|
4623
4550
|
*/
|
|
4624
|
-
|
|
4551
|
+
getCachedAdapter() {
|
|
4552
|
+
return this.cachedAdapter;
|
|
4553
|
+
}
|
|
4625
4554
|
/**
|
|
4626
|
-
*
|
|
4555
|
+
* Get all configured table names
|
|
4556
|
+
*
|
|
4557
|
+
* @returns Array of table names with explicit strategy configuration
|
|
4627
4558
|
*/
|
|
4628
|
-
|
|
4559
|
+
getConfiguredTables() {
|
|
4560
|
+
return Object.keys(this.config.tables);
|
|
4561
|
+
}
|
|
4629
4562
|
/**
|
|
4630
|
-
*
|
|
4631
|
-
*
|
|
4563
|
+
* Get the strategy for a specific table
|
|
4564
|
+
*
|
|
4565
|
+
* @param table - The table name (may include schema prefix like "core.Profile")
|
|
4566
|
+
* @returns The table strategy or undefined if not configured
|
|
4632
4567
|
*/
|
|
4633
|
-
|
|
4568
|
+
getTableStrategy(table) {
|
|
4569
|
+
const tableWithoutSchema = table.includes(".") ? table.split(".")[1] : table;
|
|
4570
|
+
return this.config.tables[table] ?? this.config.tables[tableWithoutSchema];
|
|
4571
|
+
}
|
|
4634
4572
|
/**
|
|
4635
|
-
*
|
|
4636
|
-
*
|
|
4573
|
+
* Check if a table uses PowerSync strategy
|
|
4574
|
+
*
|
|
4575
|
+
* @param table - The table name (may include schema prefix like "core.Profile")
|
|
4576
|
+
* @returns True if table uses PowerSync, Hybrid, or Auto strategy
|
|
4637
4577
|
*/
|
|
4638
|
-
|
|
4578
|
+
usesPowerSync(table) {
|
|
4579
|
+
const strategy = this.getTableStrategy(table);
|
|
4580
|
+
return strategy?.strategy === "powersync" || strategy?.strategy === "hybrid" || strategy?.strategy === "auto";
|
|
4581
|
+
}
|
|
4639
4582
|
/**
|
|
4640
|
-
*
|
|
4583
|
+
* Get all table config keys that use PowerSync (may be schema-qualified)
|
|
4584
|
+
*
|
|
4585
|
+
* @returns Array of config keys using PowerSync, Hybrid, or Auto strategy
|
|
4641
4586
|
*/
|
|
4642
|
-
|
|
4587
|
+
getPowerSyncTableKeys() {
|
|
4588
|
+
return Object.entries(this.config.tables).filter(([_, strategy]) => strategy.strategy === "powersync" || strategy.strategy === "hybrid" || strategy.strategy === "auto").map(([key]) => key);
|
|
4589
|
+
}
|
|
4643
4590
|
/**
|
|
4644
|
-
*
|
|
4591
|
+
* Get all tables that use PowerSync (returns aliases for PowerSync schema)
|
|
4592
|
+
*
|
|
4593
|
+
* @returns Array of PowerSync table aliases
|
|
4645
4594
|
*/
|
|
4646
|
-
|
|
4595
|
+
getPowerSyncTables() {
|
|
4596
|
+
return Object.entries(this.config.tables).filter(([_, strategy]) => strategy.strategy === "powersync" || strategy.strategy === "hybrid" || strategy.strategy === "auto").map(([key, strategy]) => getPowerSyncAlias(key, strategy));
|
|
4597
|
+
}
|
|
4647
4598
|
/**
|
|
4648
|
-
*
|
|
4599
|
+
* Get the PowerSync alias for a table.
|
|
4600
|
+
* Uses explicit alias from config if set, otherwise auto-generates.
|
|
4601
|
+
*
|
|
4602
|
+
* @param table - Table name (may be schema-qualified)
|
|
4603
|
+
* @returns The alias to use in PowerSync schema
|
|
4649
4604
|
*/
|
|
4650
|
-
|
|
4605
|
+
getTableAlias(table) {
|
|
4606
|
+
const strategy = this.getTableStrategy(table);
|
|
4607
|
+
const tableWithoutSchema = table.includes(".") ? table.split(".")[1] : table;
|
|
4608
|
+
const configKey = this.config.tables[table] ? table : tableWithoutSchema;
|
|
4609
|
+
return getPowerSyncAlias(configKey, strategy);
|
|
4610
|
+
}
|
|
4611
|
+
// ===========================================================================
|
|
4612
|
+
// Auto-Detection Methods
|
|
4613
|
+
// ===========================================================================
|
|
4651
4614
|
/**
|
|
4652
|
-
*
|
|
4615
|
+
* Get adapter using auto-detection.
|
|
4616
|
+
*
|
|
4617
|
+
* Simple logic: PowerSync first, Supabase fallback ONLY when online.
|
|
4618
|
+
* Once PowerSync is initialized, always use it for offline-first behavior.
|
|
4653
4619
|
*/
|
|
4654
|
-
|
|
4620
|
+
getAutoAdapter(_strategy, _table) {
|
|
4621
|
+
if (this.powerSyncAdapter) {
|
|
4622
|
+
return this.syncTrackingAdapter ?? this.powerSyncAdapter;
|
|
4623
|
+
}
|
|
4624
|
+
if (this.supabaseAdapter) {
|
|
4625
|
+
const isOnline = this.autoDetector?.getSyncStatus()?.isOnline ?? (typeof navigator !== "undefined" ? navigator.onLine : false);
|
|
4626
|
+
if (isOnline) {
|
|
4627
|
+
return this.supabaseAdapter;
|
|
4628
|
+
}
|
|
4629
|
+
throw new Error("Offline and PowerSync not ready. Wait for PowerSync initialization.");
|
|
4630
|
+
}
|
|
4631
|
+
throw new Error("No adapters available. Initialize PowerSync or Supabase.");
|
|
4632
|
+
}
|
|
4655
4633
|
/**
|
|
4656
|
-
*
|
|
4634
|
+
* Subscribe to backend changes
|
|
4635
|
+
*
|
|
4636
|
+
* @param callback - Function called when recommended backend changes
|
|
4637
|
+
* @returns Unsubscribe function
|
|
4657
4638
|
*/
|
|
4658
|
-
|
|
4639
|
+
onBackendChange(callback) {
|
|
4640
|
+
this.backendChangeListeners.add(callback);
|
|
4641
|
+
return () => {
|
|
4642
|
+
this.backendChangeListeners.delete(callback);
|
|
4643
|
+
};
|
|
4644
|
+
}
|
|
4659
4645
|
/**
|
|
4660
|
-
*
|
|
4646
|
+
* Notify listeners of backend change
|
|
4647
|
+
*
|
|
4648
|
+
* @param backend - The new recommended backend
|
|
4661
4649
|
*/
|
|
4662
|
-
|
|
4650
|
+
notifyBackendChange(backend) {
|
|
4651
|
+
this.backendChangeListeners.forEach((callback) => {
|
|
4652
|
+
try {
|
|
4653
|
+
callback(backend);
|
|
4654
|
+
} catch (error) {
|
|
4655
|
+
console.error("Error in backend change listener:", error);
|
|
4656
|
+
}
|
|
4657
|
+
});
|
|
4658
|
+
}
|
|
4663
4659
|
/**
|
|
4664
|
-
*
|
|
4660
|
+
* Get the last auto-detection result
|
|
4661
|
+
*
|
|
4662
|
+
* @returns Last detection result or null if never detected
|
|
4665
4663
|
*/
|
|
4666
|
-
|
|
4664
|
+
getLastDetectionResult() {
|
|
4665
|
+
return this.lastDetectionResult;
|
|
4666
|
+
}
|
|
4667
4667
|
/**
|
|
4668
|
-
*
|
|
4668
|
+
* Get the auto-detector instance
|
|
4669
|
+
*
|
|
4670
|
+
* @returns Auto-detector instance or null if not initialized
|
|
4669
4671
|
*/
|
|
4670
|
-
|
|
4672
|
+
getAutoDetector() {
|
|
4673
|
+
return this.autoDetector;
|
|
4674
|
+
}
|
|
4675
|
+
// ===========================================================================
|
|
4676
|
+
// Private Methods
|
|
4677
|
+
// ===========================================================================
|
|
4671
4678
|
/**
|
|
4672
|
-
*
|
|
4679
|
+
* Create an adapter based on the strategy type
|
|
4680
|
+
*
|
|
4681
|
+
* @param strategy - The table strategy configuration
|
|
4682
|
+
* @returns The created adapter
|
|
4683
|
+
* @throws Error if the required base adapter is not initialized
|
|
4673
4684
|
*/
|
|
4674
|
-
|
|
4685
|
+
createAdapter(strategy) {
|
|
4686
|
+
switch (strategy.strategy) {
|
|
4687
|
+
case "powersync":
|
|
4688
|
+
if (!this.powerSyncAdapter) {
|
|
4689
|
+
if (this.supabaseAdapter) {
|
|
4690
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
4691
|
+
console.warn(`[AdapterRegistry] PowerSync not ready, falling back to Supabase`);
|
|
4692
|
+
}
|
|
4693
|
+
return this.supabaseAdapter;
|
|
4694
|
+
}
|
|
4695
|
+
throw new Error("PowerSync adapter not initialized and Supabase not available. Ensure PowerSyncAdapter is set before accessing PowerSync tables.");
|
|
4696
|
+
}
|
|
4697
|
+
return this.syncTrackingAdapter ?? this.powerSyncAdapter;
|
|
4698
|
+
case "supabase":
|
|
4699
|
+
if (!this.supabaseAdapter) {
|
|
4700
|
+
throw new Error("Supabase adapter not initialized. Ensure SupabaseAdapter is set before accessing Supabase tables.");
|
|
4701
|
+
}
|
|
4702
|
+
return this.supabaseAdapter;
|
|
4703
|
+
case "cached":
|
|
4704
|
+
if (this.cachedAdapter) {
|
|
4705
|
+
return this.cachedAdapter;
|
|
4706
|
+
}
|
|
4707
|
+
throw new Error("CachedAdapter not yet implemented. This feature will be available in Wave 2. For now, use 'supabase' strategy as a fallback.");
|
|
4708
|
+
case "hybrid":
|
|
4709
|
+
throw new Error("HybridAdapter not yet implemented. This feature will be available in Wave 2. For now, use 'powersync' or 'supabase' strategy as a fallback.");
|
|
4710
|
+
default:
|
|
4711
|
+
if (!this.supabaseAdapter) {
|
|
4712
|
+
throw new Error("Supabase adapter not initialized. Ensure SupabaseAdapter is set before accessing tables.");
|
|
4713
|
+
}
|
|
4714
|
+
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
4715
|
+
console.warn(`[AdapterRegistry] Unknown strategy "${strategy.strategy}". Using Supabase fallback.`);
|
|
4716
|
+
}
|
|
4717
|
+
return this.supabaseAdapter;
|
|
4718
|
+
}
|
|
4719
|
+
}
|
|
4675
4720
|
// ===========================================================================
|
|
4676
|
-
//
|
|
4721
|
+
// Utility Methods
|
|
4677
4722
|
// ===========================================================================
|
|
4678
4723
|
/**
|
|
4679
|
-
*
|
|
4724
|
+
* Clear all cached adapters.
|
|
4725
|
+
* Useful when configuration changes and adapters need to be recreated.
|
|
4680
4726
|
*/
|
|
4681
|
-
|
|
4682
|
-
|
|
4727
|
+
clearCache() {
|
|
4728
|
+
this.adapters.clear();
|
|
4683
4729
|
}
|
|
4684
4730
|
/**
|
|
4685
|
-
*
|
|
4686
|
-
*
|
|
4687
|
-
*
|
|
4688
|
-
* @param deps - Dependencies needed to create adapters
|
|
4731
|
+
* Reset the registry to uninitialized state.
|
|
4732
|
+
* Used during cleanup or testing.
|
|
4689
4733
|
*/
|
|
4690
|
-
|
|
4691
|
-
this.
|
|
4692
|
-
this.
|
|
4734
|
+
reset() {
|
|
4735
|
+
this.adapters.clear();
|
|
4736
|
+
this.powerSyncAdapter = null;
|
|
4737
|
+
this.powerSyncGetter = null;
|
|
4738
|
+
this.powerSyncAdapterFactory = null;
|
|
4739
|
+
this.supabaseAdapter = null;
|
|
4740
|
+
this.cachedAdapter = null;
|
|
4741
|
+
this.deps = null;
|
|
4742
|
+
this._isInitialized = false;
|
|
4743
|
+
this.autoDetector = null;
|
|
4744
|
+
this.backendChangeListeners.clear();
|
|
4745
|
+
this.lastDetectionResult = null;
|
|
4746
|
+
this.syncTracker = null;
|
|
4747
|
+
this.syncTrackingAdapter = null;
|
|
4693
4748
|
}
|
|
4694
4749
|
/**
|
|
4695
|
-
*
|
|
4696
|
-
*
|
|
4697
|
-
* @param adapter - PowerSync adapter implementation
|
|
4750
|
+
* Dispose all adapters and clean up resources.
|
|
4751
|
+
* Called when the DataLayerProvider unmounts.
|
|
4698
4752
|
*/
|
|
4699
|
-
|
|
4700
|
-
this.
|
|
4701
|
-
if (this.syncTracker) {
|
|
4702
|
-
this.syncTrackingAdapter = new SyncTrackingAdapter(adapter, this.syncTracker);
|
|
4703
|
-
} else {
|
|
4704
|
-
this.syncTrackingAdapter = null;
|
|
4705
|
-
}
|
|
4706
|
-
this.adapters.clear();
|
|
4753
|
+
dispose() {
|
|
4754
|
+
this.reset();
|
|
4707
4755
|
}
|
|
4708
4756
|
/**
|
|
4709
|
-
*
|
|
4710
|
-
*
|
|
4711
|
-
* This allows the registry to lazily create the PowerSync adapter when
|
|
4712
|
-
* PowerSync becomes available, rather than requiring it at initialization time.
|
|
4713
|
-
* Solves race conditions where queries/mutations run before PowerSync is ready.
|
|
4714
|
-
*
|
|
4715
|
-
* @param getter - Function that returns the PowerSync database instance (or null if not ready)
|
|
4716
|
-
* @param factory - Function that creates a TableDataAdapter from the database instance
|
|
4757
|
+
* Get debug information about the registry state
|
|
4717
4758
|
*/
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
|
|
4759
|
+
getDebugInfo() {
|
|
4760
|
+
return {
|
|
4761
|
+
isInitialized: this._isInitialized,
|
|
4762
|
+
hasPowerSync: this.powerSyncAdapter !== null,
|
|
4763
|
+
hasSupabase: this.supabaseAdapter !== null,
|
|
4764
|
+
hasCached: this.cachedAdapter !== null,
|
|
4765
|
+
cachedAdapterCount: this.adapters.size,
|
|
4766
|
+
configuredTableCount: Object.keys(this.config.tables).length,
|
|
4767
|
+
powerSyncTables: this.getPowerSyncTables(),
|
|
4768
|
+
hasAutoDetector: this.autoDetector !== null,
|
|
4769
|
+
lastDetectionResult: this.lastDetectionResult
|
|
4770
|
+
};
|
|
4771
|
+
}
|
|
4772
|
+
};
|
|
4773
|
+
function createAdapterRegistry(config) {
|
|
4774
|
+
return new AdapterRegistry(config);
|
|
4775
|
+
}
|
|
4776
|
+
|
|
4777
|
+
// src/adapters/auto-detector.ts
|
|
4778
|
+
var BackendStatus = /* @__PURE__ */ ((BackendStatus2) => {
|
|
4779
|
+
BackendStatus2["AVAILABLE"] = "available";
|
|
4780
|
+
BackendStatus2["INITIALIZING"] = "initializing";
|
|
4781
|
+
BackendStatus2["UNAVAILABLE"] = "unavailable";
|
|
4782
|
+
return BackendStatus2;
|
|
4783
|
+
})(BackendStatus || {});
|
|
4784
|
+
var AdapterAutoDetector = class {
|
|
4785
|
+
constructor(powerSyncDb, supabase, options = {}) {
|
|
4786
|
+
this.powerSyncDb = powerSyncDb;
|
|
4787
|
+
this.supabase = supabase;
|
|
4788
|
+
this.options = {
|
|
4789
|
+
preferPowerSync: options.preferPowerSync ?? true,
|
|
4790
|
+
statusCheckTimeout: options.statusCheckTimeout ?? 1e3,
|
|
4791
|
+
useOnlineUntilSynced: options.useOnlineUntilSynced ?? true
|
|
4792
|
+
};
|
|
4721
4793
|
}
|
|
4794
|
+
options;
|
|
4795
|
+
listeners = /* @__PURE__ */ new Set();
|
|
4796
|
+
lastResult = null;
|
|
4797
|
+
syncStatus = null;
|
|
4722
4798
|
/**
|
|
4723
|
-
*
|
|
4724
|
-
*
|
|
4725
|
-
* Checks the getter (if set) and lazily creates the adapter if PowerSync
|
|
4726
|
-
* is now available. Also updates the auto-detector with the new reference.
|
|
4799
|
+
* Update the PowerSync database reference.
|
|
4800
|
+
* Called when PowerSync becomes available after initial construction.
|
|
4727
4801
|
*
|
|
4728
|
-
* @
|
|
4802
|
+
* @param db - PowerSync database instance or null
|
|
4729
4803
|
*/
|
|
4730
|
-
|
|
4731
|
-
|
|
4732
|
-
|
|
4733
|
-
|
|
4734
|
-
if (!this.powerSyncGetter || !this.powerSyncAdapterFactory) {
|
|
4735
|
-
return false;
|
|
4736
|
-
}
|
|
4737
|
-
const db = this.powerSyncGetter();
|
|
4738
|
-
if (!db) {
|
|
4739
|
-
return false;
|
|
4740
|
-
}
|
|
4741
|
-
const adapter = this.powerSyncAdapterFactory(db);
|
|
4742
|
-
this.setPowerSyncAdapter(adapter);
|
|
4743
|
-
if (this.autoDetector) {
|
|
4744
|
-
this.autoDetector.setPowerSyncDb(db);
|
|
4804
|
+
setPowerSyncDb(db) {
|
|
4805
|
+
this.powerSyncDb = db;
|
|
4806
|
+
if (db) {
|
|
4807
|
+
this.detect();
|
|
4745
4808
|
}
|
|
4746
|
-
return true;
|
|
4747
4809
|
}
|
|
4748
4810
|
/**
|
|
4749
|
-
*
|
|
4750
|
-
*
|
|
4751
|
-
* automatically track pending mutations.
|
|
4811
|
+
* Update the sync status from PowerSync.
|
|
4812
|
+
* Called when sync status changes to re-evaluate backend recommendation.
|
|
4752
4813
|
*
|
|
4753
|
-
*
|
|
4754
|
-
* previously cached adapters (which may have been created
|
|
4755
|
-
* before the sync tracker was available) are recreated
|
|
4756
|
-
* with the new sync tracker on next access.
|
|
4814
|
+
* @param status - Current sync status or null if not available
|
|
4757
4815
|
*/
|
|
4758
|
-
|
|
4759
|
-
|
|
4760
|
-
|
|
4761
|
-
|
|
4816
|
+
updateSyncStatus(status) {
|
|
4817
|
+
const hadSynced = this.syncStatus?.hasSynced;
|
|
4818
|
+
const hasSyncedNow = status?.hasSynced;
|
|
4819
|
+
const changed = hadSynced !== hasSyncedNow;
|
|
4820
|
+
this.syncStatus = status;
|
|
4821
|
+
if (changed) {
|
|
4822
|
+
this.detect();
|
|
4762
4823
|
}
|
|
4763
|
-
this.adapters.clear();
|
|
4764
|
-
}
|
|
4765
|
-
/**
|
|
4766
|
-
* Set the Supabase adapter instance
|
|
4767
|
-
*
|
|
4768
|
-
* @param adapter - Supabase adapter implementation
|
|
4769
|
-
*/
|
|
4770
|
-
setSupabaseAdapter(adapter) {
|
|
4771
|
-
this.supabaseAdapter = adapter;
|
|
4772
|
-
}
|
|
4773
|
-
/**
|
|
4774
|
-
* Set the Cached adapter instance
|
|
4775
|
-
*
|
|
4776
|
-
* @param adapter - Cached adapter implementation
|
|
4777
|
-
*/
|
|
4778
|
-
setCachedAdapter(adapter) {
|
|
4779
|
-
this.cachedAdapter = adapter;
|
|
4780
4824
|
}
|
|
4781
4825
|
/**
|
|
4782
|
-
*
|
|
4783
|
-
*
|
|
4784
|
-
* @param detector - The auto-detector to use
|
|
4826
|
+
* Get current sync status.
|
|
4827
|
+
* @returns Current sync status or null
|
|
4785
4828
|
*/
|
|
4786
|
-
|
|
4787
|
-
this.
|
|
4788
|
-
detector.addListener((result) => {
|
|
4789
|
-
this.lastDetectionResult = result;
|
|
4790
|
-
this.notifyBackendChange(result.recommendedBackend);
|
|
4791
|
-
});
|
|
4829
|
+
getSyncStatus() {
|
|
4830
|
+
return this.syncStatus;
|
|
4792
4831
|
}
|
|
4793
4832
|
// ===========================================================================
|
|
4794
|
-
//
|
|
4833
|
+
// Main Detection Methods
|
|
4795
4834
|
// ===========================================================================
|
|
4796
4835
|
/**
|
|
4797
|
-
*
|
|
4798
|
-
*
|
|
4799
|
-
* The adapter is selected based on the table's strategy in config.tables:
|
|
4800
|
-
* - "powersync": Returns PowerSyncAdapter
|
|
4801
|
-
* - "supabase": Returns SupabaseAdapter
|
|
4802
|
-
* - "cached": Returns CachedAdapter (wrapping Supabase)
|
|
4803
|
-
* - "hybrid": Returns HybridAdapter (combining PowerSync + Cached)
|
|
4804
|
-
* - "auto": Uses auto-detection to select the best backend
|
|
4805
|
-
*
|
|
4806
|
-
* For tables not in config, defaults to auto-detection if available,
|
|
4807
|
-
* otherwise falls back to SupabaseAdapter.
|
|
4836
|
+
* Detect backend availability and recommend best option.
|
|
4808
4837
|
*
|
|
4809
|
-
*
|
|
4810
|
-
*
|
|
4811
|
-
*
|
|
4838
|
+
* The detection logic follows this priority:
|
|
4839
|
+
* 1. If preferPowerSync is true and PowerSync is available, use PowerSync
|
|
4840
|
+
* 2. If PowerSync is initializing and online with Supabase available, use Supabase temporarily
|
|
4841
|
+
* 3. If online with Supabase available, use Supabase
|
|
4842
|
+
* 4. If offline but PowerSync available, use PowerSync (offline mode)
|
|
4843
|
+
* 5. If offline and PowerSync exists (even if initializing), use PowerSync local data
|
|
4844
|
+
* 6. Default to Supabase as fallback
|
|
4812
4845
|
*
|
|
4813
|
-
* @
|
|
4814
|
-
* @param operation - The operation type: 'read' uses fallback logic during init, 'write' always uses PowerSync for powersync tables
|
|
4815
|
-
* @returns The appropriate adapter for the table
|
|
4816
|
-
* @throws Error if adapters are not initialized
|
|
4846
|
+
* @returns Detection result with recommendation and reasoning
|
|
4817
4847
|
*/
|
|
4818
|
-
|
|
4819
|
-
this.
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
if (this.powerSyncAdapter) {
|
|
4824
|
-
const powerSyncTableKeys = this.getPowerSyncTableKeys();
|
|
4825
|
-
const isConfigured = powerSyncTableKeys.some((key) => key === table || key === tableWithoutSchema || key.includes(".") && key.split(".")[1] === tableWithoutSchema);
|
|
4826
|
-
if (!isConfigured) {
|
|
4827
|
-
if (this.supabaseAdapter) {
|
|
4828
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
4829
|
-
console.warn(`[AdapterRegistry] Table "${table}" is not configured for PowerSync sync. Using Supabase fallback.`);
|
|
4830
|
-
}
|
|
4831
|
-
return this.supabaseAdapter;
|
|
4832
|
-
}
|
|
4833
|
-
throw new Error(`Table "${table}" is not configured for PowerSync sync and Supabase adapter is not available. Either add this table to the PowerSync schema or initialize the Supabase adapter.`);
|
|
4834
|
-
}
|
|
4835
|
-
}
|
|
4836
|
-
return this.getAutoAdapter(strategy, table);
|
|
4837
|
-
}
|
|
4838
|
-
if (strategy.strategy === "powersync" && this.autoDetector && this.supabaseAdapter) {
|
|
4839
|
-
const detection = this.autoDetector.detectSilent();
|
|
4840
|
-
if (detection.powerSyncStatus === "initializing" /* INITIALIZING */ && detection.isOnline) {
|
|
4841
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
4842
|
-
console.warn(`[AdapterRegistry] Table "${table}" configured for PowerSync but using Supabase. PowerSync status: ${detection.powerSyncStatus}, Online: ${detection.isOnline}`);
|
|
4843
|
-
}
|
|
4844
|
-
return this.supabaseAdapter;
|
|
4845
|
-
}
|
|
4846
|
-
}
|
|
4847
|
-
const existing = this.adapters.get(table);
|
|
4848
|
-
if (existing) {
|
|
4849
|
-
return existing;
|
|
4848
|
+
detect() {
|
|
4849
|
+
const result = this.performDetection();
|
|
4850
|
+
if (this.hasResultChanged(result)) {
|
|
4851
|
+
this.lastResult = result;
|
|
4852
|
+
this.notifyListeners(result);
|
|
4850
4853
|
}
|
|
4851
|
-
|
|
4852
|
-
this.adapters.set(table, adapter);
|
|
4853
|
-
return adapter;
|
|
4854
|
+
return result;
|
|
4854
4855
|
}
|
|
4855
4856
|
/**
|
|
4856
|
-
*
|
|
4857
|
+
* Detect backend availability WITHOUT notifying listeners.
|
|
4857
4858
|
*
|
|
4858
|
-
*
|
|
4859
|
+
* This method is safe to call during React's render phase because it does not
|
|
4860
|
+
* trigger state updates in other components. Use this method when you need to
|
|
4861
|
+
* check backend status synchronously during render (e.g., in getAdapter()).
|
|
4862
|
+
*
|
|
4863
|
+
* The regular detect() method with listener notifications should only be called
|
|
4864
|
+
* from useEffect or event handlers to avoid the React warning:
|
|
4865
|
+
* "Cannot update a component while rendering a different component"
|
|
4866
|
+
*
|
|
4867
|
+
* @returns Detection result with recommendation and reasoning
|
|
4859
4868
|
*/
|
|
4860
|
-
|
|
4861
|
-
|
|
4869
|
+
detectSilent() {
|
|
4870
|
+
const result = this.performDetection();
|
|
4871
|
+
this.lastResult = result;
|
|
4872
|
+
return result;
|
|
4862
4873
|
}
|
|
4863
4874
|
/**
|
|
4864
|
-
*
|
|
4865
|
-
*
|
|
4866
|
-
* @returns Supabase adapter or null if not initialized
|
|
4875
|
+
* Internal detection logic shared by detect() and detectSilent().
|
|
4876
|
+
* @private
|
|
4867
4877
|
*/
|
|
4868
|
-
|
|
4869
|
-
|
|
4878
|
+
performDetection() {
|
|
4879
|
+
const powerSyncStatus = this.detectPowerSyncStatus();
|
|
4880
|
+
const supabaseStatus = this.detectSupabaseStatus();
|
|
4881
|
+
const isOnline = this.checkOnlineStatus();
|
|
4882
|
+
let recommendedBackend;
|
|
4883
|
+
let reason;
|
|
4884
|
+
if (this.options.preferPowerSync && powerSyncStatus === "available" /* AVAILABLE */) {
|
|
4885
|
+
recommendedBackend = "powersync";
|
|
4886
|
+
reason = "PowerSync is available and preferred for offline-first experience";
|
|
4887
|
+
} else if (powerSyncStatus === "initializing" /* INITIALIZING */ && isOnline && supabaseStatus === "available" /* AVAILABLE */) {
|
|
4888
|
+
recommendedBackend = "supabase";
|
|
4889
|
+
reason = "PowerSync initial sync in progress; using Supabase for fresh data";
|
|
4890
|
+
} else if (supabaseStatus === "available" /* AVAILABLE */ && isOnline) {
|
|
4891
|
+
recommendedBackend = "supabase";
|
|
4892
|
+
reason = "Using Supabase direct connection";
|
|
4893
|
+
} else if (powerSyncStatus === "available" /* AVAILABLE */) {
|
|
4894
|
+
recommendedBackend = "powersync";
|
|
4895
|
+
reason = "Offline mode using PowerSync local data";
|
|
4896
|
+
} else if (!isOnline && this.powerSyncDb) {
|
|
4897
|
+
recommendedBackend = "powersync";
|
|
4898
|
+
reason = "Offline mode - using PowerSync local data (initial sync may be incomplete)";
|
|
4899
|
+
} else {
|
|
4900
|
+
recommendedBackend = "supabase";
|
|
4901
|
+
reason = "No confirmed available backend; defaulting to Supabase";
|
|
4902
|
+
}
|
|
4903
|
+
return {
|
|
4904
|
+
powerSyncStatus,
|
|
4905
|
+
supabaseStatus,
|
|
4906
|
+
recommendedBackend,
|
|
4907
|
+
isOnline,
|
|
4908
|
+
reason
|
|
4909
|
+
};
|
|
4870
4910
|
}
|
|
4871
4911
|
/**
|
|
4872
|
-
*
|
|
4912
|
+
* Check if PowerSync is available.
|
|
4873
4913
|
*
|
|
4874
|
-
*
|
|
4914
|
+
* PowerSync status depends on:
|
|
4915
|
+
* 1. Database instance exists
|
|
4916
|
+
* 2. If useOnlineUntilSynced is true, also checks if initial sync completed
|
|
4917
|
+
*
|
|
4918
|
+
* @returns PowerSync backend status
|
|
4875
4919
|
*/
|
|
4876
|
-
|
|
4877
|
-
|
|
4920
|
+
detectPowerSyncStatus() {
|
|
4921
|
+
if (!this.powerSyncDb) {
|
|
4922
|
+
return "unavailable" /* UNAVAILABLE */;
|
|
4923
|
+
}
|
|
4924
|
+
try {
|
|
4925
|
+
if (typeof this.powerSyncDb.getAll !== "function") {
|
|
4926
|
+
return "initializing" /* INITIALIZING */;
|
|
4927
|
+
}
|
|
4928
|
+
if (this.options.useOnlineUntilSynced) {
|
|
4929
|
+
if (!this.syncStatus || !this.syncStatus.hasSynced) {
|
|
4930
|
+
return "initializing" /* INITIALIZING */;
|
|
4931
|
+
}
|
|
4932
|
+
}
|
|
4933
|
+
return "available" /* AVAILABLE */;
|
|
4934
|
+
} catch {
|
|
4935
|
+
return "initializing" /* INITIALIZING */;
|
|
4936
|
+
}
|
|
4878
4937
|
}
|
|
4879
4938
|
/**
|
|
4880
|
-
*
|
|
4939
|
+
* Check if Supabase is available.
|
|
4881
4940
|
*
|
|
4882
|
-
*
|
|
4941
|
+
* Supabase is considered available if we have a client instance.
|
|
4942
|
+
* The actual network connectivity is checked separately via checkOnlineStatus().
|
|
4943
|
+
*
|
|
4944
|
+
* @returns Supabase backend status
|
|
4883
4945
|
*/
|
|
4884
|
-
|
|
4885
|
-
|
|
4946
|
+
detectSupabaseStatus() {
|
|
4947
|
+
if (!this.supabase) {
|
|
4948
|
+
return "unavailable" /* UNAVAILABLE */;
|
|
4949
|
+
}
|
|
4950
|
+
try {
|
|
4951
|
+
if (typeof this.supabase.from === "function") {
|
|
4952
|
+
return "available" /* AVAILABLE */;
|
|
4953
|
+
}
|
|
4954
|
+
return "unavailable" /* UNAVAILABLE */;
|
|
4955
|
+
} catch {
|
|
4956
|
+
return "unavailable" /* UNAVAILABLE */;
|
|
4957
|
+
}
|
|
4886
4958
|
}
|
|
4887
4959
|
/**
|
|
4888
|
-
*
|
|
4960
|
+
* Check if device is online.
|
|
4889
4961
|
*
|
|
4890
|
-
*
|
|
4891
|
-
*
|
|
4962
|
+
* Prefers the sync status's isOnline property if available (from React Native NetInfo).
|
|
4963
|
+
* Falls back to navigator.onLine in browser environments.
|
|
4964
|
+
* Returns false by default for non-browser environments (React Native, Node.js, etc.)
|
|
4965
|
+
* to ensure offline-first behavior works correctly.
|
|
4966
|
+
*
|
|
4967
|
+
* @returns Whether the device has network connectivity
|
|
4892
4968
|
*/
|
|
4893
|
-
|
|
4894
|
-
|
|
4895
|
-
|
|
4969
|
+
checkOnlineStatus() {
|
|
4970
|
+
if (this.syncStatus?.isOnline !== void 0) {
|
|
4971
|
+
return this.syncStatus.isOnline;
|
|
4972
|
+
}
|
|
4973
|
+
if (typeof window !== "undefined" && typeof navigator !== "undefined") {
|
|
4974
|
+
return navigator.onLine;
|
|
4975
|
+
}
|
|
4976
|
+
return false;
|
|
4896
4977
|
}
|
|
4978
|
+
// ===========================================================================
|
|
4979
|
+
// Instance Management Methods
|
|
4980
|
+
// ===========================================================================
|
|
4897
4981
|
/**
|
|
4898
|
-
*
|
|
4982
|
+
* Update PowerSync instance (e.g., when it becomes available).
|
|
4899
4983
|
*
|
|
4900
|
-
* @param
|
|
4901
|
-
* @returns True if table uses PowerSync, Hybrid, or Auto strategy
|
|
4984
|
+
* @param db - New PowerSync database instance or null
|
|
4902
4985
|
*/
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
return strategy?.strategy === "powersync" || strategy?.strategy === "hybrid" || strategy?.strategy === "auto";
|
|
4986
|
+
setPowerSync(db) {
|
|
4987
|
+
this.powerSyncDb = db;
|
|
4906
4988
|
}
|
|
4907
4989
|
/**
|
|
4908
|
-
*
|
|
4990
|
+
* Update Supabase instance.
|
|
4909
4991
|
*
|
|
4910
|
-
* @
|
|
4992
|
+
* @param supabase - New Supabase client instance or null
|
|
4911
4993
|
*/
|
|
4912
|
-
|
|
4913
|
-
|
|
4994
|
+
setSupabase(supabase) {
|
|
4995
|
+
this.supabase = supabase;
|
|
4914
4996
|
}
|
|
4915
4997
|
/**
|
|
4916
|
-
* Get
|
|
4998
|
+
* Get current PowerSync instance.
|
|
4917
4999
|
*
|
|
4918
|
-
* @returns
|
|
5000
|
+
* @returns Current PowerSync database instance or null
|
|
4919
5001
|
*/
|
|
4920
|
-
|
|
4921
|
-
return
|
|
5002
|
+
getPowerSync() {
|
|
5003
|
+
return this.powerSyncDb;
|
|
4922
5004
|
}
|
|
4923
5005
|
/**
|
|
4924
|
-
* Get
|
|
4925
|
-
* Uses explicit alias from config if set, otherwise auto-generates.
|
|
5006
|
+
* Get current Supabase instance.
|
|
4926
5007
|
*
|
|
4927
|
-
* @
|
|
4928
|
-
* @returns The alias to use in PowerSync schema
|
|
5008
|
+
* @returns Current Supabase client instance or null
|
|
4929
5009
|
*/
|
|
4930
|
-
|
|
4931
|
-
|
|
4932
|
-
const tableWithoutSchema = table.includes(".") ? table.split(".")[1] : table;
|
|
4933
|
-
const configKey = this.config.tables[table] ? table : tableWithoutSchema;
|
|
4934
|
-
return getPowerSyncAlias(configKey, strategy);
|
|
5010
|
+
getSupabase() {
|
|
5011
|
+
return this.supabase;
|
|
4935
5012
|
}
|
|
4936
5013
|
// ===========================================================================
|
|
4937
|
-
//
|
|
5014
|
+
// Options Management
|
|
4938
5015
|
// ===========================================================================
|
|
4939
5016
|
/**
|
|
4940
|
-
*
|
|
5017
|
+
* Update detector options.
|
|
4941
5018
|
*
|
|
4942
|
-
* @param
|
|
4943
|
-
* @returns The automatically selected adapter
|
|
5019
|
+
* @param options - New options to merge with existing
|
|
4944
5020
|
*/
|
|
4945
|
-
|
|
4946
|
-
|
|
4947
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
4951
|
-
console.warn(`[AdapterRegistry] No auto-detector configured${table ? ` for table "${table}"` : ""}. Using Supabase fallback.`);
|
|
4952
|
-
}
|
|
4953
|
-
return this.supabaseAdapter;
|
|
4954
|
-
}
|
|
4955
|
-
const detection = this.autoDetector.detectSilent();
|
|
4956
|
-
this.lastDetectionResult = detection;
|
|
4957
|
-
if (detection.recommendedBackend === "powersync") {
|
|
4958
|
-
if (!this.powerSyncAdapter) {
|
|
4959
|
-
if (!this.supabaseAdapter) {
|
|
4960
|
-
throw new Error("Neither PowerSync nor Supabase adapters are available.");
|
|
4961
|
-
}
|
|
4962
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
4963
|
-
console.warn(`[AdapterRegistry] PowerSync recommended${table ? ` for table "${table}"` : ""} but adapter not available. Falling back to Supabase. Reason: ${detection.reason}`);
|
|
4964
|
-
}
|
|
4965
|
-
return this.supabaseAdapter;
|
|
4966
|
-
}
|
|
4967
|
-
return this.powerSyncAdapter;
|
|
4968
|
-
}
|
|
4969
|
-
if (!this.supabaseAdapter) {
|
|
4970
|
-
throw new Error("Supabase adapter not available and PowerSync not recommended.");
|
|
4971
|
-
}
|
|
4972
|
-
return this.supabaseAdapter;
|
|
5021
|
+
setOptions(options) {
|
|
5022
|
+
this.options = {
|
|
5023
|
+
...this.options,
|
|
5024
|
+
...options
|
|
5025
|
+
};
|
|
4973
5026
|
}
|
|
4974
5027
|
/**
|
|
4975
|
-
*
|
|
5028
|
+
* Get current detector options.
|
|
4976
5029
|
*
|
|
4977
|
-
* @
|
|
4978
|
-
* @returns Unsubscribe function
|
|
5030
|
+
* @returns Current detector options
|
|
4979
5031
|
*/
|
|
4980
|
-
|
|
4981
|
-
|
|
4982
|
-
|
|
4983
|
-
this.backendChangeListeners.delete(callback);
|
|
5032
|
+
getOptions() {
|
|
5033
|
+
return {
|
|
5034
|
+
...this.options
|
|
4984
5035
|
};
|
|
4985
5036
|
}
|
|
5037
|
+
// ===========================================================================
|
|
5038
|
+
// Listener Management
|
|
5039
|
+
// ===========================================================================
|
|
4986
5040
|
/**
|
|
4987
|
-
*
|
|
5041
|
+
* Add a listener for backend change events.
|
|
4988
5042
|
*
|
|
4989
|
-
* @param
|
|
5043
|
+
* @param listener - Callback to invoke when detection result changes
|
|
5044
|
+
* @returns Function to remove the listener
|
|
4990
5045
|
*/
|
|
4991
|
-
|
|
4992
|
-
this.
|
|
4993
|
-
|
|
4994
|
-
callback(backend);
|
|
4995
|
-
} catch (error) {
|
|
4996
|
-
console.error("Error in backend change listener:", error);
|
|
4997
|
-
}
|
|
4998
|
-
});
|
|
5046
|
+
addListener(listener) {
|
|
5047
|
+
this.listeners.add(listener);
|
|
5048
|
+
return () => this.listeners.delete(listener);
|
|
4999
5049
|
}
|
|
5000
5050
|
/**
|
|
5001
|
-
*
|
|
5051
|
+
* Remove a listener for backend change events.
|
|
5002
5052
|
*
|
|
5003
|
-
* @
|
|
5053
|
+
* @param listener - Listener to remove
|
|
5004
5054
|
*/
|
|
5005
|
-
|
|
5006
|
-
|
|
5055
|
+
removeListener(listener) {
|
|
5056
|
+
this.listeners.delete(listener);
|
|
5007
5057
|
}
|
|
5008
5058
|
/**
|
|
5009
|
-
* Get the
|
|
5059
|
+
* Get the last detection result.
|
|
5010
5060
|
*
|
|
5011
|
-
* @returns
|
|
5061
|
+
* @returns Last detection result or null if never detected
|
|
5012
5062
|
*/
|
|
5013
|
-
|
|
5014
|
-
return this.
|
|
5063
|
+
getLastResult() {
|
|
5064
|
+
return this.lastResult;
|
|
5015
5065
|
}
|
|
5016
5066
|
// ===========================================================================
|
|
5017
|
-
// Private Methods
|
|
5067
|
+
// Private Helper Methods
|
|
5018
5068
|
// ===========================================================================
|
|
5019
5069
|
/**
|
|
5020
|
-
*
|
|
5021
|
-
*
|
|
5022
|
-
* @param strategy - The table strategy configuration
|
|
5023
|
-
* @returns The created adapter
|
|
5024
|
-
* @throws Error if the required base adapter is not initialized
|
|
5070
|
+
* Check if the detection result has changed from the last result.
|
|
5025
5071
|
*/
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
if (!this.powerSyncAdapter) {
|
|
5030
|
-
if (this.supabaseAdapter) {
|
|
5031
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
5032
|
-
console.warn(`[AdapterRegistry] PowerSync not ready, falling back to Supabase`);
|
|
5033
|
-
}
|
|
5034
|
-
return this.supabaseAdapter;
|
|
5035
|
-
}
|
|
5036
|
-
throw new Error("PowerSync adapter not initialized and Supabase not available. Ensure PowerSyncAdapter is set before accessing PowerSync tables.");
|
|
5037
|
-
}
|
|
5038
|
-
return this.syncTrackingAdapter ?? this.powerSyncAdapter;
|
|
5039
|
-
case "supabase":
|
|
5040
|
-
if (!this.supabaseAdapter) {
|
|
5041
|
-
throw new Error("Supabase adapter not initialized. Ensure SupabaseAdapter is set before accessing Supabase tables.");
|
|
5042
|
-
}
|
|
5043
|
-
return this.supabaseAdapter;
|
|
5044
|
-
case "cached":
|
|
5045
|
-
if (this.cachedAdapter) {
|
|
5046
|
-
return this.cachedAdapter;
|
|
5047
|
-
}
|
|
5048
|
-
throw new Error("CachedAdapter not yet implemented. This feature will be available in Wave 2. For now, use 'supabase' strategy as a fallback.");
|
|
5049
|
-
case "hybrid":
|
|
5050
|
-
throw new Error("HybridAdapter not yet implemented. This feature will be available in Wave 2. For now, use 'powersync' or 'supabase' strategy as a fallback.");
|
|
5051
|
-
default:
|
|
5052
|
-
if (!this.supabaseAdapter) {
|
|
5053
|
-
throw new Error("Supabase adapter not initialized. Ensure SupabaseAdapter is set before accessing tables.");
|
|
5054
|
-
}
|
|
5055
|
-
if (typeof __DEV__ !== "undefined" && __DEV__) {
|
|
5056
|
-
console.warn(`[AdapterRegistry] Unknown strategy "${strategy.strategy}". Using Supabase fallback.`);
|
|
5057
|
-
}
|
|
5058
|
-
return this.supabaseAdapter;
|
|
5072
|
+
hasResultChanged(result) {
|
|
5073
|
+
if (!this.lastResult) {
|
|
5074
|
+
return true;
|
|
5059
5075
|
}
|
|
5060
|
-
|
|
5061
|
-
// ===========================================================================
|
|
5062
|
-
// Utility Methods
|
|
5063
|
-
// ===========================================================================
|
|
5064
|
-
/**
|
|
5065
|
-
* Clear all cached adapters.
|
|
5066
|
-
* Useful when configuration changes and adapters need to be recreated.
|
|
5067
|
-
*/
|
|
5068
|
-
clearCache() {
|
|
5069
|
-
this.adapters.clear();
|
|
5070
|
-
}
|
|
5071
|
-
/**
|
|
5072
|
-
* Reset the registry to uninitialized state.
|
|
5073
|
-
* Used during cleanup or testing.
|
|
5074
|
-
*/
|
|
5075
|
-
reset() {
|
|
5076
|
-
this.adapters.clear();
|
|
5077
|
-
this.powerSyncAdapter = null;
|
|
5078
|
-
this.powerSyncGetter = null;
|
|
5079
|
-
this.powerSyncAdapterFactory = null;
|
|
5080
|
-
this.supabaseAdapter = null;
|
|
5081
|
-
this.cachedAdapter = null;
|
|
5082
|
-
this.deps = null;
|
|
5083
|
-
this._isInitialized = false;
|
|
5084
|
-
this.autoDetector = null;
|
|
5085
|
-
this.backendChangeListeners.clear();
|
|
5086
|
-
this.lastDetectionResult = null;
|
|
5087
|
-
this.syncTracker = null;
|
|
5088
|
-
this.syncTrackingAdapter = null;
|
|
5089
|
-
}
|
|
5090
|
-
/**
|
|
5091
|
-
* Dispose all adapters and clean up resources.
|
|
5092
|
-
* Called when the DataLayerProvider unmounts.
|
|
5093
|
-
*/
|
|
5094
|
-
dispose() {
|
|
5095
|
-
this.reset();
|
|
5076
|
+
return this.lastResult.powerSyncStatus !== result.powerSyncStatus || this.lastResult.supabaseStatus !== result.supabaseStatus || this.lastResult.recommendedBackend !== result.recommendedBackend || this.lastResult.isOnline !== result.isOnline;
|
|
5096
5077
|
}
|
|
5097
5078
|
/**
|
|
5098
|
-
*
|
|
5079
|
+
* Notify all listeners of a detection result change.
|
|
5099
5080
|
*/
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
|
|
5110
|
-
|
|
5111
|
-
|
|
5081
|
+
notifyListeners(result) {
|
|
5082
|
+
const isDev = typeof __DEV__ !== "undefined" ? __DEV__ : process.env.NODE_ENV !== "production";
|
|
5083
|
+
if (isDev) {
|
|
5084
|
+
const prevBackend = this.lastResult?.recommendedBackend;
|
|
5085
|
+
if (prevBackend && prevBackend !== result.recommendedBackend) {
|
|
5086
|
+
console.log(`[DataLayer] Backend switched: ${prevBackend} \u2192 ${result.recommendedBackend}`, `| Reason: ${result.reason}`);
|
|
5087
|
+
}
|
|
5088
|
+
if (result.recommendedBackend === "supabase" && this.options.preferPowerSync) {
|
|
5089
|
+
console.log(`[DataLayer] Using online fallback (Supabase)`, `| PowerSync: ${result.powerSyncStatus}`, `| Online: ${result.isOnline}`, `| Reason: ${result.reason}`);
|
|
5090
|
+
}
|
|
5091
|
+
}
|
|
5092
|
+
Array.from(this.listeners).forEach((listener) => {
|
|
5093
|
+
try {
|
|
5094
|
+
listener(result);
|
|
5095
|
+
} catch (error) {
|
|
5096
|
+
console.error("Error in backend change listener:", error);
|
|
5097
|
+
}
|
|
5098
|
+
});
|
|
5112
5099
|
}
|
|
5113
5100
|
};
|
|
5114
|
-
function
|
|
5115
|
-
return new
|
|
5101
|
+
function createAdapterAutoDetector(powerSyncDb, supabase, options) {
|
|
5102
|
+
return new AdapterAutoDetector(powerSyncDb, supabase, options);
|
|
5116
5103
|
}
|
|
5117
5104
|
|
|
5118
5105
|
// src/adapters/supabase-adapter.ts
|
|
@@ -7942,13 +7929,13 @@ export {
|
|
|
7942
7929
|
useMutationSuccess,
|
|
7943
7930
|
useMutationSuccessRN,
|
|
7944
7931
|
ADAPTER_STRATEGIES,
|
|
7945
|
-
BackendStatus,
|
|
7946
|
-
AdapterAutoDetector,
|
|
7947
|
-
createAdapterAutoDetector,
|
|
7948
7932
|
SyncTrackingAdapter,
|
|
7949
7933
|
stripSchemaPrefix,
|
|
7950
7934
|
AdapterRegistry,
|
|
7951
7935
|
createAdapterRegistry,
|
|
7936
|
+
BackendStatus,
|
|
7937
|
+
AdapterAutoDetector,
|
|
7938
|
+
createAdapterAutoDetector,
|
|
7952
7939
|
SupabaseAdapter,
|
|
7953
7940
|
createSupabaseAdapter,
|
|
7954
7941
|
getErrorBody,
|
|
@@ -8005,4 +7992,4 @@ moment/moment.js:
|
|
|
8005
7992
|
(*! license : MIT *)
|
|
8006
7993
|
(*! momentjs.com *)
|
|
8007
7994
|
*/
|
|
8008
|
-
//# sourceMappingURL=chunk-
|
|
7995
|
+
//# sourceMappingURL=chunk-FZJX2FZS.js.map
|