local-lite 0.0.11 → 0.0.14

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.
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./google.js"), exports);
18
+ __exportStar(require("./microsoft.js"), exports);
19
+ __exportStar(require("./github.js"), exports);
20
+ __exportStar(require("./apple.js"), exports);
@@ -0,0 +1,76 @@
1
+ export declare const MICROSOFT_GRAPH_ENDPOINT = "https://graph.microsoft.com/v1.0";
2
+ export declare const DEFAULT_MICROSOFT_AUTH_SCOPES: string[];
3
+ export interface MicrosoftUser {
4
+ id: string;
5
+ email: string;
6
+ name: string;
7
+ givenName?: string;
8
+ familyName?: string;
9
+ picture?: string;
10
+ userPrincipalName?: string;
11
+ }
12
+ export type OneDriveUser = MicrosoftUser;
13
+ export interface MicrosoftAuthTokens {
14
+ accessToken: string;
15
+ refreshToken?: string;
16
+ idToken?: string;
17
+ expiresAt: number;
18
+ tokenType?: string;
19
+ scope?: string;
20
+ }
21
+ export interface MicrosoftAuthTokenStore {
22
+ getTokens(): Promise<MicrosoftAuthTokens | null>;
23
+ setTokens(tokens: MicrosoftAuthTokens): Promise<void>;
24
+ removeTokens(): Promise<void>;
25
+ getUser(): Promise<MicrosoftUser | null>;
26
+ setUser(user: MicrosoftUser | null): Promise<void>;
27
+ }
28
+ export interface MicrosoftAuthKeyValueStorage {
29
+ getItem(key: string): Promise<unknown>;
30
+ setItem(key: string, value: unknown): Promise<void>;
31
+ removeItem(key: string): Promise<void>;
32
+ }
33
+ export interface MicrosoftAuthorizationRequest {
34
+ authorizationUrl: string;
35
+ redirectUri: string;
36
+ state: string;
37
+ }
38
+ export interface MicrosoftAuthorizationResponse {
39
+ code: string;
40
+ state: string;
41
+ }
42
+ export interface MicrosoftAuthClientOptions {
43
+ clientId: string;
44
+ redirectUri: string;
45
+ tenantId?: 'common' | 'consumers' | 'organizations' | string;
46
+ scopes?: string[];
47
+ authorize: (request: MicrosoftAuthorizationRequest) => Promise<MicrosoftAuthorizationResponse | null>;
48
+ tokenStore?: MicrosoftAuthTokenStore;
49
+ storage?: MicrosoftAuthKeyValueStorage;
50
+ randomBytes?: (length: number) => Uint8Array;
51
+ fetch?: typeof fetch;
52
+ prompt?: 'select_account' | 'consent' | 'login' | 'none';
53
+ }
54
+ export declare function createInMemoryMicrosoftTokenStore(): MicrosoftAuthTokenStore;
55
+ export declare function createMicrosoftAuthTokenStore(storage: MicrosoftAuthKeyValueStorage, prefix?: string): MicrosoftAuthTokenStore;
56
+ export declare const createOneDriveAuthTokenStore: typeof createMicrosoftAuthTokenStore;
57
+ export declare class MicrosoftAuthClient {
58
+ private readonly options;
59
+ private readonly tokenStore;
60
+ private readonly fetcher;
61
+ private readonly listeners;
62
+ private readonly tenant;
63
+ private currentUser;
64
+ constructor(options: MicrosoftAuthClientOptions);
65
+ get authorizationEndpoint(): string;
66
+ get tokenEndpoint(): string;
67
+ onAuthStateChange(listener: (user: MicrosoftUser | null) => void): () => void;
68
+ private notify;
69
+ signIn(): Promise<MicrosoftUser | null>;
70
+ signOut(): Promise<void>;
71
+ isSignedIn(): Promise<boolean>;
72
+ getUser(): Promise<MicrosoftUser | null>;
73
+ getValidAccessToken(): Promise<string>;
74
+ private fetchUserProfile;
75
+ }
76
+ export declare const OneDriveAuthClient: typeof MicrosoftAuthClient;
@@ -0,0 +1,310 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.OneDriveAuthClient = exports.MicrosoftAuthClient = exports.createOneDriveAuthTokenStore = exports.DEFAULT_MICROSOFT_AUTH_SCOPES = exports.MICROSOFT_GRAPH_ENDPOINT = void 0;
4
+ exports.createInMemoryMicrosoftTokenStore = createInMemoryMicrosoftTokenStore;
5
+ exports.createMicrosoftAuthTokenStore = createMicrosoftAuthTokenStore;
6
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
7
+ const utils_js_1 = require("@noble/hashes/utils.js");
8
+ exports.MICROSOFT_GRAPH_ENDPOINT = 'https://graph.microsoft.com/v1.0';
9
+ exports.DEFAULT_MICROSOFT_AUTH_SCOPES = ['openid', 'profile', 'email', 'User.Read'];
10
+ function secureRandomBytes(length) {
11
+ const crypto = globalThis.crypto;
12
+ if (!crypto?.getRandomValues) {
13
+ throw new Error('Secure random number generation is unavailable.');
14
+ }
15
+ return crypto.getRandomValues(new Uint8Array(length));
16
+ }
17
+ function base64UrlEncode(bytes) {
18
+ const alphabet = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
19
+ let result = '';
20
+ for (let index = 0; index < bytes.length; index += 3) {
21
+ const first = bytes[index];
22
+ const second = bytes[index + 1];
23
+ const third = bytes[index + 2];
24
+ result += alphabet[first >> 2];
25
+ result += alphabet[((first & 3) << 4) | (second === undefined ? 0 : second >> 4)];
26
+ if (second !== undefined) {
27
+ result += alphabet[((second & 15) << 2) | (third === undefined ? 0 : third >> 6)];
28
+ }
29
+ if (third !== undefined)
30
+ result += alphabet[third & 63];
31
+ }
32
+ return result;
33
+ }
34
+ function validateTokens(value) {
35
+ if (!value || typeof value !== 'object')
36
+ return false;
37
+ const tokens = value;
38
+ return (typeof tokens.accessToken === 'string' &&
39
+ typeof tokens.expiresAt === 'number' &&
40
+ (tokens.refreshToken === undefined || typeof tokens.refreshToken === 'string') &&
41
+ (tokens.idToken === undefined || typeof tokens.idToken === 'string'));
42
+ }
43
+ function validateUser(value) {
44
+ if (!value || typeof value !== 'object')
45
+ return false;
46
+ const user = value;
47
+ return (typeof user.id === 'string' &&
48
+ typeof user.email === 'string' &&
49
+ typeof user.name === 'string');
50
+ }
51
+ function createInMemoryMicrosoftTokenStore() {
52
+ let storedTokens = null;
53
+ let storedUser = null;
54
+ return {
55
+ getTokens: async () => storedTokens,
56
+ setTokens: async (tokens) => {
57
+ storedTokens = tokens;
58
+ },
59
+ removeTokens: async () => {
60
+ storedTokens = null;
61
+ },
62
+ getUser: async () => storedUser,
63
+ setUser: async (user) => {
64
+ storedUser = user;
65
+ },
66
+ };
67
+ }
68
+ function createMicrosoftAuthTokenStore(storage, prefix = 'local-lite-microsoft-auth') {
69
+ const tokensKey = `${prefix}-tokens`;
70
+ const userKey = `${prefix}-user`;
71
+ return {
72
+ getTokens: async () => {
73
+ const raw = await storage.getItem(tokensKey);
74
+ return validateTokens(raw) ? raw : null;
75
+ },
76
+ setTokens: tokens => storage.setItem(tokensKey, tokens),
77
+ removeTokens: () => storage.removeItem(tokensKey),
78
+ getUser: async () => {
79
+ const raw = await storage.getItem(userKey);
80
+ return validateUser(raw) ? raw : null;
81
+ },
82
+ setUser: async (user) => {
83
+ if (user) {
84
+ await storage.setItem(userKey, user);
85
+ }
86
+ else {
87
+ await storage.removeItem(userKey);
88
+ }
89
+ },
90
+ };
91
+ }
92
+ exports.createOneDriveAuthTokenStore = createMicrosoftAuthTokenStore;
93
+ class MicrosoftAuthClient {
94
+ options;
95
+ tokenStore;
96
+ fetcher;
97
+ listeners = new Set();
98
+ tenant;
99
+ currentUser = null;
100
+ constructor(options) {
101
+ this.options = options;
102
+ if (!options.clientId?.trim()) {
103
+ throw new Error('Microsoft OAuth client ID is required.');
104
+ }
105
+ if (!options.redirectUri?.trim()) {
106
+ throw new Error('Microsoft OAuth redirect URI is required.');
107
+ }
108
+ this.tenant = options.tenantId?.trim() || 'common';
109
+ this.fetcher = options.fetch ?? fetch;
110
+ if (options.tokenStore) {
111
+ this.tokenStore = options.tokenStore;
112
+ }
113
+ else if (options.storage) {
114
+ this.tokenStore = createMicrosoftAuthTokenStore(options.storage);
115
+ }
116
+ else {
117
+ this.tokenStore = createInMemoryMicrosoftTokenStore();
118
+ }
119
+ }
120
+ get authorizationEndpoint() {
121
+ return `https://login.microsoftonline.com/${this.tenant}/oauth2/v2.0/authorize`;
122
+ }
123
+ get tokenEndpoint() {
124
+ return `https://login.microsoftonline.com/${this.tenant}/oauth2/v2.0/token`;
125
+ }
126
+ onAuthStateChange(listener) {
127
+ this.listeners.add(listener);
128
+ return () => {
129
+ this.listeners.delete(listener);
130
+ };
131
+ }
132
+ notify(user) {
133
+ this.currentUser = user;
134
+ for (const listener of this.listeners) {
135
+ try {
136
+ listener(user);
137
+ }
138
+ catch (err) {
139
+ console.error('Error in MicrosoftAuthClient listener:', err);
140
+ }
141
+ }
142
+ }
143
+ async signIn() {
144
+ const randomBytes = this.options.randomBytes ?? secureRandomBytes;
145
+ const verifierBytes = randomBytes(32);
146
+ const stateBytes = randomBytes(32);
147
+ if (verifierBytes.length !== 32 || stateBytes.length !== 32) {
148
+ throw new Error('The randomBytes provider returned an invalid number of bytes.');
149
+ }
150
+ const codeVerifier = base64UrlEncode(verifierBytes);
151
+ const codeChallenge = base64UrlEncode((0, sha2_js_1.sha256)((0, utils_js_1.utf8ToBytes)(codeVerifier)));
152
+ const state = base64UrlEncode(stateBytes);
153
+ const scopes = this.options.scopes && this.options.scopes.length > 0
154
+ ? this.options.scopes
155
+ : exports.DEFAULT_MICROSOFT_AUTH_SCOPES;
156
+ const authorizationParams = new URLSearchParams({
157
+ client_id: this.options.clientId,
158
+ response_type: 'code',
159
+ redirect_uri: this.options.redirectUri,
160
+ response_mode: 'query',
161
+ scope: scopes.join(' '),
162
+ prompt: this.options.prompt ?? 'select_account',
163
+ code_challenge: codeChallenge,
164
+ code_challenge_method: 'S256',
165
+ state,
166
+ });
167
+ const authorization = await this.options.authorize({
168
+ authorizationUrl: `${this.authorizationEndpoint}?${authorizationParams.toString()}`,
169
+ redirectUri: this.options.redirectUri,
170
+ state,
171
+ });
172
+ if (!authorization) {
173
+ return null;
174
+ }
175
+ if (authorization.state !== state) {
176
+ throw new Error('Microsoft OAuth state validation failed. Please try connecting again.');
177
+ }
178
+ if (!authorization.code) {
179
+ throw new Error('Microsoft OAuth did not return an authorization code.');
180
+ }
181
+ const response = await this.fetcher(this.tokenEndpoint, {
182
+ method: 'POST',
183
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
184
+ body: new URLSearchParams({
185
+ client_id: this.options.clientId,
186
+ code: authorization.code,
187
+ code_verifier: codeVerifier,
188
+ grant_type: 'authorization_code',
189
+ redirect_uri: this.options.redirectUri,
190
+ scope: scopes.join(' '),
191
+ }).toString(),
192
+ });
193
+ const payload = (await response.json());
194
+ if (!response.ok) {
195
+ throw new Error(payload.error_description || payload.error || `Microsoft OAuth token request failed (${response.status}).`);
196
+ }
197
+ if (!payload.access_token) {
198
+ throw new Error('Microsoft OAuth did not return an access token.');
199
+ }
200
+ const priorTokens = await this.tokenStore.getTokens();
201
+ const storedTokens = {
202
+ accessToken: payload.access_token,
203
+ refreshToken: payload.refresh_token ?? priorTokens?.refreshToken,
204
+ idToken: payload.id_token ?? priorTokens?.idToken,
205
+ expiresAt: Date.now() + (payload.expires_in ?? 3600) * 1000,
206
+ tokenType: payload.token_type,
207
+ scope: payload.scope,
208
+ };
209
+ await this.tokenStore.setTokens(storedTokens);
210
+ const user = await this.fetchUserProfile(storedTokens.accessToken);
211
+ await this.tokenStore.setUser(user);
212
+ this.notify(user);
213
+ return user;
214
+ }
215
+ async signOut() {
216
+ await this.tokenStore.removeTokens();
217
+ await this.tokenStore.setUser(null);
218
+ this.notify(null);
219
+ }
220
+ async isSignedIn() {
221
+ const tokens = await this.tokenStore.getTokens();
222
+ return validateTokens(tokens);
223
+ }
224
+ async getUser() {
225
+ if (this.currentUser) {
226
+ return this.currentUser;
227
+ }
228
+ const cached = await this.tokenStore.getUser();
229
+ if (cached) {
230
+ this.currentUser = cached;
231
+ return cached;
232
+ }
233
+ const tokens = await this.tokenStore.getTokens();
234
+ if (!validateTokens(tokens)) {
235
+ return null;
236
+ }
237
+ try {
238
+ const accessToken = await this.getValidAccessToken();
239
+ const user = await this.fetchUserProfile(accessToken);
240
+ await this.tokenStore.setUser(user);
241
+ this.currentUser = user;
242
+ return user;
243
+ }
244
+ catch {
245
+ return null;
246
+ }
247
+ }
248
+ async getValidAccessToken() {
249
+ const tokens = await this.tokenStore.getTokens();
250
+ if (!validateTokens(tokens)) {
251
+ throw new Error('No Microsoft account signed in. Please sign in first.');
252
+ }
253
+ if (tokens.expiresAt > Date.now() + 60_000) {
254
+ return tokens.accessToken;
255
+ }
256
+ if (!tokens.refreshToken) {
257
+ throw new Error('Your Microsoft sign-in session expired. Please sign in again.');
258
+ }
259
+ const scopes = this.options.scopes && this.options.scopes.length > 0
260
+ ? this.options.scopes
261
+ : exports.DEFAULT_MICROSOFT_AUTH_SCOPES;
262
+ const response = await this.fetcher(this.tokenEndpoint, {
263
+ method: 'POST',
264
+ headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
265
+ body: new URLSearchParams({
266
+ client_id: this.options.clientId,
267
+ grant_type: 'refresh_token',
268
+ refresh_token: tokens.refreshToken,
269
+ scope: scopes.join(' '),
270
+ }).toString(),
271
+ });
272
+ const payload = (await response.json());
273
+ if (!response.ok) {
274
+ throw new Error(payload.error_description || payload.error || `Microsoft OAuth token refresh failed (${response.status}).`);
275
+ }
276
+ if (!payload.access_token) {
277
+ throw new Error('Microsoft OAuth token refresh did not return an access token.');
278
+ }
279
+ const updatedTokens = {
280
+ accessToken: payload.access_token,
281
+ refreshToken: payload.refresh_token ?? tokens.refreshToken,
282
+ idToken: payload.id_token ?? tokens.idToken,
283
+ expiresAt: Date.now() + (payload.expires_in ?? 3600) * 1000,
284
+ tokenType: payload.token_type ?? tokens.tokenType,
285
+ scope: payload.scope ?? tokens.scope,
286
+ };
287
+ await this.tokenStore.setTokens(updatedTokens);
288
+ return updatedTokens.accessToken;
289
+ }
290
+ async fetchUserProfile(accessToken) {
291
+ const res = await this.fetcher(`${exports.MICROSOFT_GRAPH_ENDPOINT}/me`, {
292
+ headers: { Authorization: `Bearer ${accessToken}` },
293
+ });
294
+ if (!res.ok) {
295
+ throw new Error(`Failed to fetch Microsoft user profile (${res.status}).`);
296
+ }
297
+ const raw = (await res.json());
298
+ const email = raw.mail || raw.userPrincipalName || `${raw.id}@microsoft.account`;
299
+ return {
300
+ id: raw.id,
301
+ email,
302
+ name: raw.displayName || raw.givenName || email,
303
+ givenName: raw.givenName,
304
+ familyName: raw.surname,
305
+ userPrincipalName: raw.userPrincipalName,
306
+ };
307
+ }
308
+ }
309
+ exports.MicrosoftAuthClient = MicrosoftAuthClient;
310
+ exports.OneDriveAuthClient = MicrosoftAuthClient;
@@ -0,0 +1,18 @@
1
+ import type { BackupDatabase, BackupStorage } from './index.js';
2
+ export interface EncryptedBackupOptions {
3
+ passphrase: string;
4
+ fileName?: string;
5
+ iterations?: number;
6
+ }
7
+ export interface RestoreEncryptedBackupOptions {
8
+ passphrase: string;
9
+ }
10
+ export declare function isEncryptedBackup(data: Uint8Array): boolean;
11
+ export declare function encryptBackupData(plainData: Uint8Array, passphrase: string, options?: {
12
+ iterations?: number;
13
+ }): Promise<Uint8Array>;
14
+ export declare function decryptBackupData(encryptedData: Uint8Array, passphrase: string, options?: {
15
+ iterations?: number;
16
+ }): Promise<Uint8Array>;
17
+ export declare function createEncryptedDatabaseBackup(database: BackupDatabase, storage: BackupStorage, options: EncryptedBackupOptions): Promise<string>;
18
+ export declare function restoreEncryptedDatabaseBackup(database: BackupDatabase, storage: BackupStorage, fileName: string, options: RestoreEncryptedBackupOptions): Promise<void>;
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.isEncryptedBackup = isEncryptedBackup;
4
+ exports.encryptBackupData = encryptBackupData;
5
+ exports.decryptBackupData = decryptBackupData;
6
+ exports.createEncryptedDatabaseBackup = createEncryptedDatabaseBackup;
7
+ exports.restoreEncryptedDatabaseBackup = restoreEncryptedDatabaseBackup;
8
+ const pbkdf2_js_1 = require("@noble/hashes/pbkdf2.js");
9
+ const sha2_js_1 = require("@noble/hashes/sha2.js");
10
+ const utils_js_1 = require("@noble/hashes/utils.js");
11
+ const MAGIC_HEADER = new Uint8Array([0x4c, 0x4c, 0x45, 0x4e, 0x43, 0x31]); // "LLENC1"
12
+ const SALT_LENGTH = 16;
13
+ const IV_LENGTH = 12;
14
+ const DEFAULT_ITERATIONS = 50000;
15
+ function getRandomBytes(length) {
16
+ const crypto = globalThis.crypto;
17
+ if (!crypto?.getRandomValues) {
18
+ throw new Error('Secure random generation is unavailable for backup encryption.');
19
+ }
20
+ return crypto.getRandomValues(new Uint8Array(length));
21
+ }
22
+ const ITERATIONS_LENGTH = 4;
23
+ function isEncryptedBackup(data) {
24
+ if (data.length < MAGIC_HEADER.length + ITERATIONS_LENGTH + SALT_LENGTH + IV_LENGTH + 16) {
25
+ return false;
26
+ }
27
+ for (let i = 0; i < MAGIC_HEADER.length; i++) {
28
+ if (data[i] !== MAGIC_HEADER[i])
29
+ return false;
30
+ }
31
+ return true;
32
+ }
33
+ async function encryptBackupData(plainData, passphrase, options = {}) {
34
+ if (!passphrase || !passphrase.trim()) {
35
+ throw new Error('A non-empty passphrase is required for backup encryption.');
36
+ }
37
+ const iterations = options.iterations ?? DEFAULT_ITERATIONS;
38
+ const salt = getRandomBytes(SALT_LENGTH);
39
+ const iv = getRandomBytes(IV_LENGTH);
40
+ // Derive 256-bit AES key using PBKDF2 with SHA-256
41
+ const keyBytes = (0, pbkdf2_js_1.pbkdf2)(sha2_js_1.sha256, (0, utils_js_1.utf8ToBytes)(passphrase), salt, {
42
+ c: iterations,
43
+ dkLen: 32,
44
+ });
45
+ const crypto = globalThis.crypto;
46
+ if (!crypto?.subtle) {
47
+ throw new Error('WebCrypto subtle is unavailable for AES-GCM encryption.');
48
+ }
49
+ const cryptoKey = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, ['encrypt']);
50
+ const encryptedBuffer = await crypto.subtle.encrypt({ name: 'AES-GCM', iv: iv }, cryptoKey, plainData);
51
+ const ciphertext = new Uint8Array(encryptedBuffer);
52
+ // Payload format: [MAGIC(6)][ITERATIONS(4)][SALT(16)][IV(12)][CIPHERTEXT+TAG]
53
+ const headerSize = MAGIC_HEADER.length + ITERATIONS_LENGTH + SALT_LENGTH + IV_LENGTH;
54
+ const result = new Uint8Array(headerSize + ciphertext.length);
55
+ result.set(MAGIC_HEADER, 0);
56
+ const view = new DataView(result.buffer, result.byteOffset, result.byteLength);
57
+ view.setUint32(MAGIC_HEADER.length, iterations, false); // big-endian
58
+ result.set(salt, MAGIC_HEADER.length + ITERATIONS_LENGTH);
59
+ result.set(iv, MAGIC_HEADER.length + ITERATIONS_LENGTH + SALT_LENGTH);
60
+ result.set(ciphertext, headerSize);
61
+ return result;
62
+ }
63
+ async function decryptBackupData(encryptedData, passphrase, options = {}) {
64
+ if (!passphrase || !passphrase.trim()) {
65
+ throw new Error('A non-empty passphrase is required for backup decryption.');
66
+ }
67
+ if (!isEncryptedBackup(encryptedData)) {
68
+ throw new Error('Invalid backup file format: missing LLENC1 encryption header.');
69
+ }
70
+ const view = new DataView(encryptedData.buffer, encryptedData.byteOffset, encryptedData.byteLength);
71
+ const storedIterations = view.getUint32(MAGIC_HEADER.length, false);
72
+ const iterations = options.iterations ?? (storedIterations > 0 ? storedIterations : DEFAULT_ITERATIONS);
73
+ const saltOffset = MAGIC_HEADER.length + ITERATIONS_LENGTH;
74
+ const ivOffset = saltOffset + SALT_LENGTH;
75
+ const ciphertextOffset = ivOffset + IV_LENGTH;
76
+ const salt = encryptedData.slice(saltOffset, ivOffset);
77
+ const iv = encryptedData.slice(ivOffset, ciphertextOffset);
78
+ const ciphertext = encryptedData.slice(ciphertextOffset);
79
+ const keyBytes = (0, pbkdf2_js_1.pbkdf2)(sha2_js_1.sha256, (0, utils_js_1.utf8ToBytes)(passphrase), salt, {
80
+ c: iterations,
81
+ dkLen: 32,
82
+ });
83
+ const crypto = globalThis.crypto;
84
+ if (!crypto?.subtle) {
85
+ throw new Error('WebCrypto subtle is unavailable for AES-GCM decryption.');
86
+ }
87
+ const cryptoKey = await crypto.subtle.importKey('raw', keyBytes, 'AES-GCM', false, ['decrypt']);
88
+ try {
89
+ const decryptedBuffer = await crypto.subtle.decrypt({ name: 'AES-GCM', iv: iv }, cryptoKey, ciphertext);
90
+ return new Uint8Array(decryptedBuffer);
91
+ }
92
+ catch {
93
+ throw new Error('Backup decryption failed: Incorrect passphrase or corrupted database backup.');
94
+ }
95
+ }
96
+ async function createEncryptedDatabaseBackup(database, storage, options) {
97
+ const fileName = options.fileName ??
98
+ `local-lite-enc-${new Date().toISOString().replace(/[:.]/g, '-')}.enc.db`;
99
+ if (!fileName || /[\\/]/.test(fileName)) {
100
+ throw new Error('Backup file name must be a single file name.');
101
+ }
102
+ const plainBytes = await database.exportDatabase();
103
+ if (plainBytes.length === 0) {
104
+ throw new Error('Cannot create a backup from an empty database.');
105
+ }
106
+ const encryptedBytes = await encryptBackupData(plainBytes, options.passphrase, {
107
+ iterations: options.iterations,
108
+ });
109
+ await storage.save(fileName, encryptedBytes);
110
+ return fileName;
111
+ }
112
+ async function restoreEncryptedDatabaseBackup(database, storage, fileName, options) {
113
+ if (!fileName || /[\\/]/.test(fileName)) {
114
+ throw new Error('Backup file name must be a single file name.');
115
+ }
116
+ const rawBytes = await storage.read(fileName);
117
+ if (rawBytes.length === 0) {
118
+ throw new Error('Cannot restore an empty database backup.');
119
+ }
120
+ const decryptedBytes = await decryptBackupData(rawBytes, options.passphrase);
121
+ await database.importDatabase(decryptedBytes);
122
+ }
@@ -1,4 +1,6 @@
1
1
  export { type BackupAccessTokenProvider, type CloudBackupFile, type GoogleDriveAuthorizationRequest, type GoogleDriveAuthorizationResponse, type GoogleDriveBackupClientOptions, type GoogleDriveKeyValueStorage, type GoogleDriveTokenStore, type GoogleDriveTokens, GoogleDriveBackupClient, GoogleDriveBackupStorage, createGoogleDriveTokenStore, } from './cloud-storage.js';
