local-lite 0.0.4 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/adapters/local/better-sqlite3.d.ts +4 -1
- package/dist/adapters/local/better-sqlite3.js +34 -14
- package/dist/adapters/local/expo-sqlite.d.ts +10 -2
- package/dist/adapters/local/expo-sqlite.js +36 -20
- package/dist/adapters/local/node-sqlite.d.ts +4 -1
- package/dist/adapters/local/node-sqlite.js +29 -12
- package/dist/adapters/remote/firestore.js +39 -32
- package/dist/adapters/remote/supabase.d.ts +7 -2
- package/dist/adapters/remote/supabase.js +192 -85
- package/dist/config/types.d.ts +1 -1
- package/dist/core/client.d.ts +52 -15
- package/dist/core/client.js +195 -208
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/outbox-store.d.ts +5 -0
- package/dist/core/outbox-store.js +29 -4
- package/dist/core/query-builder.d.ts +120 -0
- package/dist/core/query-builder.js +441 -0
- package/dist/core/query.js +28 -32
- package/dist/core/schema-manager.d.ts +4 -15
- package/dist/core/schema-manager.js +48 -103
- package/dist/core/sync-worker.js +7 -2
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/react/SyncInspector.js +0 -70
- package/dist/react/index.d.ts +39 -0
- package/dist/react/index.js +232 -34
- package/dist/react/status.d.ts +5 -0
- package/dist/react/status.js +20 -12
- package/dist/schema/generator.d.ts +49 -0
- package/dist/schema/generator.js +209 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +17 -0
- package/dist/utils/uuid.d.ts +1 -1
- package/dist/utils/uuid.js +7 -16
- package/package.json +7 -2
|
@@ -137,20 +137,9 @@ class SupabaseRemoteAdapter {
|
|
|
137
137
|
batch.push(operations[i]);
|
|
138
138
|
i++;
|
|
139
139
|
}
|
|
140
|
-
const buildRowData = (item) => {
|
|
141
|
-
const row = {
|
|
142
|
-
...(item.payload || {}),
|
|
143
|
-
[this.primaryKey]: item.rowId,
|
|
144
|
-
[this.deletedColumn]: false,
|
|
145
|
-
};
|
|
146
|
-
if (this.updatedAtColumn && !row[this.updatedAtColumn]) {
|
|
147
|
-
row[this.updatedAtColumn] = new Date(item.timestamp).toISOString();
|
|
148
|
-
}
|
|
149
|
-
return row;
|
|
150
|
-
};
|
|
151
140
|
if (batch.length === 1) {
|
|
152
141
|
try {
|
|
153
|
-
const rowData = buildRowData(batch[0]);
|
|
142
|
+
const rowData = this.buildRowData(batch[0]);
|
|
154
143
|
const { error } = await client.from(op.tableName).upsert(rowData);
|
|
155
144
|
if (error) {
|
|
156
145
|
const msg = (0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'INSERT' });
|
|
@@ -174,7 +163,7 @@ class SupabaseRemoteAdapter {
|
|
|
174
163
|
const retryIndividually = async () => {
|
|
175
164
|
for (const item of batch) {
|
|
176
165
|
try {
|
|
177
|
-
const singleRow = buildRowData(item);
|
|
166
|
+
const singleRow = this.buildRowData(item);
|
|
178
167
|
const { error: singleErr } = await client.from(op.tableName).upsert(singleRow);
|
|
179
168
|
if (singleErr) {
|
|
180
169
|
const msg = (0, logger_js_1.formatSupabaseError)(singleErr, { table: op.tableName, operation: 'INSERT' });
|
|
@@ -193,7 +182,7 @@ class SupabaseRemoteAdapter {
|
|
|
193
182
|
}
|
|
194
183
|
};
|
|
195
184
|
try {
|
|
196
|
-
const rows = batch.map(buildRowData);
|
|
185
|
+
const rows = batch.map((item) => this.buildRowData(item));
|
|
197
186
|
const { error } = await client.from(op.tableName).upsert(rows);
|
|
198
187
|
if (!error) {
|
|
199
188
|
for (const item of batch)
|
|
@@ -209,87 +198,173 @@ class SupabaseRemoteAdapter {
|
|
|
209
198
|
}
|
|
210
199
|
continue;
|
|
211
200
|
}
|
|
212
|
-
//
|
|
201
|
+
// Batch consecutive DELETEs for the same table
|
|
213
202
|
if (op.operation === 'DELETE') {
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
203
|
+
const batch = [];
|
|
204
|
+
while (i < operations.length &&
|
|
205
|
+
operations[i].operation === 'DELETE' &&
|
|
206
|
+
operations[i].tableName === op.tableName) {
|
|
207
|
+
batch.push(operations[i]);
|
|
208
|
+
i++;
|
|
209
|
+
}
|
|
210
|
+
const patch = {
|
|
211
|
+
[this.deletedColumn]: this.deletedValue,
|
|
212
|
+
};
|
|
213
|
+
if (this.updatedAtColumn) {
|
|
214
|
+
patch[this.updatedAtColumn] = new Date(op.timestamp).toISOString();
|
|
215
|
+
}
|
|
216
|
+
if (batch.length === 1) {
|
|
217
|
+
try {
|
|
218
|
+
const { error } = await client
|
|
219
|
+
.from(op.tableName)
|
|
220
|
+
.update(patch)
|
|
221
|
+
.eq(this.primaryKey, op.rowId);
|
|
222
|
+
if (error) {
|
|
223
|
+
const msg = (0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'DELETE' });
|
|
224
|
+
this.logger.error(msg);
|
|
225
|
+
failedIds.push(op.id);
|
|
226
|
+
errors[op.id] = msg;
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
successfulIds.push(op.id);
|
|
230
|
+
}
|
|
220
231
|
}
|
|
221
|
-
|
|
222
|
-
.
|
|
223
|
-
.update(patch)
|
|
224
|
-
.eq(this.primaryKey, op.rowId);
|
|
225
|
-
if (error) {
|
|
226
|
-
const msg = (0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'DELETE' });
|
|
232
|
+
catch (err) {
|
|
233
|
+
const msg = (0, logger_js_1.formatSupabaseError)(err, { table: op.tableName, operation: 'DELETE' });
|
|
227
234
|
this.logger.error(msg);
|
|
228
235
|
failedIds.push(op.id);
|
|
229
236
|
errors[op.id] = msg;
|
|
230
237
|
}
|
|
231
|
-
else {
|
|
232
|
-
successfulIds.push(op.id);
|
|
233
|
-
}
|
|
234
238
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
239
|
+
else {
|
|
240
|
+
// Bulk soft-delete using .in() to collapse N round-trips into 1
|
|
241
|
+
const rowIds = batch.map((item) => item.rowId);
|
|
242
|
+
try {
|
|
243
|
+
const { error } = await client
|
|
244
|
+
.from(op.tableName)
|
|
245
|
+
.update(patch)
|
|
246
|
+
.in(this.primaryKey, rowIds);
|
|
247
|
+
if (!error) {
|
|
248
|
+
for (const item of batch)
|
|
249
|
+
successfulIds.push(item.id);
|
|
250
|
+
}
|
|
251
|
+
else {
|
|
252
|
+
// Fallback to individual updates if .in() fails
|
|
253
|
+
for (const item of batch) {
|
|
254
|
+
try {
|
|
255
|
+
const { error: singleErr } = await client
|
|
256
|
+
.from(op.tableName)
|
|
257
|
+
.update(patch)
|
|
258
|
+
.eq(this.primaryKey, item.rowId);
|
|
259
|
+
if (singleErr) {
|
|
260
|
+
const msg = (0, logger_js_1.formatSupabaseError)(singleErr, { table: op.tableName, operation: 'DELETE' });
|
|
261
|
+
failedIds.push(item.id);
|
|
262
|
+
errors[item.id] = msg;
|
|
263
|
+
}
|
|
264
|
+
else {
|
|
265
|
+
successfulIds.push(item.id);
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
catch (singleErr) {
|
|
269
|
+
const msg = (0, logger_js_1.formatSupabaseError)(singleErr, { table: op.tableName, operation: 'DELETE' });
|
|
270
|
+
failedIds.push(item.id);
|
|
271
|
+
errors[item.id] = msg;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
catch {
|
|
277
|
+
for (const item of batch)
|
|
278
|
+
failedIds.push(item.id);
|
|
279
|
+
}
|
|
240
280
|
}
|
|
241
|
-
i++;
|
|
242
281
|
continue;
|
|
243
282
|
}
|
|
244
|
-
//
|
|
283
|
+
// Batch consecutive UPDATEs for the same table
|
|
245
284
|
if (op.operation === 'UPDATE') {
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
285
|
+
const batch = [];
|
|
286
|
+
while (i < operations.length &&
|
|
287
|
+
operations[i].operation === 'UPDATE' &&
|
|
288
|
+
operations[i].tableName === op.tableName) {
|
|
289
|
+
batch.push(operations[i]);
|
|
290
|
+
i++;
|
|
291
|
+
}
|
|
292
|
+
if (batch.length === 1) {
|
|
293
|
+
const item = batch[0];
|
|
294
|
+
try {
|
|
295
|
+
const patch = this.buildRowData(item);
|
|
296
|
+
const { error, data } = await client
|
|
297
|
+
.from(op.tableName)
|
|
298
|
+
.update(patch)
|
|
299
|
+
.eq(this.primaryKey, item.rowId)
|
|
300
|
+
.select(this.primaryKey);
|
|
301
|
+
if (error) {
|
|
302
|
+
const msg = (0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'UPDATE' });
|
|
303
|
+
this.logger.error(msg);
|
|
304
|
+
failedIds.push(item.id);
|
|
305
|
+
errors[item.id] = msg;
|
|
306
|
+
}
|
|
307
|
+
else if (!data || data.length === 0) {
|
|
308
|
+
const { error: upsertErr } = await client.from(op.tableName).upsert(patch);
|
|
309
|
+
if (upsertErr) {
|
|
310
|
+
const msg = (0, logger_js_1.formatSupabaseError)(upsertErr, { table: op.tableName, operation: 'UPSERT_FALLBACK' });
|
|
311
|
+
this.logger.error(msg);
|
|
312
|
+
failedIds.push(item.id);
|
|
313
|
+
errors[item.id] = msg;
|
|
314
|
+
}
|
|
315
|
+
else {
|
|
316
|
+
successfulIds.push(item.id);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
successfulIds.push(item.id);
|
|
321
|
+
}
|
|
253
322
|
}
|
|
254
|
-
|
|
255
|
-
.
|
|
256
|
-
.update(patch)
|
|
257
|
-
.eq(this.primaryKey, op.rowId)
|
|
258
|
-
.select(this.primaryKey);
|
|
259
|
-
if (error) {
|
|
260
|
-
const msg = (0, logger_js_1.formatSupabaseError)(error, { table: op.tableName, operation: 'UPDATE' });
|
|
323
|
+
catch (err) {
|
|
324
|
+
const msg = (0, logger_js_1.formatSupabaseError)(err, { table: op.tableName, operation: 'UPDATE' });
|
|
261
325
|
this.logger.error(msg);
|
|
262
|
-
failedIds.push(
|
|
263
|
-
errors[
|
|
326
|
+
failedIds.push(item.id);
|
|
327
|
+
errors[item.id] = msg;
|
|
264
328
|
}
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
};
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
this.logger.error(msg);
|
|
275
|
-
failedIds.push(op.id);
|
|
276
|
-
errors[op.id] = msg;
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
// Bulk upsert for multiple consecutive updates (merges fields by primaryKey in 1 HTTP call)
|
|
332
|
+
const rows = batch.map((item) => this.buildRowData(item));
|
|
333
|
+
try {
|
|
334
|
+
const { error } = await client.from(op.tableName).upsert(rows);
|
|
335
|
+
if (!error) {
|
|
336
|
+
for (const item of batch)
|
|
337
|
+
successfulIds.push(item.id);
|
|
277
338
|
}
|
|
278
339
|
else {
|
|
279
|
-
|
|
340
|
+
// Fallback to individual updates if bulk upsert has a schema conflict
|
|
341
|
+
for (const item of batch) {
|
|
342
|
+
try {
|
|
343
|
+
const singleRow = this.buildRowData(item);
|
|
344
|
+
const { error: singleErr } = await client.from(op.tableName).upsert(singleRow);
|
|
345
|
+
if (singleErr) {
|
|
346
|
+
const msg = (0, logger_js_1.formatSupabaseError)(singleErr, { table: op.tableName, operation: 'UPDATE' });
|
|
347
|
+
failedIds.push(item.id);
|
|
348
|
+
errors[item.id] = msg;
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
successfulIds.push(item.id);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
catch (singleErr) {
|
|
355
|
+
const msg = (0, logger_js_1.formatSupabaseError)(singleErr, { table: op.tableName, operation: 'UPDATE' });
|
|
356
|
+
failedIds.push(item.id);
|
|
357
|
+
errors[item.id] = msg;
|
|
358
|
+
}
|
|
359
|
+
}
|
|
280
360
|
}
|
|
281
361
|
}
|
|
282
|
-
|
|
283
|
-
|
|
362
|
+
catch {
|
|
363
|
+
for (const item of batch) {
|
|
364
|
+
failedIds.push(item.id);
|
|
365
|
+
}
|
|
284
366
|
}
|
|
285
367
|
}
|
|
286
|
-
catch (err) {
|
|
287
|
-
const msg = (0, logger_js_1.formatSupabaseError)(err, { table: op.tableName, operation: 'UPDATE' });
|
|
288
|
-
this.logger.error(msg);
|
|
289
|
-
failedIds.push(op.id);
|
|
290
|
-
errors[op.id] = msg;
|
|
291
|
-
}
|
|
292
|
-
i++;
|
|
293
368
|
continue;
|
|
294
369
|
}
|
|
295
370
|
i++;
|
|
@@ -438,6 +513,20 @@ class SupabaseRemoteAdapter {
|
|
|
438
513
|
changes,
|
|
439
514
|
};
|
|
440
515
|
}
|
|
516
|
+
/**
|
|
517
|
+
* Formats a row payload for remote Supabase storage (adds primaryKey, deleted: false, updated_at).
|
|
518
|
+
*/
|
|
519
|
+
buildRowData(item) {
|
|
520
|
+
const row = {
|
|
521
|
+
...(item.payload || {}),
|
|
522
|
+
[this.primaryKey]: item.rowId,
|
|
523
|
+
[this.deletedColumn]: false,
|
|
524
|
+
};
|
|
525
|
+
if (this.updatedAtColumn && !row[this.updatedAtColumn]) {
|
|
526
|
+
row[this.updatedAtColumn] = new Date(item.timestamp).toISOString();
|
|
527
|
+
}
|
|
528
|
+
return row;
|
|
529
|
+
}
|
|
441
530
|
/**
|
|
442
531
|
* Helper to determine if a record represents a soft delete.
|
|
443
532
|
*/
|
|
@@ -454,6 +543,7 @@ class SupabaseRemoteAdapter {
|
|
|
454
543
|
}
|
|
455
544
|
/**
|
|
456
545
|
* Subscribes to live realtime postgres_changes from Supabase.
|
|
546
|
+
* Batches rapid burst events in the same microtask turn into a single SQLite sync transaction.
|
|
457
547
|
*/
|
|
458
548
|
subscribeToLiveChanges(callback) {
|
|
459
549
|
this.activeLiveCallback = callback;
|
|
@@ -479,6 +569,23 @@ class SupabaseRemoteAdapter {
|
|
|
479
569
|
};
|
|
480
570
|
}
|
|
481
571
|
const channel = client.channel(this.channelName);
|
|
572
|
+
let pendingLiveChanges = [];
|
|
573
|
+
let isScheduled = false;
|
|
574
|
+
const flushLiveChanges = () => {
|
|
575
|
+
isScheduled = false;
|
|
576
|
+
if (pendingLiveChanges.length === 0)
|
|
577
|
+
return;
|
|
578
|
+
const toSend = pendingLiveChanges;
|
|
579
|
+
pendingLiveChanges = [];
|
|
580
|
+
const maxTimestamp = Math.max(...toSend.map((c) => c.timestamp || 0), Date.now());
|
|
581
|
+
callback({
|
|
582
|
+
timestamp: maxTimestamp,
|
|
583
|
+
changes: toSend,
|
|
584
|
+
});
|
|
585
|
+
};
|
|
586
|
+
const scheduleFlush = typeof queueMicrotask === 'function'
|
|
587
|
+
? queueMicrotask
|
|
588
|
+
: (fn) => setTimeout(fn, 0);
|
|
482
589
|
for (const table of this.tables) {
|
|
483
590
|
channel.on('postgres_changes', { event: '*', schema: this.schema, table }, (payload) => {
|
|
484
591
|
let op = 'UPDATE';
|
|
@@ -495,18 +602,17 @@ class SupabaseRemoteAdapter {
|
|
|
495
602
|
const timestamp = record[this.updatedAtColumn]
|
|
496
603
|
? new Date(record[this.updatedAtColumn]).getTime()
|
|
497
604
|
: Date.now();
|
|
498
|
-
|
|
605
|
+
pendingLiveChanges.push({
|
|
606
|
+
tableName: table,
|
|
607
|
+
operation: finalOp,
|
|
608
|
+
rowId,
|
|
609
|
+
payload: finalOp === 'DELETE' ? null : record,
|
|
499
610
|
timestamp,
|
|
500
|
-
changes: [
|
|
501
|
-
{
|
|
502
|
-
tableName: table,
|
|
503
|
-
operation: finalOp,
|
|
504
|
-
rowId,
|
|
505
|
-
payload: finalOp === 'DELETE' ? null : record,
|
|
506
|
-
timestamp,
|
|
507
|
-
},
|
|
508
|
-
],
|
|
509
611
|
});
|
|
612
|
+
if (!isScheduled) {
|
|
613
|
+
isScheduled = true;
|
|
614
|
+
scheduleFlush(flushLiveChanges);
|
|
615
|
+
}
|
|
510
616
|
});
|
|
511
617
|
}
|
|
512
618
|
channel.subscribe((status) => {
|
|
@@ -516,6 +622,7 @@ class SupabaseRemoteAdapter {
|
|
|
516
622
|
});
|
|
517
623
|
this.activeChannel = channel;
|
|
518
624
|
return () => {
|
|
625
|
+
flushLiveChanges();
|
|
519
626
|
this.activeLiveCallback = undefined;
|
|
520
627
|
this.destroy();
|
|
521
628
|
};
|
package/dist/config/types.d.ts
CHANGED
|
@@ -178,7 +178,7 @@ export interface LocalLiteConfig {
|
|
|
178
178
|
*/
|
|
179
179
|
autoHydrateJSON?: boolean;
|
|
180
180
|
/**
|
|
181
|
-
* Scoped synchronization filter (e.g. {
|
|
181
|
+
* Scoped synchronization filter (e.g. { org_id: '...' } or dynamic resolver callback).
|
|
182
182
|
*/
|
|
183
183
|
syncScope?: Record<string, any> | (() => Record<string, any> | Promise<Record<string, any>>);
|
|
184
184
|
}
|
package/dist/core/client.d.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { LocalDBAdapter, LocalTransaction } from '../adapters/local/index.js';
|
|
|
2
2
|
import { RemoteDBAdapter } from '../adapters/remote/index.js';
|
|
3
3
|
import { EventEmitter } from '../reactive/event-emitter.js';
|
|
4
4
|
import { SyncWorker } from './sync-worker.js';
|
|
5
|
-
import { SchemaManager
|
|
6
|
-
import { BatchMutation, ConflictResolver, ConflictStrategy, MutateOptions, NetworkStatusProvider, OutboxRecord, OutboxStatus, QueryOptions, RetryConfig, SyncMetrics, SyncOp, TableChangeEvent } from './types.js';
|
|
5
|
+
import { SchemaManager } from './schema-manager.js';
|
|
6
|
+
import { BatchMutation, ConflictResolver, ConflictStrategy, MutateOptions, NetworkStatusProvider, OutboxRecord, OutboxStatus, QueryOptions, RetryConfig, SyncMetrics, SyncOp, TableChangeEvent, WhereFilter } from './types.js';
|
|
7
7
|
import { SyncLogger, CustomLogger, LogLevel } from '../utils/logger.js';
|
|
8
|
+
import { QueryBuilder, AggregateSpec, AggregateResult } from './query-builder.js';
|
|
8
9
|
export interface LocalFirstClientConfig {
|
|
9
10
|
local: LocalDBAdapter;
|
|
10
11
|
remote: RemoteDBAdapter;
|
|
@@ -51,6 +52,8 @@ export declare class TransactionalClient {
|
|
|
51
52
|
includeDeleted?: boolean;
|
|
52
53
|
}): Promise<T[]>;
|
|
53
54
|
findMany<T = any>(tableName: string, options?: QueryOptions): Promise<T[]>;
|
|
55
|
+
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
|
56
|
+
from<T = any>(tableName: string): QueryBuilder<T>;
|
|
54
57
|
get rawTx(): LocalTransaction;
|
|
55
58
|
}
|
|
56
59
|
export declare class LocalFirstClient {
|
|
@@ -149,15 +152,57 @@ export declare class LocalFirstClient {
|
|
|
149
152
|
* Pushes simple equality where clauses down to SQLite for high performance, with memory fallback.
|
|
150
153
|
* Gracefully returns [] if table or columns have not been populated yet on empty schemas.
|
|
151
154
|
*
|
|
155
|
+
/**
|
|
156
|
+
* Executes a raw relational SQL query with parameters directly against SQLite.
|
|
157
|
+
* Enables complex multi-table JOINs, GROUP BY aggregations (COUNT, SUM, AVG), CTEs, and window functions.
|
|
158
|
+
*/
|
|
159
|
+
query<T = any>(sql: string, params?: any[]): Promise<T[]>;
|
|
160
|
+
/**
|
|
161
|
+
* Returns a fluent SQL QueryBuilder for the specified table.
|
|
162
|
+
* Allows writing clean JavaScript method chains (select, join, leftJoin, where, orderBy, groupBy)
|
|
163
|
+
* that automatically compile into optimized native SQLite queries.
|
|
164
|
+
*
|
|
152
165
|
* Example:
|
|
153
|
-
* const
|
|
154
|
-
*
|
|
166
|
+
* const activeRentals = await client
|
|
167
|
+
* .from('contracts')
|
|
168
|
+
* .join('houses', 'contracts.house_id', 'houses.id')
|
|
169
|
+
* .leftJoin('tenants', 'contracts.tenant_id', 'tenants.id')
|
|
170
|
+
* .where({ 'contracts.business_id': bizId, 'contracts.status': 'ACTIVE' })
|
|
171
|
+
* .select('contracts.id', 'contracts.rent_amount', 'houses.house_number', 'tenants.name')
|
|
172
|
+
* .execute();
|
|
155
173
|
*/
|
|
156
|
-
|
|
174
|
+
from<T = any>(tableName: string): QueryBuilder<T>;
|
|
175
|
+
/**
|
|
176
|
+
* Executes a native SQLite COUNT(*) query and returns the count directly.
|
|
177
|
+
*/
|
|
178
|
+
count(tableName: string, options?: {
|
|
179
|
+
where?: WhereFilter;
|
|
180
|
+
}): Promise<number>;
|
|
181
|
+
/**
|
|
182
|
+
* Executes a native SQLite SUM(column) query and returns the sum directly.
|
|
183
|
+
*/
|
|
184
|
+
sum(tableName: string, column: string, options?: {
|
|
185
|
+
where?: WhereFilter;
|
|
186
|
+
}): Promise<number>;
|
|
157
187
|
/**
|
|
158
|
-
*
|
|
188
|
+
* Executes a native SQLite AVG(column) query and returns the average directly.
|
|
159
189
|
*/
|
|
160
|
-
|
|
190
|
+
avg(tableName: string, column: string, options?: {
|
|
191
|
+
where?: WhereFilter;
|
|
192
|
+
}): Promise<number | null>;
|
|
193
|
+
/**
|
|
194
|
+
* Executes native multi-metric SQLite aggregation in a single query.
|
|
195
|
+
*
|
|
196
|
+
* Example:
|
|
197
|
+
* const stats = await client.aggregate('receipts', {
|
|
198
|
+
* where: { business_id: bizId },
|
|
199
|
+
* count: true,
|
|
200
|
+
* sum: ['amount', 'refunded_amount'],
|
|
201
|
+
* avg: ['amount']
|
|
202
|
+
* });
|
|
203
|
+
*/
|
|
204
|
+
aggregate(tableName: string, spec: AggregateSpec): Promise<AggregateResult>;
|
|
205
|
+
findMany<T = any>(tableName: string, options?: QueryOptions): Promise<T[]>;
|
|
161
206
|
/**
|
|
162
207
|
* Finds a single record by primary key.
|
|
163
208
|
*
|
|
@@ -167,14 +212,6 @@ export declare class LocalFirstClient {
|
|
|
167
212
|
findById<T = any>(tableName: string, id: string, options?: {
|
|
168
213
|
includeDeleted?: boolean;
|
|
169
214
|
}): Promise<T | null>;
|
|
170
|
-
/**
|
|
171
|
-
* Finds multiple records by primary keys in a single batched SQL query.
|
|
172
|
-
* Eliminates the N-roundtrip bridge overhead of multiple findById calls.
|
|
173
|
-
* Automatically omits soft-deleted records unless includeDeleted: true.
|
|
174
|
-
*
|
|
175
|
-
* Example:
|
|
176
|
-
* const tenants = await client.findByIds('tenants', ['ten-1', 'ten-2', 'ten-3']);
|
|
177
|
-
*/
|
|
178
215
|
findByIds<T = any>(tableName: string, ids: string[], options?: {
|
|
179
216
|
includeDeleted?: boolean;
|
|
180
217
|
}): Promise<T[]>;
|