local-lite 0.0.21 → 0.0.24

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 (50) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +45 -45
  3. package/dist/actions/store.js +15 -15
  4. package/dist/adapters/remote/convex.d.ts +7 -0
  5. package/dist/adapters/remote/convex.js +10 -1
  6. package/dist/adapters/remote/firestore.d.ts +7 -0
  7. package/dist/adapters/remote/firestore.js +23 -1
  8. package/dist/adapters/remote/supabase.d.ts +7 -0
  9. package/dist/adapters/remote/supabase.js +13 -2
  10. package/dist/auth/session.js +27 -27
  11. package/dist/core/client.d.ts +15 -1
  12. package/dist/core/client.js +46 -6
  13. package/dist/core/maintenance.d.ts +3 -1
  14. package/dist/core/maintenance.js +6 -2
  15. package/dist/core/meta-store.js +3 -3
  16. package/dist/core/outbox-store.js +23 -23
  17. package/dist/{react → devtools}/BenchmarkModal.d.ts +1 -1
  18. package/dist/{react → devtools}/BenchmarkModal.js +20 -20
  19. package/dist/{react → devtools}/SyncInspector.d.ts +3 -2
  20. package/dist/{react → devtools}/SyncInspector.js +43 -33
  21. package/dist/devtools/index.d.ts +3 -0
  22. package/dist/devtools/index.js +20 -0
  23. package/dist/keys/store.js +14 -14
  24. package/dist/kv/store.js +13 -13
  25. package/dist/react/index.d.ts +0 -3
  26. package/dist/react/index.js +0 -4
  27. package/dist/scheduler/store.js +28 -28
  28. package/dist/schema/generator.js +28 -28
  29. package/dist/search/store.js +15 -15
  30. package/dist/storage/convex.d.ts +43 -0
  31. package/dist/storage/convex.js +112 -0
  32. package/dist/storage/expo.d.ts +132 -0
  33. package/dist/storage/expo.js +408 -0
  34. package/dist/storage/factory.d.ts +16 -0
  35. package/dist/storage/factory.js +22 -0
  36. package/dist/storage/firebase.d.ts +44 -0
  37. package/dist/storage/firebase.js +135 -0
  38. package/dist/storage/index.d.ts +5 -0
  39. package/dist/storage/index.js +5 -0
  40. package/dist/storage/manager.d.ts +20 -0
  41. package/dist/storage/manager.js +144 -0
  42. package/dist/storage/store.js +24 -24
  43. package/dist/storage/supabase.d.ts +44 -0
  44. package/dist/storage/supabase.js +83 -0
  45. package/dist/storage/types.d.ts +29 -3
  46. package/dist/utils/portability.d.ts +4 -0
  47. package/dist/utils/portability.js +7 -1
  48. package/dist/utils/sql.d.ts +5 -0
  49. package/dist/utils/sql.js +21 -0
  50. package/package.json +204 -195
