local-lite 0.0.0-stage → 0.0.1

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 (65) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +349 -2
  3. package/dist/adapters/local/better-sqlite3.d.ts +23 -0
  4. package/dist/adapters/local/better-sqlite3.js +46 -0
  5. package/dist/adapters/local/expo-sqlite.d.ts +8 -0
  6. package/dist/adapters/local/expo-sqlite.js +30 -0
  7. package/dist/adapters/local/index.d.ts +13 -0
  8. package/dist/adapters/local/index.js +22 -0
  9. package/dist/adapters/local/libsql.d.ts +24 -0
  10. package/dist/adapters/local/libsql.js +87 -0
  11. package/dist/adapters/local/node-sqlite.d.ts +24 -0
  12. package/dist/adapters/local/node-sqlite.js +44 -0
  13. package/dist/adapters/local/op-sqlite.d.ts +26 -0
  14. package/dist/adapters/local/op-sqlite.js +59 -0
  15. package/dist/adapters/local/sql-js.d.ts +26 -0
  16. package/dist/adapters/local/sql-js.js +63 -0
  17. package/dist/adapters/network/index.d.ts +11 -0
  18. package/dist/adapters/network/index.js +53 -0
  19. package/dist/adapters/remote/convex.d.ts +71 -0
  20. package/dist/adapters/remote/convex.js +185 -0
  21. package/dist/adapters/remote/demo.d.ts +21 -0
  22. package/dist/adapters/remote/demo.js +46 -0
  23. package/dist/adapters/remote/firestore.d.ts +64 -0
  24. package/dist/adapters/remote/firestore.js +302 -0
  25. package/dist/adapters/remote/index.d.ts +10 -0
  26. package/dist/adapters/remote/index.js +20 -0
  27. package/dist/adapters/remote/supabase.d.ts +97 -0
  28. package/dist/adapters/remote/supabase.js +276 -0
  29. package/dist/config/index.d.ts +24 -0
  30. package/dist/config/index.js +101 -0
  31. package/dist/config/resolve-local.d.ts +7 -0
  32. package/dist/config/resolve-local.js +68 -0
  33. package/dist/config/resolve-remote.d.ts +7 -0
  34. package/dist/config/resolve-remote.js +81 -0
  35. package/dist/config/types.d.ts +159 -0
  36. package/dist/config/types.js +2 -0
  37. package/dist/core/client.d.ts +100 -0
  38. package/dist/core/client.js +273 -0
  39. package/dist/core/conflict.d.ts +5 -0
  40. package/dist/core/conflict.js +38 -0
  41. package/dist/core/meta-store.d.ts +28 -0
  42. package/dist/core/meta-store.js +64 -0
  43. package/dist/core/outbox-store.d.ts +45 -0
  44. package/dist/core/outbox-store.js +141 -0
  45. package/dist/core/schema-manager.d.ts +39 -0
  46. package/dist/core/schema-manager.js +150 -0
  47. package/dist/core/sync-worker.d.ts +74 -0
  48. package/dist/core/sync-worker.js +335 -0
  49. package/dist/core/table-manager.d.ts +19 -0
  50. package/dist/core/table-manager.js +74 -0
  51. package/dist/core/types.d.ts +73 -0
  52. package/dist/core/types.js +2 -0
  53. package/dist/index.d.ts +17 -0
  54. package/dist/index.js +33 -0
  55. package/dist/react/index.d.ts +34 -0
  56. package/dist/react/index.js +103 -0
  57. package/dist/reactive/event-emitter.d.ts +7 -0
  58. package/dist/reactive/event-emitter.js +34 -0
  59. package/dist/utils/logger.d.ts +51 -0
  60. package/dist/utils/logger.js +169 -0
  61. package/dist/utils/sql.d.ts +4 -0
  62. package/dist/utils/sql.js +9 -0
  63. package/dist/utils/uuid.d.ts +7 -0
  64. package/dist/utils/uuid.js +27 -0
  65. package/package.json +130 -3
