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
|
@@ -34,10 +34,6 @@ export interface SupabaseAdapterOptions {
|
|
|
34
34
|
* Default: true
|
|
35
35
|
*/
|
|
36
36
|
deletedValue?: any;
|
|
37
|
-
/**
|
|
38
|
-
* Legacy alias for deletedColumn.
|
|
39
|
-
*/
|
|
40
|
-
deletedAtColumn?: string;
|
|
41
37
|
/**
|
|
42
38
|
* Optional custom Postgres RPC function name for batch pushing.
|
|
43
39
|
* If provided, will call `supabase.rpc(pushRpcName, { operations })`.
|
|
@@ -89,7 +85,7 @@ export declare class SupabaseRemoteAdapter implements RemoteDBAdapter {
|
|
|
89
85
|
*/
|
|
90
86
|
getClient(): AnySupabaseClient;
|
|
91
87
|
/**
|
|
92
|
-
*
|
|
88
|
+
* Active Supabase client instance.
|
|
93
89
|
*/
|
|
94
90
|
get client(): AnySupabaseClient;
|
|
95
91
|
/**
|
|
@@ -38,7 +38,7 @@ class SupabaseRemoteAdapter {
|
|
|
38
38
|
this.scopedTables = options.scopedTables;
|
|
39
39
|
this.updatedAtColumn = options.updatedAtColumn ?? 'updated_at';
|
|
40
40
|
this.primaryKey = options.primaryKey ?? 'id';
|
|
41
|
-
this.deletedColumn = options.deletedColumn ??
|
|
41
|
+
this.deletedColumn = options.deletedColumn ?? 'deleted';
|
|
42
42
|
this.deletedValue = options.deletedValue !== undefined ? options.deletedValue : true;
|
|
43
43
|
this.pushRpcName = options.pushRpcName;
|
|
44
44
|
this.schema = options.schema ?? 'public';
|
|
@@ -61,7 +61,7 @@ class SupabaseRemoteAdapter {
|
|
|
61
61
|
return this.clientResolver ?? null;
|
|
62
62
|
}
|
|
63
63
|
/**
|
|
64
|
-
*
|
|
64
|
+
* Active Supabase client instance.
|
|
65
65
|
*/
|
|
66
66
|
get client() {
|
|
67
67
|
return this.getClient();
|
package/dist/auth/google.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { GoogleDriveBackupStorage } from '../backups/cloud-storage.js';
|
|
1
|
+
import { GoogleDriveBackupStorage, GoogleDriveBackupClient, type GoogleDriveBackupClientOptions } from '../backups/cloud-storage.js';
|
|
2
2
|
export declare const GOOGLE_AUTHORIZATION_ENDPOINT = "https://accounts.google.com/o/oauth2/v2/auth";
|
|
3
3
|
export declare const GOOGLE_TOKEN_ENDPOINT = "https://oauth2.googleapis.com/token";
|
|
4
4
|
export declare const GOOGLE_USERINFO_ENDPOINT = "https://www.googleapis.com/oauth2/v3/userinfo";
|
|
@@ -72,4 +72,5 @@ export declare class GoogleAuthClient {
|
|
|
72
72
|
getValidAccessToken(): Promise<string>;
|
|
73
73
|
private fetchUserProfile;
|
|
74
74
|
createDriveBackupStorage(): GoogleDriveBackupStorage;
|
|
75
|
+
createDriveBackupClient(options?: Partial<GoogleDriveBackupClientOptions>): GoogleDriveBackupClient;
|
|
75
76
|
}
|
package/dist/auth/google.js
CHANGED
|
@@ -319,5 +319,12 @@ class GoogleAuthClient {
|
|
|
319
319
|
createDriveBackupStorage() {
|
|
320
320
|
return new cloud_storage_js_1.GoogleDriveBackupStorage(() => this.getValidAccessToken(), this.fetcher);
|
|
321
321
|
}
|
|
322
|
+
createDriveBackupClient(options = {}) {
|
|
323
|
+
return new cloud_storage_js_1.GoogleDriveBackupClient({
|
|
324
|
+
authClient: this,
|
|
325
|
+
fetch: this.fetcher,
|
|
326
|
+
...options,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
322
329
|
}
|
|
323
330
|
exports.GoogleAuthClient = GoogleAuthClient;
|
package/dist/auth/microsoft.d.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { OneDriveBackupStorage, OneDriveBackupClient, type OneDriveBackupClientOptions } from '../backups/onedrive.js';
|
|
1
2
|
export declare const MICROSOFT_GRAPH_ENDPOINT = "https://graph.microsoft.com/v1.0";
|
|
2
3
|
export declare const DEFAULT_MICROSOFT_AUTH_SCOPES: string[];
|
|
3
4
|
export interface MicrosoftUser {
|
|
@@ -9,7 +10,6 @@ export interface MicrosoftUser {
|
|
|
9
10
|
picture?: string;
|
|
10
11
|
userPrincipalName?: string;
|
|
11
12
|
}
|
|
12
|
-
export type OneDriveUser = MicrosoftUser;
|
|
13
13
|
export interface MicrosoftAuthTokens {
|
|
14
14
|
accessToken: string;
|
|
15
15
|
refreshToken?: string;
|
|
@@ -53,7 +53,6 @@ export interface MicrosoftAuthClientOptions {
|
|
|
53
53
|
}
|
|
54
54
|
export declare function createInMemoryMicrosoftTokenStore(): MicrosoftAuthTokenStore;
|
|
55
55
|
export declare function createMicrosoftAuthTokenStore(storage: MicrosoftAuthKeyValueStorage, prefix?: string): MicrosoftAuthTokenStore;
|
|
56
|
-
export declare const createOneDriveAuthTokenStore: typeof createMicrosoftAuthTokenStore;
|
|
57
56
|
export declare class MicrosoftAuthClient {
|
|
58
57
|
private readonly options;
|
|
59
58
|
private readonly tokenStore;
|
|
@@ -72,5 +71,6 @@ export declare class MicrosoftAuthClient {
|
|
|
72
71
|
getUser(): Promise<MicrosoftUser | null>;
|
|
73
72
|
getValidAccessToken(): Promise<string>;
|
|
74
73
|
private fetchUserProfile;
|
|
74
|
+
createOneDriveBackupStorage(): OneDriveBackupStorage;
|
|
75
|
+
createOneDriveBackupClient(options?: Partial<OneDriveBackupClientOptions>): OneDriveBackupClient;
|
|
75
76
|
}
|
|
76
|
-
export declare const OneDriveAuthClient: typeof MicrosoftAuthClient;
|
package/dist/auth/microsoft.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.MicrosoftAuthClient = exports.DEFAULT_MICROSOFT_AUTH_SCOPES = exports.MICROSOFT_GRAPH_ENDPOINT = void 0;
|
|
4
4
|
exports.createInMemoryMicrosoftTokenStore = createInMemoryMicrosoftTokenStore;
|
|
5
5
|
exports.createMicrosoftAuthTokenStore = createMicrosoftAuthTokenStore;
|
|
6
6
|
const sha2_js_1 = require("@noble/hashes/sha2.js");
|
|
7
7
|
const utils_js_1 = require("@noble/hashes/utils.js");
|
|
8
|
+
const onedrive_js_1 = require("../backups/onedrive.js");
|
|
8
9
|
exports.MICROSOFT_GRAPH_ENDPOINT = 'https://graph.microsoft.com/v1.0';
|
|
9
10
|
exports.DEFAULT_MICROSOFT_AUTH_SCOPES = ['openid', 'profile', 'email', 'User.Read'];
|
|
10
11
|
function secureRandomBytes(length) {
|
|
@@ -89,7 +90,6 @@ function createMicrosoftAuthTokenStore(storage, prefix = 'local-lite-microsoft-a
|
|
|
89
90
|
},
|
|
90
91
|
};
|
|
91
92
|
}
|
|
92
|
-
exports.createOneDriveAuthTokenStore = createMicrosoftAuthTokenStore;
|
|
93
93
|
class MicrosoftAuthClient {
|
|
94
94
|
options;
|
|
95
95
|
tokenStore;
|
|
@@ -305,6 +305,15 @@ class MicrosoftAuthClient {
|
|
|
305
305
|
userPrincipalName: raw.userPrincipalName,
|
|
306
306
|
};
|
|
307
307
|
}
|
|
308
|
+
createOneDriveBackupStorage() {
|
|
309
|
+
return new onedrive_js_1.OneDriveBackupStorage(() => this.getValidAccessToken(), this.fetcher);
|
|
310
|
+
}
|
|
311
|
+
createOneDriveBackupClient(options) {
|
|
312
|
+
return new onedrive_js_1.OneDriveBackupClient({
|
|
313
|
+
authClient: this,
|
|
314
|
+
fetch: this.fetcher,
|
|
315
|
+
...options,
|
|
316
|
+
});
|
|
317
|
+
}
|
|
308
318
|
}
|
|
309
319
|
exports.MicrosoftAuthClient = MicrosoftAuthClient;
|
|
310
|
-
exports.OneDriveAuthClient = MicrosoftAuthClient;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import type { BackupStorage } from './
|
|
1
|
+
import type { BackupStorage, CloudBackupClient, CreateDatabaseBackupOptions, ResolvableBackupDatabase } from './operations.js';
|
|
2
|
+
import type { EncryptedBackupOptions, RestoreEncryptedBackupOptions } from './encryption.js';
|
|
3
|
+
import type { GoogleAuthClient } from '../auth/google.js';
|
|
2
4
|
export interface CloudBackupFile {
|
|
3
5
|
id: string;
|
|
4
6
|
name: string;
|
|
@@ -6,6 +8,7 @@ export interface CloudBackupFile {
|
|
|
6
8
|
size?: number;
|
|
7
9
|
}
|
|
8
10
|
export type BackupAccessTokenProvider = () => Promise<string>;
|
|
11
|
+
export declare const GOOGLE_DRIVE_SCOPE = "https://www.googleapis.com/auth/drive.file";
|
|
9
12
|
export interface GoogleDriveTokens {
|
|
10
13
|
accessToken: string;
|
|
11
14
|
refreshToken?: string;
|
|
@@ -30,13 +33,16 @@ export interface GoogleDriveAuthorizationResponse {
|
|
|
30
33
|
state: string;
|
|
31
34
|
}
|
|
32
35
|
export interface GoogleDriveBackupClientOptions {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
36
|
+
authClient?: GoogleAuthClient;
|
|
37
|
+
clientId?: string;
|
|
38
|
+
redirectUri?: string;
|
|
39
|
+
authorize?: (request: GoogleDriveAuthorizationRequest) => Promise<GoogleDriveAuthorizationResponse | null>;
|
|
40
|
+
tokenStore?: GoogleDriveTokenStore;
|
|
41
|
+
storage?: GoogleDriveKeyValueStorage;
|
|
37
42
|
randomBytes?: (length: number) => Uint8Array;
|
|
38
43
|
fetch?: typeof fetch;
|
|
39
44
|
}
|
|
45
|
+
export declare function createInMemoryGoogleDriveTokenStore(): GoogleDriveTokenStore;
|
|
40
46
|
export declare function createGoogleDriveTokenStore(storage: GoogleDriveKeyValueStorage, key?: string): GoogleDriveTokenStore;
|
|
41
47
|
export declare class GoogleDriveBackupStorage implements BackupStorage {
|
|
42
48
|
private readonly getAccessToken;
|
|
@@ -44,18 +50,28 @@ export declare class GoogleDriveBackupStorage implements BackupStorage {
|
|
|
44
50
|
constructor(getAccessToken: BackupAccessTokenProvider, fetcher?: typeof fetch);
|
|
45
51
|
save(fileName: string, data: Uint8Array): Promise<void>;
|
|
46
52
|
read(fileId: string): Promise<Uint8Array>;
|
|
53
|
+
delete(fileId: string): Promise<void>;
|
|
47
54
|
list(): Promise<CloudBackupFile[]>;
|
|
48
55
|
}
|
|
49
|
-
export declare class GoogleDriveBackupClient implements
|
|
56
|
+
export declare class GoogleDriveBackupClient implements CloudBackupClient {
|
|
50
57
|
private readonly options;
|
|
51
58
|
private readonly storage;
|
|
52
59
|
private readonly fetcher;
|
|
60
|
+
private readonly authClient?;
|
|
61
|
+
private readonly tokenStore?;
|
|
62
|
+
private readonly listeners;
|
|
53
63
|
constructor(options: GoogleDriveBackupClientOptions);
|
|
64
|
+
onStatusChange(listener: (connected: boolean) => void): () => void;
|
|
65
|
+
private notify;
|
|
54
66
|
connect(): Promise<boolean>;
|
|
55
67
|
disconnect(): Promise<void>;
|
|
56
68
|
isConnected(): Promise<boolean>;
|
|
57
69
|
getValidAccessToken(): Promise<string>;
|
|
58
70
|
save(fileName: string, data: Uint8Array): Promise<void>;
|
|
59
71
|
read(fileId: string): Promise<Uint8Array>;
|
|
72
|
+
delete(fileId: string): Promise<void>;
|
|
73
|
+
deleteBackup(fileId: string): Promise<void>;
|
|
60
74
|
list(): Promise<CloudBackupFile[]>;
|
|
75
|
+
backup(database: ResolvableBackupDatabase, options?: CreateDatabaseBackupOptions & Partial<EncryptedBackupOptions>): Promise<string>;
|
|
76
|
+
restore(database: ResolvableBackupDatabase, fileNameOrId: string, options?: Partial<RestoreEncryptedBackupOptions>): Promise<void>;
|
|
61
77
|
}
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.GoogleDriveBackupClient = exports.GoogleDriveBackupStorage = void 0;
|
|
3
|
+
exports.GoogleDriveBackupClient = exports.GoogleDriveBackupStorage = exports.GOOGLE_DRIVE_SCOPE = void 0;
|
|
4
|
+
exports.createInMemoryGoogleDriveTokenStore = createInMemoryGoogleDriveTokenStore;
|
|
4
5
|
exports.createGoogleDriveTokenStore = createGoogleDriveTokenStore;
|
|
5
6
|
const sha2_js_1 = require("@noble/hashes/sha2.js");
|
|
6
7
|
const utils_js_1 = require("@noble/hashes/utils.js");
|
|
8
|
+
const operations_js_1 = require("./operations.js");
|
|
7
9
|
const GOOGLE_API = 'https://www.googleapis.com/drive/v3';
|
|
8
10
|
const GOOGLE_UPLOAD_API = 'https://www.googleapis.com/upload/drive/v3';
|
|
9
11
|
const GOOGLE_AUTHORIZATION_ENDPOINT = 'https://accounts.google.com/o/oauth2/v2/auth';
|
|
10
12
|
const GOOGLE_TOKEN_ENDPOINT = 'https://oauth2.googleapis.com/token';
|
|
11
|
-
|
|
13
|
+
exports.GOOGLE_DRIVE_SCOPE = 'https://www.googleapis.com/auth/drive.file';
|
|
12
14
|
const BACKUP_FOLDER = 'backups';
|
|
13
15
|
function secureRandomBytes(length) {
|
|
14
16
|
const crypto = globalThis.crypto;
|
|
@@ -42,6 +44,18 @@ function validateGoogleTokens(value) {
|
|
|
42
44
|
typeof tokens.expiresAt === 'number' &&
|
|
43
45
|
(tokens.refreshToken === undefined || typeof tokens.refreshToken === 'string'));
|
|
44
46
|
}
|
|
47
|
+
function createInMemoryGoogleDriveTokenStore() {
|
|
48
|
+
let stored = null;
|
|
49
|
+
return {
|
|
50
|
+
get: async () => stored,
|
|
51
|
+
set: async (tokens) => {
|
|
52
|
+
stored = tokens;
|
|
53
|
+
},
|
|
54
|
+
remove: async () => {
|
|
55
|
+
stored = null;
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
}
|
|
45
59
|
function createGoogleDriveTokenStore(storage, key = 'local-lite-google-drive-backup-tokens') {
|
|
46
60
|
return {
|
|
47
61
|
get: async () => {
|
|
@@ -165,6 +179,16 @@ class GoogleDriveBackupStorage {
|
|
|
165
179
|
throw await parseApiError(response);
|
|
166
180
|
return new Uint8Array(await response.arrayBuffer());
|
|
167
181
|
}
|
|
182
|
+
async delete(fileId) {
|
|
183
|
+
const token = await this.getAccessToken();
|
|
184
|
+
const response = await this.fetcher(`${GOOGLE_API}/files/${encodeURIComponent(fileId)}`, {
|
|
185
|
+
method: 'DELETE',
|
|
186
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
187
|
+
});
|
|
188
|
+
if (!response.ok && response.status !== 404) {
|
|
189
|
+
throw await parseApiError(response);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
168
192
|
async list() {
|
|
169
193
|
const token = await this.getAccessToken();
|
|
170
194
|
const folderId = await getGoogleFolderId(token, this.fetcher);
|
|
@@ -188,16 +212,60 @@ class GoogleDriveBackupClient {
|
|
|
188
212
|
options;
|
|
189
213
|
storage;
|
|
190
214
|
fetcher;
|
|
215
|
+
authClient;
|
|
216
|
+
tokenStore;
|
|
217
|
+
listeners = new Set();
|
|
191
218
|
constructor(options) {
|
|
192
219
|
this.options = options;
|
|
193
|
-
if (!options.clientId.trim())
|
|
194
|
-
throw new Error('Google OAuth client ID is required.');
|
|
195
|
-
if (!options.redirectUri.trim())
|
|
196
|
-
throw new Error('Google OAuth redirect URI is required.');
|
|
197
220
|
this.fetcher = options.fetch ?? fetch;
|
|
198
|
-
|
|
221
|
+
if (options.authClient) {
|
|
222
|
+
this.authClient = options.authClient;
|
|
223
|
+
this.storage = new GoogleDriveBackupStorage(() => this.authClient.getValidAccessToken(), this.fetcher);
|
|
224
|
+
this.authClient.onAuthStateChange(user => {
|
|
225
|
+
this.notify(Boolean(user));
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
if (!options.clientId?.trim()) {
|
|
230
|
+
throw new Error('Google OAuth client ID is required when authClient is not provided.');
|
|
231
|
+
}
|
|
232
|
+
if (!options.redirectUri?.trim()) {
|
|
233
|
+
throw new Error('Google OAuth redirect URI is required when authClient is not provided.');
|
|
234
|
+
}
|
|
235
|
+
if (!options.authorize) {
|
|
236
|
+
throw new Error('Google OAuth authorize function is required when authClient is not provided.');
|
|
237
|
+
}
|
|
238
|
+
this.tokenStore =
|
|
239
|
+
options.tokenStore ??
|
|
240
|
+
(options.storage
|
|
241
|
+
? createGoogleDriveTokenStore(options.storage)
|
|
242
|
+
: createInMemoryGoogleDriveTokenStore());
|
|
243
|
+
this.storage = new GoogleDriveBackupStorage(() => this.getValidAccessToken(), this.fetcher);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
onStatusChange(listener) {
|
|
247
|
+
this.listeners.add(listener);
|
|
248
|
+
return () => {
|
|
249
|
+
this.listeners.delete(listener);
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
notify(connected) {
|
|
253
|
+
for (const listener of this.listeners) {
|
|
254
|
+
try {
|
|
255
|
+
listener(connected);
|
|
256
|
+
}
|
|
257
|
+
catch (err) {
|
|
258
|
+
console.error('Error in GoogleDriveBackupClient status listener:', err);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
199
261
|
}
|
|
200
262
|
async connect() {
|
|
263
|
+
if (this.authClient) {
|
|
264
|
+
const user = await this.authClient.signIn();
|
|
265
|
+
const connected = user !== null;
|
|
266
|
+
this.notify(connected);
|
|
267
|
+
return connected;
|
|
268
|
+
}
|
|
201
269
|
const randomBytes = this.options.randomBytes ?? secureRandomBytes;
|
|
202
270
|
const verifierBytes = randomBytes(32);
|
|
203
271
|
const stateBytes = randomBytes(32);
|
|
@@ -211,7 +279,7 @@ class GoogleDriveBackupClient {
|
|
|
211
279
|
client_id: this.options.clientId,
|
|
212
280
|
redirect_uri: this.options.redirectUri,
|
|
213
281
|
response_type: 'code',
|
|
214
|
-
scope: GOOGLE_DRIVE_SCOPE,
|
|
282
|
+
scope: exports.GOOGLE_DRIVE_SCOPE,
|
|
215
283
|
access_type: 'offline',
|
|
216
284
|
prompt: 'consent',
|
|
217
285
|
code_challenge: codeChallenge,
|
|
@@ -222,8 +290,10 @@ class GoogleDriveBackupClient {
|
|
|
222
290
|
authorizationUrl: `${GOOGLE_AUTHORIZATION_ENDPOINT}?${authorizationParams}`,
|
|
223
291
|
redirectUri: this.options.redirectUri,
|
|
224
292
|
});
|
|
225
|
-
if (!authorization)
|
|
293
|
+
if (!authorization) {
|
|
294
|
+
this.notify(false);
|
|
226
295
|
return false;
|
|
296
|
+
}
|
|
227
297
|
if (authorization.state !== state) {
|
|
228
298
|
throw new Error('Google OAuth state validation failed. Please try connecting again.');
|
|
229
299
|
}
|
|
@@ -244,19 +314,32 @@ class GoogleDriveBackupClient {
|
|
|
244
314
|
const payload = await parseTokenResponse(response);
|
|
245
315
|
if (!response.ok)
|
|
246
316
|
throwTokenError(payload, response.status);
|
|
247
|
-
const prior = await this.
|
|
248
|
-
await this.
|
|
317
|
+
const prior = await this.tokenStore.get();
|
|
318
|
+
await this.tokenStore.set(createStoredTokens(payload, prior));
|
|
319
|
+
this.notify(true);
|
|
249
320
|
return true;
|
|
250
321
|
}
|
|
251
322
|
async disconnect() {
|
|
252
|
-
|
|
323
|
+
if (this.authClient) {
|
|
324
|
+
await this.authClient.signOut();
|
|
325
|
+
}
|
|
326
|
+
else if (this.tokenStore) {
|
|
327
|
+
await this.tokenStore.remove();
|
|
328
|
+
}
|
|
329
|
+
this.notify(false);
|
|
253
330
|
}
|
|
254
331
|
async isConnected() {
|
|
255
|
-
|
|
332
|
+
if (this.authClient) {
|
|
333
|
+
return this.authClient.isSignedIn();
|
|
334
|
+
}
|
|
335
|
+
const tokens = await this.tokenStore.get();
|
|
256
336
|
return validateGoogleTokens(tokens);
|
|
257
337
|
}
|
|
258
338
|
async getValidAccessToken() {
|
|
259
|
-
|
|
339
|
+
if (this.authClient) {
|
|
340
|
+
return this.authClient.getValidAccessToken();
|
|
341
|
+
}
|
|
342
|
+
const tokens = await this.tokenStore.get();
|
|
260
343
|
if (!validateGoogleTokens(tokens)) {
|
|
261
344
|
throw new Error('Connect Google Drive before using cloud backups.');
|
|
262
345
|
}
|
|
@@ -272,14 +355,14 @@ class GoogleDriveBackupClient {
|
|
|
272
355
|
client_id: this.options.clientId,
|
|
273
356
|
grant_type: 'refresh_token',
|
|
274
357
|
refresh_token: tokens.refreshToken,
|
|
275
|
-
scope: GOOGLE_DRIVE_SCOPE,
|
|
358
|
+
scope: exports.GOOGLE_DRIVE_SCOPE,
|
|
276
359
|
}).toString(),
|
|
277
360
|
});
|
|
278
361
|
const payload = await parseTokenResponse(response);
|
|
279
362
|
if (!response.ok)
|
|
280
363
|
throwTokenError(payload, response.status);
|
|
281
364
|
const updatedTokens = createStoredTokens(payload, tokens);
|
|
282
|
-
await this.
|
|
365
|
+
await this.tokenStore.set(updatedTokens);
|
|
283
366
|
return updatedTokens.accessToken;
|
|
284
367
|
}
|
|
285
368
|
save(fileName, data) {
|
|
@@ -288,8 +371,20 @@ class GoogleDriveBackupClient {
|
|
|
288
371
|
read(fileId) {
|
|
289
372
|
return this.storage.read(fileId);
|
|
290
373
|
}
|
|
374
|
+
delete(fileId) {
|
|
375
|
+
return this.storage.delete(fileId);
|
|
376
|
+
}
|
|
377
|
+
deleteBackup(fileId) {
|
|
378
|
+
return this.delete(fileId);
|
|
379
|
+
}
|
|
291
380
|
list() {
|
|
292
381
|
return this.storage.list();
|
|
293
382
|
}
|
|
383
|
+
backup(database, options) {
|
|
384
|
+
return (0, operations_js_1.performBackup)(database, this, options);
|
|
385
|
+
}
|
|
386
|
+
restore(database, fileNameOrId, options) {
|
|
387
|
+
return (0, operations_js_1.performRestore)(database, this, fileNameOrId, options);
|
|
388
|
+
}
|
|
294
389
|
}
|
|
295
390
|
exports.GoogleDriveBackupClient = GoogleDriveBackupClient;
|
package/dist/backups/index.d.ts
CHANGED
|
@@ -1,16 +1,4 @@
|
|
|
1
|
-
export { type
|
|
2
|
-
export {
|
|
1
|
+
export { type BackupDatabase, type BackupStorage, type CloudBackupClient, type CreateDatabaseBackupOptions, type ResolvableBackupDatabase, createDatabaseBackup, restoreDatabaseBackup, performBackup, performRestore, resolveBackupDatabase, } from './operations.js';
|
|
2
|
+
export { type BackupAccessTokenProvider, type CloudBackupFile, type GoogleDriveAuthorizationRequest, type GoogleDriveAuthorizationResponse, type GoogleDriveBackupClientOptions, type GoogleDriveKeyValueStorage, type GoogleDriveTokenStore, type GoogleDriveTokens, GOOGLE_DRIVE_SCOPE, GoogleDriveBackupClient, GoogleDriveBackupStorage, createGoogleDriveTokenStore, createInMemoryGoogleDriveTokenStore, } from './cloud-storage.js';
|
|
3
|
+
export { type OneDriveBackupClientOptions, MICROSOFT_GRAPH_DRIVE, OneDriveBackupStorage, OneDriveBackupClient, } from './onedrive.js';
|
|
3
4
|
export { type EncryptedBackupOptions, type RestoreEncryptedBackupOptions, isEncryptedBackup, encryptBackupData, decryptBackupData, createEncryptedDatabaseBackup, restoreEncryptedDatabaseBackup, } from './encryption.js';
|
|
4
|
-
export interface BackupDatabase {
|
|
5
|
-
exportDatabase(): Promise<Uint8Array>;
|
|
6
|
-
importDatabase(data: Uint8Array): Promise<void>;
|
|
7
|
-
}
|
|
8
|
-
export interface BackupStorage {
|
|
9
|
-
save(fileName: string, data: Uint8Array): Promise<void>;
|
|
10
|
-
read(fileName: string): Promise<Uint8Array>;
|
|
11
|
-
}
|
|
12
|
-
export interface CreateDatabaseBackupOptions {
|
|
13
|
-
fileName?: string;
|
|
14
|
-
}
|
|
15
|
-
export declare function createDatabaseBackup(database: BackupDatabase, storage: BackupStorage, options?: CreateDatabaseBackupOptions): Promise<string>;
|
|
16
|
-
export declare function restoreDatabaseBackup(database: BackupDatabase, storage: BackupStorage, fileName: string): Promise<void>;
|
package/dist/backups/index.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.restoreEncryptedDatabaseBackup = exports.createEncryptedDatabaseBackup = exports.decryptBackupData = exports.encryptBackupData = exports.isEncryptedBackup = exports.OneDriveBackupClient = exports.OneDriveBackupStorage = exports.MICROSOFT_GRAPH_DRIVE = exports.createGoogleDriveTokenStore = exports.GoogleDriveBackupStorage = exports.GoogleDriveBackupClient = void 0;
|
|
4
|
-
|
|
5
|
-
exports.
|
|
3
|
+
exports.restoreEncryptedDatabaseBackup = exports.createEncryptedDatabaseBackup = exports.decryptBackupData = exports.encryptBackupData = exports.isEncryptedBackup = exports.OneDriveBackupClient = exports.OneDriveBackupStorage = exports.MICROSOFT_GRAPH_DRIVE = exports.createInMemoryGoogleDriveTokenStore = exports.createGoogleDriveTokenStore = exports.GoogleDriveBackupStorage = exports.GoogleDriveBackupClient = exports.GOOGLE_DRIVE_SCOPE = exports.resolveBackupDatabase = exports.performRestore = exports.performBackup = exports.restoreDatabaseBackup = exports.createDatabaseBackup = void 0;
|
|
4
|
+
var operations_js_1 = require("./operations.js");
|
|
5
|
+
Object.defineProperty(exports, "createDatabaseBackup", { enumerable: true, get: function () { return operations_js_1.createDatabaseBackup; } });
|
|
6
|
+
Object.defineProperty(exports, "restoreDatabaseBackup", { enumerable: true, get: function () { return operations_js_1.restoreDatabaseBackup; } });
|
|
7
|
+
Object.defineProperty(exports, "performBackup", { enumerable: true, get: function () { return operations_js_1.performBackup; } });
|
|
8
|
+
Object.defineProperty(exports, "performRestore", { enumerable: true, get: function () { return operations_js_1.performRestore; } });
|
|
9
|
+
Object.defineProperty(exports, "resolveBackupDatabase", { enumerable: true, get: function () { return operations_js_1.resolveBackupDatabase; } });
|
|
6
10
|
var cloud_storage_js_1 = require("./cloud-storage.js");
|
|
11
|
+
Object.defineProperty(exports, "GOOGLE_DRIVE_SCOPE", { enumerable: true, get: function () { return cloud_storage_js_1.GOOGLE_DRIVE_SCOPE; } });
|
|
7
12
|
Object.defineProperty(exports, "GoogleDriveBackupClient", { enumerable: true, get: function () { return cloud_storage_js_1.GoogleDriveBackupClient; } });
|
|
8
13
|
Object.defineProperty(exports, "GoogleDriveBackupStorage", { enumerable: true, get: function () { return cloud_storage_js_1.GoogleDriveBackupStorage; } });
|
|
9
14
|
Object.defineProperty(exports, "createGoogleDriveTokenStore", { enumerable: true, get: function () { return cloud_storage_js_1.createGoogleDriveTokenStore; } });
|
|
15
|
+
Object.defineProperty(exports, "createInMemoryGoogleDriveTokenStore", { enumerable: true, get: function () { return cloud_storage_js_1.createInMemoryGoogleDriveTokenStore; } });
|
|
10
16
|
var onedrive_js_1 = require("./onedrive.js");
|
|
11
17
|
Object.defineProperty(exports, "MICROSOFT_GRAPH_DRIVE", { enumerable: true, get: function () { return onedrive_js_1.MICROSOFT_GRAPH_DRIVE; } });
|
|
12
18
|
Object.defineProperty(exports, "OneDriveBackupStorage", { enumerable: true, get: function () { return onedrive_js_1.OneDriveBackupStorage; } });
|
|
@@ -17,26 +23,3 @@ Object.defineProperty(exports, "encryptBackupData", { enumerable: true, get: fun
|
|
|
17
23
|
Object.defineProperty(exports, "decryptBackupData", { enumerable: true, get: function () { return encryption_js_1.decryptBackupData; } });
|
|
18
24
|
Object.defineProperty(exports, "createEncryptedDatabaseBackup", { enumerable: true, get: function () { return encryption_js_1.createEncryptedDatabaseBackup; } });
|
|
19
25
|
Object.defineProperty(exports, "restoreEncryptedDatabaseBackup", { enumerable: true, get: function () { return encryption_js_1.restoreEncryptedDatabaseBackup; } });
|
|
20
|
-
function validateFileName(fileName) {
|
|
21
|
-
if (!fileName || /[\\/]/.test(fileName)) {
|
|
22
|
-
throw new Error('Backup file name must be a single file name.');
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
async function createDatabaseBackup(database, storage, options = {}) {
|
|
26
|
-
const fileName = options.fileName ?? `local-lite-${new Date().toISOString().replace(/[:.]/g, '-')}.db`;
|
|
27
|
-
validateFileName(fileName);
|
|
28
|
-
const databaseBytes = await database.exportDatabase();
|
|
29
|
-
if (databaseBytes.length === 0) {
|
|
30
|
-
throw new Error('Cannot create a backup from an empty database.');
|
|
31
|
-
}
|
|
32
|
-
await storage.save(fileName, databaseBytes);
|
|
33
|
-
return fileName;
|
|
34
|
-
}
|
|
35
|
-
async function restoreDatabaseBackup(database, storage, fileName) {
|
|
36
|
-
validateFileName(fileName);
|
|
37
|
-
const databaseBytes = await storage.read(fileName);
|
|
38
|
-
if (databaseBytes.length === 0) {
|
|
39
|
-
throw new Error('Cannot restore an empty database backup.');
|
|
40
|
-
}
|
|
41
|
-
await database.importDatabase(databaseBytes);
|
|
42
|
-
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import type { BackupAccessTokenProvider, BackupStorage,
|
|
2
|
-
import {
|
|
1
|
+
import type { BackupAccessTokenProvider, BackupStorage, CloudBackupClient, CreateDatabaseBackupOptions, ResolvableBackupDatabase } from './operations.js';
|
|
2
|
+
import type { CloudBackupFile } from './cloud-storage.js';
|
|
3
|
+
import type { EncryptedBackupOptions, RestoreEncryptedBackupOptions } from './encryption.js';
|
|
4
|
+
import { MicrosoftAuthClient, type MicrosoftAuthClientOptions } from '../auth/microsoft.js';
|
|
3
5
|
export declare const MICROSOFT_GRAPH_DRIVE = "https://graph.microsoft.com/v1.0/me/drive";
|
|
4
6
|
export declare class OneDriveBackupStorage implements BackupStorage {
|
|
5
7
|
private readonly getAccessToken;
|
|
@@ -7,16 +9,28 @@ export declare class OneDriveBackupStorage implements BackupStorage {
|
|
|
7
9
|
constructor(getAccessToken: BackupAccessTokenProvider, fetcher?: typeof fetch);
|
|
8
10
|
save(fileName: string, data: Uint8Array): Promise<void>;
|
|
9
11
|
read(fileIdOrName: string): Promise<Uint8Array>;
|
|
12
|
+
delete(fileIdOrName: string): Promise<void>;
|
|
10
13
|
list(): Promise<CloudBackupFile[]>;
|
|
11
14
|
}
|
|
12
|
-
export
|
|
15
|
+
export type OneDriveBackupClientOptions = MicrosoftAuthClientOptions | {
|
|
16
|
+
authClient: MicrosoftAuthClient;
|
|
17
|
+
fetch?: typeof fetch;
|
|
18
|
+
};
|
|
19
|
+
export declare class OneDriveBackupClient implements CloudBackupClient {
|
|
13
20
|
private readonly storage;
|
|
14
21
|
private readonly authClient;
|
|
15
|
-
|
|
22
|
+
private readonly listeners;
|
|
23
|
+
constructor(options: OneDriveBackupClientOptions);
|
|
24
|
+
onStatusChange(listener: (connected: boolean) => void): () => void;
|
|
25
|
+
private notify;
|
|
16
26
|
connect(): Promise<boolean>;
|
|
17
27
|
disconnect(): Promise<void>;
|
|
18
28
|
isConnected(): Promise<boolean>;
|
|
19
29
|
save(fileName: string, data: Uint8Array): Promise<void>;
|
|
20
30
|
read(fileId: string): Promise<Uint8Array>;
|
|
31
|
+
delete(fileIdOrName: string): Promise<void>;
|
|
32
|
+
deleteBackup(fileIdOrName: string): Promise<void>;
|
|
21
33
|
list(): Promise<CloudBackupFile[]>;
|
|
34
|
+
backup(database: ResolvableBackupDatabase, options?: CreateDatabaseBackupOptions & Partial<EncryptedBackupOptions>): Promise<string>;
|
|
35
|
+
restore(database: ResolvableBackupDatabase, fileNameOrId: string, options?: Partial<RestoreEncryptedBackupOptions>): Promise<void>;
|
|
22
36
|
}
|
package/dist/backups/onedrive.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.OneDriveBackupClient = exports.OneDriveBackupStorage = exports.MICROSOFT_GRAPH_DRIVE = void 0;
|
|
4
|
+
const operations_js_1 = require("./operations.js");
|
|
4
5
|
const microsoft_js_1 = require("../auth/microsoft.js");
|
|
5
6
|
exports.MICROSOFT_GRAPH_DRIVE = 'https://graph.microsoft.com/v1.0/me/drive';
|
|
6
7
|
const BACKUP_FOLDER = 'backups';
|
|
@@ -50,6 +51,20 @@ class OneDriveBackupStorage {
|
|
|
50
51
|
}
|
|
51
52
|
return new Uint8Array(await response.arrayBuffer());
|
|
52
53
|
}
|
|
54
|
+
async delete(fileIdOrName) {
|
|
55
|
+
const token = await this.getAccessToken();
|
|
56
|
+
let url = `${exports.MICROSOFT_GRAPH_DRIVE}/items/${encodeURIComponent(fileIdOrName)}`;
|
|
57
|
+
if (fileIdOrName.endsWith('.db') && !fileIdOrName.startsWith('item-')) {
|
|
58
|
+
url = `${exports.MICROSOFT_GRAPH_DRIVE}/special/approot:/${BACKUP_FOLDER}/${encodeURIComponent(fileIdOrName)}`;
|
|
59
|
+
}
|
|
60
|
+
const response = await this.fetcher(url, {
|
|
61
|
+
method: 'DELETE',
|
|
62
|
+
headers: { Authorization: `Bearer ${token}` },
|
|
63
|
+
});
|
|
64
|
+
if (!response.ok && response.status !== 404) {
|
|
65
|
+
throw new Error(`OneDrive delete failed (${response.status}): ${response.statusText}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
53
68
|
async list() {
|
|
54
69
|
const token = await this.getAccessToken();
|
|
55
70
|
const url = `${exports.MICROSOFT_GRAPH_DRIVE}/special/approot:/${BACKUP_FOLDER}:/children?$select=id,name,size,lastModifiedDateTime`;
|
|
@@ -77,26 +92,56 @@ exports.OneDriveBackupStorage = OneDriveBackupStorage;
|
|
|
77
92
|
class OneDriveBackupClient {
|
|
78
93
|
storage;
|
|
79
94
|
authClient;
|
|
95
|
+
listeners = new Set();
|
|
80
96
|
constructor(options) {
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
97
|
+
if ('authClient' in options && options.authClient) {
|
|
98
|
+
this.authClient = options.authClient;
|
|
99
|
+
this.storage = new OneDriveBackupStorage(() => this.authClient.getValidAccessToken(), options.fetch ?? fetch);
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
const msOptions = options;
|
|
103
|
+
const scopes = msOptions.scopes ?? [
|
|
104
|
+
'openid',
|
|
105
|
+
'profile',
|
|
106
|
+
'email',
|
|
107
|
+
'User.Read',
|
|
108
|
+
'Files.ReadWrite.AppFolder',
|
|
109
|
+
];
|
|
110
|
+
this.authClient = new microsoft_js_1.MicrosoftAuthClient({
|
|
111
|
+
...msOptions,
|
|
112
|
+
scopes,
|
|
113
|
+
});
|
|
114
|
+
this.storage = new OneDriveBackupStorage(() => this.authClient.getValidAccessToken(), msOptions.fetch ?? fetch);
|
|
115
|
+
}
|
|
116
|
+
this.authClient.onAuthStateChange(user => {
|
|
117
|
+
this.notify(Boolean(user));
|
|
91
118
|
});
|
|
92
|
-
|
|
119
|
+
}
|
|
120
|
+
onStatusChange(listener) {
|
|
121
|
+
this.listeners.add(listener);
|
|
122
|
+
return () => {
|
|
123
|
+
this.listeners.delete(listener);
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
notify(connected) {
|
|
127
|
+
for (const listener of this.listeners) {
|
|
128
|
+
try {
|
|
129
|
+
listener(connected);
|
|
130
|
+
}
|
|
131
|
+
catch (err) {
|
|
132
|
+
console.error('Error in OneDriveBackupClient status listener:', err);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
93
135
|
}
|
|
94
136
|
async connect() {
|
|
95
137
|
const user = await this.authClient.signIn();
|
|
96
|
-
|
|
138
|
+
const connected = user !== null;
|
|
139
|
+
this.notify(connected);
|
|
140
|
+
return connected;
|
|
97
141
|
}
|
|
98
142
|
async disconnect() {
|
|
99
143
|
await this.authClient.signOut();
|
|
144
|
+
this.notify(false);
|
|
100
145
|
}
|
|
101
146
|
async isConnected() {
|
|
102
147
|
return this.authClient.isSignedIn();
|
|
@@ -107,8 +152,20 @@ class OneDriveBackupClient {
|
|
|
107
152
|
read(fileId) {
|
|
108
153
|
return this.storage.read(fileId);
|
|
109
154
|
}
|
|
155
|
+
delete(fileIdOrName) {
|
|
156
|
+
return this.storage.delete(fileIdOrName);
|
|
157
|
+
}
|
|
158
|
+
deleteBackup(fileIdOrName) {
|
|
159
|
+
return this.delete(fileIdOrName);
|
|
160
|
+
}
|
|
110
161
|
list() {
|
|
111
162
|
return this.storage.list();
|
|
112
163
|
}
|
|
164
|
+
backup(database, options) {
|
|
165
|
+
return (0, operations_js_1.performBackup)(database, this, options);
|
|
166
|
+
}
|
|
167
|
+
restore(database, fileNameOrId, options) {
|
|
168
|
+
return (0, operations_js_1.performRestore)(database, this, fileNameOrId, options);
|
|
169
|
+
}
|
|
113
170
|
}
|
|
114
171
|
exports.OneDriveBackupClient = OneDriveBackupClient;
|