local-lite 0.0.0-stage → 0.0.1
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 -0
- package/README.md +349 -2
- package/dist/adapters/local/better-sqlite3.d.ts +23 -0
- package/dist/adapters/local/better-sqlite3.js +46 -0
- package/dist/adapters/local/expo-sqlite.d.ts +8 -0
- package/dist/adapters/local/expo-sqlite.js +30 -0
- package/dist/adapters/local/index.d.ts +13 -0
- package/dist/adapters/local/index.js +22 -0
- package/dist/adapters/local/libsql.d.ts +24 -0
- package/dist/adapters/local/libsql.js +87 -0
- package/dist/adapters/local/node-sqlite.d.ts +24 -0
- package/dist/adapters/local/node-sqlite.js +44 -0
- package/dist/adapters/local/op-sqlite.d.ts +26 -0
- package/dist/adapters/local/op-sqlite.js +59 -0
- package/dist/adapters/local/sql-js.d.ts +26 -0
- package/dist/adapters/local/sql-js.js +63 -0
- package/dist/adapters/network/index.d.ts +11 -0
- package/dist/adapters/network/index.js +53 -0
- package/dist/adapters/remote/convex.d.ts +71 -0
- package/dist/adapters/remote/convex.js +185 -0
- package/dist/adapters/remote/demo.d.ts +21 -0
- package/dist/adapters/remote/demo.js +46 -0
- package/dist/adapters/remote/firestore.d.ts +64 -0
- package/dist/adapters/remote/firestore.js +302 -0
- package/dist/adapters/remote/index.d.ts +10 -0
- package/dist/adapters/remote/index.js +20 -0
- package/dist/adapters/remote/supabase.d.ts +97 -0
- package/dist/adapters/remote/supabase.js +276 -0
- package/dist/config/index.d.ts +24 -0
- package/dist/config/index.js +101 -0
- package/dist/config/resolve-local.d.ts +7 -0
- package/dist/config/resolve-local.js +68 -0
- package/dist/config/resolve-remote.d.ts +7 -0
- package/dist/config/resolve-remote.js +81 -0
- package/dist/config/types.d.ts +159 -0
- package/dist/config/types.js +2 -0
- package/dist/core/client.d.ts +100 -0
- package/dist/core/client.js +273 -0
- package/dist/core/conflict.d.ts +5 -0
- package/dist/core/conflict.js +38 -0
- package/dist/core/meta-store.d.ts +28 -0
- package/dist/core/meta-store.js +64 -0
- package/dist/core/outbox-store.d.ts +45 -0
- package/dist/core/outbox-store.js +141 -0
- package/dist/core/schema-manager.d.ts +39 -0
- package/dist/core/schema-manager.js +150 -0
- package/dist/core/sync-worker.d.ts +74 -0
- package/dist/core/sync-worker.js +335 -0
- package/dist/core/table-manager.d.ts +19 -0
- package/dist/core/table-manager.js +74 -0
- package/dist/core/types.d.ts +73 -0
- package/dist/core/types.js +2 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.js +33 -0
- package/dist/react/index.d.ts +34 -0
- package/dist/react/index.js +103 -0
- package/dist/reactive/event-emitter.d.ts +7 -0
- package/dist/reactive/event-emitter.js +34 -0
- package/dist/utils/logger.d.ts +51 -0
- package/dist/utils/logger.js +169 -0
- package/dist/utils/sql.d.ts +4 -0
- package/dist/utils/sql.js +9 -0
- package/dist/utils/uuid.d.ts +7 -0
- package/dist/utils/uuid.js +27 -0
- package/package.json +130 -3
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useLiveQuery = useLiveQuery;
|
|
4
|
+
exports.useOutboxStatus = useOutboxStatus;
|
|
5
|
+
const react_1 = require("react");
|
|
6
|
+
/**
|
|
7
|
+
* Hook to execute a live reactive table query that automatically re-runs
|
|
8
|
+
* whenever the table is mutated locally or via incoming remote delta.
|
|
9
|
+
*
|
|
10
|
+
* Supports 3 flexible usage patterns:
|
|
11
|
+
* 1. Zero-SQL (all rows):
|
|
12
|
+
* const { data: todos } = useLiveQuery<Todo>(client, 'todos');
|
|
13
|
+
*
|
|
14
|
+
* 2. Declarative options (filtering & sorting without SQL):
|
|
15
|
+
* const { data: active } = useLiveQuery<Todo>(client, 'todos', {
|
|
16
|
+
* where: { completed: 0 },
|
|
17
|
+
* orderBy: 'created_at',
|
|
18
|
+
* order: 'desc'
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* 3. Custom query callback (advanced raw SQL / joins):
|
|
22
|
+
* const { data } = useLiveQuery(client, 'todos', (c) => c.db.execute('SELECT ...'));
|
|
23
|
+
*/
|
|
24
|
+
function useLiveQuery(client, tableName, queryOrOptions) {
|
|
25
|
+
const [data, setData] = (0, react_1.useState)([]);
|
|
26
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
27
|
+
const queryParamRef = (0, react_1.useRef)(queryOrOptions);
|
|
28
|
+
queryParamRef.current = queryOrOptions;
|
|
29
|
+
const refetch = (0, react_1.useCallback)(async () => {
|
|
30
|
+
if (!client || !client.db)
|
|
31
|
+
return;
|
|
32
|
+
try {
|
|
33
|
+
const param = queryParamRef.current;
|
|
34
|
+
let res;
|
|
35
|
+
if (typeof param === 'function') {
|
|
36
|
+
res = (await param(client)) || [];
|
|
37
|
+
}
|
|
38
|
+
else if (typeof param === 'object' && param !== null) {
|
|
39
|
+
res = await client.findMany(tableName, param);
|
|
40
|
+
}
|
|
41
|
+
else {
|
|
42
|
+
res = await client.findMany(tableName);
|
|
43
|
+
}
|
|
44
|
+
setData(res || []);
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const msg = String(err?.message || err || '');
|
|
48
|
+
if (msg.includes('no such column') || msg.includes('no such table')) {
|
|
49
|
+
// Table or columns have not been populated yet on fresh database; default to empty list
|
|
50
|
+
setData([]);
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
console.error(`Error in useLiveQuery("${tableName}"):`, err);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
finally {
|
|
57
|
+
setLoading(false);
|
|
58
|
+
}
|
|
59
|
+
}, [client, tableName]);
|
|
60
|
+
(0, react_1.useEffect)(() => {
|
|
61
|
+
if (!client || !client.db)
|
|
62
|
+
return;
|
|
63
|
+
refetch();
|
|
64
|
+
const unsubscribe = client.events.on(tableName, () => {
|
|
65
|
+
refetch();
|
|
66
|
+
});
|
|
67
|
+
return () => {
|
|
68
|
+
unsubscribe();
|
|
69
|
+
};
|
|
70
|
+
}, [client, tableName, refetch]);
|
|
71
|
+
return { data, refetch, loading };
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Hook that tracks the status and pending items in sync_outbox in real time.
|
|
75
|
+
*/
|
|
76
|
+
function useOutboxStatus(client) {
|
|
77
|
+
const [pendingCount, setPendingCount] = (0, react_1.useState)(0);
|
|
78
|
+
const [pendingRecords, setPendingRecords] = (0, react_1.useState)([]);
|
|
79
|
+
const refresh = (0, react_1.useCallback)(async () => {
|
|
80
|
+
if (!client || !client.db)
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
const records = await client.db.execute('SELECT * FROM sync_outbox ORDER BY timestamp DESC');
|
|
84
|
+
setPendingRecords(records || []);
|
|
85
|
+
setPendingCount((records || []).length);
|
|
86
|
+
}
|
|
87
|
+
catch {
|
|
88
|
+
// Ignored if table doesn't exist yet
|
|
89
|
+
}
|
|
90
|
+
}, [client]);
|
|
91
|
+
(0, react_1.useEffect)(() => {
|
|
92
|
+
if (!client || !client.db)
|
|
93
|
+
return;
|
|
94
|
+
refresh();
|
|
95
|
+
const unsubscribe = client.events.on('sync_outbox', () => {
|
|
96
|
+
refresh();
|
|
97
|
+
});
|
|
98
|
+
return () => {
|
|
99
|
+
unsubscribe();
|
|
100
|
+
};
|
|
101
|
+
}, [client, refresh]);
|
|
102
|
+
return { pendingCount, pendingRecords, refresh };
|
|
103
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventEmitter = void 0;
|
|
4
|
+
class EventEmitter {
|
|
5
|
+
listeners = new Map();
|
|
6
|
+
on(event, callback) {
|
|
7
|
+
if (!this.listeners.has(event)) {
|
|
8
|
+
this.listeners.set(event, new Set());
|
|
9
|
+
}
|
|
10
|
+
this.listeners.get(event).add(callback);
|
|
11
|
+
return () => {
|
|
12
|
+
const eventSet = this.listeners.get(event);
|
|
13
|
+
if (eventSet) {
|
|
14
|
+
eventSet.delete(callback);
|
|
15
|
+
if (eventSet.size === 0)
|
|
16
|
+
this.listeners.delete(event);
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
emit(event, data) {
|
|
21
|
+
const eventSet = this.listeners.get(event);
|
|
22
|
+
if (eventSet) {
|
|
23
|
+
eventSet.forEach(callback => {
|
|
24
|
+
try {
|
|
25
|
+
callback(data);
|
|
26
|
+
}
|
|
27
|
+
catch (err) {
|
|
28
|
+
console.error("Emitter error:", err);
|
|
29
|
+
}
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
exports.EventEmitter = EventEmitter;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export type LogLevel = 'debug' | 'info' | 'warn' | 'error' | 'silent';
|
|
2
|
+
export interface CustomLogger {
|
|
3
|
+
debug?(message: string, ...args: any[]): void;
|
|
4
|
+
info?(message: string, ...args: any[]): void;
|
|
5
|
+
warn?(message: string, ...args: any[]): void;
|
|
6
|
+
error?(message: string, ...args: any[]): void;
|
|
7
|
+
}
|
|
8
|
+
export interface LoggerConfig {
|
|
9
|
+
level?: LogLevel;
|
|
10
|
+
customLogger?: CustomLogger;
|
|
11
|
+
enabled?: boolean;
|
|
12
|
+
}
|
|
13
|
+
export declare class SyncLogger {
|
|
14
|
+
private level;
|
|
15
|
+
private customLogger?;
|
|
16
|
+
private enabled;
|
|
17
|
+
constructor(config?: LoggerConfig | boolean);
|
|
18
|
+
setLevel(level: LogLevel): void;
|
|
19
|
+
setEnabled(enabled: boolean): void;
|
|
20
|
+
setCustomLogger(logger?: CustomLogger): void;
|
|
21
|
+
private shouldLog;
|
|
22
|
+
debug(message: string, ...args: any[]): void;
|
|
23
|
+
info(message: string, ...args: any[]): void;
|
|
24
|
+
warn(message: string, ...args: any[]): void;
|
|
25
|
+
error(message: string, ...args: any[]): void;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Global default logger instance.
|
|
29
|
+
*/
|
|
30
|
+
export declare const defaultLogger: SyncLogger;
|
|
31
|
+
/**
|
|
32
|
+
* Translates PostgreSQL / Supabase PostgREST error codes into actionable, human-readable explanations.
|
|
33
|
+
*/
|
|
34
|
+
export declare function formatSupabaseError(error: any, context?: {
|
|
35
|
+
table?: string;
|
|
36
|
+
operation?: string;
|
|
37
|
+
}): string;
|
|
38
|
+
/**
|
|
39
|
+
* Translates Firestore error codes into human-readable explanations.
|
|
40
|
+
*/
|
|
41
|
+
export declare function formatFirestoreError(error: any, context?: {
|
|
42
|
+
collection?: string;
|
|
43
|
+
operation?: string;
|
|
44
|
+
}): string;
|
|
45
|
+
/**
|
|
46
|
+
* Translates Convex error codes into human-readable explanations.
|
|
47
|
+
*/
|
|
48
|
+
export declare function formatConvexError(error: any, context?: {
|
|
49
|
+
table?: string;
|
|
50
|
+
operation?: string;
|
|
51
|
+
}): string;
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.defaultLogger = exports.SyncLogger = void 0;
|
|
4
|
+
exports.formatSupabaseError = formatSupabaseError;
|
|
5
|
+
exports.formatFirestoreError = formatFirestoreError;
|
|
6
|
+
exports.formatConvexError = formatConvexError;
|
|
7
|
+
const LOG_LEVEL_WEIGHTS = {
|
|
8
|
+
debug: 0,
|
|
9
|
+
info: 1,
|
|
10
|
+
warn: 2,
|
|
11
|
+
error: 3,
|
|
12
|
+
silent: 4,
|
|
13
|
+
};
|
|
14
|
+
class SyncLogger {
|
|
15
|
+
level = 'info';
|
|
16
|
+
customLogger;
|
|
17
|
+
enabled = true;
|
|
18
|
+
constructor(config) {
|
|
19
|
+
if (typeof config === 'boolean') {
|
|
20
|
+
this.enabled = config;
|
|
21
|
+
}
|
|
22
|
+
else if (config) {
|
|
23
|
+
if (config.enabled !== undefined)
|
|
24
|
+
this.enabled = config.enabled;
|
|
25
|
+
if (config.level)
|
|
26
|
+
this.level = config.level;
|
|
27
|
+
if (config.customLogger)
|
|
28
|
+
this.customLogger = config.customLogger;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
setLevel(level) {
|
|
32
|
+
this.level = level;
|
|
33
|
+
}
|
|
34
|
+
setEnabled(enabled) {
|
|
35
|
+
this.enabled = enabled;
|
|
36
|
+
}
|
|
37
|
+
setCustomLogger(logger) {
|
|
38
|
+
this.customLogger = logger;
|
|
39
|
+
}
|
|
40
|
+
shouldLog(level) {
|
|
41
|
+
if (!this.enabled || this.level === 'silent')
|
|
42
|
+
return false;
|
|
43
|
+
return LOG_LEVEL_WEIGHTS[level] >= LOG_LEVEL_WEIGHTS[this.level];
|
|
44
|
+
}
|
|
45
|
+
debug(message, ...args) {
|
|
46
|
+
if (!this.shouldLog('debug'))
|
|
47
|
+
return;
|
|
48
|
+
if (this.customLogger?.debug) {
|
|
49
|
+
this.customLogger.debug(message, ...args);
|
|
50
|
+
}
|
|
51
|
+
else {
|
|
52
|
+
console.debug(`[local-lite] ${message}`, ...args);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
info(message, ...args) {
|
|
56
|
+
if (!this.shouldLog('info'))
|
|
57
|
+
return;
|
|
58
|
+
if (this.customLogger?.info) {
|
|
59
|
+
this.customLogger.info(message, ...args);
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
console.info(`[local-lite] ${message}`, ...args);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
warn(message, ...args) {
|
|
66
|
+
if (!this.shouldLog('warn'))
|
|
67
|
+
return;
|
|
68
|
+
if (this.customLogger?.warn) {
|
|
69
|
+
this.customLogger.warn(message, ...args);
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
console.warn(`[local-lite] ${message}`, ...args);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
error(message, ...args) {
|
|
76
|
+
if (!this.shouldLog('error'))
|
|
77
|
+
return;
|
|
78
|
+
if (this.customLogger?.error) {
|
|
79
|
+
this.customLogger.error(message, ...args);
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
console.error(`[local-lite] ${message}`, ...args);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
exports.SyncLogger = SyncLogger;
|
|
87
|
+
/**
|
|
88
|
+
* Global default logger instance.
|
|
89
|
+
*/
|
|
90
|
+
exports.defaultLogger = new SyncLogger();
|
|
91
|
+
/**
|
|
92
|
+
* Translates PostgreSQL / Supabase PostgREST error codes into actionable, human-readable explanations.
|
|
93
|
+
*/
|
|
94
|
+
function formatSupabaseError(error, context = {}) {
|
|
95
|
+
if (!error)
|
|
96
|
+
return 'Unknown Supabase error';
|
|
97
|
+
const table = context.table ? `"${context.table}"` : 'table';
|
|
98
|
+
const op = context.operation ? ` (${context.operation})` : '';
|
|
99
|
+
const code = String(error.code || error.status || '').trim();
|
|
100
|
+
const rawMsg = error.message || error.details || String(error);
|
|
101
|
+
// PostgreSQL RLS 42501 (insufficient_privilege)
|
|
102
|
+
if (code === '42501' || rawMsg.toLowerCase().includes('row-level security')) {
|
|
103
|
+
return `🔒 RLS Permission Denied on ${table}${op}: Row-Level Security blocked this mutation. Check that the user is authenticated and matching RLS policies exist on Postgres.`;
|
|
104
|
+
}
|
|
105
|
+
// PostgreSQL 23502 (not_null_violation)
|
|
106
|
+
if (code === '23502') {
|
|
107
|
+
const hint = error.details || rawMsg;
|
|
108
|
+
return `⚠️ Missing Required Column on ${table}${op}: NOT NULL constraint violated. (${hint})`;
|
|
109
|
+
}
|
|
110
|
+
// PostgreSQL 23505 (unique_violation)
|
|
111
|
+
if (code === '23505') {
|
|
112
|
+
const hint = error.details || rawMsg;
|
|
113
|
+
return `⚠️ Duplicate Key on ${table}${op}: Unique constraint violated. (${hint})`;
|
|
114
|
+
}
|
|
115
|
+
// PostgreSQL 23503 (foreign_key_violation)
|
|
116
|
+
if (code === '23503') {
|
|
117
|
+
const hint = error.details || rawMsg;
|
|
118
|
+
return `⚠️ Foreign Key Missing on ${table}${op}: Referenced relation does not exist. (${hint})`;
|
|
119
|
+
}
|
|
120
|
+
// Supabase Auth / JWT expired (PGRST301, JWT expired)
|
|
121
|
+
if (code === 'PGRST301' || rawMsg.toLowerCase().includes('jwt')) {
|
|
122
|
+
return `🔑 Session Expired on ${table}${op}: Supabase auth token is invalid or expired.`;
|
|
123
|
+
}
|
|
124
|
+
// Network / Offline errors
|
|
125
|
+
if (rawMsg.toLowerCase().includes('fetch failed') ||
|
|
126
|
+
rawMsg.toLowerCase().includes('enotfound') ||
|
|
127
|
+
rawMsg.toLowerCase().includes('network')) {
|
|
128
|
+
return `📡 Network Offline: Failed to reach Supabase backend. Mutations safely preserved in local SQLite outbox.`;
|
|
129
|
+
}
|
|
130
|
+
return `❌ Supabase error on ${table}${op} [code: ${code || 'N/A'}]: ${rawMsg}`;
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* Translates Firestore error codes into human-readable explanations.
|
|
134
|
+
*/
|
|
135
|
+
function formatFirestoreError(error, context = {}) {
|
|
136
|
+
if (!error)
|
|
137
|
+
return 'Unknown Firestore error';
|
|
138
|
+
const collection = context.collection ? `"${context.collection}"` : 'collection';
|
|
139
|
+
const op = context.operation ? ` (${context.operation})` : '';
|
|
140
|
+
const code = String(error.code || '').toLowerCase();
|
|
141
|
+
const rawMsg = error.message || String(error);
|
|
142
|
+
if (code.includes('permission-denied') || rawMsg.toLowerCase().includes('insufficient permissions')) {
|
|
143
|
+
return `🔒 Permission Denied on collection ${collection}${op}: Check your Firestore Security Rules.`;
|
|
144
|
+
}
|
|
145
|
+
if (code.includes('unauthenticated')) {
|
|
146
|
+
return `🔑 Unauthenticated on collection ${collection}${op}: User must be signed into Firebase Auth.`;
|
|
147
|
+
}
|
|
148
|
+
if (code.includes('unavailable') || rawMsg.toLowerCase().includes('network')) {
|
|
149
|
+
return `📡 Firestore Offline: Backend unreachable. Mutations preserved in local SQLite outbox.`;
|
|
150
|
+
}
|
|
151
|
+
return `❌ Firestore error on ${collection}${op}: ${rawMsg}`;
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Translates Convex error codes into human-readable explanations.
|
|
155
|
+
*/
|
|
156
|
+
function formatConvexError(error, context = {}) {
|
|
157
|
+
if (!error)
|
|
158
|
+
return 'Unknown Convex error';
|
|
159
|
+
const table = context.table ? `"${context.table}"` : 'table';
|
|
160
|
+
const op = context.operation ? ` (${context.operation})` : '';
|
|
161
|
+
const rawMsg = error.message || String(error);
|
|
162
|
+
if (rawMsg.toLowerCase().includes('unauthorized') || rawMsg.toLowerCase().includes('unauthenticated')) {
|
|
163
|
+
return `🔒 Convex Permission Denied on ${table}${op}: Request unauthorized.`;
|
|
164
|
+
}
|
|
165
|
+
if (rawMsg.toLowerCase().includes('network') || rawMsg.toLowerCase().includes('failed to fetch')) {
|
|
166
|
+
return `📡 Convex Offline: Server unreachable. Mutations preserved in local SQLite outbox.`;
|
|
167
|
+
}
|
|
168
|
+
return `❌ Convex error on ${table}${op}: ${rawMsg}`;
|
|
169
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.escapeIdentifier = escapeIdentifier;
|
|
4
|
+
/**
|
|
5
|
+
* Safely escape a SQL identifier (table name or column name) with double quotes.
|
|
6
|
+
*/
|
|
7
|
+
function escapeIdentifier(identifier) {
|
|
8
|
+
return `"${identifier.replace(/"/g, '""')}"`;
|
|
9
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.uuidv4 = void 0;
|
|
4
|
+
exports.generateUUID = generateUUID;
|
|
5
|
+
const uuid_1 = require("uuid");
|
|
6
|
+
Object.defineProperty(exports, "uuidv4", { enumerable: true, get: function () { return uuid_1.v4; } });
|
|
7
|
+
/**
|
|
8
|
+
* Generates an RFC4122 v4 compliant UUID.
|
|
9
|
+
* Uses native crypto.randomUUID() when supported, with uuid.v4 fallback.
|
|
10
|
+
*/
|
|
11
|
+
function generateUUID() {
|
|
12
|
+
try {
|
|
13
|
+
if (typeof globalThis !== 'undefined' &&
|
|
14
|
+
globalThis.crypto &&
|
|
15
|
+
typeof globalThis.crypto.randomUUID === 'function') {
|
|
16
|
+
return globalThis.crypto.randomUUID();
|
|
17
|
+
}
|
|
18
|
+
return (0, uuid_1.v4)();
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, (c) => {
|
|
22
|
+
const r = (Math.random() * 16) | 0;
|
|
23
|
+
const v = c === 'x' ? r : (r & 0x3) | 0x8;
|
|
24
|
+
return v.toString(16);
|
|
25
|
+
});
|
|
26
|
+
}
|
|
27
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,133 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-lite",
|
|
3
|
-
"version": "0.0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Vendor-agnostic local-first sync engine with outbox pattern, Supabase, Convex, and Firebase support",
|
|
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
|
+
}
|
|
6
133
|
}
|