local-lite 0.0.21 → 0.0.22

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,408 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ExpoFileDriver = void 0;
4
+ exports.bytesToBase64 = bytesToBase64;
5
+ exports.base64ToBytes = base64ToBytes;
6
+ /**
7
+ * Universal binary <-> base64 utilities compatible with Node, Browser, and React Native (Hermes).
8
+ */
9
+ function bytesToBase64(bytes) {
10
+ if (typeof Buffer !== 'undefined') {
11
+ return Buffer.from(bytes.buffer, bytes.byteOffset, bytes.byteLength).toString('base64');
12
+ }
13
+ let binary = '';
14
+ const len = bytes.byteLength;
15
+ for (let i = 0; i < len; i++) {
16
+ binary += String.fromCharCode(bytes[i]);
17
+ }
18
+ return btoa(binary);
19
+ }
20
+ function base64ToBytes(base64) {
21
+ if (typeof Buffer !== 'undefined') {
22
+ const buf = Buffer.from(base64, 'base64');
23
+ return new Uint8Array(buf.buffer, buf.byteOffset, buf.byteLength);
24
+ }
25
+ const binary = atob(base64);
26
+ const bytes = new Uint8Array(binary.length);
27
+ for (let i = 0; i < binary.length; i++) {
28
+ bytes[i] = binary.charCodeAt(i);
29
+ }
30
+ return bytes;
31
+ }
32
+ /**
33
+ * FileDriver implementation for Expo / React Native environments.
34
+ * Uses modern Expo (>57/58) `File`, `Directory`, and `Paths` APIs when available,
35
+ * with seamless fallback for legacy `expo-file-system` environments.
36
+ */
37
+ class ExpoFileDriver {
38
+ FileClass;
39
+ DirectoryClass;
40
+ PathsObj;
41
+ legacyFs;
42
+ baseDirOption;
43
+ useCache;
44
+ resolvedBaseDir = null;
45
+ resolvedBaseDirPath = null;
46
+ ensuredDirs = new Set();
47
+ constructor(options = {}) {
48
+ this.FileClass = options.File ?? options.fileSystem?.File;
49
+ this.DirectoryClass = options.Directory ?? options.fileSystem?.Directory;
50
+ this.PathsObj = options.Paths ?? options.fileSystem?.Paths;
51
+ this.legacyFs = options.fileSystem;
52
+ this.baseDirOption = options.baseDir;
53
+ this.useCache = Boolean(options.useCache);
54
+ }
55
+ async resolveClasses() {
56
+ if (!this.FileClass || !this.DirectoryClass || !this.PathsObj) {
57
+ try {
58
+ // @ts-ignore
59
+ const mod = await import('expo-file-system');
60
+ this.FileClass = this.FileClass ?? mod.File;
61
+ this.DirectoryClass = this.DirectoryClass ?? mod.Directory;
62
+ this.PathsObj = this.PathsObj ?? mod.Paths;
63
+ this.legacyFs = this.legacyFs ?? mod;
64
+ }
65
+ catch {
66
+ // Dynamic import may fail if not installed or bundler forbids
67
+ }
68
+ }
69
+ return {
70
+ FileClass: this.FileClass,
71
+ DirectoryClass: this.DirectoryClass,
72
+ PathsObj: this.PathsObj,
73
+ legacyFs: this.legacyFs,
74
+ };
75
+ }
76
+ async getBaseDirectory() {
77
+ if (this.resolvedBaseDir)
78
+ return this.resolvedBaseDir;
79
+ const { DirectoryClass, PathsObj, legacyFs } = await this.resolveClasses();
80
+ if (this.baseDirOption) {
81
+ if (typeof this.baseDirOption === 'string') {
82
+ if (DirectoryClass) {
83
+ this.resolvedBaseDir = new DirectoryClass(this.baseDirOption);
84
+ }
85
+ else {
86
+ this.resolvedBaseDirPath = this.baseDirOption.endsWith('/')
87
+ ? this.baseDirOption
88
+ : `${this.baseDirOption}/`;
89
+ }
90
+ }
91
+ else {
92
+ this.resolvedBaseDir = this.baseDirOption;
93
+ }
94
+ }
95
+ else if (DirectoryClass && PathsObj) {
96
+ const root = this.useCache ? PathsObj.cache : PathsObj.document;
97
+ this.resolvedBaseDir = new DirectoryClass(root, 'local-lite-blobs');
98
+ }
99
+ else if (legacyFs) {
100
+ const root = this.useCache ? legacyFs.cacheDirectory : legacyFs.documentDirectory;
101
+ if (!root) {
102
+ throw new Error('ExpoFileDriver: FileSystem documentDirectory is null or unavailable.');
103
+ }
104
+ this.resolvedBaseDirPath = root.endsWith('/') ? `${root}local-lite-blobs/` : `${root}/local-lite-blobs/`;
105
+ }
106
+ if (this.resolvedBaseDir) {
107
+ try {
108
+ if (!this.resolvedBaseDir.exists && typeof this.resolvedBaseDir.create === 'function') {
109
+ this.resolvedBaseDir.create({ idempotent: true });
110
+ }
111
+ }
112
+ catch { }
113
+ }
114
+ return this.resolvedBaseDir;
115
+ }
116
+ /**
117
+ * Resolves a key to a modern Expo `File` instance (if File class is available)
118
+ * or a string URI path.
119
+ */
120
+ async resolveFileOrUri(key) {
121
+ const { FileClass, DirectoryClass } = await this.resolveClasses();
122
+ const baseDir = await this.getBaseDirectory();
123
+ const safeKey = key.replace(/\.\./g, '_').replace(/^[/\\?%*:|"<>]+/, '');
124
+ // Modern Expo File & Directory path
125
+ if (FileClass && baseDir) {
126
+ const segments = safeKey.split('/').filter(Boolean);
127
+ const fileName = segments.pop() || 'blob';
128
+ let currentDir = baseDir;
129
+ for (const seg of segments) {
130
+ const safeSeg = seg.replace(/[/\\?%*:|"<>]/g, '_');
131
+ if (DirectoryClass) {
132
+ const subDir = new DirectoryClass(currentDir, safeSeg);
133
+ try {
134
+ if (!subDir.exists && typeof subDir.create === 'function') {
135
+ subDir.create({ idempotent: true });
136
+ }
137
+ }
138
+ catch { }
139
+ currentDir = subDir;
140
+ }
141
+ }
142
+ const safeFileName = fileName.replace(/[/\\?%*:|"<>]/g, '_');
143
+ const file = new FileClass(currentDir, safeFileName);
144
+ return { file, uri: file.uri };
145
+ }
146
+ // Legacy URI resolution
147
+ const basePath = this.resolvedBaseDirPath ||
148
+ (this.resolvedBaseDir?.uri
149
+ ? this.resolvedBaseDir.uri.endsWith('/')
150
+ ? this.resolvedBaseDir.uri
151
+ : `${this.resolvedBaseDir.uri}/`
152
+ : 'file:///local-lite-blobs/');
153
+ const uri = `${basePath}${safeKey}`;
154
+ return { file: null, uri };
155
+ }
156
+ async resolveUri(key) {
157
+ const { uri } = await this.resolveFileOrUri(key);
158
+ return uri;
159
+ }
160
+ async getUri(key) {
161
+ return this.resolveUri(key);
162
+ }
163
+ async ensureDirectory(fileUri) {
164
+ const { legacyFs } = await this.resolveClasses();
165
+ if (!legacyFs || typeof legacyFs.makeDirectoryAsync !== 'function')
166
+ return;
167
+ const lastSlash = fileUri.lastIndexOf('/');
168
+ if (lastSlash === -1)
169
+ return;
170
+ const dir = fileUri.substring(0, lastSlash);
171
+ if (this.ensuredDirs.has(dir))
172
+ return;
173
+ try {
174
+ await legacyFs.makeDirectoryAsync(dir, { intermediates: true });
175
+ this.ensuredDirs.add(dir);
176
+ }
177
+ catch {
178
+ this.ensuredDirs.add(dir);
179
+ }
180
+ }
181
+ async write(key, data) {
182
+ const { file, uri } = await this.resolveFileOrUri(key);
183
+ // Modern Expo File (>57/58)
184
+ if (file) {
185
+ // Ensure parent directory exists before writing
186
+ try {
187
+ if (file.parentDirectory && !file.parentDirectory.exists) {
188
+ file.parentDirectory.create({ idempotent: true });
189
+ }
190
+ }
191
+ catch { }
192
+ if (typeof file.write === 'function') {
193
+ await file.write(data);
194
+ return file.uri;
195
+ }
196
+ if (typeof file.writeSync === 'function') {
197
+ file.writeSync(data);
198
+ return file.uri;
199
+ }
200
+ }
201
+ // Legacy FileSystem API
202
+ const { legacyFs } = await this.resolveClasses();
203
+ if (legacyFs && typeof legacyFs.writeAsStringAsync === 'function') {
204
+ await this.ensureDirectory(uri);
205
+ const base64 = bytesToBase64(data);
206
+ const encoding = legacyFs.EncodingType?.Base64 ?? 'base64';
207
+ await legacyFs.writeAsStringAsync(uri, base64, { encoding });
208
+ return uri;
209
+ }
210
+ throw new Error('ExpoFileDriver: No supported File or write method found on expo-file-system.');
211
+ }
212
+ async read(key) {
213
+ const { file, uri } = await this.resolveFileOrUri(key);
214
+ // Modern Expo File (>57/58)
215
+ if (file) {
216
+ try {
217
+ if (!file.exists) {
218
+ return null;
219
+ }
220
+ if (typeof file.bytes === 'function') {
221
+ const res = await file.bytes();
222
+ return new Uint8Array(res);
223
+ }
224
+ if (typeof file.bytesSync === 'function') {
225
+ return new Uint8Array(file.bytesSync());
226
+ }
227
+ if (typeof file.arrayBuffer === 'function') {
228
+ const ab = await file.arrayBuffer();
229
+ return new Uint8Array(ab);
230
+ }
231
+ if (typeof file.base64 === 'function') {
232
+ const b64 = await file.base64();
233
+ return base64ToBytes(b64);
234
+ }
235
+ if (typeof file.base64Sync === 'function') {
236
+ return base64ToBytes(file.base64Sync());
237
+ }
238
+ }
239
+ catch {
240
+ return null;
241
+ }
242
+ }
243
+ // Legacy FileSystem API
244
+ const { legacyFs } = await this.resolveClasses();
245
+ if (legacyFs && typeof legacyFs.readAsStringAsync === 'function') {
246
+ try {
247
+ if (typeof legacyFs.getInfoAsync === 'function') {
248
+ const info = await legacyFs.getInfoAsync(uri);
249
+ if (!info.exists)
250
+ return null;
251
+ }
252
+ const encoding = legacyFs.EncodingType?.Base64 ?? 'base64';
253
+ const base64 = await legacyFs.readAsStringAsync(uri, { encoding });
254
+ return base64ToBytes(base64);
255
+ }
256
+ catch {
257
+ return null;
258
+ }
259
+ }
260
+ return null;
261
+ }
262
+ async delete(key) {
263
+ const { file, uri } = await this.resolveFileOrUri(key);
264
+ // Modern Expo File (>57/58)
265
+ if (file) {
266
+ try {
267
+ if (file.exists && typeof file.delete === 'function') {
268
+ file.delete();
269
+ }
270
+ return;
271
+ }
272
+ catch {
273
+ return;
274
+ }
275
+ }
276
+ // Legacy FileSystem API
277
+ const { legacyFs } = await this.resolveClasses();
278
+ if (legacyFs && typeof legacyFs.deleteAsync === 'function') {
279
+ try {
280
+ await legacyFs.deleteAsync(uri, { idempotent: true });
281
+ }
282
+ catch { }
283
+ }
284
+ }
285
+ async exists(key) {
286
+ const { file, uri } = await this.resolveFileOrUri(key);
287
+ // Modern Expo File (>57/58)
288
+ if (file) {
289
+ try {
290
+ return Boolean(file.exists);
291
+ }
292
+ catch {
293
+ return false;
294
+ }
295
+ }
296
+ // Legacy FileSystem API
297
+ const { legacyFs } = await this.resolveClasses();
298
+ if (legacyFs && typeof legacyFs.getInfoAsync === 'function') {
299
+ try {
300
+ const info = await legacyFs.getInfoAsync(uri);
301
+ return Boolean(info.exists);
302
+ }
303
+ catch {
304
+ return false;
305
+ }
306
+ }
307
+ return false;
308
+ }
309
+ async getSize(key) {
310
+ const { file, uri } = await this.resolveFileOrUri(key);
311
+ // Modern Expo File (>57/58)
312
+ if (file) {
313
+ try {
314
+ if (!file.exists)
315
+ return 0;
316
+ if (typeof file.size === 'number')
317
+ return file.size;
318
+ if (typeof file.info === 'function') {
319
+ const info = file.info();
320
+ return info?.size ?? 0;
321
+ }
322
+ }
323
+ catch {
324
+ return 0;
325
+ }
326
+ }
327
+ // Legacy FileSystem API
328
+ const { legacyFs } = await this.resolveClasses();
329
+ if (legacyFs && typeof legacyFs.getInfoAsync === 'function') {
330
+ try {
331
+ const info = await legacyFs.getInfoAsync(uri, { size: true });
332
+ return info.size ?? 0;
333
+ }
334
+ catch {
335
+ return 0;
336
+ }
337
+ }
338
+ return 0;
339
+ }
340
+ /**
341
+ * Fast file copy from an existing local URI (e.g. from expo-image-picker or expo-document-picker)
342
+ * into local-lite managed storage without reading entire file contents into memory.
343
+ */
344
+ async copyFrom(sourceUri, key) {
345
+ const { file, uri } = await this.resolveFileOrUri(key);
346
+ const { FileClass, legacyFs } = await this.resolveClasses();
347
+ // Modern Expo File (>57/58)
348
+ if (file && FileClass) {
349
+ try {
350
+ const srcFile = new FileClass(sourceUri);
351
+ if (srcFile.exists && typeof srcFile.copy === 'function') {
352
+ if (file.parentDirectory && !file.parentDirectory.exists) {
353
+ file.parentDirectory.create({ idempotent: true });
354
+ }
355
+ await srcFile.copy(file);
356
+ return file.uri;
357
+ }
358
+ }
359
+ catch { }
360
+ }
361
+ // Legacy FileSystem API
362
+ if (legacyFs && typeof legacyFs.copyAsync === 'function') {
363
+ await this.ensureDirectory(uri);
364
+ await legacyFs.copyAsync({ from: sourceUri, to: uri });
365
+ return uri;
366
+ }
367
+ // Fallback: read bytes and write
368
+ if (legacyFs && typeof legacyFs.readAsStringAsync === 'function') {
369
+ const encoding = legacyFs.EncodingType?.Base64 ?? 'base64';
370
+ const content = await legacyFs.readAsStringAsync(sourceUri, { encoding });
371
+ await this.write(key, base64ToBytes(content));
372
+ return uri;
373
+ }
374
+ return uri;
375
+ }
376
+ /**
377
+ * Downloads a remote URL directly to the destination key on disk.
378
+ */
379
+ async downloadFrom(remoteUrl, key) {
380
+ const { file, uri } = await this.resolveFileOrUri(key);
381
+ const { FileClass, legacyFs } = await this.resolveClasses();
382
+ // Modern Expo File (>57/58)
383
+ if (file && FileClass && typeof FileClass.downloadFileAsync === 'function') {
384
+ try {
385
+ if (file.parentDirectory && !file.parentDirectory.exists) {
386
+ file.parentDirectory.create({ idempotent: true });
387
+ }
388
+ await FileClass.downloadFileAsync(remoteUrl, file, { idempotent: true });
389
+ return file.uri;
390
+ }
391
+ catch { }
392
+ }
393
+ // Legacy FileSystem API
394
+ if (legacyFs && typeof legacyFs.downloadAsync === 'function') {
395
+ await this.ensureDirectory(uri);
396
+ const res = await legacyFs.downloadAsync(remoteUrl, uri);
397
+ return res.uri;
398
+ }
399
+ // Universal fetch fallback
400
+ const response = await fetch(remoteUrl);
401
+ if (!response.ok) {
402
+ throw new Error(`Failed to download file from "${remoteUrl}": ${response.statusText}`);
403
+ }
404
+ const arrayBuffer = await response.arrayBuffer();
405
+ return await this.write(key, new Uint8Array(arrayBuffer));
406
+ }
407
+ }
408
+ exports.ExpoFileDriver = ExpoFileDriver;
@@ -0,0 +1,16 @@
1
+ import { RemoteStorageUploader, StorageProviderType } from './types.js';
2
+ import { SupabaseStorageUploaderOptions } from './supabase.js';
3
+ import { ConvexStorageUploaderOptions } from './convex.js';
4
+ import { FirebaseStorageUploaderOptions } from './firebase.js';
5
+ export type UnifiedStorageOptions = ({
6
+ provider: 'supabase';
7
+ } & SupabaseStorageUploaderOptions) | ({
8
+ provider: 'convex';
9
+ } & ConvexStorageUploaderOptions) | ({
10
+ provider: 'firebase';
11
+ } & FirebaseStorageUploaderOptions);
12
+ /**
13
+ * Universal factory function to create a remote storage uploader for any supported provider:
14
+ * 'supabase', 'convex', or 'firebase'.
15
+ */
16
+ export declare function createStorageUploader(provider: StorageProviderType, client: any, options?: any): RemoteStorageUploader;
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createStorageUploader = createStorageUploader;
4
+ const supabase_js_1 = require("./supabase.js");
5
+ const convex_js_1 = require("./convex.js");
6
+ const firebase_js_1 = require("./firebase.js");
7
+ /**
8
+ * Universal factory function to create a remote storage uploader for any supported provider:
9
+ * 'supabase', 'convex', or 'firebase'.
10
+ */
11
+ function createStorageUploader(provider, client, options = {}) {
12
+ switch (provider) {
13
+ case 'supabase':
14
+ return (0, supabase_js_1.createSupabaseStorageUploader)(client, options);
15
+ case 'convex':
16
+ return (0, convex_js_1.createConvexStorageUploader)(client, options);
17
+ case 'firebase':
18
+ return (0, firebase_js_1.createFirebaseStorageUploader)(client, options);
19
+ default:
20
+ throw new Error(`Unsupported storage provider "${provider}". Supported providers: "supabase", "convex", "firebase".`);
21
+ }
22
+ }
@@ -0,0 +1,44 @@
1
+ import { BlobRecord, RemoteStorageUploader } from './types.js';
2
+ export interface FirebaseStorageModule {
3
+ ref?: (storage: any, path: string) => any;
4
+ uploadBytes?: (ref: any, data: Uint8Array | Blob | ArrayBuffer, metadata?: any) => Promise<any>;
5
+ getDownloadURL?: (ref: any) => Promise<string>;
6
+ deleteObject?: (ref: any) => Promise<void>;
7
+ }
8
+ export interface FirebaseStorageUploaderOptions {
9
+ /**
10
+ * Optional bucket name or gs:// bucket URL.
11
+ */
12
+ bucket?: string;
13
+ /**
14
+ * Base folder or path prefix in Firebase Storage (e.g. 'media/').
15
+ */
16
+ basePath?: string;
17
+ /**
18
+ * Custom path transformation function.
19
+ * Default: `${basePath}/${key}`
20
+ */
21
+ pathTransform?: (key: string, blob?: BlobRecord) => string;
22
+ /**
23
+ * Optional reference to modular `firebase/storage` functions
24
+ * (e.g. `import * as storageModule from 'firebase/storage'`).
25
+ */
26
+ storageModule?: FirebaseStorageModule;
27
+ /**
28
+ * Optional custom metadata attached to uploaded blobs.
29
+ */
30
+ customMetadata?: Record<string, string> | ((blob: BlobRecord) => Record<string, string>);
31
+ /**
32
+ * Cache control HTTP header (e.g. 'public, max-age=31536000').
33
+ */
34
+ cacheControl?: string;
35
+ }
36
+ export interface FirebaseStorageUploaderInstance extends RemoteStorageUploader {
37
+ getDownloadUrl(keyOrPath: string): Promise<string>;
38
+ delete(keyOrPath: string): Promise<void>;
39
+ }
40
+ /**
41
+ * Creates a RemoteStorageUploader that syncs binary files to Firebase Cloud Storage.
42
+ * Supports both Firebase v9+ modular SDK and v8/compat SDKs.
43
+ */
44
+ export declare function createFirebaseStorageUploader(storageOrApp: any, options?: FirebaseStorageUploaderOptions): FirebaseStorageUploaderInstance;
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createFirebaseStorageUploader = createFirebaseStorageUploader;
4
+ /**
5
+ * Creates a RemoteStorageUploader that syncs binary files to Firebase Cloud Storage.
6
+ * Supports both Firebase v9+ modular SDK and v8/compat SDKs.
7
+ */
8
+ function createFirebaseStorageUploader(storageOrApp, options = {}) {
9
+ if (!storageOrApp) {
10
+ throw new Error('Firebase Storage instance, app, or modular client must be provided.');
11
+ }
12
+ // Determine storage instance
13
+ let storageInstance = storageOrApp;
14
+ if (typeof storageOrApp.storage === 'function') {
15
+ // FirebaseApp compat
16
+ storageInstance = storageOrApp.storage(options.bucket);
17
+ }
18
+ const mod = options.storageModule || {};
19
+ const resolveRemotePath = (key, blob) => {
20
+ if (options.pathTransform) {
21
+ return options.pathTransform(key, blob);
22
+ }
23
+ const cleanKey = key.replace(/^\/+/, '');
24
+ if (options.basePath) {
25
+ const cleanBase = options.basePath.replace(/^\/+|\/+$/g, '');
26
+ return `${cleanBase}/${cleanKey}`;
27
+ }
28
+ return cleanKey;
29
+ };
30
+ const getRef = (path) => {
31
+ // 1. Modular SDK ref(storage, path)
32
+ if (typeof mod.ref === 'function') {
33
+ return mod.ref(storageInstance, path);
34
+ }
35
+ // 2. Storage instance ref(path) method
36
+ if (typeof storageInstance.ref === 'function') {
37
+ return storageInstance.ref(path);
38
+ }
39
+ // 3. Storage instance from bucket
40
+ if (typeof storageInstance.bucket === 'function') {
41
+ const b = storageInstance.bucket(options.bucket);
42
+ return typeof b.file === 'function' ? b.file(path) : b;
43
+ }
44
+ throw new Error('Cannot resolve Firebase Storage reference. Pass storageModule ({ ref, uploadBytes, getDownloadURL }) or a FirebaseStorage instance.');
45
+ };
46
+ return {
47
+ async upload(blob, data) {
48
+ const remotePath = resolveRemotePath(blob.key, blob);
49
+ const fileRef = getRef(remotePath);
50
+ const metadata = {
51
+ contentType: blob.mimeType || 'application/octet-stream',
52
+ };
53
+ if (options.cacheControl) {
54
+ metadata.cacheControl = options.cacheControl;
55
+ }
56
+ if (options.customMetadata) {
57
+ const extra = typeof options.customMetadata === 'function'
58
+ ? options.customMetadata(blob)
59
+ : options.customMetadata;
60
+ metadata.customMetadata = extra;
61
+ }
62
+ // Method A: Modular SDK uploadBytes
63
+ if (typeof mod.uploadBytes === 'function') {
64
+ await mod.uploadBytes(fileRef, data, metadata);
65
+ let remoteUrl = remotePath;
66
+ if (typeof mod.getDownloadURL === 'function') {
67
+ remoteUrl = await mod.getDownloadURL(fileRef);
68
+ }
69
+ return { remoteUrl, metadata };
70
+ }
71
+ // Method B: Compat put() or upload()
72
+ if (typeof fileRef.put === 'function') {
73
+ const snapshot = await fileRef.put(data, metadata);
74
+ let remoteUrl;
75
+ if (typeof snapshot?.ref?.getDownloadURL === 'function') {
76
+ remoteUrl = await snapshot.ref.getDownloadURL();
77
+ }
78
+ else if (typeof fileRef.getDownloadURL === 'function') {
79
+ remoteUrl = await fileRef.getDownloadURL();
80
+ }
81
+ else {
82
+ remoteUrl = remotePath;
83
+ }
84
+ return { remoteUrl, metadata };
85
+ }
86
+ // Method C: GCS admin save()
87
+ if (typeof fileRef.save === 'function') {
88
+ await fileRef.save(Buffer.isBuffer(data) ? data : Buffer.from(data), {
89
+ contentType: metadata.contentType,
90
+ metadata: metadata.customMetadata,
91
+ });
92
+ let remoteUrl = remotePath;
93
+ if (typeof fileRef.getSignedUrl === 'function') {
94
+ const [url] = await fileRef.getSignedUrl({
95
+ action: 'read',
96
+ expires: Date.now() + 86400 * 1000,
97
+ });
98
+ remoteUrl = url;
99
+ }
100
+ return { remoteUrl, metadata };
101
+ }
102
+ throw new Error('Unsupported Firebase Storage client: missing uploadBytes, put, or save method.');
103
+ },
104
+ async getDownloadUrl(keyOrPath) {
105
+ const remotePath = resolveRemotePath(keyOrPath);
106
+ const fileRef = getRef(remotePath);
107
+ if (typeof mod.getDownloadURL === 'function') {
108
+ return await mod.getDownloadURL(fileRef);
109
+ }
110
+ if (typeof fileRef.getDownloadURL === 'function') {
111
+ return await fileRef.getDownloadURL();
112
+ }
113
+ if (typeof fileRef.getSignedUrl === 'function') {
114
+ const [url] = await fileRef.getSignedUrl({
115
+ action: 'read',
116
+ expires: Date.now() + 86400 * 1000,
117
+ });
118
+ return url;
119
+ }
120
+ return remotePath;
121
+ },
122
+ async delete(keyOrPath) {
123
+ const remotePath = resolveRemotePath(keyOrPath);
124
+ const fileRef = getRef(remotePath);
125
+ if (typeof mod.deleteObject === 'function') {
126
+ await mod.deleteObject(fileRef);
127
+ return;
128
+ }
129
+ if (typeof fileRef.delete === 'function') {
130
+ await fileRef.delete();
131
+ return;
132
+ }
133
+ },
134
+ };
135
+ }
@@ -2,3 +2,8 @@ export * from './types.js';
2
2
  export * from './driver.js';
3
3
  export * from './store.js';
4
4
  export * from './manager.js';
5
+ export * from './expo.js';
6
+ export * from './supabase.js';
7
+ export * from './convex.js';
8
+ export * from './firebase.js';
9
+ export * from './factory.js';
@@ -18,3 +18,8 @@ __exportStar(require("./types.js"), exports);
18
18
  __exportStar(require("./driver.js"), exports);
19
19
  __exportStar(require("./store.js"), exports);
20
20
  __exportStar(require("./manager.js"), exports);
21
+ __exportStar(require("./expo.js"), exports);
22
+ __exportStar(require("./supabase.js"), exports);
23
+ __exportStar(require("./convex.js"), exports);
24
+ __exportStar(require("./firebase.js"), exports);
25
+ __exportStar(require("./factory.js"), exports);
@@ -6,6 +6,7 @@ export declare class BlobStorageManager {
6
6
  private uploader?;
7
7
  private network?;
8
8
  private tableName;
9
+ private deleteRemoteOnDelete;
9
10
  constructor(db: DBExecutable, options?: BlobStorageManagerOptions);
10
11
  initialize(): Promise<void>;
11
12
  setDriver(driver: FileDriver): void;
@@ -38,6 +39,25 @@ export declare class BlobStorageManager {
38
39
  * Deletes the blob binary and metadata record.
39
40
  */
40
41
  delete(key: string): Promise<void>;
42
+ /**
43
+ * Retrieves the remote download/serving URL for a stored blob key.
44
+ */
45
+ getDownloadUrl(key: string): Promise<string | null>;
46
+ /**
47
+ * Retrieves the local file path or URI (e.g. file:///... on mobile) for a stored blob key.
48
+ * Ideal for React Native <Image source={{ uri }} /> without loading bytes into JS memory.
49
+ */
50
+ getLocalUri(key: string): Promise<string | null>;
51
+ /**
52
+ * Directly imports a file from a local filesystem URI (e.g. expo-image-picker or expo-document-picker)
53
+ * into local-lite managed storage without having to manually read and encode byte arrays in memory.
54
+ */
55
+ saveFromUri(key: string, sourceUri: string, options?: SaveBlobOptions): Promise<BlobRecord>;
56
+ /**
57
+ * Downloads a remote asset (e.g. from Supabase Storage) and saves it to local disk.
58
+ * Caches metadata with status 'UPLOADED'.
59
+ */
60
+ download(key: string, remoteUrl: string, options?: SaveBlobOptions): Promise<BlobRecord>;
41
61
  /**
42
62
  * Flushes and syncs pending or failed uploads to the remote storage uploader.
43
63
  */