local-lite 0.0.3 → 0.0.5

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 (63) 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 +133 -5
  28. package/dist/core/client.js +431 -46
  29. package/dist/core/index.d.ts +15 -0
  30. package/dist/core/index.js +30 -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-builder.d.ts +119 -0
  34. package/dist/core/query-builder.js +372 -0
  35. package/dist/core/query.d.ts +8 -0
  36. package/dist/core/query.js +37 -0
  37. package/dist/core/schema-manager.d.ts +9 -0
  38. package/dist/core/schema-manager.js +103 -0
  39. package/dist/core/sync-worker.d.ts +38 -1
  40. package/dist/core/sync-worker.js +351 -104
  41. package/dist/core/table-manager.js +26 -29
  42. package/dist/core/types.d.ts +8 -0
  43. package/dist/index.d.ts +2 -0
  44. package/dist/index.js +2 -0
  45. package/dist/react/BenchmarkModal.d.ts +32 -0
  46. package/dist/react/BenchmarkModal.js +777 -0
  47. package/dist/react/SyncInspector.d.ts +31 -0
  48. package/dist/react/SyncInspector.js +585 -0
  49. package/dist/react/index.d.ts +89 -18
  50. package/dist/react/index.js +450 -105
  51. package/dist/react/runner.d.ts +7 -0
  52. package/dist/react/runner.js +42 -0
  53. package/dist/react/status.d.ts +24 -0
  54. package/dist/react/status.js +123 -0
  55. package/dist/react/theme.d.ts +18 -0
  56. package/dist/react/theme.js +46 -0
  57. package/dist/schema/generator.d.ts +49 -0
  58. package/dist/schema/generator.js +209 -0
  59. package/dist/schema/index.d.ts +1 -0
  60. package/dist/schema/index.js +17 -0
  61. package/dist/utils/logger.d.ts +4 -0
  62. package/dist/utils/logger.js +20 -0
  63. package/package.json +143 -133