@@ -22,16 +22,16 @@ class OutboxStore {
22
22
  * Initializes the sync_outbox schema if it does not already exist.
23
23
  */
24
24
  static async ensureSchema(db) {
25
- await db.execute(`CREATE TABLE IF NOT EXISTS sync_outbox (
26
- id TEXT PRIMARY KEY,
27
- table_name TEXT NOT NULL,
28
- row_id TEXT NOT NULL,
29
- operation TEXT NOT NULL,
30
- payload TEXT,
31
- timestamp INTEGER NOT NULL,
32
- status TEXT NOT NULL DEFAULT 'PENDING',
33
- retry_count INTEGER NOT NULL DEFAULT 0,
34
- last_error TEXT
25
+ await db.execute(`CREATE TABLE IF NOT EXISTS sync_outbox (
26
+ id TEXT PRIMARY KEY,
27
+ table_name TEXT NOT NULL,
28
+ row_id TEXT NOT NULL,
29
+ operation TEXT NOT NULL,
30
+ payload TEXT,
31
+ timestamp INTEGER NOT NULL,
32
+ status TEXT NOT NULL DEFAULT 'PENDING',
33
+ retry_count INTEGER NOT NULL DEFAULT 0,
34
+ last_error TEXT
35
35
  );`);
36
36
  // Indexes for the hot paths: pending scans (push), per-row conflict lookups (pull),
37
37
  // and status counts (telemetry). Executed separately: many drivers prepare one statement.
@@ -43,7 +43,7 @@ class OutboxStore {
43
43
  * Inserts a new outbox entry within an existing database transaction.
44
44
  */
45
45
  static async insertRecord(tx, op) {
46
- await tx.execute(`INSERT INTO sync_outbox (id, table_name, row_id, operation, payload, timestamp, status, retry_count)
46
+ await tx.execute(`INSERT INTO sync_outbox (id, table_name, row_id, operation, payload, timestamp, status, retry_count)
47
47
  VALUES (?, ?, ?, ?, ?, ?, 'PENDING', 0)`, [
48
48
  op.id,
49
49
  op.tableName,
@@ -85,11 +85,11 @@ class OutboxStore {
85
85
  static async getPending(db, maxRetries = 5, limit = 50) {
86
86
  const isInfinite = !Number.isFinite(maxRetries) || maxRetries === -1;
87
87
  const query = isInfinite
88
- ? `SELECT * FROM sync_outbox
89
- WHERE status = 'PENDING' OR status = 'FAILED'
88
+ ? `SELECT * FROM sync_outbox
89
+ WHERE status = 'PENDING' OR status = 'FAILED'
90
90
  ORDER BY timestamp ASC LIMIT ?`
91
- : `SELECT * FROM sync_outbox
92
- WHERE status = 'PENDING' OR (status = 'FAILED' AND retry_count < ?)
91
+ : `SELECT * FROM sync_outbox
92
+ WHERE status = 'PENDING' OR (status = 'FAILED' AND retry_count < ?)
93
93
  ORDER BY timestamp ASC LIMIT ?`;
94
94
  const params = isInfinite ? [limit] : [maxRetries, limit];
95
95
  const rows = await db.execute(query, params);
@@ -101,8 +101,8 @@ class OutboxStore {
101
101
  * Finds the latest pending outbox record for a given table and row.
102
102
  */
103
103
  static async findPendingByRow(tx, tableName, rowId) {
104
- const rows = await tx.execute(`SELECT * FROM sync_outbox
105
- WHERE table_name = ? AND row_id = ? AND status != 'FAILED'
104
+ const rows = await tx.execute(`SELECT * FROM sync_outbox
105
+ WHERE table_name = ? AND row_id = ? AND status != 'FAILED'
106
106
  ORDER BY timestamp DESC LIMIT 1`, [tableName, rowId]);
107
107
  if (!rows || rows.length === 0)
108
108
  return undefined;
@@ -193,10 +193,10 @@ class OutboxStore {
193
193
  if (ids.length === 0)
194
194
  return;
195
195
  const placeholders = ids.map(() => '?').join(',');
196
- await db.execute(`UPDATE sync_outbox
197
- SET status = 'FAILED',
198
- retry_count = retry_count + 1,
199
- last_error = ?
196
+ await db.execute(`UPDATE sync_outbox
197
+ SET status = 'FAILED',
198
+ retry_count = retry_count + 1,
199
+ last_error = ?
200
200
  WHERE id IN (${placeholders})`, [errorMessage, ...ids]);
201
201
  }
202
202
  /**
@@ -235,8 +235,8 @@ class OutboxStore {
235
235
  try {
236
236
  if (ids && ids.length > 0) {
237
237
  const placeholders = ids.map(() => '?').join(',');
238
- await db.execute(`UPDATE sync_outbox
239
- SET status = 'PENDING', retry_count = 0, last_error = NULL
238
+ await db.execute(`UPDATE sync_outbox
239
+ SET status = 'PENDING', retry_count = 0, last_error = NULL
240
240
  WHERE status = 'FAILED' AND id IN (${placeholders})`, ids);
241
241
  return ids.length;
242
242
  }
@@ -1,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { LocalFirstClient } from '../core/client.js';
3
- import { type SyncThemeColors } from './theme.js';
3
+ import { type SyncThemeColors } from '../react/theme.js';
4
4
  export interface BuiltinBenchmarkResult {
5
5
  name: string;
6
6
  category: 'Local-Lite Client' | 'ACID / Transactions' | 'Outbox / Sync' | 'Memory / JSON';
@@ -37,7 +37,7 @@ exports.runSQLiteBenchmark = runSQLiteBenchmark;
37
37
  exports.BenchmarkModal = BenchmarkModal;
38
38
  const jsx_runtime_1 = require("react/jsx-runtime");
39
39
  const react_1 = __importStar(require("react"));
40
- const theme_js_1 = require("./theme.js");
40
+ const theme_js_1 = require("../react/theme.js");
41
41
  let RN;
42
42
  try {
43
43
  RN = require('react-native');
@@ -68,27 +68,27 @@ async function runSQLiteBenchmark(clientOrDb, onProgress) {
68
68
  await db.execute('DROP TABLE IF EXISTS "_sync_bench_tags"').catch(() => { });
69
69
  await db.execute('DROP TABLE IF EXISTS "_sync_bench_temp"').catch(() => { });
70
70
  await db.execute('DROP TABLE IF EXISTS "_sync_bench_outbox"').catch(() => { });
71
- await db.execute(`
72
- CREATE TABLE IF NOT EXISTS "_sync_bench_temp" (
73
- id TEXT PRIMARY KEY,
74
- title TEXT,
75
- score INTEGER,
76
- metadata TEXT,
77
- created_at TEXT
78
- )
71
+ await db.execute(`
72
+ CREATE TABLE IF NOT EXISTS "_sync_bench_temp" (
73
+ id TEXT PRIMARY KEY,
74
+ title TEXT,
75
+ score INTEGER,
76
+ metadata TEXT,
77
+ created_at TEXT
78
+ )
79
79
  `);
80
80
  await db.execute('CREATE INDEX IF NOT EXISTS "idx_sync_bench_score" ON "_sync_bench_temp" (score)');
81
- await db.execute(`
82
- CREATE TABLE IF NOT EXISTS "_sync_bench_outbox" (
83
- id TEXT PRIMARY KEY,
84
- tableName TEXT NOT NULL,
85
- rowId TEXT NOT NULL,
86
- operation TEXT NOT NULL,
87
- payload TEXT,
88
- timestamp INTEGER NOT NULL,
89
- status TEXT DEFAULT 'PENDING',
90
- retryCount INTEGER DEFAULT 0
91
- )
81
+ await db.execute(`
82
+ CREATE TABLE IF NOT EXISTS "_sync_bench_outbox" (
83
+ id TEXT PRIMARY KEY,
84
+ tableName TEXT NOT NULL,
85
+ rowId TEXT NOT NULL,
86
+ operation TEXT NOT NULL,
87
+ payload TEXT,
88
+ timestamp INTEGER NOT NULL,
89
+ status TEXT DEFAULT 'PENDING',
90
+ retryCount INTEGER DEFAULT 0
91
+ )
92
92
  `);
93
93
  await db.execute('CREATE INDEX IF NOT EXISTS "idx_sync_bench_outbox" ON "_sync_bench_outbox" (status, timestamp)');
94
94
  // 1. client.batch() Ingestion (500 rows in single atomic transaction)
@@ -1,8 +1,9 @@
1
1
  import React from 'react';
2
2
  import { LocalFirstClient } from '../core/client.js';
3
- import { type SyncThemeColors } from './theme.js';
3
+ import { type SyncThemeColors } from '../react/theme.js';
4
4
  export interface SyncInspectorProps {
5
5
  client: LocalFirstClient | null | undefined;
6
+ title?: string;
6
7
  position?: 'bottom-right' | 'bottom-left' | 'top-right' | 'top-left' | 'top' | 'bottom' | 'inline';
7
8
  variant?: 'pill' | 'compact' | 'icon' | 'minimal';
8
9
  initialOpen?: boolean;
@@ -26,4 +27,4 @@ export interface SyncInspectorProps {
26
27
  * Respects device safe areas and active theme (dark/light/custom).
27
28
  * Displays real-time outbox telemetry, connection status, metrics, and triggers manual push, pull, reset, and benchmarks.
28
29
  */
29
- export declare function SyncInspector({ client, position, variant, initialOpen, enableBenchmark, onOpenBenchmark, style, theme, alwaysVisible, renderTrigger, }: SyncInspectorProps): React.JSX.Element | null;
30
+ export declare function SyncInspector({ client, position, variant, initialOpen, enableBenchmark, onOpenBenchmark, style, theme, alwaysVisible, renderTrigger, title, }: SyncInspectorProps): React.JSX.Element | null;
@@ -3,9 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.SyncInspector = SyncInspector;
4
4
  const jsx_runtime_1 = require("react/jsx-runtime");
5
5
  const react_1 = require("react");
6
- const status_js_1 = require("./status.js");
6
+ const status_js_1 = require("../react/status.js");
7
7
  const BenchmarkModal_js_1 = require("./BenchmarkModal.js");
8
- const theme_js_1 = require("./theme.js");
8
+ const theme_js_1 = require("../react/theme.js");
9
9
  let RN;
10
10
  try {
11
11
  RN = require('react-native');
@@ -26,7 +26,7 @@ const BENCHMARK_DESCRIPTION = 'Runs automated stress tests directly validating l
26
26
  * Respects device safe areas and active theme (dark/light/custom).
27
27
  * Displays real-time outbox telemetry, connection status, metrics, and triggers manual push, pull, reset, and benchmarks.
28
28
  */
29
- function SyncInspector({ client, position = 'bottom-right', variant = 'pill', initialOpen = false, enableBenchmark = true, onOpenBenchmark, style, theme = 'auto', alwaysVisible = true, renderTrigger, }) {
29
+ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', initialOpen = false, enableBenchmark = true, onOpenBenchmark, style, theme = 'auto', alwaysVisible = true, renderTrigger, title = '⚡ Local Lite DevTools', }) {
30
30
  const [isOpen, setIsOpen] = (0, react_1.useState)(initialOpen);
31
31
  const [isBenchmarkModalOpen, setIsBenchmarkModalOpen] = (0, react_1.useState)(false);
32
32
  const status = (0, status_js_1.useSyncStatus)(client);
@@ -59,6 +59,33 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
59
59
  await client.syncWorker.pullRemoteChanges();
60
60
  });
61
61
  const handleRetryFailed = () => runAction('Re-queueing failed mutations...', (count) => `Re-queued ${count || 0} mutations`, () => client.retryFailedMutations());
62
+ const handleClearOutbox = () => {
63
+ const clearOutbox = () => runAction('Clearing stuck outbox...', (count) => `Cleared ${count || 0} stuck outbox mutations`, async () => {
64
+ client.syncWorker.pause();
65
+ try {
66
+ const count = await client.maintenance.pruneOutbox({ statuses: ['PENDING', 'PROCESSING', 'FAILED'] });
67
+ if (count > 0) {
68
+ client.events.emit('sync_outbox', { operation: 'DELETE', rowId: 'clear-stuck' });
69
+ }
70
+ await status.refresh();
71
+ return count;
72
+ }
73
+ finally {
74
+ client.syncWorker.resume();
75
+ }
76
+ });
77
+ const message = 'This permanently removes queued pending, processing, and failed mutations. Local database records are not deleted.';
78
+ if (RN?.Alert?.alert) {
79
+ RN.Alert.alert('Clear stuck outbox?', message, [
80
+ { text: 'Cancel', style: 'cancel' },
81
+ { text: 'Clear outbox', style: 'destructive', onPress: () => { void clearOutbox(); } },
82
+ ]);
83
+ return;
84
+ }
85
+ if (typeof globalThis.confirm === 'function' && globalThis.confirm(message)) {
86
+ void clearOutbox();
87
+ }
88
+ };
62
89
  const handleBenchmarkPress = () => {
63
90
  if (onOpenBenchmark) {
64
91
  onOpenBenchmark();
@@ -97,7 +124,8 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
97
124
  { id: 'pull', label: '📥 Pull Changes', onPress: handlePull, bg: '#2563EB', color: '#FFFFFF' },
98
125
  { id: 'retry', label: '🔄 Retry Failed', onPress: handleRetryFailed, bg: currentTheme.actionBg, color: currentTheme.actionText },
99
126
  { id: 'reset', label: '⏪ Reset Cursor', onPress: handleResetCursor, bg: currentTheme.actionBg, color: currentTheme.actionText },
100
- ], [handlePush, handlePull, handleRetryFailed, handleResetCursor, currentTheme.actionBg, currentTheme.actionText]);
127
+ { id: 'clear-outbox', label: 'Clear Stuck Outbox', onPress: handleClearOutbox, disabled: status.pendingCount === 0, bg: '#EF4444', color: '#FFFFFF' },
128
+ ], [handlePush, handlePull, handleRetryFailed, handleResetCursor, handleClearOutbox, currentTheme.actionBg, currentTheme.actionText, status.pendingCount]);
101
129
  const triggerLabel = variant === 'compact'
102
130
  ? status.pendingCount > 0
103
131
  ? `${status.pendingCount} queued`
@@ -141,7 +169,7 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
141
169
  borderBottomColor: currentTheme.border,
142
170
  },
143
171
  !RNSafeArea?.SafeAreaView && isAndroid && { paddingTop: statusBarHeight + 10 },
144
- ], children: [(0, jsx_runtime_1.jsxs)(View, { style: { flex: 1 }, children: [(0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.topBarTitle, { color: currentTheme.text }], children: "\u26A1 Local Lite DevTools" }), (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.topBarSub, { color: currentTheme.textMuted }], children: "Engine Inspector & Sync Telemetry" })] }), (0, jsx_runtime_1.jsx)(View, { style: [rnStyles.statusBadge, { borderColor: statusColor, backgroundColor: `${statusColor}20` }], children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.statusBadgeText, { color: statusColor }], children: statusLabel }) }), (0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: () => setIsOpen(false), style: [rnStyles.closeCircle, { backgroundColor: currentTheme.actionBg }], activeOpacity: 0.7, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.closeCircleText, { color: currentTheme.actionText }], children: "\u2715" }) })] }), (0, jsx_runtime_1.jsxs)(ScrollView, { style: [rnStyles.scrollBody, { backgroundColor: currentTheme.bg }], contentContainerStyle: rnStyles.scrollContent, showsVerticalScrollIndicator: true, children: [actionMessage && ((0, jsx_runtime_1.jsx)(View, { style: rnStyles.actionMsgBox, children: (0, jsx_runtime_1.jsx)(Text, { style: rnStyles.actionMsgText, children: actionMessage }) })), (0, jsx_runtime_1.jsx)(View, { style: rnStyles.statGrid, children: statCards.map((card) => ((0, jsx_runtime_1.jsxs)(View, { style: [
172
+ ], children: [(0, jsx_runtime_1.jsxs)(View, { style: { flex: 1 }, children: [(0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.topBarTitle, { color: currentTheme.text }], children: title }), (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.topBarSub, { color: currentTheme.textMuted }], children: "Engine Inspector & Sync Telemetry" })] }), (0, jsx_runtime_1.jsx)(View, { style: [rnStyles.statusBadge, { borderColor: statusColor, backgroundColor: `${statusColor}20` }], children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.statusBadgeText, { color: statusColor }], children: statusLabel }) }), (0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: () => setIsOpen(false), style: [rnStyles.closeCircle, { backgroundColor: currentTheme.actionBg }], activeOpacity: 0.7, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.closeCircleText, { color: currentTheme.actionText }], children: "\u2715" }) })] }), (0, jsx_runtime_1.jsxs)(ScrollView, { style: [rnStyles.scrollBody, { backgroundColor: currentTheme.bg }], contentContainerStyle: rnStyles.scrollContent, showsVerticalScrollIndicator: true, children: [actionMessage && ((0, jsx_runtime_1.jsx)(View, { style: rnStyles.actionMsgBox, children: (0, jsx_runtime_1.jsx)(Text, { style: rnStyles.actionMsgText, children: actionMessage }) })), (0, jsx_runtime_1.jsx)(View, { style: rnStyles.statGrid, children: statCards.map((card) => ((0, jsx_runtime_1.jsxs)(View, { style: [
145
173
  rnStyles.statCard,
146
174
  {
147
175
  backgroundColor: currentTheme.card,
@@ -153,7 +181,7 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
153
181
  backgroundColor: currentTheme.card,
154
182
  borderColor: currentTheme.border,
155
183
  },
156
- ], children: [(0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.infoCardTitle, { color: currentTheme.textMuted }], children: "ENGINE ACTIONS" }), (0, jsx_runtime_1.jsx)(View, { style: rnStyles.btnRow, children: actionButtons.slice(0, 2).map((btn) => ((0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: btn.onPress, style: [rnStyles.actionBtn, { backgroundColor: btn.bg }], activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.actionBtnText, { color: btn.color }], children: btn.label }) }, btn.id))) }), (0, jsx_runtime_1.jsx)(View, { style: [rnStyles.btnRow, { marginTop: 8 }], children: actionButtons.slice(2, 4).map((btn) => ((0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: btn.onPress, style: [rnStyles.actionBtn, { backgroundColor: btn.bg }], activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.actionBtnText, { color: btn.color }], children: btn.label }) }, btn.id))) })] }), (metrics?.lastPushError || metrics?.lastPullError) && ((0, jsx_runtime_1.jsxs)(View, { style: [rnStyles.infoCard, { borderColor: '#EF444480', backgroundColor: '#7F1D1D20' }], children: [(0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.infoCardTitle, { color: '#F87171' }], children: "SYNC ENGINE ERRORS" }), metrics?.lastPushError && ((0, jsx_runtime_1.jsxs)(View, { style: { marginBottom: 6 }, children: [(0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 11, color: '#FCA5A5', fontWeight: '700' }, children: "Last Push Error:" }), (0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 12, color: '#FECACA' }, children: String(metrics.lastPushError) })] })), metrics?.lastPullError && ((0, jsx_runtime_1.jsxs)(View, { children: [(0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 11, color: '#FCA5A5', fontWeight: '700' }, children: "Last Pull Error:" }), (0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 12, color: '#FECACA' }, children: String(metrics.lastPullError) })] }))] })), status.pendingRecords && status.pendingRecords.length > 0 && ((0, jsx_runtime_1.jsxs)(View, { style: [
184
+ ], children: [(0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.infoCardTitle, { color: currentTheme.textMuted }], children: "ENGINE ACTIONS" }), (0, jsx_runtime_1.jsx)(View, { style: rnStyles.btnRow, children: actionButtons.slice(0, 2).map((btn) => ((0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: btn.onPress, style: [rnStyles.actionBtn, { backgroundColor: btn.bg }], activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.actionBtnText, { color: btn.color }], children: btn.label }) }, btn.id))) }), (0, jsx_runtime_1.jsx)(View, { style: [rnStyles.btnRow, { marginTop: 8 }], children: actionButtons.slice(2, 4).map((btn) => ((0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: btn.onPress, style: [rnStyles.actionBtn, { backgroundColor: btn.bg }, btn.disabled && { opacity: 0.5 }], disabled: btn.disabled, activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.actionBtnText, { color: btn.color }], children: btn.label }) }, btn.id))) }), (0, jsx_runtime_1.jsx)(View, { style: [rnStyles.btnRow, { marginTop: 8 }], children: actionButtons.slice(4, 5).map((btn) => ((0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: btn.onPress, style: [rnStyles.actionBtn, { backgroundColor: btn.bg }, btn.disabled && { opacity: 0.5 }], disabled: btn.disabled, activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.actionBtnText, { color: btn.color }], children: btn.label }) }, btn.id))) })] }), (metrics?.lastPushError || metrics?.lastPullError) && ((0, jsx_runtime_1.jsxs)(View, { style: [rnStyles.infoCard, { borderColor: '#EF444480', backgroundColor: '#7F1D1D20' }], children: [(0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.infoCardTitle, { color: '#F87171' }], children: "SYNC ENGINE ERRORS" }), metrics?.lastPushError && ((0, jsx_runtime_1.jsxs)(View, { style: { marginBottom: 6 }, children: [(0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 11, color: '#FCA5A5', fontWeight: '700' }, children: "Last Push Error:" }), (0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 12, color: '#FECACA' }, children: String(metrics.lastPushError) })] })), metrics?.lastPullError && ((0, jsx_runtime_1.jsxs)(View, { children: [(0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 11, color: '#FCA5A5', fontWeight: '700' }, children: "Last Pull Error:" }), (0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 12, color: '#FECACA' }, children: String(metrics.lastPullError) })] }))] })), status.pendingRecords && status.pendingRecords.length > 0 && ((0, jsx_runtime_1.jsxs)(View, { style: [
157
185
  rnStyles.infoCard,
158
186
  {
159
187
  backgroundColor: currentTheme.card,
@@ -194,14 +222,7 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
194
222
  fontSize: 10,
195
223
  color: currentTheme.isDark ? '#A78BFA' : '#7C3AED',
196
224
  fontWeight: '700',
197
- }, children: "ACID & WAL" })] }), (0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 12, color: currentTheme.textMuted, marginBottom: 12, lineHeight: 18 }, children: BENCHMARK_DESCRIPTION }), (0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: handleBenchmarkPress, style: [rnStyles.actionBtn, { backgroundColor: '#8B5CF6' }], activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.actionBtnText, { color: '#FFFFFF', fontWeight: '700' }], children: "\u26A1 Open SQLite Benchmark Suite" }) })] }))] }), (0, jsx_runtime_1.jsx)(View, { style: [
198
- rnStyles.bottomBar,
199
- {
200
- backgroundColor: currentTheme.surface,
201
- borderTopColor: currentTheme.border,
202
- },
203
- !RNSafeArea?.SafeAreaView && isAndroid && { paddingBottom: 32 },
204
- ], children: (0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: () => setIsOpen(false), style: [rnStyles.doneBtn, { backgroundColor: currentTheme.actionBg }], activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.doneBtnText, { color: currentTheme.actionText }], children: "Close DevTools" }) }) })] }) }), enableBenchmark && !onOpenBenchmark && ((0, jsx_runtime_1.jsx)(BenchmarkModal_js_1.BenchmarkModal, { client: client, visible: isBenchmarkModalOpen, onDismiss: () => setIsBenchmarkModalOpen(false), theme: theme }))] }));
225
+ }, children: "ACID & WAL" })] }), (0, jsx_runtime_1.jsx)(Text, { style: { fontSize: 12, color: currentTheme.textMuted, marginBottom: 12, lineHeight: 18 }, children: BENCHMARK_DESCRIPTION }), (0, jsx_runtime_1.jsx)(TouchableOpacity, { onPress: handleBenchmarkPress, style: [rnStyles.actionBtn, { backgroundColor: '#8B5CF6' }], activeOpacity: 0.8, children: (0, jsx_runtime_1.jsx)(Text, { style: [rnStyles.actionBtnText, { color: '#FFFFFF', fontWeight: '700' }], children: "\u26A1 Open SQLite Benchmark Suite" }) })] }))] })] }) }), enableBenchmark && !onOpenBenchmark && ((0, jsx_runtime_1.jsx)(BenchmarkModal_js_1.BenchmarkModal, { client: client, visible: isBenchmarkModalOpen, onDismiss: () => setIsBenchmarkModalOpen(false), theme: theme }))] }));
205
226
  }
