local-lite 0.0.0-stage → 0.0.2

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 (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +361 -2
  3. package/dist/adapters/local/better-sqlite3.d.ts +23 -0
  4. package/dist/adapters/local/better-sqlite3.js +46 -0
  5. package/dist/adapters/local/expo-sqlite.d.ts +8 -0
  6. package/dist/adapters/local/expo-sqlite.js +30 -0
  7. package/dist/adapters/local/index.d.ts +13 -0
  8. package/dist/adapters/local/index.js +22 -0
  9. package/dist/adapters/local/libsql.d.ts +24 -0
  10. package/dist/adapters/local/libsql.js +87 -0
  11. package/dist/adapters/local/node-sqlite.d.ts +24 -0
  12. package/dist/adapters/local/node-sqlite.js +44 -0
  13. package/dist/adapters/local/op-sqlite.d.ts +26 -0
  14. package/dist/adapters/local/op-sqlite.js +59 -0
  15. package/dist/adapters/local/sql-js.d.ts +26 -0
  16. package/dist/adapters/local/sql-js.js +63 -0
  17. package/dist/adapters/network/index.d.ts +11 -0
  18. package/dist/adapters/network/index.js +53 -0
  19. package/dist/adapters/remote/convex.d.ts +71 -0
  20. package/dist/adapters/remote/convex.js +185 -0
  21. package/dist/adapters/remote/demo.d.ts +21 -0
  22. package/dist/adapters/remote/demo.js +46 -0
  23. package/dist/adapters/remote/firestore.d.ts +64 -0
  24. package/dist/adapters/remote/firestore.js +302 -0
  25. package/dist/adapters/remote/index.d.ts +10 -0
  26. package/dist/adapters/remote/index.js +20 -0
  27. package/dist/adapters/remote/supabase.d.ts +97 -0
  28. package/dist/adapters/remote/supabase.js +276 -0
  29. package/dist/config/index.d.ts +24 -0
  30. package/dist/config/index.js +101 -0
  31. package/dist/config/resolve-local.d.ts +7 -0
  32. package/dist/config/resolve-local.js +68 -0
  33. package/dist/config/resolve-remote.d.ts +7 -0
  34. package/dist/config/resolve-remote.js +81 -0
  35. package/dist/config/types.d.ts +159 -0
  36. package/dist/config/types.js +2 -0
  37. package/dist/core/client.d.ts +153 -0
  38. package/dist/core/client.js +445 -0
  39. package/dist/core/conflict.d.ts +5 -0
  40. package/dist/core/conflict.js +38 -0
  41. package/dist/core/meta-store.d.ts +28 -0
  42. package/dist/core/meta-store.js +64 -0
  43. package/dist/core/outbox-store.d.ts +76 -0
  44. package/dist/core/outbox-store.js +265 -0
  45. package/dist/core/query.d.ts +13 -0
  46. package/dist/core/query.js +150 -0
  47. package/dist/core/schema-manager.d.ts +39 -0
  48. package/dist/core/schema-manager.js +150 -0
  49. package/dist/core/sync-worker.d.ts +89 -0
  50. package/dist/core/sync-worker.js +473 -0
  51. package/dist/core/table-manager.d.ts +19 -0
  52. package/dist/core/table-manager.js +74 -0
  53. package/dist/core/types.d.ts +156 -0
  54. package/dist/core/types.js +2 -0
  55. package/dist/index.d.ts +18 -0
  56. package/dist/index.js +34 -0
  57. package/dist/react/index.d.ts +50 -0
  58. package/dist/react/index.js +174 -0
  59. package/dist/reactive/event-emitter.d.ts +7 -0
  60. package/dist/reactive/event-emitter.js +34 -0
  61. package/dist/utils/logger.d.ts +51 -0
  62. package/dist/utils/logger.js +169 -0
  63. package/dist/utils/sql.d.ts +4 -0
  64. package/dist/utils/sql.js +9 -0
  65. package/dist/utils/uuid.d.ts +7 -0
  66. package/dist/utils/uuid.js +27 -0
  67. package/package.json +131 -4
@@ -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,4 @@
1
+ /**
2
+ * Safely escape a SQL identifier (table name or column name) with double quotes.
3
+ */
4
+ export declare function escapeIdentifier(identifier: string): string;
@@ -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,7 @@
1
+ import { v4 as uuidv4 } from 'uuid';
2
+ /**
3
+ * Generates an RFC4122 v4 compliant UUID.
4
+ * Uses native crypto.randomUUID() when supported, with uuid.v4 fallback.
5
+ */
6
+ export declare function generateUUID(): string;
7
+ export { uuidv4 };
@@ -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.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
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
+ }