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
@@ -0,0 +1,123 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSyncStatus = useSyncStatus;
4
+ exports.useSyncMetrics = useSyncMetrics;
5
+ const react_1 = require("react");
6
+ const runner_js_1 = require("./runner.js");
7
+ /**
8
+ * Consolidated hook that tracks real-time sync connectivity, sync worker activity
9
+ * (pushing/pulling/syncing), and pending items in the local SQLite outbox queue.
10
+ */
11
+ function useSyncStatus(client) {
12
+ const [isOnline, setIsOnline] = (0, react_1.useState)(true);
13
+ const [isPushing, setIsPushing] = (0, react_1.useState)(false);
14
+ const [isPulling, setIsPulling] = (0, react_1.useState)(false);
15
+ const [pendingCount, setPendingCount] = (0, react_1.useState)(0);
16
+ const [pendingRecords, setPendingRecords] = (0, react_1.useState)([]);
17
+ const [lastSyncedAt, setLastSyncedAt] = (0, react_1.useState)(null);
18
+ const refresh = (0, react_1.useCallback)(async () => {
19
+ if (!client || !client.db)
20
+ return;
21
+ try {
22
+ const [metrics, records, lastSynced] = await Promise.all([
23
+ client.getMetrics().catch(() => null),
24
+ client.getOutboxRecords({ limit: 50 }).catch(() => []),
25
+ client.getLastSyncedAt().catch(() => null),
26
+ ]);
27
+ if (metrics) {
28
+ setIsOnline(metrics.isOnline);
29
+ setIsPushing(metrics.isPushing);
30
+ setIsPulling(metrics.isPulling);
31
+ }
32
+ if (records) {
33
+ setPendingRecords(records);
34
+ const activeCount = metrics
35
+ ? metrics.outboxPendingCount + metrics.outboxProcessingCount + metrics.outboxFailedCount
36
+ : records.length;
37
+ setPendingCount(activeCount);
38
+ }
39
+ if (lastSynced !== null && lastSynced !== undefined) {
40
+ setLastSyncedAt(lastSynced > 0 ? lastSynced : null);
41
+ }
42
+ }
43
+ catch {
44
+ // Ignored if client is stopping or tables initializing
45
+ }
46
+ }, [client]);
47
+ const scheduleRefresh = (0, runner_js_1.useCoalescedRunner)(refresh);
48
+ (0, react_1.useEffect)(() => {
49
+ if (!client || !client.db)
50
+ return;
51
+ refresh();
52
+ const unsubStatus = client.events.on('sync:status', (e) => {
53
+ setIsOnline(e.isOnline);
54
+ setIsPushing(e.isPushing);
55
+ setIsPulling(e.isPulling);
56
+ setPendingCount(e.pendingCount);
57
+ });
58
+ const unsubOutbox = client.events.on('sync_outbox', () => {
59
+ scheduleRefresh();
60
+ });
61
+ const unsubPush = client.events.on('sync:push', () => {
62
+ scheduleRefresh();
63
+ });
64
+ const unsubPull = client.events.on('sync:pull', () => {
65
+ scheduleRefresh();
66
+ });
67
+ return () => {
68
+ unsubStatus();
69
+ unsubOutbox();
70
+ unsubPush();
71
+ unsubPull();
72
+ };
73
+ }, [client, refresh, scheduleRefresh]);
74
+ return {
75
+ isOnline,
76
+ isPushing,
77
+ isPulling,
78
+ isSyncing: isPushing || isPulling,
79
+ pendingCount,
80
+ pendingRecords,
81
+ lastSyncedAt,
82
+ refresh,
83
+ };
84
+ }
85
+ /**
86
+ * Hook that provides real-time sync telemetry and engine performance metrics.
87
+ */
88
+ function useSyncMetrics(client, pollIntervalMs = 3000) {
89
+ const [metrics, setMetrics] = (0, react_1.useState)(null);
90
+ const refresh = (0, react_1.useCallback)(async () => {
91
+ if (!client)
92
+ return;
93
+ try {
94
+ const data = await client.getMetrics();
95
+ setMetrics(data);
96
+ }
97
+ catch {
98
+ // Ignored
99
+ }
100
+ }, [client]);
101
+ const scheduleRefresh = (0, runner_js_1.useCoalescedRunner)(refresh);
102
+ (0, react_1.useEffect)(() => {
103
+ if (!client)
104
+ return;
105
+ refresh();
106
+ // Listen to sync events for immediate (coalesced) refresh
107
+ const unsubs = [
108
+ client.events.on('sync:push', () => scheduleRefresh()),
109
+ client.events.on('sync:pull', () => scheduleRefresh()),
110
+ client.events.on('sync:status', () => scheduleRefresh()),
111
+ ];
112
+ let timer;
113
+ if (pollIntervalMs > 0) {
114
+ timer = setInterval(scheduleRefresh, pollIntervalMs);
115
+ }
116
+ return () => {
117
+ unsubs.forEach((u) => u());
118
+ if (timer)
119
+ clearInterval(timer);
120
+ };
121
+ }, [client, refresh, scheduleRefresh, pollIntervalMs]);
122
+ return { metrics, refresh };
123
+ }
@@ -0,0 +1,18 @@
1
+ export interface SyncThemeColors {
2
+ isDark: boolean;
3
+ bg: string;
4
+ surface: string;
5
+ surfaceSubtle: string;
6
+ card: string;
7
+ border: string;
8
+ borderSubtle: string;
9
+ text: string;
10
+ textSecondary: string;
11
+ textMuted: string;
12
+ actionBg: string;
13
+ actionText: string;
14
+ accent: string;
15
+ }
16
+ export declare const darkTheme: SyncThemeColors;
17
+ export declare const lightTheme: SyncThemeColors;
18
+ export declare function resolveSyncTheme(themeProp?: 'auto' | 'dark' | 'light' | Partial<SyncThemeColors>, systemColorScheme?: 'dark' | 'light' | null): SyncThemeColors;
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.lightTheme = exports.darkTheme = void 0;
4
+ exports.resolveSyncTheme = resolveSyncTheme;
5
+ exports.darkTheme = {
6
+ isDark: true,
7
+ bg: '#111827',
8
+ surface: '#1F2937',
9
+ surfaceSubtle: '#1F293780',
10
+ card: '#1F2937',
11
+ border: '#374151',
12
+ borderSubtle: '#37415160',
13
+ text: '#F9FAFB',
14
+ textSecondary: '#D1D5DB',
15
+ textMuted: '#9CA3AF',
16
+ actionBg: '#374151',
17
+ actionText: '#F9FAFB',
18
+ accent: '#8B5CF6',
19
+ };
20
+ exports.lightTheme = {
21
+ isDark: false,
22
+ bg: '#F9FAFB',
23
+ surface: '#FFFFFF',
24
+ surfaceSubtle: '#F3F4F6',
25
+ card: '#FFFFFF',
26
+ border: '#E5E7EB',
27
+ borderSubtle: '#E5E7EB80',
28
+ text: '#111827',
29
+ textSecondary: '#374151',
30
+ textMuted: '#6B7280',
31
+ actionBg: '#E5E7EB',
32
+ actionText: '#1F2937',
33
+ accent: '#7C3AED',
34
+ };
35
+ function resolveSyncTheme(themeProp, systemColorScheme) {
36
+ if (typeof themeProp === 'object' && themeProp !== null) {
37
+ const base = themeProp.isDark === false ? exports.lightTheme : exports.darkTheme;
38
+ return { ...base, ...themeProp };
39
+ }
40
+ const mode = themeProp && themeProp !== 'auto'
41
+ ? themeProp
42
+ : systemColorScheme === 'light'
43
+ ? 'light'
44
+ : 'dark';
45
+ return mode === 'light' ? exports.lightTheme : exports.darkTheme;
46
+ }
@@ -28,6 +28,10 @@ export declare class SyncLogger {
28
28
  * Global default logger instance.
29
29
  */
30
30
  export declare const defaultLogger: SyncLogger;
31
+ /**
32
+ * Factory to create or resolve a SyncLogger from boolean, config, or existing logger.
33
+ */
34
+ export declare function createSyncLogger(logger?: SyncLogger | CustomLogger | boolean, logLevel?: LogLevel): SyncLogger;
31
35
  /**
32
36
  * Translates PostgreSQL / Supabase PostgREST error codes into actionable, human-readable explanations.
33
37
  */
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.defaultLogger = exports.SyncLogger = void 0;
4
+ exports.createSyncLogger = createSyncLogger;
4
5
  exports.formatSupabaseError = formatSupabaseError;
5
6
  exports.formatFirestoreError = formatFirestoreError;
6
7
  exports.formatConvexError = formatConvexError;
@@ -75,6 +76,10 @@ class SyncLogger {
75
76
  error(message, ...args) {
76
77
  if (!this.shouldLog('error'))
77
78
  return;
79
+ if (message.includes('Network Offline') || message.includes('Failed to reach')) {
80
+ this.warn(message, ...args);
81
+ return;
82
+ }
78
83
  if (this.customLogger?.error) {
79
84
  this.customLogger.error(message, ...args);
80
85
  }
@@ -88,6 +93,21 @@ exports.SyncLogger = SyncLogger;
88
93
  * Global default logger instance.
89
94
  */
90
95
  exports.defaultLogger = new SyncLogger();
96
+ /**
97
+ * Factory to create or resolve a SyncLogger from boolean, config, or existing logger.
98
+ */
99
+ function createSyncLogger(logger, logLevel) {
100
+ if (logger instanceof SyncLogger) {
101
+ return logger;
102
+ }
103
+ if (typeof logger === 'boolean') {
104
+ return new SyncLogger({ enabled: logger, level: logLevel });
105
+ }
106
+ if (logger && typeof logger === 'object') {
107
+ return new SyncLogger({ customLogger: logger, level: logLevel });
108
+ }
109
+ return logLevel ? new SyncLogger({ level: logLevel }) : exports.defaultLogger;
110
+ }
91
111
  /**
92
112
  * Translates PostgreSQL / Supabase PostgREST error codes into actionable, human-readable explanations.
93
113
  */
package/package.json CHANGED
@@ -1,133 +1,138 @@
1
- {
2
- "name": "local-lite",
3
- "version": "0.0.2",
4
- "description": "Never let a lost connection break a promise. Local-first sync that faithfully keeps data safe until you're back online.",
5
- "license": "MIT",
6
- "main": "dist/index.js",
7
- "types": "dist/index.d.ts",
8
- "files": [
9
- "dist",
10
- "README.md",
11
- "LICENSE"
12
- ],
13
- "exports": {
14
- ".": {
15
- "types": "./dist/index.d.ts",
16
- "import": "./dist/index.js",
17
- "default": "./dist/index.js"
18
- },
19
- "./react": {
20
- "types": "./dist/react/index.d.ts",
21
- "import": "./dist/react/index.js",
22
- "default": "./dist/react/index.js"
23
- },
24
- "./config": {
25
- "types": "./dist/config/index.d.ts",
26
- "import": "./dist/config/index.js",
27
- "default": "./dist/config/index.js"
28
- },
29
- "./supabase": {
30
- "types": "./dist/adapters/remote/supabase.d.ts",
31
- "import": "./dist/adapters/remote/supabase.js",
32
- "default": "./dist/adapters/remote/supabase.js"
33
- },
34
- "./convex": {
35
- "types": "./dist/adapters/remote/convex.d.ts",
36
- "import": "./dist/adapters/remote/convex.js",
37
- "default": "./dist/adapters/remote/convex.js"
38
- },
39
- "./firebase": {
40
- "types": "./dist/adapters/remote/firestore.d.ts",
41
- "import": "./dist/adapters/remote/firestore.js",
42
- "default": "./dist/adapters/remote/firestore.js"
43
- },
44
- "./expo-sqlite": {
45
- "types": "./dist/adapters/local/expo-sqlite.d.ts",
46
- "import": "./dist/adapters/local/expo-sqlite.js",
47
- "default": "./dist/adapters/local/expo-sqlite.js"
48
- },
49
- "./op-sqlite": {
50
- "types": "./dist/adapters/local/op-sqlite.d.ts",
51
- "import": "./dist/adapters/local/op-sqlite.js",
52
- "default": "./dist/adapters/local/op-sqlite.js"
53
- },
54
- "./better-sqlite3": {
55
- "types": "./dist/adapters/local/better-sqlite3.d.ts",
56
- "import": "./dist/adapters/local/better-sqlite3.js",
57
- "default": "./dist/adapters/local/better-sqlite3.js"
58
- },
59
- "./libsql": {
60
- "types": "./dist/adapters/local/libsql.d.ts",
61
- "import": "./dist/adapters/local/libsql.js",
62
- "default": "./dist/adapters/local/libsql.js"
63
- },
64
- "./sql-js": {
65
- "types": "./dist/adapters/local/sql-js.d.ts",
66
- "import": "./dist/adapters/local/sql-js.js",
67
- "default": "./dist/adapters/local/sql-js.js"
68
- },
69
- "./node-sqlite": {
70
- "types": "./dist/adapters/local/node-sqlite.d.ts",
71
- "import": "./dist/adapters/local/node-sqlite.js",
72
- "default": "./dist/adapters/local/node-sqlite.js"
73
- }
74
- },
75
- "scripts": {
76
- "build": "tsc",
77
- "test": "vitest run",
78
- "test:watch": "vitest",
79
- "prepublishOnly": "npm run test && npm run build"
80
- },
81
- "peerDependencies": {
82
- "@libsql/client": "^0.6.0",
83
- "@op-engineering/op-sqlite": "^0.1.0",
84
- "@supabase/supabase-js": "^2.0.0",
85
- "better-sqlite3": "^8.0.0",
86
- "convex": ">=1.0.0",
87
- "expo-sqlite": "^57.0.0",
88
- "firebase": ">=9.0.0",
89
- "react": ">=18.0.0",
90
- "sql.js": "^1.8.0"
91
- },
92
- "peerDependenciesMeta": {
93
- "react": {
94
- "optional": true
95
- },
96
- "@supabase/supabase-js": {
97
- "optional": true
98
- },
99
- "convex": {
100
- "optional": true
101
- },
102
- "firebase": {
103
- "optional": true
104
- },
105
- "expo-sqlite": {
106
- "optional": true
107
- },
108
- "@op-engineering/op-sqlite": {
109
- "optional": true
110
- },
111
- "better-sqlite3": {
112
- "optional": true
113
- },
114
- "@libsql/client": {
115
- "optional": true
116
- },
117
- "sql.js": {
118
- "optional": true
119
- }
120
- },
121
- "devDependencies": {
122
- "@supabase/supabase-js": "^2.117.3",
123
- "@types/react": "^19.3.0",
124
- "@types/uuid": "^11.0.0",
125
- "expo-sqlite": "^57.0.4",
126
- "react": "^19.3.0",
127
- "typescript": "^7.0.2",
128
- "vitest": "^5.0.4"
129
- },
130
- "dependencies": {
131
- "uuid": "^14.0.3"
132
- }
133
- }
1
+ {
2
+ "name": "local-lite",
3
+ "version": "0.0.4",
4
+ "description": "Never let a lost connection break a promise. Local-first sync that faithfully keeps data safe until you're back online.",
5
+ "license": "MIT",
6
+ "main": "dist/index.js",
7
+ "types": "dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md",
11
+ "LICENSE"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "import": "./dist/index.js",
17
+ "default": "./dist/index.js"
18
+ },
19
+ "./react": {
20
+ "types": "./dist/react/index.d.ts",
21
+ "import": "./dist/react/index.js",
22
+ "default": "./dist/react/index.js"
23
+ },
24
+ "./config": {
25
+ "types": "./dist/config/index.d.ts",
26
+ "import": "./dist/config/index.js",
27
+ "default": "./dist/config/index.js"
28
+ },
29
+ "./supabase": {
30
+ "types": "./dist/adapters/remote/supabase.d.ts",
31
+ "import": "./dist/adapters/remote/supabase.js",
32
+ "default": "./dist/adapters/remote/supabase.js"
33
+ },
34
+ "./convex": {
35
+ "types": "./dist/adapters/remote/convex.d.ts",
36
+ "import": "./dist/adapters/remote/convex.js",
37
+ "default": "./dist/adapters/remote/convex.js"
38
+ },
39
+ "./firebase": {
40
+ "types": "./dist/adapters/remote/firestore.d.ts",
41
+ "import": "./dist/adapters/remote/firestore.js",
42
+ "default": "./dist/adapters/remote/firestore.js"
43
+ },
44
+ "./expo-sqlite": {
45
+ "types": "./dist/adapters/local/expo-sqlite.d.ts",
46
+ "import": "./dist/adapters/local/expo-sqlite.js",
47
+ "default": "./dist/adapters/local/expo-sqlite.js"
48
+ },
49
+ "./op-sqlite": {
50
+ "types": "./dist/adapters/local/op-sqlite.d.ts",
51
+ "import": "./dist/adapters/local/op-sqlite.js",
52
+ "default": "./dist/adapters/local/op-sqlite.js"
53
+ },
54
+ "./better-sqlite3": {
55
+ "types": "./dist/adapters/local/better-sqlite3.d.ts",
56
+ "import": "./dist/adapters/local/better-sqlite3.js",
57
+ "default": "./dist/adapters/local/better-sqlite3.js"
58
+ },
59
+ "./libsql": {
60
+ "types": "./dist/adapters/local/libsql.d.ts",
61
+ "import": "./dist/adapters/local/libsql.js",
62
+ "default": "./dist/adapters/local/libsql.js"
63
+ },
64
+ "./sql-js": {
65
+ "types": "./dist/adapters/local/sql-js.d.ts",
66
+ "import": "./dist/adapters/local/sql-js.js",
67
+ "default": "./dist/adapters/local/sql-js.js"
68
+ },
69
+ "./node-sqlite": {
70
+ "types": "./dist/adapters/local/node-sqlite.d.ts",
71
+ "import": "./dist/adapters/local/node-sqlite.js",
72
+ "default": "./dist/adapters/local/node-sqlite.js"
73
+ },
74
+ "./core": {
75
+ "types": "./dist/core/index.d.ts",
76
+ "import": "./dist/core/index.js",
77
+ "default": "./dist/core/index.js"
78
+ }
79
+ },
80
+ "scripts": {
81
+ "build": "tsc",
82
+ "test": "vitest run",
83
+ "test:watch": "vitest",
84
+ "prepublishOnly": "npm run test && npm run build"
85
+ },
86
+ "peerDependencies": {
87
+ "@libsql/client": "^0.6.0",
88
+ "@op-engineering/op-sqlite": "^0.1.0",
89
+ "@supabase/supabase-js": "^2.0.0",
90
+ "better-sqlite3": "^8.0.0",
91
+ "convex": ">=1.0.0",
92
+ "expo-sqlite": ">=57.0.0",
93
+ "firebase": ">=9.0.0",
94
+ "react": ">=18.0.0",
95
+ "sql.js": "^1.8.0"
96
+ },
97
+ "peerDependenciesMeta": {
98
+ "react": {
99
+ "optional": true
100
+ },
101
+ "@supabase/supabase-js": {
102
+ "optional": true
103
+ },
104
+ "convex": {
105
+ "optional": true
106
+ },
107
+ "firebase": {
108
+ "optional": true
109
+ },
110
+ "expo-sqlite": {
111
+ "optional": true
112
+ },
113
+ "@op-engineering/op-sqlite": {
114
+ "optional": true
115
+ },
116
+ "better-sqlite3": {
117
+ "optional": true
118
+ },
119
+ "@libsql/client": {
120
+ "optional": true
121
+ },
122
+ "sql.js": {
123
+ "optional": true
124
+ }
125
+ },
126
+ "devDependencies": {
127
+ "@supabase/supabase-js": "^2.118.0",
128
+ "@types/react": "^19.3.0",
129
+ "@types/uuid": "^11.0.0",
130
+ "expo-sqlite": "^57.0.4",
131
+ "react": "^19.3.1",
132
+ "typescript": "^7.0.2",
133
+ "vitest": "^5.0.5"
134
+ },
135
+ "dependencies": {
136
+ "uuid": "^14.0.3"
137
+ }
138
+ }