local-lite 0.0.2 → 0.0.4

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 (57) hide show
  1. package/README.md +421 -362
  2. package/dist/adapters/local/better-sqlite3.d.ts +2 -0
  3. package/dist/adapters/local/better-sqlite3.js +46 -25
  4. package/dist/adapters/local/expo-sqlite.d.ts +5 -1
  5. package/dist/adapters/local/expo-sqlite.js +40 -7
  6. package/dist/adapters/local/index.d.ts +1 -0
  7. package/dist/adapters/local/node-sqlite.d.ts +2 -0
  8. package/dist/adapters/local/node-sqlite.js +45 -22
  9. package/dist/adapters/local/op-sqlite.d.ts +1 -0
  10. package/dist/adapters/local/op-sqlite.js +6 -0
  11. package/dist/adapters/local/sql-js.d.ts +1 -0
  12. package/dist/adapters/local/sql-js.js +5 -0
  13. package/dist/adapters/network/index.js +33 -0
  14. package/dist/adapters/remote/convex.d.ts +2 -2
  15. package/dist/adapters/remote/convex.js +8 -12
  16. package/dist/adapters/remote/demo.d.ts +1 -1
  17. package/dist/adapters/remote/demo.js +3 -0
  18. package/dist/adapters/remote/firestore.d.ts +3 -2
  19. package/dist/adapters/remote/firestore.js +69 -46
  20. package/dist/adapters/remote/index.d.ts +6 -4
  21. package/dist/adapters/remote/supabase.d.ts +34 -6
  22. package/dist/adapters/remote/supabase.js +340 -79
  23. package/dist/config/index.js +7 -10
  24. package/dist/config/resolve-local.js +12 -1
  25. package/dist/config/resolve-remote.js +33 -28
  26. package/dist/config/types.d.ts +30 -5
  27. package/dist/core/client.d.ts +86 -3
  28. package/dist/core/client.js +423 -41
  29. package/dist/core/index.d.ts +14 -0
  30. package/dist/core/index.js +29 -0
  31. package/dist/core/outbox-store.d.ts +8 -4
  32. package/dist/core/outbox-store.js +84 -65
  33. package/dist/core/query.d.ts +8 -0
  34. package/dist/core/query.js +37 -0
  35. package/dist/core/schema-manager.d.ts +9 -0
  36. package/dist/core/schema-manager.js +68 -0
  37. package/dist/core/sync-worker.d.ts +38 -1
  38. package/dist/core/sync-worker.js +351 -104
  39. package/dist/core/table-manager.js +26 -29
  40. package/dist/core/types.d.ts +8 -0
  41. package/dist/index.d.ts +1 -1
  42. package/dist/index.js +0 -1
  43. package/dist/react/BenchmarkModal.d.ts +32 -0
  44. package/dist/react/BenchmarkModal.js +777 -0
  45. package/dist/react/SyncInspector.d.ts +31 -0
  46. package/dist/react/SyncInspector.js +655 -0
  47. package/dist/react/index.d.ts +49 -19
  48. package/dist/react/index.js +266 -95
  49. package/dist/react/runner.d.ts +7 -0
  50. package/dist/react/runner.js +42 -0
  51. package/dist/react/status.d.ts +24 -0
  52. package/dist/react/status.js +123 -0
  53. package/dist/react/theme.d.ts +18 -0
  54. package/dist/react/theme.js +46 -0
  55. package/dist/utils/logger.d.ts +4 -0
  56. package/dist/utils/logger.js +20 -0
  57. package/package.json +138 -133
@@ -4,6 +4,15 @@ exports.TableManager = void 0;
4
4
  const sql_js_1 = require("../utils/sql.js");
5
5
  const schema_manager_js_1 = require("./schema-manager.js");
6
6
  const defaultSchemaManager = new schema_manager_js_1.SchemaManager();