@@ -0,0 +1,276 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SupabaseRemoteAdapter = void 0;
4
+ const logger_js_1 = require("../../utils/logger.js");
5
+ class SupabaseRemoteAdapter {
6
+ client;
7
+ tables;
8
+ updatedAtColumn;
9
+ primaryKey;
10
+ deletedColumn;
11
+ deletedValue;
12
+ pushRpcName;
13
+ schema;
14
+ channelName;
15
+ activeChannel;
16
+ logger;
17
+ constructor(client, options = {}) {
18
+ this.client = client;
19
+ this.tables = options.tables ?? [];
20
+ this.updatedAtColumn = options.updatedAtColumn ?? 'updated_at';
21
+ this.primaryKey = options.primaryKey ?? 'id';
22
+ this.deletedColumn = options.deletedColumn ?? options.deletedAtColumn ?? 'deleted';
23
+ this.deletedValue = options.deletedValue !== undefined ? options.deletedValue : true;
24
+ this.pushRpcName = options.pushRpcName;
25
+ this.schema = options.schema ?? 'public';
26
+ this.channelName = options.channelName ?? 'local_first_sync';
27
+ if (options.logger instanceof logger_js_1.SyncLogger) {
28
+ this.logger = options.logger;
29
+ }
30
+ else if (typeof options.logger === 'boolean') {
31
+ this.logger = new logger_js_1.SyncLogger({ enabled: options.logger, level: options.logLevel });
32
+ }
33
+ else if (options.logger && typeof options.logger === 'object') {
34
+ this.logger = new logger_js_1.SyncLogger({ customLogger: options.logger, level: options.logLevel });
35
+ }
36
+ else {
37
+ this.logger = options.logLevel ? new logger_js_1.SyncLogger({ level: options.logLevel }) : logger_js_1.defaultLogger;
38
+ }
39
+ }
40
+ /**
41
+ * Set or update tracked tables dynamically.
42
+ */
43
+ setTables(tables) {
44
+ this.tables = tables;
45
+ }
46
+ /**
47
+ * Pushes outbox operations to Supabase.
48
+ */
49
+ async pushChanges(operations) {
50
+ if (!operations || operations.length === 0) {
51
+ return true;
52
+ }
53
+ // 1. If custom RPC is configured, perform atomic batch RPC
54
+ if (this.pushRpcName) {
55
+ const { error } = await this.client.rpc(this.pushRpcName, {
56
+ operations: operations.map((op) => ({
57
+ id: op.id,
58
+ table_name: op.tableName,
59
+ row_id: op.rowId,
60
+ operation: op.operation,
61
+ payload: op.payload,
62
+ timestamp: op.timestamp,
63
+ })),
64
+ });
65
+ if (error) {
66
+ this.logger.error((0, logger_js_1.formatSupabaseError)(error, { operation: 'batch_rpc' }));
67
+ return false;
68
+ }
69
+ return true;
70
+ }
71
+ // 2. Direct table operations
72
+ for (const op of operations) {
73
+ try {
74
+ if (op.operation === 'DELETE') {
75
+ // Soft delete: sets deleted = true and stamps updated_at
76
+ const patch = {
77
+ [this.deletedColumn]: this.deletedValue,
78
+ };
79
+ if (this.updatedAtColumn) {
80
+ patch[this.updatedAtColumn] = new Date(op.timestamp).toISOString();
81
+ }
82
+ const { error } = await this.client
83
+ .from(op.tableName)
84
+ .update(patch)
85
+ .eq(this.primaryKey, op.rowId);
86
+ if (error) {
87
+ this.logger.error((0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'DELETE' }));
88
+ return false;
89
+ }
90
+ }
91
+ else if (op.operation === 'UPDATE') {
92
+ // Partial UPDATE on existing record: only updates the modified columns to respect NOT NULL constraints
93
+ const patch = {
94
+ ...(op.payload || {}),
95
+ [this.deletedColumn]: false,
96
+ };
97
+ if (this.updatedAtColumn) {
98
+ patch[this.updatedAtColumn] = new Date(op.timestamp).toISOString();
99
+ }
100
+ const { error, data } = await this.client
101
+ .from(op.tableName)
102
+ .update(patch)
103
+ .eq(this.primaryKey, op.rowId)
104
+ .select(this.primaryKey);
105
+ if (error) {
106
+ this.logger.error((0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'UPDATE' }));
107
+ return false;
108
+ }
109
+ // If row did not exist remotely (e.g. created & modified offline), fallback to upsert
110
+ if (!data || data.length === 0) {
111
+ const rowData = {
112
+ ...patch,
113
+ [this.primaryKey]: op.rowId,
114
+ };
115
+ const { error: upsertErr } = await this.client
116
+ .from(op.tableName)
117
+ .upsert(rowData);
118
+ if (upsertErr) {
119
+ this.logger.error((0, logger_js_1.formatSupabaseError)(upsertErr, { table: op.tableName, operation: 'UPSERT_FALLBACK' }));
120
+ return false;
121
+ }
122
+ }
123
+ }
124
+ else {
125
+ // INSERT -> upsert into table with deleted: false
126
+ const rowData = {
127
+ ...(op.payload || {}),
128
+ [this.primaryKey]: op.rowId,
129
+ [this.deletedColumn]: false,
130
+ };
131
+ if (this.updatedAtColumn && !rowData[this.updatedAtColumn]) {
132
+ rowData[this.updatedAtColumn] = new Date(op.timestamp).toISOString();
133
+ }
134
+ const { error } = await this.client
135
+ .from(op.tableName)
136
+ .upsert(rowData);
137
+ if (error) {
138
+ this.logger.error((0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'INSERT' }));
139
+ return false;
140
+ }
141
+ }
142
+ }
143
+ catch (err) {
144
+ this.logger.error((0, logger_js_1.formatSupabaseError)(err, { table: op.tableName, operation: op.operation }));
145
+ return false;
146
+ }
147
+ }
148
+ return true;
149
+ }
150
+ /**
151
+ * Fetches delta changes from Supabase since a given timestamp.
152
+ * Reads updated records and checks `deleted: true` to propagate soft deletes.
153
+ */
154
+ async fetchChanges(sinceTimestamp) {
155
+ const changes = [];
156
+ const sinceIso = sinceTimestamp > 0 ? new Date(sinceTimestamp).toISOString() : null;
157
+ let maxServerTimestamp = sinceTimestamp;
158
+ for (const table of this.tables) {
159
+ try {
160
+ let query = this.client
161
+ .from(table)
162
+ .select('*');
163
+ if (sinceIso) {
164
+ query = query.gt(this.updatedAtColumn, sinceIso);
165
+ }
166
+ if (typeof query.order === 'function') {
167
+ query = query.order(this.updatedAtColumn, { ascending: true });
168
+ }
169
+ const { data, error } = await query;
170
+ if (error) {
171
+ this.logger.error((0, logger_js_1.formatSupabaseError)(error, { table, operation: 'PULL' }));
172
+ continue;
173
+ }
174
+ if (data && Array.isArray(data)) {
175
+ for (const row of data) {
176
+ const isSoftDeleted = this.isSoftDeleted(row);
177
+ const operation = isSoftDeleted ? 'DELETE' : 'UPDATE';
178
+ const rowTime = row[this.updatedAtColumn]
179
+ ? new Date(row[this.updatedAtColumn]).getTime()
180
+ : Date.now();
181
+ if (rowTime > maxServerTimestamp) {
182
+ maxServerTimestamp = rowTime;
183
+ }
184
+ changes.push({
185
+ tableName: table,
186
+ operation,
187
+ rowId: String(row[this.primaryKey]),
188
+ payload: isSoftDeleted ? null : row,
189
+ timestamp: rowTime,
190
+ });
191
+ }
192
+ }
193
+ }
194
+ catch (err) {
195
+ this.logger.error((0, logger_js_1.formatSupabaseError)(err, { table, operation: 'PULL' }));
196
+ }
197
+ }
198
+ return {
199
+ timestamp: changes.length > 0 ? maxServerTimestamp : sinceTimestamp,
200
+ changes,
201
+ };
202
+ }
203
+ /**
204
+ * Helper to determine if a record represents a soft delete.
205
+ */
206
+ isSoftDeleted(record) {
207
+ if (!record)
208
+ return false;
209
+ const val = record[this.deletedColumn];
210
+ if (val === true || val === 1)
211
+ return true;
212
+ if (val && !['false', '0', 'null', 'undefined'].includes(String(val).toLowerCase())) {
213
+ return true;
214
+ }
215
+ return false;
216
+ }
217
+ /**
218
+ * Subscribes to live realtime postgres_changes from Supabase.
219
+ */
220
+ subscribeToLiveChanges(callback) {
221
+ if (this.activeChannel) {
222
+ this.client.removeChannel(this.activeChannel);
223
+ this.activeChannel = undefined;
224
+ }
225
+ if (this.tables.length === 0) {
226
+ return;
227
+ }
228
+ const channel = this.client.channel(this.channelName);
229
+ for (const table of this.tables) {
230
+ channel.on('postgres_changes', { event: '*', schema: this.schema, table }, (payload) => {
231
+ let op = 'UPDATE';
232
+ if (payload.eventType === 'INSERT')
233
+ op = 'INSERT';
234
+ else if (payload.eventType === 'DELETE')
235
+ op = 'DELETE';
236
+ const record = op === 'DELETE' ? payload.old : payload.new;
237
+ if (!record)
238
+ return;
239
+ const isSoftDeleted = op !== 'DELETE' && this.isSoftDeleted(record);
240
+ const finalOp = isSoftDeleted ? 'DELETE' : op;
241
+ const rowId = String(record[this.primaryKey]);
242
+ const timestamp = record[this.updatedAtColumn]
243
+ ? new Date(record[this.updatedAtColumn]).getTime()
244
+ : Date.now();
245
+ callback({
246
+ timestamp,
247
+ changes: [
248
+ {
249
+ tableName: table,
250
+ operation: finalOp,
251
+ rowId,
252
+ payload: finalOp === 'DELETE' ? null : record,
253
+ timestamp,
254
+ },
255
+ ],
256
+ });
257
+ });
258
+ }
259
+ channel.subscribe((status) => {
260
+ if (status === 'SUBSCRIBED') {
261
+ // Successfully subscribed to Supabase realtime
262
+ }
263
+ });
264
+ this.activeChannel = channel;
265
+ }
266
+ /**
267
+ * Clean up active realtime channels.
268
+ */
269
+ destroy() {
270
+ if (this.activeChannel) {
271
+ this.client.removeChannel(this.activeChannel);
272
+ this.activeChannel = undefined;
273
+ }
274
+ }
275
+ }
276
+ exports.SupabaseRemoteAdapter = SupabaseRemoteAdapter;
@@ -0,0 +1,24 @@
1
+ import { LocalFirstClient } from '../core/client.js';
2
+ import { LocalLiteConfig } from './types.js';
3
+ export * from './types.js';
4
+ export { resolveLocalAdapter } from './resolve-local.js';
5
+ export { resolveRemoteAdapter } from './resolve-remote.js';
6
+ export { LocalFirstClient } from '../core/client.js';
7
+ export { ExpoSQLiteAdapter } from '../adapters/local/expo-sqlite.js';
8
+ export { OpSQLiteAdapter } from '../adapters/local/op-sqlite.js';
9
+ export { BetterSQLite3Adapter } from '../adapters/local/better-sqlite3.js';
10
+ export { LibSQLAdapter } from '../adapters/local/libsql.js';
11
+ export { SqlJsAdapter } from '../adapters/local/sql-js.js';
12
+ export { NodeSQLiteAdapter } from '../adapters/local/node-sqlite.js';
13
+ export { SupabaseRemoteAdapter } from '../adapters/remote/supabase.js';
14
+ export { ConvexRemoteAdapter } from '../adapters/remote/convex.js';
15
+ export { FirestoreRemoteAdapter } from '../adapters/remote/firestore.js';
16
+ export { MemoryDemoRemoteAdapter } from '../adapters/remote/demo.js';
17
+ /**
18
+ * Type-safe configuration helper function (similar to defineConfig in Vite/Tailwind).
19
+ */
20
+ export declare function defineSyncConfig(config: LocalLiteConfig): LocalLiteConfig;
21
+ /**
22
+ * Factory that instantiates, wires up, and initializes a LocalFirstClient from a LocalLiteConfig.
23
+ */
24
+ export declare function createSyncClient(config: LocalLiteConfig): Promise<LocalFirstClient>;
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.MemoryDemoRemoteAdapter = exports.FirestoreRemoteAdapter = exports.ConvexRemoteAdapter = exports.SupabaseRemoteAdapter = exports.NodeSQLiteAdapter = exports.SqlJsAdapter = exports.LibSQLAdapter = exports.BetterSQLite3Adapter = exports.OpSQLiteAdapter = exports.ExpoSQLiteAdapter = exports.LocalFirstClient = exports.resolveRemoteAdapter = exports.resolveLocalAdapter = void 0;
18
+ exports.defineSyncConfig = defineSyncConfig;
19
+ exports.createSyncClient = createSyncClient;
20
+ const client_js_1 = require("../core/client.js");
21
+ const index_js_1 = require("../adapters/network/index.js");
22
+ const resolve_local_js_1 = require("./resolve-local.js");
23
+ const resolve_remote_js_1 = require("./resolve-remote.js");
24
+ // Export all config types
25
+ __exportStar(require("./types.js"), exports);
26
+ // Export adapter resolvers
27
+ var resolve_local_js_2 = require("./resolve-local.js");
28
+ Object.defineProperty(exports, "resolveLocalAdapter", { enumerable: true, get: function () { return resolve_local_js_2.resolveLocalAdapter; } });
29
+ var resolve_remote_js_2 = require("./resolve-remote.js");
30
+ Object.defineProperty(exports, "resolveRemoteAdapter", { enumerable: true, get: function () { return resolve_remote_js_2.resolveRemoteAdapter; } });
31
+ // Re-export adapters and core client for convenience
32
+ var client_js_2 = require("../core/client.js");
33
+ Object.defineProperty(exports, "LocalFirstClient", { enumerable: true, get: function () { return client_js_2.LocalFirstClient; } });
34
+ var expo_sqlite_js_1 = require("../adapters/local/expo-sqlite.js");
35
+ Object.defineProperty(exports, "ExpoSQLiteAdapter", { enumerable: true, get: function () { return expo_sqlite_js_1.ExpoSQLiteAdapter; } });
36
+ var op_sqlite_js_1 = require("../adapters/local/op-sqlite.js");
37
+ Object.defineProperty(exports, "OpSQLiteAdapter", { enumerable: true, get: function () { return op_sqlite_js_1.OpSQLiteAdapter; } });
38
+ var better_sqlite3_js_1 = require("../adapters/local/better-sqlite3.js");
39
+ Object.defineProperty(exports, "BetterSQLite3Adapter", { enumerable: true, get: function () { return better_sqlite3_js_1.BetterSQLite3Adapter; } });
40
+ var libsql_js_1 = require("../adapters/local/libsql.js");
41
+ Object.defineProperty(exports, "LibSQLAdapter", { enumerable: true, get: function () { return libsql_js_1.LibSQLAdapter; } });
42
+ var sql_js_js_1 = require("../adapters/local/sql-js.js");
43
+ Object.defineProperty(exports, "SqlJsAdapter", { enumerable: true, get: function () { return sql_js_js_1.SqlJsAdapter; } });
44
+ var node_sqlite_js_1 = require("../adapters/local/node-sqlite.js");
45
+ Object.defineProperty(exports, "NodeSQLiteAdapter", { enumerable: true, get: function () { return node_sqlite_js_1.NodeSQLiteAdapter; } });
46
+ var supabase_js_1 = require("../adapters/remote/supabase.js");
47
+ Object.defineProperty(exports, "SupabaseRemoteAdapter", { enumerable: true, get: function () { return supabase_js_1.SupabaseRemoteAdapter; } });
48
+ var convex_js_1 = require("../adapters/remote/convex.js");
49
+ Object.defineProperty(exports, "ConvexRemoteAdapter", { enumerable: true, get: function () { return convex_js_1.ConvexRemoteAdapter; } });
50
+ var firestore_js_1 = require("../adapters/remote/firestore.js");
51
+ Object.defineProperty(exports, "FirestoreRemoteAdapter", { enumerable: true, get: function () { return firestore_js_1.FirestoreRemoteAdapter; } });
52
+ var demo_js_1 = require("../adapters/remote/demo.js");
53
+ Object.defineProperty(exports, "MemoryDemoRemoteAdapter", { enumerable: true, get: function () { return demo_js_1.MemoryDemoRemoteAdapter; } });
54
+ /**
55
+ * Type-safe configuration helper function (similar to defineConfig in Vite/Tailwind).
56
+ */
57
+ function defineSyncConfig(config) {
58
+ return config;
59
+ }
60
+ /**
61
+ * Factory that instantiates, wires up, and initializes a LocalFirstClient from a LocalLiteConfig.
62
+ */
63
+ async function createSyncClient(config) {
64
+ // 1. Resolve Local Database Adapter
65
+ const localAdapter = await (0, resolve_local_js_1.resolveLocalAdapter)(config);
66
+ // 2. Resolve Remote Cloud Adapter
67
+ const remoteAdapter = await (0, resolve_remote_js_1.resolveRemoteAdapter)(config);
68
+ // 3. Resolve Network Provider
69
+ const network = config.network ?? new index_js_1.DefaultNetworkStatusProvider(true);
70
+ const autoSchema = config.autoSchema !== undefined
71
+ ? config.autoSchema
72
+ : !config.migrations || config.migrations.length === 0;
73
+ // 4. Instantiate Local-First Client
74
+ const client = new client_js_1.LocalFirstClient({
75
+ local: localAdapter,
76
+ remote: remoteAdapter,
77
+ network,
78
+ primaryKey: config.primaryKey ?? 'id',
79
+ conflictResolver: config.conflictResolver ?? 'last-write-wins',
80
+ pushIntervalMs: config.pushIntervalMs ?? 5000,
81
+ pullIntervalMs: config.pullIntervalMs ?? 10000,
82
+ maxRetries: config.maxRetries ?? 5,
83
+ retry: config.retry,
84
+ logger: config.logger,
85
+ logLevel: config.logLevel,
86
+ autoSchema,
87
+ tables: config.tables ?? [],
88
+ });
89
+ // 5. Execute Migrations & Auto-initialize
90
+ if (config.autoInitialize !== false) {
91
+ await client.initialize();
92
+ if (config.migrations && config.migrations.length > 0) {
93
+ for (const query of config.migrations) {
94
+ if (query && query.trim()) {
95
+ await client.db.execute(query);
96
+ }
97
+ }
98
+ }
99
+ }
100
+ return client;
101
+ }
@@ -0,0 +1,7 @@
1
+ import { LocalDBAdapter } from '../adapters/local/index.js';
2
+ import { LocalLiteConfig } from './types.js';
3
+ /**
4
+ * Resolves or instantiates the appropriate LocalDBAdapter based on the provided configuration.
5
+ * Auto-detects Expo SQLite, OP-SQLite, better-sqlite3, LibSQL, sql.js, and Node.js built-in sqlite.
6
+ */
7
+ export declare function resolveLocalAdapter(config: LocalLiteConfig): Promise<LocalDBAdapter>;
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveLocalAdapter = resolveLocalAdapter;
4
+ const expo_sqlite_js_1 = require("../adapters/local/expo-sqlite.js");
5
+ const op_sqlite_js_1 = require("../adapters/local/op-sqlite.js");
6
+ const better_sqlite3_js_1 = require("../adapters/local/better-sqlite3.js");
7
+ const libsql_js_1 = require("../adapters/local/libsql.js");
8
+ const sql_js_js_1 = require("../adapters/local/sql-js.js");
9
+ const node_sqlite_js_1 = require("../adapters/local/node-sqlite.js");
10
+ /**
11
+ * Resolves or instantiates the appropriate LocalDBAdapter based on the provided configuration.
12
+ * Auto-detects Expo SQLite, OP-SQLite, better-sqlite3, LibSQL, sql.js, and Node.js built-in sqlite.
13
+ */
14
+ async function resolveLocalAdapter(config) {
15
+ let rawDb = config.database;
16
+ if (typeof rawDb === 'function') {
17
+ rawDb = await rawDb();
18
+ }
19
+ // 1. @libsql/client (Turso / LibSQL) instance
20
+ if (rawDb && typeof rawDb.batch === 'function' && typeof rawDb.execute === 'function') {
21
+ return new libsql_js_1.LibSQLAdapter(rawDb);
22
+ }
23
+ // 2. sql.js Database instance
24
+ if (rawDb && typeof rawDb.export === 'function' && typeof rawDb.exec === 'function') {
25
+ return new sql_js_js_1.SqlJsAdapter(rawDb);
26
+ }
27
+ // 3. Custom object already implementing LocalDBAdapter
28
+ if (rawDb && typeof rawDb.execute === 'function' && typeof rawDb.transaction === 'function') {
29
+ return rawDb;
30
+ }
31
+ // 4. Expo SQLite database instance (openDatabaseAsync)
32
+ if (rawDb && typeof rawDb.getAllAsync === 'function') {
33
+ return new expo_sqlite_js_1.ExpoSQLiteAdapter(rawDb);
34
+ }
35
+ // 5. @op-engineering/op-sqlite or react-native-quick-sqlite instance
36
+ if (rawDb && typeof rawDb.executeAsync === 'function') {
37
+ return new op_sqlite_js_1.OpSQLiteAdapter(rawDb);
38
+ }
39
+ // 6. better-sqlite3 database instance
40
+ if (rawDb && typeof rawDb.prepare === 'function' && typeof rawDb.pragma === 'function') {
41
+ return new better_sqlite3_js_1.BetterSQLite3Adapter(rawDb);
42
+ }
43
+ // 7. Node.js 22+ built-in node:sqlite (DatabaseSync) instance
44
+ if (rawDb && typeof rawDb.prepare === 'function' && typeof rawDb.exec === 'function') {
45
+ return new node_sqlite_js_1.NodeSQLiteAdapter(rawDb);
46
+ }
47
+ // 8. Fallback for prepare-based SQLite instances
48
+ if (rawDb && typeof rawDb.prepare === 'function') {
49
+ return new better_sqlite3_js_1.BetterSQLite3Adapter(rawDb);
50
+ }
51
+ // 9. Auto-open by name using expo-sqlite if available
52
+ if (typeof rawDb === 'string' || config.databaseName) {
53
+ const dbName = typeof rawDb === 'string' ? rawDb : (config.databaseName ?? 'local_lite.db');
54
+ try {
55
+ const expoSqlite = await import('expo-sqlite');
56
+ if (expoSqlite && typeof expoSqlite.openDatabaseAsync === 'function') {
57
+ const expoDb = await expoSqlite.openDatabaseAsync(dbName);
58
+ return new expo_sqlite_js_1.ExpoSQLiteAdapter(expoDb);
59
+ }
60
+ throw new Error('expo-sqlite openDatabaseAsync not found');
61
+ }
62
+ catch (err) {
63
+ throw new Error(`Failed to auto-open SQLite database "${dbName}". ` +
64
+ `Please pass the database instance to config.database: ${err}`);
65
+ }
66
+ }
67
+ throw new Error('No database provided in sync config. Please specify config.database or config.databaseName.');
68
+ }
@@ -0,0 +1,7 @@
1
+ import { RemoteDBAdapter } from '../adapters/remote/index.js';
2
+ import { LocalLiteConfig } from './types.js';
3
+ /**
4
+ * Resolves or instantiates the appropriate RemoteDBAdapter based on the provided configuration.
5
+ * Supports Supabase, Convex, Firebase / Firestore, in-memory demo adapter, or custom adapters.
6
+ */
7
+ export declare function resolveRemoteAdapter(config: LocalLiteConfig): Promise<RemoteDBAdapter>;
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveRemoteAdapter = resolveRemoteAdapter;
4
+ const supabase_js_1 = require("../adapters/remote/supabase.js");
5
+ const convex_js_1 = require("../adapters/remote/convex.js");
6
+ const firestore_js_1 = require("../adapters/remote/firestore.js");
7
+ const demo_js_1 = require("../adapters/remote/demo.js");
8
+ /**
9
+ * Resolves or instantiates the appropriate RemoteDBAdapter based on the provided configuration.
10
+ * Supports Supabase, Convex, Firebase / Firestore, in-memory demo adapter, or custom adapters.
11
+ */
12
+ async function resolveRemoteAdapter(config) {
13
+ // 1. Explicit custom remote adapter
14
+ if (config.remote) {
15
+ return config.remote;
16
+ }
17
+ // 2. Firebase / Cloud Firestore
18
+ if (config.firebase?.firestore) {
19
+ return new firestore_js_1.FirestoreRemoteAdapter(config.firebase.firestore, {
20
+ tables: config.tables,
21
+ primaryKey: config.firebase.primaryKey ?? config.primaryKey,
22
+ deletedColumn: config.firebase.deletedColumn,
23
+ updatedAtColumn: config.firebase.updatedAtColumn,
24
+ firestoreModule: config.firebase.firestoreModule,
25
+ });
26
+ }
27
+ // 3. Convex
28
+ if (config.convex?.client) {
29
+ return new convex_js_1.ConvexRemoteAdapter(config.convex.client, {
30
+ tables: config.tables,
31
+ primaryKey: config.convex.primaryKey ?? config.primaryKey,
32
+ pushMutation: config.convex.pushMutation,
33
+ pullQuery: config.convex.pullQuery,
34
+ deletedColumn: config.convex.deletedColumn,
35
+ updatedAtColumn: config.convex.updatedAtColumn,
36
+ });
37
+ }
38
+ // 4. Supabase with pre-configured client
39
+ if (config.supabase?.client) {
40
+ return new supabase_js_1.SupabaseRemoteAdapter(config.supabase.client, {
41
+ tables: config.tables,
42
+ primaryKey: config.primaryKey,
43
+ schema: config.supabase.schema,
44
+ updatedAtColumn: config.supabase.updatedAtColumn,
45
+ deletedColumn: config.supabase.deletedColumn,
46
+ deletedValue: config.supabase.deletedValue,
47
+ deletedAtColumn: config.supabase.deletedAtColumn,
48
+ pushRpcName: config.supabase.pushRpcName,
49
+ channelName: config.supabase.channelName,
50
+ });
51
+ }
52
+ // 5. Supabase with URL + anonKey (lazy dynamic import)
53
+ if (config.supabase?.url && config.supabase?.anonKey) {
54
+ let supabaseModule;
55
+ try {
56
+ // @ts-ignore
57
+ supabaseModule = await import('@supabase/supabase-js');
58
+ }
59
+ catch {
60
+ throw new Error('To initialize Supabase from url and anonKey, please install @supabase/supabase-js or provide a pre-configured client to config.supabase.client');
61
+ }
62
+ const createClientFn = supabaseModule.createClient || supabaseModule.default?.createClient;
63
+ const supabaseClient = createClientFn(config.supabase.url, config.supabase.anonKey);
64
+ return new supabase_js_1.SupabaseRemoteAdapter(supabaseClient, {
65
+ tables: config.tables,
66
+ primaryKey: config.primaryKey,
67
+ schema: config.supabase.schema,
68
+ updatedAtColumn: config.supabase.updatedAtColumn,
69
+ deletedColumn: config.supabase.deletedColumn,
70
+ deletedValue: config.supabase.deletedValue,
71
+ deletedAtColumn: config.supabase.deletedAtColumn,
72
+ pushRpcName: config.supabase.pushRpcName,
73
+ channelName: config.supabase.channelName,
74
+ });
75
+ }
76
+ // 6. In-Memory Simulated Remote (Default when no cloud credentials provided)
77
+ if (config.inMemoryRemote !== false) {
78
+ return new demo_js_1.MemoryDemoRemoteAdapter(300);
79
+ }
80
+ throw new Error('No remote adapter configured. Please specify config.supabase, config.convex, config.firebase, or config.remote.');
81
+ }