206
227
  // Web / DOM Renderer Fallback
207
228
  return ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [isOpen ? ((0, jsx_runtime_1.jsxs)("div", { style: {
@@ -225,7 +246,7 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
225
246
  height: 12,
226
247
  borderRadius: 6,
227
248
  backgroundColor: statusColor,
228
- } }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: 17, fontWeight: 700, color: currentTheme.text }, children: "\u26A1 Local Lite DevTools" }), (0, jsx_runtime_1.jsx)("div", { style: { fontSize: 11, color: currentTheme.textMuted, marginTop: 2 }, children: "Engine Inspector & Sync Telemetry" })] })] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', alignItems: 'center', gap: 12 }, children: [(0, jsx_runtime_1.jsx)("div", { style: {
249
+ } }), (0, jsx_runtime_1.jsxs)("div", { children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: 17, fontWeight: 700, color: currentTheme.text }, children: title }), (0, jsx_runtime_1.jsx)("div", { style: { fontSize: 11, color: currentTheme.textMuted, marginTop: 2 }, children: "Engine Inspector & Sync Telemetry" })] })] }), (0, jsx_runtime_1.jsxs)("div", { style: { display: 'flex', alignItems: 'center', gap: 12 }, children: [(0, jsx_runtime_1.jsx)("div", { style: {
229
250
  border: `1px solid ${statusColor}`,
230
251
  color: statusColor,
231
252
  padding: '3px 10px',
@@ -273,7 +294,7 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
273
294
  backgroundColor: currentTheme.card,
274
295
  border: `1px solid ${currentTheme.border}`,
275
296
  borderRadius: 12,
276
- padding: 16,
297
+ padding: 8,
277
298
  }, children: [(0, jsx_runtime_1.jsx)("div", { style: { fontSize: 24, fontWeight: 800, color: card.numColor }, children: card.num }), (0, jsx_runtime_1.jsx)("div", { style: { fontSize: 12, color: currentTheme.textSecondary, marginTop: 4, fontWeight: 600 }, children: card.label }), (0, jsx_runtime_1.jsx)("div", { style: { fontSize: 11, color: currentTheme.textMuted, marginTop: 2 }, children: card.sub })] }, card.id))) }), (0, jsx_runtime_1.jsxs)("div", { style: {
278
299
  backgroundColor: currentTheme.card,
279
300
  border: `1px solid ${currentTheme.border}`,
@@ -285,14 +306,15 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
285
306
  color: currentTheme.textMuted,
286
307
  letterSpacing: '0.8px',
287
308
  marginBottom: 12,
288
- }, children: "ENGINE ACTIONS" }), (0, jsx_runtime_1.jsx)("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }, children: actionButtons.map((btn) => ((0, jsx_runtime_1.jsx)("button", { onClick: btn.onPress, style: {
309
+ }, children: "ENGINE ACTIONS" }), (0, jsx_runtime_1.jsx)("div", { style: { display: 'grid', gridTemplateColumns: '1fr 1fr', gap: 10 }, children: actionButtons.map((btn) => ((0, jsx_runtime_1.jsx)("button", { onClick: btn.onPress, disabled: btn.disabled, style: {
289
310
  padding: '10px 14px',
290
311
  backgroundColor: btn.bg,
291
312
  color: btn.color,
292
313
  border: 'none',
293
314
  borderRadius: 8,
294
315
  fontWeight: 600,
295
- cursor: 'pointer',
316
+ cursor: btn.disabled ? 'not-allowed' : 'pointer',
317
+ opacity: btn.disabled ? 0.5 : 1,
296
318
  }, children: btn.label }, btn.id))) })] }), enableBenchmark && ((0, jsx_runtime_1.jsxs)("div", { style: {
297
319
  backgroundColor: currentTheme.isDark ? '#1E1B4B25' : '#F5F3FF',
298
320
  border: '1px solid #8B5CF640',
@@ -321,20 +343,7 @@ function SyncInspector({ client, position = 'bottom-right', variant = 'pill', in
321
343
  fontWeight: 700,
322
344
  fontSize: 13,
323
345
  cursor: 'pointer',
324
- }, children: "\u26A1 Open SQLite Benchmark Suite" })] }))] }), (0, jsx_runtime_1.jsx)("div", { style: {
325
- padding: '14px 20px',
326
- backgroundColor: currentTheme.surface,
327
- borderTop: `1px solid ${currentTheme.border}`,
328
- }, children: (0, jsx_runtime_1.jsx)("button", { onClick: () => setIsOpen(false), style: {
329
- width: '100%',
330
- padding: '12px',
331
- backgroundColor: currentTheme.actionBg,
332
- color: currentTheme.actionText,
333
- border: 'none',
334
- borderRadius: 8,
335
- fontWeight: 700,
336
- cursor: 'pointer',
337
- }, children: "Close DevTools" }) })] })) : ((0, jsx_runtime_1.jsx)("div", { style: isInline
346
+ }, children: "\u26A1 Open SQLite Benchmark Suite" })] }))] })] })) : ((0, jsx_runtime_1.jsx)("div", { style: isInline
338
347
  ? { position: 'relative', display: 'inline-flex', ...style }
339
348
  : {
340
349
  position: 'fixed',
@@ -453,7 +462,8 @@ const rnStyles = RN && RN.StyleSheet
453
462
  minWidth: '45%',
454
463
  borderWidth: 1,
455
464
  borderRadius: 12,
456
- padding: 14,
465
+ paddingVertical: 8,
466
+ paddingHorizontal: 12,
457
467
  },
458
468
  statNum: {
459
469
  fontSize: 24,
@@ -0,0 +1,3 @@
1
+ export * from './BenchmarkModal.js';
2
+ export { SyncInspector } from './SyncInspector.js';
3
+ export type { SyncInspectorProps } from './SyncInspector.js';
@@ -0,0 +1,20 @@
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.SyncInspector = void 0;
18
+ __exportStar(require("./BenchmarkModal.js"), exports);
19
+ var SyncInspector_js_1 = require("./SyncInspector.js");
20
+ Object.defineProperty(exports, "SyncInspector", { enumerable: true, get: function () { return SyncInspector_js_1.SyncInspector; } });
@@ -23,25 +23,25 @@ class KeyStore {
23
23
  * Initializes the API keys schema and indexes if they do not exist.
24
24
  */
25
25
  static async ensureSchema(db, tableName = exports.DEFAULT_KEYS_TABLE) {
26
- await db.execute(`CREATE TABLE IF NOT EXISTS ${tableName} (
27
- id TEXT PRIMARY KEY,
28
- name TEXT NOT NULL,
29
- prefix TEXT NOT NULL,
30
- display TEXT NOT NULL,
31
- key_hash TEXT NOT NULL UNIQUE,
32
- metadata TEXT,
33
- created_at INTEGER NOT NULL,
34
- expires_at INTEGER,
35
- last_used_at INTEGER,
36
- revoked INTEGER NOT NULL DEFAULT 0,
37
- revoked_at INTEGER
26
+ await db.execute(`CREATE TABLE IF NOT EXISTS ${tableName} (
27
+ id TEXT PRIMARY KEY,
28
+ name TEXT NOT NULL,
29
+ prefix TEXT NOT NULL,
30
+ display TEXT NOT NULL,
31
+ key_hash TEXT NOT NULL UNIQUE,
32
+ metadata TEXT,
33
+ created_at INTEGER NOT NULL,
34
+ expires_at INTEGER,
35
+ last_used_at INTEGER,
36
+ revoked INTEGER NOT NULL DEFAULT 0,
37
+ revoked_at INTEGER
38
38
  );`);
39
39
  await db.execute(`CREATE INDEX IF NOT EXISTS idx_${tableName}_hash ON ${tableName} (key_hash);`);
40
40
  await db.execute(`CREATE INDEX IF NOT EXISTS idx_${tableName}_revoked ON ${tableName} (revoked, expires_at);`);
41
41
  }
42
42
  static async insertRecord(db, tableName = exports.DEFAULT_KEYS_TABLE, record) {
43
- await db.execute(`INSERT INTO ${tableName} (
44
- id, name, prefix, display, key_hash, metadata, created_at, expires_at, last_used_at, revoked, revoked_at
43
+ await db.execute(`INSERT INTO ${tableName} (
44
+ id, name, prefix, display, key_hash, metadata, created_at, expires_at, last_used_at, revoked, revoked_at
45
45
  ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);`, [
46
46
  record.id,
47
47
  record.name,
package/dist/kv/store.js CHANGED
@@ -31,23 +31,23 @@ function mapKVRow(r) {
31
31
  }
32
32
  class KVStore {
33
33
  static async ensureSchema(db, tableName = exports.DEFAULT_KV_TABLE) {
34
- await db.execute(`CREATE TABLE IF NOT EXISTS ${tableName} (
35
- key TEXT PRIMARY KEY,
36
- value TEXT NOT NULL,
37
- type TEXT NOT NULL,
38
- created_at INTEGER NOT NULL,
39
- updated_at INTEGER NOT NULL,
40
- expires_at INTEGER
34
+ await db.execute(`CREATE TABLE IF NOT EXISTS ${tableName} (
35
+ key TEXT PRIMARY KEY,
36
+ value TEXT NOT NULL,
37
+ type TEXT NOT NULL,
38
+ created_at INTEGER NOT NULL,
39
+ updated_at INTEGER NOT NULL,
40
+ expires_at INTEGER
41
41
  );`);
42
42
  await db.execute(`CREATE INDEX IF NOT EXISTS idx_${tableName}_expires ON ${tableName} (expires_at);`);
43
43
  }
44
44
  static async set(db, tableName = exports.DEFAULT_KV_TABLE, key, valueStr, type, now, expiresAt) {
45
- await db.execute(`INSERT INTO ${tableName} (key, value, type, created_at, updated_at, expires_at)
46
- VALUES (?, ?, ?, ?, ?, ?)
47
- ON CONFLICT(key) DO UPDATE SET
48
- value = excluded.value,
49
- type = excluded.type,
50
- updated_at = excluded.updated_at,
45
+ await db.execute(`INSERT INTO ${tableName} (key, value, type, created_at, updated_at, expires_at)
46
+ VALUES (?, ?, ?, ?, ?, ?)
47
+ ON CONFLICT(key) DO UPDATE SET
48
+ value = excluded.value,
49
+ type = excluded.type,
50
+ updated_at = excluded.updated_at,
51
51
  expires_at = excluded.expires_at;`, [key, valueStr, type, now, now, expiresAt]);
52
52
  }
53
53
  static async get(db, tableName = exports.DEFAULT_KV_TABLE, key) {
@@ -126,9 +126,6 @@ export declare function useDatabaseMaintenance(client: LocalFirstClient | null |
126
126
  export * from './runner.js';
127
127
  export * from './status.js';
128
128
  export * from './theme.js';
129
- export * from './BenchmarkModal.js';
130
- export { SyncInspector } from './SyncInspector.js';
131
- export type { SyncInspectorProps } from './SyncInspector.js';
132
129
  export * from './auth.js';
133
130
  export * from './backups.js';
134
131
  export * from './kv.js';
@@ -14,7 +14,6 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
16
  Object.defineProperty(exports, "__esModule", { value: true });
17
- exports.SyncInspector = void 0;
18
17
  exports.clearLiveQueryCache = clearLiveQueryCache;
19
18
  exports.useLiveQuery = useLiveQuery;
20
19
  exports.useLiveQueries = useLiveQueries;
@@ -641,9 +640,6 @@ function useDatabaseMaintenance(client) {
641
640
  __exportStar(require("./runner.js"), exports);
642
641
  __exportStar(require("./status.js"), exports);
643
642
  __exportStar(require("./theme.js"), exports);
644
- __exportStar(require("./BenchmarkModal.js"), exports);
645
- var SyncInspector_js_1 = require("./SyncInspector.js");
646
- Object.defineProperty(exports, "SyncInspector", { enumerable: true, get: function () { return SyncInspector_js_1.SyncInspector; } });
647
643
  __exportStar(require("./auth.js"), exports);
648
644
  __exportStar(require("./backups.js"), exports);
649
645
  __exportStar(require("./kv.js"), exports);
@@ -40,40 +40,40 @@ function mapJobRunRow(r) {
40
40
  }
41
41
  class JobStore {
42
42
  static async ensureSchema(db, jobsTable = exports.DEFAULT_JOBS_TABLE, runsTable = exports.DEFAULT_JOB_RUNS_TABLE) {
43
- await db.execute(`CREATE TABLE IF NOT EXISTS ${jobsTable} (
44
- id TEXT PRIMARY KEY,
45
- name TEXT NOT NULL,
46
- schedule TEXT NOT NULL,
47
- interval_ms INTEGER NOT NULL,
48
- last_run_at INTEGER,
49
- next_run_at INTEGER NOT NULL,
50
- enabled INTEGER NOT NULL DEFAULT 1,
51
- payload TEXT,
52
- created_at INTEGER NOT NULL
43
+ await db.execute(`CREATE TABLE IF NOT EXISTS ${jobsTable} (
44
+ id TEXT PRIMARY KEY,
45
+ name TEXT NOT NULL,
46
+ schedule TEXT NOT NULL,
47
+ interval_ms INTEGER NOT NULL,
48
+ last_run_at INTEGER,
49
+ next_run_at INTEGER NOT NULL,
50
+ enabled INTEGER NOT NULL DEFAULT 1,
51
+ payload TEXT,
52
+ created_at INTEGER NOT NULL
53
53
  );`);
54
54
  await db.execute(`CREATE INDEX IF NOT EXISTS idx_${jobsTable}_next ON ${jobsTable} (next_run_at);`);
55
- await db.execute(`CREATE TABLE IF NOT EXISTS ${runsTable} (
56
- id TEXT PRIMARY KEY,
57
- job_id TEXT NOT NULL,
58
- status TEXT NOT NULL,
59
- started_at INTEGER NOT NULL,
60
- finished_at INTEGER,
61
- duration_ms INTEGER,
62
- error TEXT
55
+ await db.execute(`CREATE TABLE IF NOT EXISTS ${runsTable} (
56
+ id TEXT PRIMARY KEY,
57
+ job_id TEXT NOT NULL,
58
+ status TEXT NOT NULL,
59
+ started_at INTEGER NOT NULL,
60
+ finished_at INTEGER,
61
+ duration_ms INTEGER,
62
+ error TEXT
63
63
  );`);
64
64
  await db.execute(`CREATE INDEX IF NOT EXISTS idx_${runsTable}_job ON ${runsTable} (job_id, started_at);`);
65
65
  }
66
66
  static async upsertJob(db, jobsTable = exports.DEFAULT_JOBS_TABLE, job) {
67
67
  const payloadStr = job.payload !== undefined ? JSON.stringify(job.payload) : null;
68
- await db.execute(`INSERT INTO ${jobsTable} (id, name, schedule, interval_ms, last_run_at, next_run_at, enabled, payload, created_at)
69
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
70
- ON CONFLICT(id) DO UPDATE SET
71
- name = excluded.name,
72
- schedule = excluded.schedule,
73
- interval_ms = excluded.interval_ms,
74
- last_run_at = excluded.last_run_at,
75
- next_run_at = excluded.next_run_at,
76
- enabled = excluded.enabled,
68
+ await db.execute(`INSERT INTO ${jobsTable} (id, name, schedule, interval_ms, last_run_at, next_run_at, enabled, payload, created_at)
69
+ VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
70
+ ON CONFLICT(id) DO UPDATE SET
71
+ name = excluded.name,
72
+ schedule = excluded.schedule,
73
+ interval_ms = excluded.interval_ms,
74
+ last_run_at = excluded.last_run_at,
75
+ next_run_at = excluded.next_run_at,
76
+ enabled = excluded.enabled,
77
77
  payload = excluded.payload;`, [
78
78
  job.id,
79
79
  job.name,
@@ -111,7 +111,7 @@ class JobStore {
111
111
  }
112
112
  // --- Run History ---
113
113
  static async recordRunStart(db, runsTable = exports.DEFAULT_JOB_RUNS_TABLE, run) {
114
- await db.execute(`INSERT INTO ${runsTable} (id, job_id, status, started_at, finished_at, duration_ms, error)
114
+ await db.execute(`INSERT INTO ${runsTable} (id, job_id, status, started_at, finished_at, duration_ms, error)
115
115
  VALUES (?, ?, ?, ?, ?, ?, ?);`, [run.id, run.jobId, run.status, run.startedAt, null, null, null]);
116
116
  }
117
117
  static async recordRunFinish(db, runsTable = exports.DEFAULT_JOB_RUNS_TABLE, id, status, finishedAt, durationMs, error) {
@@ -134,34 +134,34 @@ function parseSchemaIndexes(content, options = {}) {
134
134
  */
135
135
  function formatSchemaIndexesCode(indexes) {
136
136
  const sqlStatements = indexes.map(i => i.sql).join('\n');
137
- return `/**
138
- * AUTO-GENERATED FILE - DO NOT EDIT MANUALLY.
139
- * Generated by local-lite schema index generator.
140
- */
141
-
142
- /**
143
- * SQLite index statements extracted from Supabase database schema.
144
- * Total indexes: ${indexes.length}
145
- */
146
- export const SCHEMA_INDEXES: readonly string[] = \`
147
- ${sqlStatements}
148
- \`.trim().split('\\n');
149
-
150
- /**
151
- * Applies all schema indexes into SQLite to ensure zero-latency local reads.
152
- * Executes each statement safely, ignoring errors if tables are not yet created.
153
- */
154
- export async function ensureSchemaIndexes(db: {
155
- execute: (query: string, params?: any[]) => Promise<any>;
156
- }): Promise<void> {
157
- for (const sql of SCHEMA_INDEXES) {
158
- try {
159
- await db.execute(sql);
160
- } catch {
161
- // Safe fallback if table has not been created yet
162
- }
163
- }
164
- }
137
+ return `/**
138
+ * AUTO-GENERATED FILE - DO NOT EDIT MANUALLY.
139
+ * Generated by local-lite schema index generator.
140
+ */
141
+
142
+ /**
143
+ * SQLite index statements extracted from Supabase database schema.
144
+ * Total indexes: ${indexes.length}
145
+ */
146
+ export const SCHEMA_INDEXES: readonly string[] = \`
147
+ ${sqlStatements}
148
+ \`.trim().split('\\n');
149
+
150
+ /**
151
+ * Applies all schema indexes into SQLite to ensure zero-latency local reads.
152
+ * Executes each statement safely, ignoring errors if tables are not yet created.
153
+ */
154
+ export async function ensureSchemaIndexes(db: {
155
+ execute: (query: string, params?: any[]) => Promise<any>;
156
+ }): Promise<void> {
157
+ for (const sql of SCHEMA_INDEXES) {
158
+ try {
159
+ await db.execute(sql);
160
+ } catch {
161
+ // Safe fallback if table has not been created yet
162
+ }
163
+ }
164
+ }
165
165
  `;
166
166
  }
167
167
  /**