@@ -0,0 +1,24 @@
1
+ import { LocalFirstClient } from '../core/client.js';
2
+ import { SyncMetrics } from '../core/types.js';
3
+ export interface UseSyncStatusResult {
4
+ isOnline: boolean;
5
+ isPushing: boolean;
6
+ isPulling: boolean;
7
+ isSyncing: boolean;
8
+ pendingCount: number;
9
+ pendingRecords: any[];
10
+ lastSyncedAt: number | null;
11
+ refresh: () => Promise<void>;
12
+ }
13
+ /**
14
+ * Consolidated hook that tracks real-time sync connectivity, sync worker activity
15
+ * (pushing/pulling/syncing), and pending items in the local SQLite outbox queue.
16
+ */
17
+ export declare function useSyncStatus(client: LocalFirstClient | null | undefined): UseSyncStatusResult;
18
+ /**
19
+ * Hook that provides real-time sync telemetry and engine performance metrics.
20
+ */
21
+ export declare function useSyncMetrics(client: LocalFirstClient | null | undefined, pollIntervalMs?: number): {
22
+ metrics: SyncMetrics | null;
23
+ refresh: () => Promise<void>;
24
+ };
@@ -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
+ }
@@ -0,0 +1,49 @@
1
+ export interface SchemaIndexDefinition {
2
+ table: string;
3
+ columns: string[];
4
+ isOneToOne: boolean;
5
+ sql: string;
6
+ referencedRelation?: string;
7
+ referencedColumns?: string[];
8
+ comment?: string;
9
+ }
10
+ export interface GenerateSchemaIndexesOptions {
11
+ /**
12
+ * Path to the Supabase database.types.ts file.
13
+ */
14
+ typesPath?: string;
15
+ /**
16
+ * Raw string content of the database.types.ts file.
17
+ */
18
+ typesContent?: string;
19
+ /**
20
+ * Target path where the generated TypeScript file should be written.
21
+ */
22
+ outputPath?: string;
23
+ /**
24
+ * Whether to also generate composite sync indexes for tables with business_id and deleted.
25
+ * Default: true
26
+ */
27
+ includeBusinessSyncIndexes?: boolean;
28
+ }
29
+ export interface GenerateSchemaIndexesResult {
30
+ indexes: SchemaIndexDefinition[];
31
+ code: string;
32
+ written: boolean;
33
+ outputPath?: string;
34
+ }
35
+ /**
36
+ * Parses raw Supabase database.types.ts content and extracts foreign-key index definitions.
37
+ */
38
+ export declare function parseSchemaIndexes(content: string, options?: {
39
+ includeBusinessSyncIndexes?: boolean;
40
+ }): SchemaIndexDefinition[];
41
+ /**
42
+ * Generates TypeScript source code containing index definitions and an applicator helper.
43
+ */
44
+ export declare function formatSchemaIndexesCode(indexes: SchemaIndexDefinition[]): string;
45
+ /**
46
+ * Core generator function.
47
+ * Accepts the path or content of database.types.ts and generates schema indexes.
48
+ */
49
+ export declare function generateSchemaIndexes(typesPathOrOptions: string | GenerateSchemaIndexesOptions, maybeOutputPath?: string): Promise<GenerateSchemaIndexesResult>;
@@ -0,0 +1,209 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.parseSchemaIndexes = parseSchemaIndexes;
4
+ exports.formatSchemaIndexesCode = formatSchemaIndexesCode;
5
+ exports.generateSchemaIndexes = generateSchemaIndexes;
6
+ async function getNodeModules() {
7
+ try {
8
+ const fsModName = 'node:fs';
9
+ const pathModName = 'node:path';
10
+ const fsMod = await import(/* @vite-ignore */ fsModName);
11
+ const pathMod = await import(/* @vite-ignore */ pathModName);
12
+ return {
13
+ fs: (fsMod.default || fsMod),
14
+ path: (pathMod.default || pathMod),
15
+ };
16
+ }
17
+ catch {
18
+ const req = typeof require !== 'undefined' ? require : null;
19
+ if (req) {
20
+ return {
21
+ fs: req('fs'),
22
+ path: req('path'),
23
+ };
24
+ }
25
+ throw new Error('Node.js filesystem (fs) and path modules are not available in this environment.');
26
+ }
27
+ }
28
+ /**
29
+ * Parses raw Supabase database.types.ts content and extracts foreign-key index definitions.
30
+ */
31
+ function parseSchemaIndexes(content, options = {}) {
32
+ const lines = content.split(/\r?\n/);
33
+ const includeBusiness = options.includeBusinessSyncIndexes ?? true;
34
+ let currentTable = null;
35
+ let inRow = false;
36
+ let inRelationships = false;
37
+ let relLines = [];
38
+ const tableColumns = new Map();
39
+ const relationships = [];
40
+ for (const line of lines) {
41
+ // Detect table definition: " <table_name>: {"
42
+ const tableMatch = line.match(/^\s{6}([a-z0-9_]+):\s*\{/);
43
+ if (tableMatch && !['Row', 'Insert', 'Update'].includes(tableMatch[1])) {
44
+ currentTable = tableMatch[1];
45
+ if (!tableColumns.has(currentTable)) {
46
+ tableColumns.set(currentTable, new Set());
47
+ }
48
+ }
49
+ // Track column names in Row: { ... }
50
+ if (line.includes('Row: {')) {
51
+ inRow = true;
52
+ continue;
53
+ }
54
+ if (inRow) {
55
+ if (line.trim() === '};' || line.trim() === '}') {
56
+ inRow = false;
57
+ }
58
+ else if (currentTable) {
59
+ const colMatch = line.match(/^\s{10}([a-z0-9_]+):/);
60
+ if (colMatch) {
61
+ tableColumns.get(currentTable)?.add(colMatch[1]);
62
+ }
63
+ }
64
+ }
65
+ // Skip empty relationships
66
+ if (line.includes('Relationships: [];') || line.includes('Relationships: []')) {
67
+ continue;
68
+ }
69
+ if (line.includes('Relationships: [')) {
70
+ inRelationships = true;
71
+ relLines = [];
72
+ continue;
73
+ }
74
+ if (inRelationships) {
75
+ if (line.trim() === '];' || line.trim() === ']') {
76
+ inRelationships = false;
77
+ if (currentTable) {
78
+ const block = relLines.join('\n');
79
+ const itemRegex = /foreignKeyName:\s*['"]([^'"]+)['"][\s\S]*?columns:\s*\[([^\]]+)\][\s\S]*?isOneToOne:\s*(true|false)[\s\S]*?referencedRelation:\s*['"]([^'"]+)['"](?:[\s\S]*?referencedColumns:\s*\[([^\]]+)\])?/g;
80
+ const matches = [...block.matchAll(itemRegex)];
81
+ for (const m of matches) {
82
+ const columns = m[2].split(',').map(c => c.trim().replace(/['"]/g, ''));
83
+ const isOneToOne = m[3] === 'true';
84
+ const referencedRelation = m[4];
85
+ const referencedColumns = m[5]
86
+ ? m[5].split(',').map(c => c.trim().replace(/['"]/g, ''))
87
+ : undefined;
88
+ const isUnique = isOneToOne ? 'UNIQUE ' : '';
89
+ const colSql = columns.map(c => `"${c}"`).join(', ');
90
+ const colSlug = columns.join('_');
91
+ const indexName = `idx_${currentTable}_${colSlug}`;
92
+ relationships.push({
93
+ table: currentTable,
94
+ columns,
95
+ isOneToOne,
96
+ referencedRelation,
97
+ referencedColumns,
98
+ sql: `CREATE ${isUnique}INDEX IF NOT EXISTS "${indexName}" ON "${currentTable}" (${colSql});`,
99
+ comment: referencedRelation
100
+ ? `References ${referencedRelation}${referencedColumns ? `(${referencedColumns.join(', ')})` : ''}`
101
+ : undefined,
102
+ });
103
+ }
104
+ }
105
+ continue;
106
+ }
107
+ relLines.push(line);
108
+ }
109
+ }
110
+ // Optionally append composite business sync indexes
111
+ if (includeBusiness) {
112
+ for (const [table, cols] of tableColumns.entries()) {
113
+ if (cols.has('business_id') && cols.has('deleted')) {
114
+ const idxName = `idx_${table}_biz_del`;
115
+ if (!relationships.some(r => r.table === table &&
116
+ r.columns.length === 2 &&
117
+ r.columns.includes('business_id') &&
118
+ r.columns.includes('deleted'))) {
119
+ relationships.push({
120
+ table,
121
+ columns: ['business_id', 'deleted'],
122
+ isOneToOne: false,
123
+ sql: `CREATE INDEX IF NOT EXISTS "${idxName}" ON "${table}" ("business_id", "deleted");`,
124
+ comment: 'Composite business partition index for zero-latency local queries',
125
+ });
126
+ }
127
+ }
128
+ }
129
+ }
130
+ return relationships;
131
+ }
132
+ /**
133
+ * Generates TypeScript source code containing index definitions and an applicator helper.
134
+ */
135
+ function formatSchemaIndexesCode(indexes) {
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
+ }
165
+ `;
166
+ }
167
+ /**
168
+ * Core generator function.
169
+ * Accepts the path or content of database.types.ts and generates schema indexes.
170
+ */
171
+ async function generateSchemaIndexes(typesPathOrOptions, maybeOutputPath) {
172
+ const options = typeof typesPathOrOptions === 'string'
173
+ ? { typesPath: typesPathOrOptions, outputPath: maybeOutputPath }
174
+ : typesPathOrOptions;
175
+ let content = options.typesContent;
176
+ if (!content && options.typesPath) {
177
+ const { fs, path } = await getNodeModules();
178
+ const resolvedPath = path.resolve(options.typesPath);
179
+ if (!fs.existsSync(resolvedPath)) {
180
+ throw new Error(`Types file not found at: ${resolvedPath}`);
181
+ }
182
+ content = fs.readFileSync(resolvedPath, 'utf8');
183
+ }
184
+ if (!content) {
185
+ throw new Error('Either typesPath or typesContent must be provided to generateSchemaIndexes.');
186
+ }
187
+ const indexes = parseSchemaIndexes(content, {
188
+ includeBusinessSyncIndexes: options.includeBusinessSyncIndexes,
189
+ });
190
+ const code = formatSchemaIndexesCode(indexes);
191
+ let written = false;
192
+ let resolvedOutputPath;
193
+ if (options.outputPath) {
194
+ const { fs, path } = await getNodeModules();
195
+ resolvedOutputPath = path.resolve(options.outputPath);
196
+ const parentDir = path.dirname(resolvedOutputPath);
197
+ if (!fs.existsSync(parentDir)) {
198
+ fs.mkdirSync(parentDir, { recursive: true });
199
+ }
200
+ fs.writeFileSync(resolvedOutputPath, code, 'utf8');
201
+ written = true;
202
+ }
203
+ return {
204
+ indexes,
205
+ code,
206
+ written,
207
+ outputPath: resolvedOutputPath,
208
+ };
209
+ }
@@ -0,0 +1 @@
1
+ export * from './generator.js';
@@ -0,0 +1,17 @@
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
+ __exportStar(require("./generator.js"), exports);
@@ -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
  */