7
+ function formatPayload(payload) {
8
+ const formatted = {};
9
+ if (payload && typeof payload === 'object') {
10
+ for (const [k, v] of Object.entries(payload)) {
11
+ formatted[k] = (0, schema_manager_js_1.formatValueForStorage)(v);
12
+ }
13
+ }
14
+ return formatted;
15
+ }
7
16
  class TableManager {
8
17
  /**
9
18
  * Applies an insert, update, or delete mutation on a local data table.
@@ -21,40 +30,28 @@ class TableManager {
21
30
  await tx.execute(`DELETE FROM ${safeTable} WHERE ${safePk} = ?`, [rowId]);
22
31
  return;
23
32
  }
24
- let rowData;
25
- if (options?.autoSchema) {
26
- const formatted = await sm.ensureColumns(tx, tableName, payload, primaryKey);
27
- rowData = { ...formatted, [primaryKey]: rowId };
28
- }
29
- else {
30
- const formatted = {};
31
- if (payload && typeof payload === 'object') {
32
- for (const k of Object.keys(payload)) {
33
- formatted[k] = (0, schema_manager_js_1.formatValueForStorage)(payload[k]);
34
- }
35
- }
36
- rowData = { ...formatted, [primaryKey]: rowId };
37
- }
33
+ const formatted = options?.autoSchema
34
+ ? await sm.ensureColumns(tx, tableName, payload, primaryKey)
35
+ : formatPayload(payload);
36
+ const rowData = { ...formatted, [primaryKey]: rowId };
38
37
  const keys = Object.keys(rowData);
39
38
  if (keys.length === 0)
40
39
  return;
41
- const existing = await tx.execute(`SELECT 1 FROM ${safeTable} WHERE ${safePk} = ? LIMIT 1`, [rowId]);
42
- if (existing && existing.length > 0) {
43
- // Row exists -> perform UPDATE
44
- const updateKeys = keys.filter((k) => k !== primaryKey);
45
- if (updateKeys.length > 0) {
46
- const setClause = updateKeys.map((k) => `${(0, sql_js_1.escapeIdentifier)(k)} = ?`).join(', ');
47
- const values = updateKeys.map((k) => rowData[k]);
48
- values.push(rowId);
49
- await tx.execute(`UPDATE ${safeTable} SET ${setClause} WHERE ${safePk} = ?`, values);
50
- }
40
+ // Use atomic SQLite UPSERT (INSERT ... ON CONFLICT DO UPDATE) for both INSERT and UPDATE.
41
+ // Handles missing rows, existing rows, and partial updates in a single SQL round-trip,
42
+ // completely eliminating the redundant SELECT 1 pre-check on every mutation!
43
+ const updateKeys = keys.filter((k) => k !== primaryKey);
44
+ const cols = keys.map((k) => (0, sql_js_1.escapeIdentifier)(k)).join(', ');
45
+ const placeholders = keys.map(() => '?').join(', ');
46
+ const values = keys.map((k) => rowData[k]);
47
+ if (updateKeys.length > 0) {
48
+ const updateClause = updateKeys
49
+ .map((k) => `${(0, sql_js_1.escapeIdentifier)(k)} = excluded.${(0, sql_js_1.escapeIdentifier)(k)}`)
50
+ .join(', ');
51
+ await tx.execute(`INSERT INTO ${safeTable} (${cols}) VALUES (${placeholders}) ON CONFLICT (${safePk}) DO UPDATE SET ${updateClause}`, values);
51
52
  }
52
53
  else {
53
- // Row does not exist -> perform INSERT
54
- const cols = keys.map((k) => (0, sql_js_1.escapeIdentifier)(k)).join(', ');
55
- const placeholders = keys.map(() => '?').join(', ');
56
- const values = keys.map((k) => rowData[k]);
57
- await tx.execute(`INSERT INTO ${safeTable} (${cols}) VALUES (${placeholders})`, values);
54
+ await tx.execute(`INSERT OR IGNORE INTO ${safeTable} (${cols}) VALUES (${placeholders})`, values);
58
55
  }
59
56
  }
60
57
  /**
@@ -8,6 +8,12 @@ export interface SyncOperation {
8
8
  payload: Record<string, any> | null;
9
9
  timestamp: number;
10
10
  }
11
+ export interface PushResult {
12
+ success?: boolean;
13
+ successfulIds?: string[];
14
+ failedIds?: string[];
15
+ errors?: Record<string, string>;
16
+ }
11
17
  export interface OutboxRecord extends SyncOperation {
12
18
  status: OutboxStatus;
13
19
  retryCount: number;
@@ -90,11 +96,13 @@ export type WhereFilter = Record<string, FieldFilter> | ((row: Record<string, an
90
96
  [key: string]: any;
91
97
  };
92
98
  export interface QueryOptions {
99
+ select?: string[];
93
100
  where?: WhereFilter;
94
101
  orderBy?: string;
95
102
  order?: 'asc' | 'desc' | 'ASC' | 'DESC';
96
103
  limit?: number;
97
104
  offset?: number;
105
+ includeDeleted?: boolean;
98
106
  }
99
107
  /**
100
108
  * Individual mutation descriptor for atomic batch execution.
package/dist/index.d.ts CHANGED
@@ -12,7 +12,7 @@ export * from './adapters/local/index.js';
12
12
  export * from './adapters/remote/index.js';
13
13
  export * from './adapters/network/index.js';
14
14
  export * from './config/index.js';
15
- export * from './react/index.js';
15
+ export type * from './react/index.js';
16
16
  export * from './utils/uuid.js';
17
17
  export * from './utils/sql.js';
18
18
  export * from './utils/logger.js';
package/dist/index.js CHANGED
@@ -28,7 +28,6 @@ __exportStar(require("./adapters/local/index.js"), exports);
28
28
  __exportStar(require("./adapters/remote/index.js"), exports);
29
29
  __exportStar(require("./adapters/network/index.js"), exports);
30
30
  __exportStar(require("./config/index.js"), exports);
31
- __exportStar(require("./react/index.js"), exports);
32
31
  __exportStar(require("./utils/uuid.js"), exports);
33
32
  __exportStar(require("./utils/sql.js"), exports);
34
33
  __exportStar(require("./utils/logger.js"), exports);
@@ -0,0 +1,32 @@
1
+ import React from 'react';
2
+ import { LocalFirstClient } from '../core/client.js';
3
+ import { type SyncThemeColors } from './theme.js';
4
+ export interface BuiltinBenchmarkResult {
5
+ name: string;
6
+ category: 'Local-Lite Client' | 'ACID / Transactions' | 'Outbox / Sync' | 'Memory / JSON';
7
+ description: string;
8
+ operations: number;
9
+ durationMs: number;
10
+ opsPerSec: number;
11
+ status: 'pending' | 'running' | 'success' | 'failed';
12
+ note?: string;
13
+ }
14
+ export interface BenchmarkModalProps {
15
+ client: LocalFirstClient | null | undefined;
16
+ visible: boolean;
17
+ onDismiss: () => void;
18
+ theme?: 'auto' | 'dark' | 'light' | Partial<SyncThemeColors>;
19
+ }
20
+ /**
21
+ * Executes a comprehensive performance & ACID test suite
22
+ * directly testing local-lite client APIs:
23
+ * client.batch(), client.insert(), client.update(), client.findById(),
24
+ * client.findMany() with SQL pushdown ($gt, $contains), client.transaction() rollbacks,
25
+ * client.trackMutation() outbox pipeline, and client.delete().
26
+ */
27
+ export declare function runSQLiteBenchmark(clientOrDb: any, onProgress?: (step: string, progress: number) => void): Promise<BuiltinBenchmarkResult[]>;
28
+ /**
29
+ * Pure package-driven, full-height Performance Benchmark Modal.
30
+ * Opens full-height with zero screen flickering or dynamic jumping.
31
+ */
32
+ export declare function BenchmarkModal({ client, visible, onDismiss, theme }: BenchmarkModalProps): React.JSX.Element | null;