2
+ export { MICROSOFT_GRAPH_DRIVE, OneDriveBackupStorage, OneDriveBackupClient, } from './onedrive.js';
3
+ export { type EncryptedBackupOptions, type RestoreEncryptedBackupOptions, isEncryptedBackup, encryptBackupData, decryptBackupData, createEncryptedDatabaseBackup, restoreEncryptedDatabaseBackup, } from './encryption.js';
2
4
  export interface BackupDatabase {
3
5
  exportDatabase(): Promise<Uint8Array>;
4
6
  importDatabase(data: Uint8Array): Promise<void>;
@@ -1,12 +1,22 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.createGoogleDriveTokenStore = exports.GoogleDriveBackupStorage = exports.GoogleDriveBackupClient = void 0;
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
4
  exports.createDatabaseBackup = createDatabaseBackup;
5
5
  exports.restoreDatabaseBackup = restoreDatabaseBackup;
6
6
  var cloud_storage_js_1 = require("./cloud-storage.js");
7
7
  Object.defineProperty(exports, "GoogleDriveBackupClient", { enumerable: true, get: function () { return cloud_storage_js_1.GoogleDriveBackupClient; } });
8
8
  Object.defineProperty(exports, "GoogleDriveBackupStorage", { enumerable: true, get: function () { return cloud_storage_js_1.GoogleDriveBackupStorage; } });
9
9
  Object.defineProperty(exports, "createGoogleDriveTokenStore", { enumerable: true, get: function () { return cloud_storage_js_1.createGoogleDriveTokenStore; } });
10
+ var onedrive_js_1 = require("./onedrive.js");
11
+ Object.defineProperty(exports, "MICROSOFT_GRAPH_DRIVE", { enumerable: true, get: function () { return onedrive_js_1.MICROSOFT_GRAPH_DRIVE; } });
12
+ Object.defineProperty(exports, "OneDriveBackupStorage", { enumerable: true, get: function () { return onedrive_js_1.OneDriveBackupStorage; } });
13
+ Object.defineProperty(exports, "OneDriveBackupClient", { enumerable: true, get: function () { return onedrive_js_1.OneDriveBackupClient; } });
14
+ var encryption_js_1 = require("./encryption.js");
15
+ Object.defineProperty(exports, "isEncryptedBackup", { enumerable: true, get: function () { return encryption_js_1.isEncryptedBackup; } });
16
+ Object.defineProperty(exports, "encryptBackupData", { enumerable: true, get: function () { return encryption_js_1.encryptBackupData; } });
17
+ Object.defineProperty(exports, "decryptBackupData", { enumerable: true, get: function () { return encryption_js_1.decryptBackupData; } });
18
+ Object.defineProperty(exports, "createEncryptedDatabaseBackup", { enumerable: true, get: function () { return encryption_js_1.createEncryptedDatabaseBackup; } });
19
+ Object.defineProperty(exports, "restoreEncryptedDatabaseBackup", { enumerable: true, get: function () { return encryption_js_1.restoreEncryptedDatabaseBackup; } });
10
20
  function validateFileName(fileName) {
11
21
  if (!fileName || /[\\/]/.test(fileName)) {
12
22
  throw new Error('Backup file name must be a single file name.');
@@ -0,0 +1,22 @@
1
+ import type { BackupAccessTokenProvider, BackupStorage, CloudBackupFile } from './index.js';
2
+ import { type MicrosoftAuthClientOptions } from '../auth/microsoft.js';
3
+ export declare const MICROSOFT_GRAPH_DRIVE = "https://graph.microsoft.com/v1.0/me/drive";
4
+ export declare class OneDriveBackupStorage implements BackupStorage {
5
+ private readonly getAccessToken;
6
+ private readonly fetcher;
7
+ constructor(getAccessToken: BackupAccessTokenProvider, fetcher?: typeof fetch);
8
+ save(fileName: string, data: Uint8Array): Promise<void>;
9
+ read(fileIdOrName: string): Promise<Uint8Array>;
10
+ list(): Promise<CloudBackupFile[]>;
11
+ }
12
+ export declare class OneDriveBackupClient implements BackupStorage {
13
+ private readonly storage;
14
+ private readonly authClient;
15
+ constructor(options: MicrosoftAuthClientOptions);
16
+ connect(): Promise<boolean>;
17
+ disconnect(): Promise<void>;
18
+ isConnected(): Promise<boolean>;
19
+ save(fileName: string, data: Uint8Array): Promise<void>;
20
+ read(fileId: string): Promise<Uint8Array>;
21
+ list(): Promise<CloudBackupFile[]>;
22
+ }