local-lite 0.0.14 → 0.0.16
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/dist/adapters/remote/supabase.d.ts +1 -5
- package/dist/adapters/remote/supabase.js +2 -2
- package/dist/auth/google.d.ts +2 -1
- package/dist/auth/google.js +7 -0
- package/dist/auth/microsoft.d.ts +3 -3
- package/dist/auth/microsoft.js +12 -3
- package/dist/backups/cloud-storage.d.ts +22 -6
- package/dist/backups/cloud-storage.js +111 -16
- package/dist/backups/index.d.ts +3 -15
- package/dist/backups/index.js +9 -26
- package/dist/backups/onedrive.d.ts +18 -4
- package/dist/backups/onedrive.js +69 -12
- package/dist/backups/operations.d.ts +39 -0
- package/dist/backups/operations.js +74 -0
- package/dist/config/resolve-remote.js +0 -1
- package/dist/core/client.d.ts +51 -0
- package/dist/core/client.js +189 -0
- package/dist/core/index.d.ts +1 -0
- package/dist/core/index.js +1 -0
- package/dist/core/sync-worker.js +5 -2
- package/dist/react/SyncInspector.d.ts +0 -2
- package/dist/react/SyncInspector.js +0 -2
- package/dist/react/auth.d.ts +0 -2
- package/dist/react/auth.js +0 -2
- package/dist/react/backups.d.ts +28 -0
- package/dist/react/backups.js +265 -0
- package/dist/react/index.d.ts +15 -2
- package/dist/react/index.js +53 -2
- package/package.json +11 -1
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useCloudBackup = useCloudBackup;
|
|
4
|
+
exports.useGoogleDriveBackup = useGoogleDriveBackup;
|
|
5
|
+
exports.useOneDriveBackup = useOneDriveBackup;
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const index_js_1 = require("../backups/index.js");
|
|
8
|
+
function useCloudBackup(client, database, options) {
|
|
9
|
+
const [isConnected, setIsConnected] = (0, react_1.useState)(false);
|
|
10
|
+
const [isLoading, setIsLoading] = (0, react_1.useState)(true);
|
|
11
|
+
const [isBackingUp, setIsBackingUp] = (0, react_1.useState)(false);
|
|
12
|
+
const [isRestoring, setIsRestoring] = (0, react_1.useState)(false);
|
|
13
|
+
const [backups, setBackups] = (0, react_1.useState)([]);
|
|
14
|
+
const [lastBackupAt, setLastBackupAt] = (0, react_1.useState)(null);
|
|
15
|
+
const [error, setError] = (0, react_1.useState)(null);
|
|
16
|
+
const databaseRef = (0, react_1.useRef)(database);
|
|
17
|
+
databaseRef.current = database;
|
|
18
|
+
const defaultPassphrase = options?.passphrase;
|
|
19
|
+
const autoRefresh = options?.autoRefresh ?? true;
|
|
20
|
+
const refreshBackups = (0, react_1.useCallback)(async () => {
|
|
21
|
+
if (!client) {
|
|
22
|
+
setBackups([]);
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const connected = await client.isConnected();
|
|
27
|
+
if (!connected) {
|
|
28
|
+
setBackups([]);
|
|
29
|
+
return [];
|
|
30
|
+
}
|
|
31
|
+
const list = await client.list();
|
|
32
|
+
setBackups(list);
|
|
33
|
+
return list;
|
|
34
|
+
}
|
|
35
|
+
catch (err) {
|
|
36
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
37
|
+
setError(e);
|
|
38
|
+
throw e;
|
|
39
|
+
}
|
|
40
|
+
}, [client]);
|
|
41
|
+
(0, react_1.useEffect)(() => {
|
|
42
|
+
if (!client) {
|
|
43
|
+
setIsConnected(false);
|
|
44
|
+
setIsLoading(false);
|
|
45
|
+
setBackups([]);
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
let isMounted = true;
|
|
49
|
+
setIsLoading(true);
|
|
50
|
+
client
|
|
51
|
+
.isConnected()
|
|
52
|
+
.then(async (connected) => {
|
|
53
|
+
if (!isMounted)
|
|
54
|
+
return;
|
|
55
|
+
setIsConnected(connected);
|
|
56
|
+
setIsLoading(false);
|
|
57
|
+
if (connected && autoRefresh) {
|
|
58
|
+
try {
|
|
59
|
+
const list = await client.list();
|
|
60
|
+
if (isMounted)
|
|
61
|
+
setBackups(list);
|
|
62
|
+
}
|
|
63
|
+
catch (listErr) {
|
|
64
|
+
if (isMounted) {
|
|
65
|
+
setError(listErr instanceof Error ? listErr : new Error(String(listErr)));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
})
|
|
70
|
+
.catch(err => {
|
|
71
|
+
if (!isMounted)
|
|
72
|
+
return;
|
|
73
|
+
setError(err instanceof Error ? err : new Error(String(err)));
|
|
74
|
+
setIsLoading(false);
|
|
75
|
+
});
|
|
76
|
+
let unsubscribe;
|
|
77
|
+
if (typeof client.onStatusChange === 'function') {
|
|
78
|
+
unsubscribe = client.onStatusChange(async (connected) => {
|
|
79
|
+
if (!isMounted)
|
|
80
|
+
return;
|
|
81
|
+
setIsConnected(connected);
|
|
82
|
+
if (connected && autoRefresh) {
|
|
83
|
+
try {
|
|
84
|
+
const list = await client.list();
|
|
85
|
+
if (isMounted)
|
|
86
|
+
setBackups(list);
|
|
87
|
+
}
|
|
88
|
+
catch (listErr) {
|
|
89
|
+
if (isMounted) {
|
|
90
|
+
setError(listErr instanceof Error ? listErr : new Error(String(listErr)));
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
else if (!connected) {
|
|
95
|
+
setBackups([]);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
return () => {
|
|
100
|
+
isMounted = false;
|
|
101
|
+
if (unsubscribe)
|
|
102
|
+
unsubscribe();
|
|
103
|
+
};
|
|
104
|
+
}, [client, autoRefresh]);
|
|
105
|
+
const connect = (0, react_1.useCallback)(async () => {
|
|
106
|
+
if (!client)
|
|
107
|
+
throw new Error('Cloud backup client is not initialized.');
|
|
108
|
+
setError(null);
|
|
109
|
+
setIsLoading(true);
|
|
110
|
+
try {
|
|
111
|
+
const connected = await client.connect();
|
|
112
|
+
setIsConnected(connected);
|
|
113
|
+
if (connected && autoRefresh) {
|
|
114
|
+
const list = await client.list();
|
|
115
|
+
setBackups(list);
|
|
116
|
+
}
|
|
117
|
+
return connected;
|
|
118
|
+
}
|
|
119
|
+
catch (err) {
|
|
120
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
121
|
+
setError(e);
|
|
122
|
+
throw e;
|
|
123
|
+
}
|
|
124
|
+
finally {
|
|
125
|
+
setIsLoading(false);
|
|
126
|
+
}
|
|
127
|
+
}, [client, autoRefresh]);
|
|
128
|
+
const disconnect = (0, react_1.useCallback)(async () => {
|
|
129
|
+
if (!client)
|
|
130
|
+
return;
|
|
131
|
+
setError(null);
|
|
132
|
+
setIsLoading(true);
|
|
133
|
+
try {
|
|
134
|
+
await client.disconnect();
|
|
135
|
+
setIsConnected(false);
|
|
136
|
+
setBackups([]);
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
140
|
+
setError(e);
|
|
141
|
+
throw e;
|
|
142
|
+
}
|
|
143
|
+
finally {
|
|
144
|
+
setIsLoading(false);
|
|
145
|
+
}
|
|
146
|
+
}, [client]);
|
|
147
|
+
const backup = (0, react_1.useCallback)(async (backupOptions) => {
|
|
148
|
+
if (!client)
|
|
149
|
+
throw new Error('Cloud backup client is not initialized.');
|
|
150
|
+
const currentDb = databaseRef.current;
|
|
151
|
+
if (!currentDb) {
|
|
152
|
+
throw new Error('A database instance or LocalFirstClient must be provided to create a backup.');
|
|
153
|
+
}
|
|
154
|
+
setError(null);
|
|
155
|
+
setIsBackingUp(true);
|
|
156
|
+
try {
|
|
157
|
+
const passphrase = backupOptions?.passphrase ?? defaultPassphrase;
|
|
158
|
+
let createdFileName;
|
|
159
|
+
if (typeof client.backup === 'function') {
|
|
160
|
+
createdFileName = await client.backup(currentDb, {
|
|
161
|
+
fileName: backupOptions?.fileName,
|
|
162
|
+
passphrase,
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
createdFileName = await (0, index_js_1.performBackup)(currentDb, client, {
|
|
167
|
+
fileName: backupOptions?.fileName,
|
|
168
|
+
passphrase,
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
setLastBackupAt(new Date().toISOString());
|
|
172
|
+
if (autoRefresh) {
|
|
173
|
+
try {
|
|
174
|
+
const list = await client.list();
|
|
175
|
+
setBackups(list);
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
// Backup succeeded; silent list update failure is tolerated
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return createdFileName;
|
|
182
|
+
}
|
|
183
|
+
catch (err) {
|
|
184
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
185
|
+
setError(e);
|
|
186
|
+
throw e;
|
|
187
|
+
}
|
|
188
|
+
finally {
|
|
189
|
+
setIsBackingUp(false);
|
|
190
|
+
}
|
|
191
|
+
}, [client, defaultPassphrase, autoRefresh]);
|
|
192
|
+
const restore = (0, react_1.useCallback)(async (fileNameOrId, restoreOptions) => {
|
|
193
|
+
if (!client)
|
|
194
|
+
throw new Error('Cloud backup client is not initialized.');
|
|
195
|
+
const currentDb = databaseRef.current;
|
|
196
|
+
if (!currentDb) {
|
|
197
|
+
throw new Error('A database instance or LocalFirstClient must be provided to restore a backup.');
|
|
198
|
+
}
|
|
199
|
+
setError(null);
|
|
200
|
+
setIsRestoring(true);
|
|
201
|
+
try {
|
|
202
|
+
const passphrase = restoreOptions?.passphrase ?? defaultPassphrase;
|
|
203
|
+
if (typeof client.restore === 'function') {
|
|
204
|
+
await client.restore(currentDb, fileNameOrId, { passphrase });
|
|
205
|
+
}
|
|
206
|
+
else {
|
|
207
|
+
await (0, index_js_1.performRestore)(currentDb, client, fileNameOrId, { passphrase });
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
212
|
+
setError(e);
|
|
213
|
+
throw e;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
setIsRestoring(false);
|
|
217
|
+
}
|
|
218
|
+
}, [client, defaultPassphrase]);
|
|
219
|
+
const deleteBackup = (0, react_1.useCallback)(async (fileNameOrId) => {
|
|
220
|
+
if (!client)
|
|
221
|
+
throw new Error('Cloud backup client is not initialized.');
|
|
222
|
+
setError(null);
|
|
223
|
+
try {
|
|
224
|
+
if (typeof client.deleteBackup === 'function') {
|
|
225
|
+
await client.deleteBackup(fileNameOrId);
|
|
226
|
+
}
|
|
227
|
+
else if (typeof client.delete === 'function') {
|
|
228
|
+
await client.delete(fileNameOrId);
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
throw new Error('Delete operation is not supported by this cloud storage client.');
|
|
232
|
+
}
|
|
233
|
+
if (autoRefresh) {
|
|
234
|
+
const list = await client.list();
|
|
235
|
+
setBackups(list);
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
catch (err) {
|
|
239
|
+
const e = err instanceof Error ? err : new Error(String(err));
|
|
240
|
+
setError(e);
|
|
241
|
+
throw e;
|
|
242
|
+
}
|
|
243
|
+
}, [client, autoRefresh]);
|
|
244
|
+
return {
|
|
245
|
+
isConnected,
|
|
246
|
+
isLoading,
|
|
247
|
+
isBackingUp,
|
|
248
|
+
isRestoring,
|
|
249
|
+
backups,
|
|
250
|
+
lastBackupAt,
|
|
251
|
+
error,
|
|
252
|
+
connect,
|
|
253
|
+
disconnect,
|
|
254
|
+
refreshBackups,
|
|
255
|
+
backup,
|
|
256
|
+
restore,
|
|
257
|
+
deleteBackup,
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
function useGoogleDriveBackup(client, database, options) {
|
|
261
|
+
return useCloudBackup(client, database, options);
|
|
262
|
+
}
|
|
263
|
+
function useOneDriveBackup(client, database, options) {
|
|
264
|
+
return useCloudBackup(client, database, options);
|
|
265
|
+
}
|
package/dist/react/index.d.ts
CHANGED
|
@@ -111,10 +111,23 @@ export interface UseMutationsResult<TTable extends string = string> {
|
|
|
111
111
|
* Universal mutations hook providing memoized, null-safe helpers for database operations.
|
|
112
112
|
*/
|
|
113
113
|
export declare function useMutations<TTable extends string = string>(client: LocalFirstClient | null | undefined): UseMutationsResult<TTable>;
|
|
114
|
+
export interface UseDatabaseMaintenanceResult {
|
|
115
|
+
stats: import('../core/maintenance.js').StorageStats | null;
|
|
116
|
+
loading: boolean;
|
|
117
|
+
refreshStats: () => Promise<import('../core/maintenance.js').StorageStats | null>;
|
|
118
|
+
pruneOutbox: (options?: import('../core/maintenance.js').PruneOutboxOptions) => Promise<number>;
|
|
119
|
+
vacuum: () => Promise<void>;
|
|
120
|
+
checkpoint: (mode?: import('../core/maintenance.js').CheckpointMode) => Promise<void>;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Reactive hook for database maintenance (vacuum, WAL checkpoint, outbox pruning, and storage metrics).
|
|
124
|
+
*/
|
|
125
|
+
export declare function useDatabaseMaintenance(client: LocalFirstClient | null | undefined): UseDatabaseMaintenanceResult;
|
|
114
126
|
export * from './runner.js';
|
|
115
127
|
export * from './status.js';
|
|
116
128
|
export * from './theme.js';
|
|
117
129
|
export * from './BenchmarkModal.js';
|
|
118
|
-
export { SyncInspector
|
|
119
|
-
export type { SyncInspectorProps
|
|
130
|
+
export { SyncInspector } from './SyncInspector.js';
|
|
131
|
+
export type { SyncInspectorProps } from './SyncInspector.js';
|
|
120
132
|
export * from './auth.js';
|
|
133
|
+
export * from './backups.js';
|
package/dist/react/index.js
CHANGED
|
@@ -14,13 +14,14 @@ 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.
|
|
17
|
+
exports.SyncInspector = void 0;
|
|
18
18
|
exports.clearLiveQueryCache = clearLiveQueryCache;
|
|
19
19
|
exports.useLiveQuery = useLiveQuery;
|
|
20
20
|
exports.useLiveQueries = useLiveQueries;
|
|
21
21
|
exports.useLiveRow = useLiveRow;
|
|
22
22
|
exports.useLiveInfiniteQuery = useLiveInfiniteQuery;
|
|
23
23
|
exports.useMutations = useMutations;
|
|
24
|
+
exports.useDatabaseMaintenance = useDatabaseMaintenance;
|
|
24
25
|
const react_1 = require("react");
|
|
25
26
|
const runner_js_1 = require("./runner.js");
|
|
26
27
|
function areRowsEqual(a, b) {
|
|
@@ -587,11 +588,61 @@ function useMutations(client) {
|
|
|
587
588
|
transaction: (action) => client ? client.transaction(action) : Promise.resolve(undefined),
|
|
588
589
|
}), [client]);
|
|
589
590
|
}
|
|
591
|
+
/**
|
|
592
|
+
* Reactive hook for database maintenance (vacuum, WAL checkpoint, outbox pruning, and storage metrics).
|
|
593
|
+
*/
|
|
594
|
+
function useDatabaseMaintenance(client) {
|
|
595
|
+
const [stats, setStats] = (0, react_1.useState)(null);
|
|
596
|
+
const [loading, setLoading] = (0, react_1.useState)(false);
|
|
597
|
+
const refreshStats = (0, react_1.useCallback)(async () => {
|
|
598
|
+
if (!client)
|
|
599
|
+
return null;
|
|
600
|
+
setLoading(true);
|
|
601
|
+
try {
|
|
602
|
+
const res = await client.maintenance.getStorageStats();
|
|
603
|
+
setStats(res);
|
|
604
|
+
return res;
|
|
605
|
+
}
|
|
606
|
+
catch {
|
|
607
|
+
return null;
|
|
608
|
+
}
|
|
609
|
+
finally {
|
|
610
|
+
setLoading(false);
|
|
611
|
+
}
|
|
612
|
+
}, [client]);
|
|
613
|
+
const pruneOutbox = (0, react_1.useCallback)(async (options) => {
|
|
614
|
+
if (!client)
|
|
615
|
+
return 0;
|
|
616
|
+
const count = await client.maintenance.pruneOutbox(options);
|
|
617
|
+
await refreshStats();
|
|
618
|
+
return count;
|
|
619
|
+
}, [client, refreshStats]);
|
|
620
|
+
const vacuum = (0, react_1.useCallback)(async () => {
|
|
621
|
+
if (!client)
|
|
622
|
+
return;
|
|
623
|
+
await client.maintenance.vacuum();
|
|
624
|
+
await refreshStats();
|
|
625
|
+
}, [client, refreshStats]);
|
|
626
|
+
const checkpoint = (0, react_1.useCallback)(async (mode) => {
|
|
627
|
+
if (!client)
|
|
628
|
+
return;
|
|
629
|
+
await client.maintenance.checkpoint(mode);
|
|
630
|
+
await refreshStats();
|
|
631
|
+
}, [client, refreshStats]);
|
|
632
|
+
return {
|
|
633
|
+
stats,
|
|
634
|
+
loading,
|
|
635
|
+
refreshStats,
|
|
636
|
+
pruneOutbox,
|
|
637
|
+
vacuum,
|
|
638
|
+
checkpoint,
|
|
639
|
+
};
|
|
640
|
+
}
|
|
590
641
|
__exportStar(require("./runner.js"), exports);
|
|
591
642
|
__exportStar(require("./status.js"), exports);
|
|
592
643
|
__exportStar(require("./theme.js"), exports);
|
|
593
644
|
__exportStar(require("./BenchmarkModal.js"), exports);
|
|
594
645
|
var SyncInspector_js_1 = require("./SyncInspector.js");
|
|
595
646
|
Object.defineProperty(exports, "SyncInspector", { enumerable: true, get: function () { return SyncInspector_js_1.SyncInspector; } });
|
|
596
|
-
Object.defineProperty(exports, "SyncIndicator", { enumerable: true, get: function () { return SyncInspector_js_1.SyncIndicator; } });
|
|
597
647
|
__exportStar(require("./auth.js"), exports);
|
|
648
|
+
__exportStar(require("./backups.js"), exports);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-lite",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.16",
|
|
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
5
|
"license": "MIT",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -56,6 +56,16 @@
|
|
|
56
56
|
"import": "./dist/backups/index.js",
|
|
57
57
|
"default": "./dist/backups/index.js"
|
|
58
58
|
},
|
|
59
|
+
"./portability": {
|
|
60
|
+
"types": "./dist/utils/portability.d.ts",
|
|
61
|
+
"import": "./dist/utils/portability.js",
|
|
62
|
+
"default": "./dist/utils/portability.js"
|
|
63
|
+
},
|
|
64
|
+
"./maintenance": {
|
|
65
|
+
"types": "./dist/core/maintenance.d.ts",
|
|
66
|
+
"import": "./dist/core/maintenance.js",
|
|
67
|
+
"default": "./dist/core/maintenance.js"
|
|
68
|
+
},
|
|
59
69
|
"./expo-sqlite": {
|
|
60
70
|
"types": "./dist/adapters/local/expo-sqlite.d.ts",
|
|
61
71
|
"import": "./dist/adapters/local/expo-sqlite.js",
|