local-lite 0.0.21 → 0.0.24
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/LICENSE +21 -21
- package/README.md +45 -45
- package/dist/actions/store.js +15 -15
- 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/session.js +27 -27
- package/dist/core/client.d.ts +15 -1
- package/dist/core/client.js +46 -6
- package/dist/core/maintenance.d.ts +3 -1
- package/dist/core/maintenance.js +6 -2
- package/dist/core/meta-store.js +3 -3
- package/dist/core/outbox-store.js +23 -23
- package/dist/{react → devtools}/BenchmarkModal.d.ts +1 -1
- package/dist/{react → devtools}/BenchmarkModal.js +20 -20
- package/dist/{react → devtools}/SyncInspector.d.ts +3 -2
- package/dist/{react → devtools}/SyncInspector.js +43 -33
- package/dist/devtools/index.d.ts +3 -0
- package/dist/devtools/index.js +20 -0
- package/dist/keys/store.js +14 -14
- package/dist/kv/store.js +13 -13
- package/dist/react/index.d.ts +0 -3
- package/dist/react/index.js +0 -4
- package/dist/scheduler/store.js +28 -28
- package/dist/schema/generator.js +28 -28
- package/dist/search/store.js +15 -15
- 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/store.js +24 -24
- package/dist/storage/supabase.d.ts +44 -0
- package/dist/storage/supabase.js +83 -0
- package/dist/storage/types.d.ts +29 -3
- package/dist/utils/portability.d.ts +4 -0
- package/dist/utils/portability.js +7 -1
- package/dist/utils/sql.d.ts +5 -0
- package/dist/utils/sql.js +21 -0
- package/package.json +204 -195
package/dist/search/store.js
CHANGED
|
@@ -96,15 +96,15 @@ class SearchStore {
|
|
|
96
96
|
// Snippet column index in FTS5: columns are 0-based: col 0 is row_id, col 1 is columns[0], etc.
|
|
97
97
|
// We pick column index 1 (or the first indexed text column) for snippet extraction
|
|
98
98
|
const snippetColIndex = 1;
|
|
99
|
-
let sql = `
|
|
100
|
-
SELECT
|
|
101
|
-
row_id,
|
|
102
|
-
rank,
|
|
103
|
-
snippet(${ftsTable}, ${snippetColIndex}, '${preTag}', '${postTag}', '...', 15) as snippet_text
|
|
104
|
-
FROM ${ftsTable}
|
|
105
|
-
WHERE ${ftsTable} MATCH ?
|
|
106
|
-
ORDER BY rank
|
|
107
|
-
LIMIT ? OFFSET ?;
|
|
99
|
+
let sql = `
|
|
100
|
+
SELECT
|
|
101
|
+
row_id,
|
|
102
|
+
rank,
|
|
103
|
+
snippet(${ftsTable}, ${snippetColIndex}, '${preTag}', '${postTag}', '...', 15) as snippet_text
|
|
104
|
+
FROM ${ftsTable}
|
|
105
|
+
WHERE ${ftsTable} MATCH ?
|
|
106
|
+
ORDER BY rank
|
|
107
|
+
LIMIT ? OFFSET ?;
|
|
108
108
|
`;
|
|
109
109
|
try {
|
|
110
110
|
const rows = await db.execute(sql, [matchQuery, limit, offset]);
|
|
@@ -117,12 +117,12 @@ class SearchStore {
|
|
|
117
117
|
catch (err) {
|
|
118
118
|
// In case snippet() function is unavailable in the environment, fallback to rank-only search
|
|
119
119
|
if (err?.message?.includes('snippet') || err?.message?.includes('no such function')) {
|
|
120
|
-
const fallbackSql = `
|
|
121
|
-
SELECT row_id, rank
|
|
122
|
-
FROM ${ftsTable}
|
|
123
|
-
WHERE ${ftsTable} MATCH ?
|
|
124
|
-
ORDER BY rank
|
|
125
|
-
LIMIT ? OFFSET ?;
|
|
120
|
+
const fallbackSql = `
|
|
121
|
+
SELECT row_id, rank
|
|
122
|
+
FROM ${ftsTable}
|
|
123
|
+
WHERE ${ftsTable} MATCH ?
|
|
124
|
+
ORDER BY rank
|
|
125
|
+
LIMIT ? OFFSET ?;
|
|
126
126
|
`;
|
|
127
127
|
const rows = await db.execute(fallbackSql, [matchQuery, limit, offset]);
|
|
128
128
|
return (rows || []).map((r) => ({
|
|
@@ -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
|
+
}
|