local-lite 0.0.20 → 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.
- package/dist/adapters/remote/convex.d.ts +7 -0
- package/dist/adapters/remote/convex.js +10 -1
- package/dist/adapters/remote/firestore.d.ts +7 -0
- package/dist/adapters/remote/firestore.js +23 -1
- package/dist/adapters/remote/supabase.d.ts +7 -0
- package/dist/adapters/remote/supabase.js +13 -2
- package/dist/auth/index.d.ts +1 -0
- package/dist/auth/index.js +1 -0
- package/dist/auth/session.d.ts +107 -0
- package/dist/auth/session.js +259 -0
- package/dist/core/client.d.ts +3 -0
- package/dist/core/client.js +21 -0
- package/dist/react/index.d.ts +1 -0
- package/dist/react/index.js +1 -0
- package/dist/react/session.d.ts +26 -0
- package/dist/react/session.js +118 -0
- package/dist/storage/convex.d.ts +43 -0
- package/dist/storage/convex.js +112 -0
- package/dist/storage/expo.d.ts +132 -0
- package/dist/storage/expo.js +408 -0
- package/dist/storage/factory.d.ts +16 -0
- package/dist/storage/factory.js +22 -0
- package/dist/storage/firebase.d.ts +44 -0
- package/dist/storage/firebase.js +135 -0
- package/dist/storage/index.d.ts +5 -0
- package/dist/storage/index.js +5 -0
- package/dist/storage/manager.d.ts +20 -0
- package/dist/storage/manager.js +144 -0
- package/dist/storage/supabase.d.ts +44 -0
- package/dist/storage/supabase.js +83 -0
- package/dist/storage/types.d.ts +29 -3
- package/package.json +6 -2
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.useSession = useSession;
|
|
4
|
+
const react_1 = require("react");
|
|
5
|
+
/**
|
|
6
|
+
* Reactive React hook for persistent, offline-first user session management.
|
|
7
|
+
* The session remains authenticated even when offline, ensuring uninterrupted UX.
|
|
8
|
+
*
|
|
9
|
+
* Example:
|
|
10
|
+
* const { user, isAuthenticated, isOffline, signOut } = useSession(client);
|
|
11
|
+
*/
|
|
12
|
+
function useSession(client) {
|
|
13
|
+
const [session, setSession] = (0, react_1.useState)(null);
|
|
14
|
+
const [loading, setLoading] = (0, react_1.useState)(true);
|
|
15
|
+
const [isOffline, setIsOffline] = (0, react_1.useState)(false);
|
|
16
|
+
const loadSession = (0, react_1.useCallback)(async () => {
|
|
17
|
+
if (!client) {
|
|
18
|
+
setSession(null);
|
|
19
|
+
setLoading(false);
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
try {
|
|
23
|
+
const active = await client.session.getActiveSession();
|
|
24
|
+
setSession(active);
|
|
25
|
+
return active;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
finally {
|
|
31
|
+
setLoading(false);
|
|
32
|
+
}
|
|
33
|
+
}, [client]);
|
|
34
|
+
(0, react_1.useEffect)(() => {
|
|
35
|
+
if (!client) {
|
|
36
|
+
setSession(null);
|
|
37
|
+
setLoading(false);
|
|
38
|
+
return;
|
|
39
|
+
}
|
|
40
|
+
let isMounted = true;
|
|
41
|
+
// Check network
|
|
42
|
+
const checkNetwork = () => {
|
|
43
|
+
const net = client.syncWorker?.networkStatusProvider;
|
|
44
|
+
if (net && typeof net.isOnline === 'function') {
|
|
45
|
+
const res = net.isOnline();
|
|
46
|
+
if (typeof res === 'boolean') {
|
|
47
|
+
if (isMounted)
|
|
48
|
+
setIsOffline(!res);
|
|
49
|
+
}
|
|
50
|
+
else if (res && typeof res.then === 'function') {
|
|
51
|
+
res.then((online) => {
|
|
52
|
+
if (isMounted)
|
|
53
|
+
setIsOffline(!online);
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
checkNetwork();
|
|
59
|
+
loadSession();
|
|
60
|
+
// Listen to session changes
|
|
61
|
+
const unsubscribeSession = client.events.on('_local_sessions', () => {
|
|
62
|
+
if (isMounted) {
|
|
63
|
+
loadSession();
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
// Listen to network changes if available
|
|
67
|
+
const unsubscribeNetwork = client.events.on('network', (status) => {
|
|
68
|
+
if (isMounted && status && typeof status.online === 'boolean') {
|
|
69
|
+
setIsOffline(!status.online);
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
return () => {
|
|
73
|
+
isMounted = false;
|
|
74
|
+
unsubscribeSession();
|
|
75
|
+
if (typeof unsubscribeNetwork === 'function') {
|
|
76
|
+
unsubscribeNetwork();
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
}, [client, loadSession]);
|
|
80
|
+
const createSession = (0, react_1.useCallback)(async (options) => {
|
|
81
|
+
if (!client)
|
|
82
|
+
throw new Error('Client not provided to useSession.');
|
|
83
|
+
const res = await client.session.createSession(options);
|
|
84
|
+
setSession(res);
|
|
85
|
+
return res;
|
|
86
|
+
}, [client]);
|
|
87
|
+
const updateUser = (0, react_1.useCallback)(async (partialUser) => {
|
|
88
|
+
if (!client)
|
|
89
|
+
return null;
|
|
90
|
+
const res = await client.session.updateUser(partialUser);
|
|
91
|
+
setSession(res);
|
|
92
|
+
return res;
|
|
93
|
+
}, [client]);
|
|
94
|
+
const updateTokens = (0, react_1.useCallback)(async (tokens) => {
|
|
95
|
+
if (!client)
|
|
96
|
+
return;
|
|
97
|
+
await client.session.updateTokens(tokens);
|
|
98
|
+
await loadSession();
|
|
99
|
+
}, [client, loadSession]);
|
|
100
|
+
const signOut = (0, react_1.useCallback)(async () => {
|
|
101
|
+
if (!client)
|
|
102
|
+
return;
|
|
103
|
+
await client.session.clearSession();
|
|
104
|
+
setSession(null);
|
|
105
|
+
}, [client]);
|
|
106
|
+
return {
|
|
107
|
+
session,
|
|
108
|
+
user: session ? session.user : null,
|
|
109
|
+
isAuthenticated: session !== null,
|
|
110
|
+
isOffline,
|
|
111
|
+
loading,
|
|
112
|
+
createSession,
|
|
113
|
+
updateUser,
|
|
114
|
+
updateTokens,
|
|
115
|
+
signOut,
|
|
116
|
+
refetchSession: loadSession,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { BlobRecord, RemoteStorageUploader } from './types.js';
|
|
2
|
+
export interface ConvexStorageUploaderOptions {
|
|
3
|
+
/**
|
|
4
|
+
* Name or function reference for Convex mutation that generates an upload URL.
|
|
5
|
+
* Default: 'sync:generateUploadUrl' (or can be `api.files.generateUploadUrl`)
|
|
6
|
+
*/
|
|
7
|
+
uploadMutation?: any;
|
|
8
|
+
/**
|
|
9
|
+
* Optional arguments passed to uploadMutation.
|
|
10
|
+
*/
|
|
11
|
+
uploadMutationArgs?: Record<string, any> | ((blob: BlobRecord) => Record<string, any>);
|
|
12
|
+
/**
|
|
13
|
+
* Optional name or function reference for Convex query that gets a serving URL from a storageId.
|
|
14
|
+
* e.g. `api.files.getUrl`
|
|
15
|
+
*/
|
|
16
|
+
getUrlQuery?: any;
|
|
17
|
+
/**
|
|
18
|
+
* Optional name or function reference for Convex mutation called after file is uploaded,
|
|
19
|
+
* allowing you to persist metadata or link the storageId to your Convex schema.
|
|
20
|
+
*/
|
|
21
|
+
saveFileMutation?: any;
|
|
22
|
+
/**
|
|
23
|
+
* Optional name or function reference for Convex mutation called when a file is deleted.
|
|
24
|
+
*/
|
|
25
|
+
deleteMutation?: any;
|
|
26
|
+
/**
|
|
27
|
+
* Custom path / metadata transformation.
|
|
28
|
+
*/
|
|
29
|
+
pathTransform?: (key: string, blob?: BlobRecord) => string;
|
|
30
|
+
/**
|
|
31
|
+
* Custom fetch function (defaults to global fetch).
|
|
32
|
+
*/
|
|
33
|
+
fetchFn?: typeof fetch;
|
|
34
|
+
}
|
|
35
|
+
export interface ConvexStorageUploaderInstance extends RemoteStorageUploader {
|
|
36
|
+
getUrl(storageId: string): Promise<string | null>;
|
|
37
|
+
getDownloadUrl(keyOrStorageId: string): Promise<string>;
|
|
38
|
+
delete(storageIdOrKey: string): Promise<void>;
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Creates a RemoteStorageUploader that syncs binary files to Convex File Storage.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createConvexStorageUploader(convex: any, options?: ConvexStorageUploaderOptions): ConvexStorageUploaderInstance;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createConvexStorageUploader = createConvexStorageUploader;
|
|
4
|
+
/**
|
|
5
|
+
* Creates a RemoteStorageUploader that syncs binary files to Convex File Storage.
|
|
6
|
+
*/
|
|
7
|
+
function createConvexStorageUploader(convex, options = {}) {
|
|
8
|
+
const uploadMutation = options.uploadMutation ?? 'sync:generateUploadUrl';
|
|
9
|
+
const fetchFn = options.fetchFn ?? (typeof fetch !== 'undefined' ? fetch : undefined);
|
|
10
|
+
if (!convex) {
|
|
11
|
+
throw new Error('Convex client must be provided to createConvexStorageUploader.');
|
|
12
|
+
}
|
|
13
|
+
// Cache of key/storageId mapping
|
|
14
|
+
const storageIdMap = new Map();
|
|
15
|
+
return {
|
|
16
|
+
async upload(blob, data) {
|
|
17
|
+
if (!fetchFn) {
|
|
18
|
+
throw new Error('Fetch API is not available in the current environment.');
|
|
19
|
+
}
|
|
20
|
+
// 1. Generate upload URL via mutation
|
|
21
|
+
const mutationArgs = typeof options.uploadMutationArgs === 'function'
|
|
22
|
+
? options.uploadMutationArgs(blob)
|
|
23
|
+
: options.uploadMutationArgs || {};
|
|
24
|
+
let postUrl;
|
|
25
|
+
if (typeof convex.mutation === 'function') {
|
|
26
|
+
postUrl = await convex.mutation(uploadMutation, mutationArgs);
|
|
27
|
+
}
|
|
28
|
+
else if (typeof convex === 'function') {
|
|
29
|
+
postUrl = await convex(uploadMutation, mutationArgs);
|
|
30
|
+
}
|
|
31
|
+
else {
|
|
32
|
+
throw new Error('Convex client must support .mutation() method.');
|
|
33
|
+
}
|
|
34
|
+
if (!postUrl || typeof postUrl !== 'string') {
|
|
35
|
+
throw new Error(`Convex generateUploadUrl mutation did not return a valid URL: ${JSON.stringify(postUrl)}`);
|
|
36
|
+
}
|
|
37
|
+
// 2. POST binary data to upload URL
|
|
38
|
+
const response = await fetchFn(postUrl, {
|
|
39
|
+
method: 'POST',
|
|
40
|
+
headers: {
|
|
41
|
+
'Content-Type': blob.mimeType || 'application/octet-stream',
|
|
42
|
+
},
|
|
43
|
+
body: data,
|
|
44
|
+
});
|
|
45
|
+
if (!response.ok) {
|
|
46
|
+
throw new Error(`Convex file upload failed with status ${response.status}: ${response.statusText}`);
|
|
47
|
+
}
|
|
48
|
+
const jsonResult = await response.json();
|
|
49
|
+
const storageId = jsonResult?.storageId || jsonResult?.id || jsonResult;
|
|
50
|
+
if (!storageId) {
|
|
51
|
+
throw new Error('Convex upload response did not contain a valid storageId.');
|
|
52
|
+
}
|
|
53
|
+
storageIdMap.set(blob.key, storageId);
|
|
54
|
+
// 3. Optional: save file record in Convex schema
|
|
55
|
+
if (options.saveFileMutation) {
|
|
56
|
+
const filePayload = {
|
|
57
|
+
storageId,
|
|
58
|
+
key: blob.key,
|
|
59
|
+
name: blob.name,
|
|
60
|
+
mimeType: blob.mimeType,
|
|
61
|
+
size: blob.size,
|
|
62
|
+
sha256: blob.sha256,
|
|
63
|
+
metadata: blob.metadata,
|
|
64
|
+
};
|
|
65
|
+
if (typeof convex.mutation === 'function') {
|
|
66
|
+
await convex.mutation(options.saveFileMutation, filePayload);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
// 4. Resolve serving URL if query is provided
|
|
70
|
+
let remoteUrl = storageId;
|
|
71
|
+
if (options.getUrlQuery) {
|
|
72
|
+
const resolved = await this.getUrl(storageId);
|
|
73
|
+
if (resolved) {
|
|
74
|
+
remoteUrl = resolved;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
remoteUrl,
|
|
79
|
+
storageId,
|
|
80
|
+
metadata: { storageId },
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
async getUrl(storageId) {
|
|
84
|
+
if (!options.getUrlQuery) {
|
|
85
|
+
return null;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
if (typeof convex.query === 'function') {
|
|
89
|
+
return await convex.query(options.getUrlQuery, { storageId });
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
async getDownloadUrl(keyOrStorageId) {
|
|
98
|
+
const storageId = storageIdMap.get(keyOrStorageId) || keyOrStorageId;
|
|
99
|
+
const url = await this.getUrl(storageId);
|
|
100
|
+
return url || storageId;
|
|
101
|
+
},
|
|
102
|
+
async delete(storageIdOrKey) {
|
|
103
|
+
const storageId = storageIdMap.get(storageIdOrKey) || storageIdOrKey;
|
|
104
|
+
storageIdMap.delete(storageIdOrKey);
|
|
105
|
+
if (options.deleteMutation) {
|
|
106
|
+
if (typeof convex.mutation === 'function') {
|
|
107
|
+
await convex.mutation(options.deleteMutation, { storageId });
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { FileDriver } from './types.js';
|
|
2
|
+
export interface ExpoFileSystemModule {
|
|
3
|
+
File?: any;
|
|
4
|
+
Directory?: any;
|
|
5
|
+
Paths?: any;
|
|
6
|
+
documentDirectory?: string | null;
|
|
7
|
+
cacheDirectory?: string | null;
|
|
8
|
+
EncodingType?: {
|
|
9
|
+
UTF8: string;
|
|
10
|
+
Base64: string;
|
|
11
|
+
};
|
|
12
|
+
getInfoAsync?(fileUri: string, options?: {
|
|
13
|
+
md5?: boolean;
|
|
14
|
+
size?: boolean;
|
|
15
|
+
}): Promise<{
|
|
16
|
+
exists: boolean;
|
|
17
|
+
isDirectory?: boolean;
|
|
18
|
+
size?: number;
|
|
19
|
+
uri?: string;
|
|
20
|
+
}>;
|
|
21
|
+
writeAsStringAsync?(fileUri: string, contents: string, options?: {
|
|
22
|
+
encoding?: string;
|
|
23
|
+
}): Promise<void>;
|
|
24
|
+
readAsStringAsync?(fileUri: string, options?: {
|
|
25
|
+
encoding?: string;
|
|
26
|
+
length?: number;
|
|
27
|
+
position?: number;
|
|
28
|
+
}): Promise<string>;
|
|
29
|
+
makeDirectoryAsync?(fileUri: string, options?: {
|
|
30
|
+
intermediates?: boolean;
|
|
31
|
+
}): Promise<void>;
|
|
32
|
+
deleteAsync?(fileUri: string, options?: {
|
|
33
|
+
idempotent?: boolean;
|
|
34
|
+
}): Promise<void>;
|
|
35
|
+
copyAsync?(options: {
|
|
36
|
+
from: string;
|
|
37
|
+
to: string;
|
|
38
|
+
}): Promise<void>;
|
|
39
|
+
moveAsync?(options: {
|
|
40
|
+
from: string;
|
|
41
|
+
to: string;
|
|
42
|
+
}): Promise<void>;
|
|
43
|
+
downloadAsync?(url: string, fileUri: string, options?: {
|
|
44
|
+
md5?: boolean;
|
|
45
|
+
headers?: Record<string, string>;
|
|
46
|
+
sessionType?: any;
|
|
47
|
+
}): Promise<{
|
|
48
|
+
uri: string;
|
|
49
|
+
status: number;
|
|
50
|
+
headers: Record<string, string>;
|
|
51
|
+
md5?: string;
|
|
52
|
+
}>;
|
|
53
|
+
}
|
|
54
|
+
export interface ExpoFileDriverOptions {
|
|
55
|
+
/**
|
|
56
|
+
* The modern `File` class from `expo-file-system` (SDK >57/58).
|
|
57
|
+
* e.g. `import { File } from 'expo-file-system'`
|
|
58
|
+
*/
|
|
59
|
+
File?: any;
|
|
60
|
+
/**
|
|
61
|
+
* The modern `Directory` class from `expo-file-system`.
|
|
62
|
+
* e.g. `import { Directory } from 'expo-file-system'`
|
|
63
|
+
*/
|
|
64
|
+
Directory?: any;
|
|
65
|
+
/**
|
|
66
|
+
* The modern `Paths` object from `expo-file-system`.
|
|
67
|
+
* e.g. `import { Paths } from 'expo-file-system'`
|
|
68
|
+
*/
|
|
69
|
+
Paths?: any;
|
|
70
|
+
/**
|
|
71
|
+
* The `expo-file-system` module instance.
|
|
72
|
+
* e.g. `import * as FileSystem from 'expo-file-system'`
|
|
73
|
+
*/
|
|
74
|
+
fileSystem?: ExpoFileSystemModule | any;
|
|
75
|
+
/**
|
|
76
|
+
* Base directory path or `Directory` instance for storing files.
|
|
77
|
+
* Defaults to: `new Directory(Paths.document, 'local-lite-blobs')`
|
|
78
|
+
*/
|
|
79
|
+
baseDir?: any;
|
|
80
|
+
/**
|
|
81
|
+
* Whether to store files in `Paths.cache` instead of `Paths.document`.
|
|
82
|
+
* Files in cache directory may be cleared by the OS if device storage runs low.
|
|
83
|
+
* Default: false
|
|
84
|
+
*/
|
|
85
|
+
useCache?: boolean;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Universal binary <-> base64 utilities compatible with Node, Browser, and React Native (Hermes).
|
|
89
|
+
*/
|
|
90
|
+
export declare function bytesToBase64(bytes: Uint8Array): string;
|
|
91
|
+
export declare function base64ToBytes(base64: string): Uint8Array;
|
|
92
|
+
/**
|
|
93
|
+
* FileDriver implementation for Expo / React Native environments.
|
|
94
|
+
* Uses modern Expo (>57/58) `File`, `Directory`, and `Paths` APIs when available,
|
|
95
|
+
* with seamless fallback for legacy `expo-file-system` environments.
|
|
96
|
+
*/
|
|
97
|
+
export declare class ExpoFileDriver implements FileDriver {
|
|
98
|
+
private FileClass?;
|
|
99
|
+
private DirectoryClass?;
|
|
100
|
+
private PathsObj?;
|
|
101
|
+
private legacyFs?;
|
|
102
|
+
private baseDirOption?;
|
|
103
|
+
private useCache;
|
|
104
|
+
private resolvedBaseDir;
|
|
105
|
+
private resolvedBaseDirPath;
|
|
106
|
+
private ensuredDirs;
|
|
107
|
+
constructor(options?: ExpoFileDriverOptions);
|
|
108
|
+
private resolveClasses;
|
|
109
|
+
private getBaseDirectory;
|
|
110
|
+
/**
|
|
111
|
+
* Resolves a key to a modern Expo `File` instance (if File class is available)
|
|
112
|
+
* or a string URI path.
|
|
113
|
+
*/
|
|
114
|
+
private resolveFileOrUri;
|
|
115
|
+
resolveUri(key: string): Promise<string>;
|
|
116
|
+
getUri(key: string): Promise<string>;
|
|
117
|
+
private ensureDirectory;
|
|
118
|
+
write(key: string, data: Uint8Array): Promise<string>;
|
|
119
|
+
read(key: string): Promise<Uint8Array | null>;
|
|
120
|
+
delete(key: string): Promise<void>;
|
|
121
|
+
exists(key: string): Promise<boolean>;
|
|
122
|
+
getSize(key: string): Promise<number>;
|
|
123
|
+
/**
|
|
124
|
+
* Fast file copy from an existing local URI (e.g. from expo-image-picker or expo-document-picker)
|
|
125
|
+
* into local-lite managed storage without reading entire file contents into memory.
|
|
126
|
+
*/
|
|
127
|
+
copyFrom(sourceUri: string, key: string): Promise<string>;
|
|
128
|
+
/**
|
|
129
|
+
* Downloads a remote URL directly to the destination key on disk.
|
|
130
|
+
*/
|
|
131
|
+
downloadFrom(remoteUrl: string, key: string): Promise<string>;
|
|
132
|
+
}
|