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.
- package/LICENSE +21 -21
- package/README.md +45 -45
- package/dist/actions/store.js +15 -15
- package/dist/adapters/remote/convex.d.ts +7 -0
- package/dist/adapters/remote/convex.js +10 -1
- package/dist/adapters/remote/firestore.d.ts +7 -0
- package/dist/adapters/remote/firestore.js +23 -1
- package/dist/adapters/remote/supabase.d.ts +7 -0
- package/dist/adapters/remote/supabase.js +13 -2
- package/dist/auth/session.js +27 -27
- package/dist/core/client.d.ts +15 -1
- package/dist/core/client.js +46 -6
- package/dist/core/maintenance.d.ts +3 -1
- package/dist/core/maintenance.js +6 -2
- package/dist/core/meta-store.js +3 -3
- package/dist/core/outbox-store.js +23 -23
- package/dist/{react → devtools}/BenchmarkModal.d.ts +1 -1
- package/dist/{react → devtools}/BenchmarkModal.js +20 -20
- package/dist/{react → devtools}/SyncInspector.d.ts +3 -2
- package/dist/{react → devtools}/SyncInspector.js +43 -33
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +20 -0
- package/dist/keys/store.js +14 -14
- package/dist/kv/store.js +13 -13
- package/dist/react/index.d.ts +0 -3
- package/dist/react/index.js +0 -4
- package/dist/scheduler/store.js +28 -28
- package/dist/schema/generator.js +28 -28
- package/dist/search/store.js +15 -15
- package/dist/storage/convex.d.ts +43 -0
- package/dist/storage/convex.js +112 -0
- package/dist/storage/expo.d.ts +132 -0
- package/dist/storage/expo.js +408 -0
- package/dist/storage/factory.d.ts +16 -0
- package/dist/storage/factory.js +22 -0
- package/dist/storage/firebase.d.ts +44 -0
- package/dist/storage/firebase.js +135 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/manager.d.ts +20 -0
- package/dist/storage/manager.js +144 -0
- package/dist/storage/store.js +24 -24
- package/dist/storage/supabase.d.ts +44 -0
- package/dist/storage/supabase.js +83 -0
- package/dist/storage/types.d.ts +29 -3
- package/dist/utils/portability.d.ts +4 -0
- package/dist/utils/portability.js +7 -1
- package/dist/utils/sql.d.ts +5 -0
- package/dist/utils/sql.js +21 -0
- 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 '
|
|
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("
|
|
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 '
|
|
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("
|
|
6
|
+
const status_js_1 = require("../react/status.js");
|
|
7
7
|
const BenchmarkModal_js_1 = require("./BenchmarkModal.js");
|
|
8
|
-
const theme_js_1 = require("
|
|
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
|
-
|
|
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:
|
|
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)(
|
|
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:
|
|
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:
|
|
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" })] }))] })
|
|
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
|
-
|
|
465
|
+
paddingVertical: 8,
|
|
466
|
+
paddingHorizontal: 12,
|
|
457
467
|
},
|
|
458
468
|
statNum: {
|
|
459
469
|
fontSize: 24,
|
|
@@ -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; } });
|
package/dist/keys/store.js
CHANGED
|
@@ -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) {
|
package/dist/react/index.d.ts
CHANGED
|
@@ -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';
|
package/dist/react/index.js
CHANGED
|
@@ -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);
|
package/dist/scheduler/store.js
CHANGED
|
@@ -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) {
|
package/dist/schema/generator.js
CHANGED
|
@@ -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
|
/**
|