@symbiote-native/file-system 0.1.0
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 -0
- package/README.md +262 -0
- package/build/angular/index.d.ts +1 -0
- package/build/angular/index.js +1 -0
- package/build/core/file-system.d.ts +169 -0
- package/build/core/file-system.js +404 -0
- package/build/core/index.d.ts +2 -0
- package/build/core/index.js +2 -0
- package/build/core/native-module.d.ts +32 -0
- package/build/core/native-module.js +2 -0
- package/build/core/types.d.ts +104 -0
- package/build/core/types.js +15 -0
- package/build/next/directory.d.ts +36 -0
- package/build/next/directory.js +59 -0
- package/build/next/file.d.ts +71 -0
- package/build/next/file.js +207 -0
- package/build/next/index.d.ts +5 -0
- package/build/next/index.js +5 -0
- package/build/next/native-module.d.ts +109 -0
- package/build/next/native-module.js +2 -0
- package/build/next/network-tasks.d.ts +118 -0
- package/build/next/network-tasks.js +333 -0
- package/build/next/node-path.d.ts +16 -0
- package/build/next/node-path.js +353 -0
- package/build/next/path-utilities.d.ts +42 -0
- package/build/next/path-utilities.js +110 -0
- package/build/next/paths.d.ts +22 -0
- package/build/next/paths.js +43 -0
- package/build/next/streams.d.ts +25 -0
- package/build/next/streams.js +55 -0
- package/build/next/types.d.ts +152 -0
- package/build/next/types.js +19 -0
- package/build/next/url-utilities.d.ts +3 -0
- package/build/next/url-utilities.js +62 -0
- package/build/next/watcher.d.ts +19 -0
- package/build/next/watcher.js +64 -0
- package/build-ngc/angular/index.d.ts +1 -0
- package/build-ngc/angular/index.js +2 -0
- package/build-ngc/angular/index.js.map +1 -0
- package/build-ngc/next/directory.d.ts +36 -0
- package/build-ngc/next/directory.js +60 -0
- package/build-ngc/next/directory.js.map +1 -0
- package/build-ngc/next/file.d.ts +71 -0
- package/build-ngc/next/file.js +208 -0
- package/build-ngc/next/file.js.map +1 -0
- package/build-ngc/next/index.d.ts +5 -0
- package/build-ngc/next/index.js +6 -0
- package/build-ngc/next/index.js.map +1 -0
- package/build-ngc/next/native-module.d.ts +109 -0
- package/build-ngc/next/native-module.js +3 -0
- package/build-ngc/next/native-module.js.map +1 -0
- package/build-ngc/next/network-tasks.d.ts +118 -0
- package/build-ngc/next/network-tasks.js +334 -0
- package/build-ngc/next/network-tasks.js.map +1 -0
- package/build-ngc/next/node-path.d.ts +16 -0
- package/build-ngc/next/node-path.js +354 -0
- package/build-ngc/next/node-path.js.map +1 -0
- package/build-ngc/next/path-utilities.d.ts +42 -0
- package/build-ngc/next/path-utilities.js +111 -0
- package/build-ngc/next/path-utilities.js.map +1 -0
- package/build-ngc/next/paths.d.ts +22 -0
- package/build-ngc/next/paths.js +44 -0
- package/build-ngc/next/paths.js.map +1 -0
- package/build-ngc/next/streams.d.ts +25 -0
- package/build-ngc/next/streams.js +56 -0
- package/build-ngc/next/streams.js.map +1 -0
- package/build-ngc/next/types.d.ts +152 -0
- package/build-ngc/next/types.js +20 -0
- package/build-ngc/next/types.js.map +1 -0
- package/build-ngc/next/url-utilities.d.ts +3 -0
- package/build-ngc/next/url-utilities.js +63 -0
- package/build-ngc/next/url-utilities.js.map +1 -0
- package/build-ngc/next/watcher.d.ts +19 -0
- package/build-ngc/next/watcher.js +65 -0
- package/build-ngc/next/watcher.js.map +1 -0
- package/native-link.json +17 -0
- package/package.json +154 -0
- package/src/angular/index.ts +1 -0
- package/src/core/file-system.ts +634 -0
- package/src/core/index.ts +2 -0
- package/src/core/native-module.ts +99 -0
- package/src/core/types.ts +135 -0
- package/src/next/directory.ts +83 -0
- package/src/next/file.ts +318 -0
- package/src/next/index.ts +5 -0
- package/src/next/native-module.ts +188 -0
- package/src/next/network-tasks.ts +456 -0
- package/src/next/node-path.ts +362 -0
- package/src/next/path-utilities.ts +144 -0
- package/src/next/paths.ts +52 -0
- package/src/next/streams-ambient.d.ts +34 -0
- package/src/next/streams.ts +81 -0
- package/src/next/types.ts +194 -0
- package/src/next/url-utilities.ts +70 -0
- package/src/next/watcher.ts +106 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
import { UnavailabilityError, uuid, } from 'expo-modules-core';
|
|
2
|
+
import { Platform } from 'react-native';
|
|
3
|
+
import { expoFileSystem } from './native-module.js';
|
|
4
|
+
import { FileSystemSessionType, FileSystemUploadType, } from './types.js';
|
|
5
|
+
if (!expoFileSystem) {
|
|
6
|
+
console.warn('No native ExponentFileSystem module found, are you sure the @symbiote-native/file-system module is linked properly?');
|
|
7
|
+
}
|
|
8
|
+
function normalizeEndingSlash(p) {
|
|
9
|
+
if (p != null) {
|
|
10
|
+
return p.replace(/\/*$/, '') + '/';
|
|
11
|
+
}
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* `file://` URI pointing to the directory where user documents for this app will be stored.
|
|
16
|
+
* Files stored here will remain until explicitly deleted by the app. Ends with a trailing `/`.
|
|
17
|
+
*/
|
|
18
|
+
export const documentDirectory = normalizeEndingSlash(expoFileSystem?.documentDirectory ?? null);
|
|
19
|
+
/**
|
|
20
|
+
* `file://` URI pointing to the directory where temporary files used by this app will be stored.
|
|
21
|
+
* Files stored here may be automatically deleted by the system when low on storage.
|
|
22
|
+
*/
|
|
23
|
+
export const cacheDirectory = normalizeEndingSlash(expoFileSystem?.cacheDirectory ?? null);
|
|
24
|
+
/**
|
|
25
|
+
* URI to the directory where assets bundled with the application are stored.
|
|
26
|
+
*/
|
|
27
|
+
export const bundleDirectory = normalizeEndingSlash(expoFileSystem?.bundleDirectory ?? null);
|
|
28
|
+
/**
|
|
29
|
+
* Get metadata information about a file, directory or external content/asset.
|
|
30
|
+
*/
|
|
31
|
+
export async function getInfoAsync(fileUri, options = {}) {
|
|
32
|
+
if (!expoFileSystem?.getInfoAsync) {
|
|
33
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'getInfoAsync');
|
|
34
|
+
}
|
|
35
|
+
return await expoFileSystem.getInfoAsync(fileUri, options);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Read the entire contents of a file as a string. Binary is returned in raw format — prepend
|
|
39
|
+
* `data:image/png;base64,` yourself to use it as a data URI.
|
|
40
|
+
*/
|
|
41
|
+
export async function readAsStringAsync(fileUri, options = {}) {
|
|
42
|
+
if (!expoFileSystem?.readAsStringAsync) {
|
|
43
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'readAsStringAsync');
|
|
44
|
+
}
|
|
45
|
+
return await expoFileSystem.readAsStringAsync(fileUri, options);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Takes a `file://` URI and converts it into a `content://` URI so that it can be accessed by
|
|
49
|
+
* other applications outside of the app.
|
|
50
|
+
* @platform android
|
|
51
|
+
*/
|
|
52
|
+
export async function getContentUriAsync(fileUri) {
|
|
53
|
+
if (Platform.OS === 'android') {
|
|
54
|
+
if (!expoFileSystem?.getContentUriAsync) {
|
|
55
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'getContentUriAsync');
|
|
56
|
+
}
|
|
57
|
+
return await expoFileSystem.getContentUriAsync(fileUri);
|
|
58
|
+
}
|
|
59
|
+
return fileUri;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Write the entire contents of a file as a string.
|
|
63
|
+
*/
|
|
64
|
+
export async function writeAsStringAsync(fileUri, contents, options = {}) {
|
|
65
|
+
if (!expoFileSystem?.writeAsStringAsync) {
|
|
66
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'writeAsStringAsync');
|
|
67
|
+
}
|
|
68
|
+
return await expoFileSystem.writeAsStringAsync(fileUri, contents, options);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Delete a file or directory. If the URI points to a directory, the directory and all its
|
|
72
|
+
* contents are recursively deleted.
|
|
73
|
+
*/
|
|
74
|
+
export async function deleteAsync(fileUri, options = {}) {
|
|
75
|
+
if (!expoFileSystem?.deleteAsync) {
|
|
76
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'deleteAsync');
|
|
77
|
+
}
|
|
78
|
+
return await expoFileSystem.deleteAsync(fileUri, options);
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Move a file or directory to a new location.
|
|
82
|
+
*/
|
|
83
|
+
export async function moveAsync(options) {
|
|
84
|
+
if (!expoFileSystem?.moveAsync) {
|
|
85
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'moveAsync');
|
|
86
|
+
}
|
|
87
|
+
return await expoFileSystem.moveAsync(options);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Create a copy of a file or directory. Directories are recursively copied with all of their
|
|
91
|
+
* contents. Also works to copy content shared by other apps into local storage.
|
|
92
|
+
*/
|
|
93
|
+
export async function copyAsync(options) {
|
|
94
|
+
if (!expoFileSystem?.copyAsync) {
|
|
95
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'copyAsync');
|
|
96
|
+
}
|
|
97
|
+
return await expoFileSystem.copyAsync(options);
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Create a new empty directory.
|
|
101
|
+
*/
|
|
102
|
+
export async function makeDirectoryAsync(fileUri, options = {}) {
|
|
103
|
+
if (!expoFileSystem?.makeDirectoryAsync) {
|
|
104
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'makeDirectoryAsync');
|
|
105
|
+
}
|
|
106
|
+
return await expoFileSystem.makeDirectoryAsync(fileUri, options);
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Enumerate the contents of a directory.
|
|
110
|
+
*/
|
|
111
|
+
export async function readDirectoryAsync(fileUri) {
|
|
112
|
+
if (!expoFileSystem?.readDirectoryAsync) {
|
|
113
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'readDirectoryAsync');
|
|
114
|
+
}
|
|
115
|
+
return await expoFileSystem.readDirectoryAsync(fileUri);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Gets the available internal disk storage size, in bytes.
|
|
119
|
+
*/
|
|
120
|
+
export async function getFreeDiskStorageAsync() {
|
|
121
|
+
if (!expoFileSystem?.getFreeDiskStorageAsync) {
|
|
122
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'getFreeDiskStorageAsync');
|
|
123
|
+
}
|
|
124
|
+
return await expoFileSystem.getFreeDiskStorageAsync();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* Gets total internal disk storage size, in bytes.
|
|
128
|
+
*/
|
|
129
|
+
export async function getTotalDiskCapacityAsync() {
|
|
130
|
+
if (!expoFileSystem?.getTotalDiskCapacityAsync) {
|
|
131
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'getTotalDiskCapacityAsync');
|
|
132
|
+
}
|
|
133
|
+
return await expoFileSystem.getTotalDiskCapacityAsync();
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Download the contents at a remote URI to a file in the app's file system. The directory for
|
|
137
|
+
* the local file URI must exist prior to calling this function.
|
|
138
|
+
*/
|
|
139
|
+
export async function downloadAsync(uri, fileUri, options = {}) {
|
|
140
|
+
if (!expoFileSystem?.downloadAsync) {
|
|
141
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'downloadAsync');
|
|
142
|
+
}
|
|
143
|
+
return await expoFileSystem.downloadAsync(uri, fileUri, {
|
|
144
|
+
sessionType: FileSystemSessionType.BACKGROUND,
|
|
145
|
+
...options,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Upload the contents of the file pointed by `fileUri` to the remote url.
|
|
150
|
+
*/
|
|
151
|
+
export async function uploadAsync(url, fileUri, options = {}) {
|
|
152
|
+
if (!expoFileSystem?.uploadAsync) {
|
|
153
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'uploadAsync');
|
|
154
|
+
}
|
|
155
|
+
return await expoFileSystem.uploadAsync(url, fileUri, {
|
|
156
|
+
sessionType: FileSystemSessionType.BACKGROUND,
|
|
157
|
+
uploadType: FileSystemUploadType.BINARY_CONTENT,
|
|
158
|
+
...options,
|
|
159
|
+
httpMethod: (options.httpMethod || 'POST').toUpperCase(),
|
|
160
|
+
});
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Create a `DownloadResumable` object which can start, pause, and resume a download of contents
|
|
164
|
+
* at a remote URI to a file in the app's file system.
|
|
165
|
+
*/
|
|
166
|
+
export function createDownloadResumable(uri, fileUri, options, callback, resumeData) {
|
|
167
|
+
return new DownloadResumable(uri, fileUri, options, callback, resumeData);
|
|
168
|
+
}
|
|
169
|
+
export function createUploadTask(url, fileUri, options, callback) {
|
|
170
|
+
return new UploadTask(url, fileUri, options, callback);
|
|
171
|
+
}
|
|
172
|
+
export class FileSystemCancellableNetworkTask {
|
|
173
|
+
_uuid = uuid.v4();
|
|
174
|
+
taskWasCanceled = false;
|
|
175
|
+
subscription;
|
|
176
|
+
async cancelAsync() {
|
|
177
|
+
if (!expoFileSystem?.networkTaskCancelAsync) {
|
|
178
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'networkTaskCancelAsync');
|
|
179
|
+
}
|
|
180
|
+
this.removeSubscription();
|
|
181
|
+
this.taskWasCanceled = true;
|
|
182
|
+
return await expoFileSystem.networkTaskCancelAsync(this.uuid);
|
|
183
|
+
}
|
|
184
|
+
isTaskCancelled() {
|
|
185
|
+
if (this.taskWasCanceled) {
|
|
186
|
+
console.warn('This task was already canceled.');
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
return false;
|
|
190
|
+
}
|
|
191
|
+
get uuid() {
|
|
192
|
+
return this._uuid;
|
|
193
|
+
}
|
|
194
|
+
addSubscription() {
|
|
195
|
+
if (this.subscription || !expoFileSystem?.addListener) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
this.subscription = expoFileSystem.addListener(this.getEventName(), (event) => {
|
|
199
|
+
const progressEvent = event;
|
|
200
|
+
if (progressEvent.uuid === this.uuid) {
|
|
201
|
+
const callback = this.getCallback();
|
|
202
|
+
if (callback) {
|
|
203
|
+
callback(progressEvent.data);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
}
|
|
208
|
+
removeSubscription() {
|
|
209
|
+
if (!this.subscription) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
this.subscription.remove();
|
|
213
|
+
this.subscription = null;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
export class UploadTask extends FileSystemCancellableNetworkTask {
|
|
217
|
+
url;
|
|
218
|
+
fileUri;
|
|
219
|
+
callback;
|
|
220
|
+
options;
|
|
221
|
+
constructor(url, fileUri, options, callback) {
|
|
222
|
+
super();
|
|
223
|
+
this.url = url;
|
|
224
|
+
this.fileUri = fileUri;
|
|
225
|
+
this.callback = callback;
|
|
226
|
+
const httpMethod = (options?.httpMethod?.toUpperCase() ||
|
|
227
|
+
'POST');
|
|
228
|
+
this.options = {
|
|
229
|
+
sessionType: FileSystemSessionType.BACKGROUND,
|
|
230
|
+
uploadType: FileSystemUploadType.BINARY_CONTENT,
|
|
231
|
+
...options,
|
|
232
|
+
httpMethod,
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
getEventName() {
|
|
236
|
+
return 'expo-file-system.uploadProgress';
|
|
237
|
+
}
|
|
238
|
+
getCallback() {
|
|
239
|
+
return this.callback;
|
|
240
|
+
}
|
|
241
|
+
async uploadAsync() {
|
|
242
|
+
if (!expoFileSystem?.uploadTaskStartAsync) {
|
|
243
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'uploadTaskStartAsync');
|
|
244
|
+
}
|
|
245
|
+
if (this.isTaskCancelled()) {
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
this.addSubscription();
|
|
249
|
+
const result = await expoFileSystem.uploadTaskStartAsync(this.url, this.fileUri, this.uuid, this.options);
|
|
250
|
+
this.removeSubscription();
|
|
251
|
+
return result;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
export class DownloadResumable extends FileSystemCancellableNetworkTask {
|
|
255
|
+
url;
|
|
256
|
+
_fileUri;
|
|
257
|
+
options;
|
|
258
|
+
callback;
|
|
259
|
+
resumeData;
|
|
260
|
+
constructor(url, _fileUri, options = {}, callback, resumeData) {
|
|
261
|
+
super();
|
|
262
|
+
this.url = url;
|
|
263
|
+
this._fileUri = _fileUri;
|
|
264
|
+
this.options = options;
|
|
265
|
+
this.callback = callback;
|
|
266
|
+
this.resumeData = resumeData;
|
|
267
|
+
}
|
|
268
|
+
get fileUri() {
|
|
269
|
+
return this._fileUri;
|
|
270
|
+
}
|
|
271
|
+
getEventName() {
|
|
272
|
+
return 'expo-file-system.downloadProgress';
|
|
273
|
+
}
|
|
274
|
+
getCallback() {
|
|
275
|
+
return this.callback;
|
|
276
|
+
}
|
|
277
|
+
/**
|
|
278
|
+
* Download the contents at a remote URI to a file in the app's file system.
|
|
279
|
+
*/
|
|
280
|
+
async downloadAsync() {
|
|
281
|
+
if (!expoFileSystem?.downloadResumableStartAsync) {
|
|
282
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'downloadResumableStartAsync');
|
|
283
|
+
}
|
|
284
|
+
if (this.isTaskCancelled()) {
|
|
285
|
+
return;
|
|
286
|
+
}
|
|
287
|
+
this.addSubscription();
|
|
288
|
+
return await expoFileSystem.downloadResumableStartAsync(this.url, this._fileUri, this.uuid, this.options, this.resumeData);
|
|
289
|
+
}
|
|
290
|
+
/**
|
|
291
|
+
* Pause the current download operation. `resumeData` is added to the `DownloadResumable`
|
|
292
|
+
* object after a successful pause operation. Returns an object that can be persisted
|
|
293
|
+
* (e.g. via a storage module) for future retrieval.
|
|
294
|
+
*/
|
|
295
|
+
async pauseAsync() {
|
|
296
|
+
if (!expoFileSystem?.downloadResumablePauseAsync) {
|
|
297
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'downloadResumablePauseAsync');
|
|
298
|
+
}
|
|
299
|
+
if (this.isTaskCancelled()) {
|
|
300
|
+
return {
|
|
301
|
+
fileUri: this._fileUri,
|
|
302
|
+
options: this.options,
|
|
303
|
+
url: this.url,
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
const pauseResult = await expoFileSystem.downloadResumablePauseAsync(this.uuid);
|
|
307
|
+
this.removeSubscription();
|
|
308
|
+
if (pauseResult) {
|
|
309
|
+
this.resumeData = pauseResult.resumeData;
|
|
310
|
+
return this.savable();
|
|
311
|
+
}
|
|
312
|
+
throw new Error('Unable to generate a savable pause state');
|
|
313
|
+
}
|
|
314
|
+
/**
|
|
315
|
+
* Resume a paused download operation.
|
|
316
|
+
*/
|
|
317
|
+
async resumeAsync() {
|
|
318
|
+
if (!expoFileSystem?.downloadResumableStartAsync) {
|
|
319
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'downloadResumableStartAsync');
|
|
320
|
+
}
|
|
321
|
+
if (this.isTaskCancelled()) {
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
this.addSubscription();
|
|
325
|
+
return await expoFileSystem.downloadResumableStartAsync(this.url, this.fileUri, this.uuid, this.options, this.resumeData);
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The object which can be persisted for future retrieval (e.g. via a storage module), and
|
|
329
|
+
* fed back into `createDownloadResumable` after an app restart.
|
|
330
|
+
*/
|
|
331
|
+
savable() {
|
|
332
|
+
return {
|
|
333
|
+
url: this.url,
|
|
334
|
+
fileUri: this.fileUri,
|
|
335
|
+
options: this.options,
|
|
336
|
+
resumeData: this.resumeData,
|
|
337
|
+
};
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
const baseReadAsStringAsync = readAsStringAsync;
|
|
341
|
+
const baseWriteAsStringAsync = writeAsStringAsync;
|
|
342
|
+
const baseDeleteAsync = deleteAsync;
|
|
343
|
+
const baseMoveAsync = moveAsync;
|
|
344
|
+
const baseCopyAsync = copyAsync;
|
|
345
|
+
/**
|
|
346
|
+
* `StorageAccessFramework` encapsulates the functions usable with SAF URIs, for accessing
|
|
347
|
+
* user-selected directories outside the app's own storage.
|
|
348
|
+
* @platform android
|
|
349
|
+
*/
|
|
350
|
+
export const StorageAccessFramework = {
|
|
351
|
+
/**
|
|
352
|
+
* Gets a SAF URI pointing to a folder in the Android root directory, given the folder name.
|
|
353
|
+
* @platform android
|
|
354
|
+
*/
|
|
355
|
+
getUriForDirectoryInRoot(folderName) {
|
|
356
|
+
return `content://com.android.externalstorage.documents/tree/primary:${folderName}/document/primary:${folderName}`;
|
|
357
|
+
},
|
|
358
|
+
/**
|
|
359
|
+
* Allows users to select a specific directory, granting the app access to all of the files and
|
|
360
|
+
* sub-directories within it.
|
|
361
|
+
* @platform android 11+
|
|
362
|
+
*/
|
|
363
|
+
async requestDirectoryPermissionsAsync(initialFileUrl = null) {
|
|
364
|
+
if (!expoFileSystem?.requestDirectoryPermissionsAsync) {
|
|
365
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'StorageAccessFramework.requestDirectoryPermissionsAsync');
|
|
366
|
+
}
|
|
367
|
+
return await expoFileSystem.requestDirectoryPermissionsAsync(initialFileUrl);
|
|
368
|
+
},
|
|
369
|
+
/**
|
|
370
|
+
* Enumerate the contents of a directory addressed by a SAF URI.
|
|
371
|
+
* @platform android
|
|
372
|
+
*/
|
|
373
|
+
async readDirectoryAsync(dirUri) {
|
|
374
|
+
if (!expoFileSystem?.readSAFDirectoryAsync) {
|
|
375
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'StorageAccessFramework.readDirectoryAsync');
|
|
376
|
+
}
|
|
377
|
+
return await expoFileSystem.readSAFDirectoryAsync(dirUri);
|
|
378
|
+
},
|
|
379
|
+
/**
|
|
380
|
+
* Creates a new empty directory addressed by a SAF URI.
|
|
381
|
+
* @platform android
|
|
382
|
+
*/
|
|
383
|
+
async makeDirectoryAsync(parentUri, dirName) {
|
|
384
|
+
if (!expoFileSystem?.makeSAFDirectoryAsync) {
|
|
385
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'StorageAccessFramework.makeDirectoryAsync');
|
|
386
|
+
}
|
|
387
|
+
return await expoFileSystem.makeSAFDirectoryAsync(parentUri, dirName);
|
|
388
|
+
},
|
|
389
|
+
/**
|
|
390
|
+
* Creates a new empty file addressed by a SAF URI.
|
|
391
|
+
* @platform android
|
|
392
|
+
*/
|
|
393
|
+
async createFileAsync(parentUri, fileName, mimeType) {
|
|
394
|
+
if (!expoFileSystem?.createSAFFileAsync) {
|
|
395
|
+
throw new UnavailabilityError('@symbiote-native/file-system', 'StorageAccessFramework.createFileAsync');
|
|
396
|
+
}
|
|
397
|
+
return await expoFileSystem.createSAFFileAsync(parentUri, fileName, mimeType);
|
|
398
|
+
},
|
|
399
|
+
writeAsStringAsync: baseWriteAsStringAsync,
|
|
400
|
+
readAsStringAsync: baseReadAsStringAsync,
|
|
401
|
+
deleteAsync: baseDeleteAsync,
|
|
402
|
+
moveAsync: baseMoveAsync,
|
|
403
|
+
copyAsync: baseCopyAsync,
|
|
404
|
+
};
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type EventSubscription } from 'expo-modules-core';
|
|
2
|
+
import type { IFileSystemDeletingOptions, IFileSystemDownloadOptions, IFileSystemDownloadResult, IFileSystemFileInfo, IFileSystemInfoOptions, IFileSystemMakeDirectoryOptions, IFileSystemRelocatingOptions, IFileSystemRequestDirectoryPermissionsResult, IFileSystemUploadOptions, IFileSystemUploadResult, IFileSystemWritingOptions } from './types';
|
|
3
|
+
export type INativeFileSystemModule = {
|
|
4
|
+
documentDirectory: string | null;
|
|
5
|
+
cacheDirectory: string | null;
|
|
6
|
+
bundleDirectory: string | null;
|
|
7
|
+
getInfoAsync?: (fileUri: string, options: IFileSystemInfoOptions) => Promise<IFileSystemFileInfo>;
|
|
8
|
+
readAsStringAsync?: (fileUri: string, options: Record<string, unknown>) => Promise<string>;
|
|
9
|
+
getContentUriAsync?: (fileUri: string) => Promise<string>;
|
|
10
|
+
writeAsStringAsync?: (fileUri: string, contents: string, options: IFileSystemWritingOptions) => Promise<void>;
|
|
11
|
+
deleteAsync?: (fileUri: string, options: IFileSystemDeletingOptions) => Promise<void>;
|
|
12
|
+
moveAsync?: (options: IFileSystemRelocatingOptions) => Promise<void>;
|
|
13
|
+
copyAsync?: (options: IFileSystemRelocatingOptions) => Promise<void>;
|
|
14
|
+
makeDirectoryAsync?: (fileUri: string, options: IFileSystemMakeDirectoryOptions) => Promise<void>;
|
|
15
|
+
readDirectoryAsync?: (fileUri: string) => Promise<string[]>;
|
|
16
|
+
getFreeDiskStorageAsync?: () => Promise<number>;
|
|
17
|
+
getTotalDiskCapacityAsync?: () => Promise<number>;
|
|
18
|
+
downloadAsync?: (uri: string, fileUri: string, options: IFileSystemDownloadOptions) => Promise<IFileSystemDownloadResult>;
|
|
19
|
+
uploadAsync?: (url: string, fileUri: string, options: IFileSystemUploadOptions) => Promise<IFileSystemUploadResult>;
|
|
20
|
+
networkTaskCancelAsync?: (uuid: string) => Promise<void>;
|
|
21
|
+
addListener?: (eventName: string, callback: (event: unknown) => void) => EventSubscription;
|
|
22
|
+
uploadTaskStartAsync?: (url: string, fileUri: string, uuid: string, options: IFileSystemUploadOptions) => Promise<IFileSystemUploadResult | undefined | null>;
|
|
23
|
+
downloadResumableStartAsync?: (url: string, fileUri: string, uuid: string, options: IFileSystemDownloadOptions, resumeData?: string) => Promise<IFileSystemDownloadResult | undefined>;
|
|
24
|
+
downloadResumablePauseAsync?: (uuid: string) => Promise<{
|
|
25
|
+
resumeData?: string;
|
|
26
|
+
} | undefined>;
|
|
27
|
+
requestDirectoryPermissionsAsync?: (initialFileUrl: string | null) => Promise<IFileSystemRequestDirectoryPermissionsResult>;
|
|
28
|
+
readSAFDirectoryAsync?: (dirUri: string) => Promise<string[]>;
|
|
29
|
+
makeSAFDirectoryAsync?: (parentUri: string, dirName: string) => Promise<string>;
|
|
30
|
+
createSAFFileAsync?: (parentUri: string, fileName: string, mimeType: string) => Promise<string>;
|
|
31
|
+
};
|
|
32
|
+
export declare const expoFileSystem: INativeFileSystemModule | null;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export declare enum FileSystemSessionType {
|
|
2
|
+
BACKGROUND = 0,
|
|
3
|
+
FOREGROUND = 1
|
|
4
|
+
}
|
|
5
|
+
export declare enum FileSystemUploadType {
|
|
6
|
+
BINARY_CONTENT = 0,
|
|
7
|
+
MULTIPART = 1
|
|
8
|
+
}
|
|
9
|
+
export type IFileSystemDownloadOptions = {
|
|
10
|
+
md5?: boolean;
|
|
11
|
+
cache?: boolean;
|
|
12
|
+
headers?: Record<string, string>;
|
|
13
|
+
sessionType?: FileSystemSessionType;
|
|
14
|
+
};
|
|
15
|
+
export type IFileSystemHttpResult = {
|
|
16
|
+
headers: Record<string, string>;
|
|
17
|
+
status: number;
|
|
18
|
+
mimeType: string | null;
|
|
19
|
+
};
|
|
20
|
+
export type IFileSystemDownloadResult = IFileSystemHttpResult & {
|
|
21
|
+
uri: string;
|
|
22
|
+
md5?: string;
|
|
23
|
+
};
|
|
24
|
+
export type IFileSystemAcceptedUploadHttpMethod = 'POST' | 'PUT' | 'PATCH';
|
|
25
|
+
export type IFileSystemUploadOptionsBinary = {
|
|
26
|
+
uploadType?: FileSystemUploadType;
|
|
27
|
+
};
|
|
28
|
+
export type IFileSystemUploadOptionsMultipart = {
|
|
29
|
+
uploadType: FileSystemUploadType;
|
|
30
|
+
fieldName?: string;
|
|
31
|
+
mimeType?: string;
|
|
32
|
+
parameters?: Record<string, string>;
|
|
33
|
+
};
|
|
34
|
+
export type IFileSystemUploadOptions = (IFileSystemUploadOptionsBinary | IFileSystemUploadOptionsMultipart) & {
|
|
35
|
+
headers?: Record<string, string>;
|
|
36
|
+
httpMethod?: IFileSystemAcceptedUploadHttpMethod;
|
|
37
|
+
sessionType?: FileSystemSessionType;
|
|
38
|
+
};
|
|
39
|
+
export type IFileSystemUploadResult = IFileSystemHttpResult & {
|
|
40
|
+
body: string;
|
|
41
|
+
};
|
|
42
|
+
export type IFileSystemNetworkTaskProgressCallback<T extends IFileSystemDownloadProgressData | IFileSystemUploadProgressData> = (data: T) => void;
|
|
43
|
+
export type IFileSystemDownloadProgressData = {
|
|
44
|
+
totalBytesWritten: number;
|
|
45
|
+
totalBytesExpectedToWrite: number;
|
|
46
|
+
};
|
|
47
|
+
export type IFileSystemUploadProgressData = {
|
|
48
|
+
totalBytesSent: number;
|
|
49
|
+
totalBytesExpectedToSend: number;
|
|
50
|
+
};
|
|
51
|
+
export type IFileSystemDownloadPauseState = {
|
|
52
|
+
url: string;
|
|
53
|
+
fileUri: string;
|
|
54
|
+
options: IFileSystemDownloadOptions;
|
|
55
|
+
resumeData?: string;
|
|
56
|
+
};
|
|
57
|
+
export type IFileSystemFileInfo = {
|
|
58
|
+
exists: true;
|
|
59
|
+
uri: string;
|
|
60
|
+
size: number;
|
|
61
|
+
isDirectory: boolean;
|
|
62
|
+
modificationTime: number;
|
|
63
|
+
md5?: string;
|
|
64
|
+
} | {
|
|
65
|
+
exists: false;
|
|
66
|
+
uri: string;
|
|
67
|
+
isDirectory: false;
|
|
68
|
+
};
|
|
69
|
+
export declare enum FileSystemEncodingType {
|
|
70
|
+
UTF8 = "utf8",
|
|
71
|
+
Base64 = "base64"
|
|
72
|
+
}
|
|
73
|
+
export type IFileSystemReadingOptions = {
|
|
74
|
+
encoding?: FileSystemEncodingType | 'utf8' | 'base64';
|
|
75
|
+
position?: number;
|
|
76
|
+
length?: number;
|
|
77
|
+
};
|
|
78
|
+
export type IFileSystemWritingOptions = {
|
|
79
|
+
encoding?: FileSystemEncodingType | 'utf8' | 'base64';
|
|
80
|
+
append?: boolean;
|
|
81
|
+
};
|
|
82
|
+
export type IFileSystemDeletingOptions = {
|
|
83
|
+
idempotent?: boolean;
|
|
84
|
+
};
|
|
85
|
+
export type IFileSystemInfoOptions = {
|
|
86
|
+
md5?: boolean;
|
|
87
|
+
};
|
|
88
|
+
export type IFileSystemRelocatingOptions = {
|
|
89
|
+
from: string;
|
|
90
|
+
to: string;
|
|
91
|
+
};
|
|
92
|
+
export type IFileSystemMakeDirectoryOptions = {
|
|
93
|
+
intermediates?: boolean;
|
|
94
|
+
};
|
|
95
|
+
export type IFileSystemProgressEvent<T> = {
|
|
96
|
+
uuid: string;
|
|
97
|
+
data: T;
|
|
98
|
+
};
|
|
99
|
+
export type IFileSystemRequestDirectoryPermissionsResult = {
|
|
100
|
+
granted: false;
|
|
101
|
+
} | {
|
|
102
|
+
granted: true;
|
|
103
|
+
directoryUri: string;
|
|
104
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
export var FileSystemSessionType;
|
|
2
|
+
(function (FileSystemSessionType) {
|
|
3
|
+
FileSystemSessionType[FileSystemSessionType["BACKGROUND"] = 0] = "BACKGROUND";
|
|
4
|
+
FileSystemSessionType[FileSystemSessionType["FOREGROUND"] = 1] = "FOREGROUND";
|
|
5
|
+
})(FileSystemSessionType || (FileSystemSessionType = {}));
|
|
6
|
+
export var FileSystemUploadType;
|
|
7
|
+
(function (FileSystemUploadType) {
|
|
8
|
+
FileSystemUploadType[FileSystemUploadType["BINARY_CONTENT"] = 0] = "BINARY_CONTENT";
|
|
9
|
+
FileSystemUploadType[FileSystemUploadType["MULTIPART"] = 1] = "MULTIPART";
|
|
10
|
+
})(FileSystemUploadType || (FileSystemUploadType = {}));
|
|
11
|
+
export var FileSystemEncodingType;
|
|
12
|
+
(function (FileSystemEncodingType) {
|
|
13
|
+
FileSystemEncodingType["UTF8"] = "utf8";
|
|
14
|
+
FileSystemEncodingType["Base64"] = "base64";
|
|
15
|
+
})(FileSystemEncodingType || (FileSystemEncodingType = {}));
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { File } from './file';
|
|
2
|
+
import { expoFileSystemNext } from './native-module';
|
|
3
|
+
import type { IFileSystemWatchEvent, IFileSystemWatchOptions, IFileSystemWatchSubscription } from './types';
|
|
4
|
+
/**
|
|
5
|
+
* Represents a directory on the filesystem.
|
|
6
|
+
*
|
|
7
|
+
* A `Directory` instance can be created for any path, and does not need to exist on the
|
|
8
|
+
* filesystem during creation.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Directory extends expoFileSystemNext.FileSystemDirectory {
|
|
11
|
+
static pickDirectoryAsync: (initialUri?: string) => Promise<Directory>;
|
|
12
|
+
/**
|
|
13
|
+
* Creates an instance of a directory. It can be created for any path, and does not need to
|
|
14
|
+
* exist on the filesystem during creation.
|
|
15
|
+
*/
|
|
16
|
+
constructor(...uris: (string | File | Directory)[]);
|
|
17
|
+
/** Directory containing this directory. */
|
|
18
|
+
get parentDirectory(): Directory;
|
|
19
|
+
/**
|
|
20
|
+
* Lists the contents of a directory. Calling this method if the parent directory does not
|
|
21
|
+
* exist will throw an error.
|
|
22
|
+
*/
|
|
23
|
+
list(): (Directory | File)[];
|
|
24
|
+
/** Directory name. */
|
|
25
|
+
get name(): string;
|
|
26
|
+
createFile(name: string, mimeType: string | null): File;
|
|
27
|
+
createDirectory(name: string): Directory;
|
|
28
|
+
/**
|
|
29
|
+
* Watches this directory for changes to its contents or the directory itself. On iOS,
|
|
30
|
+
* child changes are surfaced as a coarse-grained `modified` event on the directory itself, so
|
|
31
|
+
* filtering for child-level `created`, `deleted`, or `renamed` events is not reliable. The
|
|
32
|
+
* watcher automatically stops when the directory is deleted or renamed. To stop watching
|
|
33
|
+
* manually, call `remove()` on the returned subscription.
|
|
34
|
+
*/
|
|
35
|
+
watch(callback: (event: IFileSystemWatchEvent<File | Directory>) => void, options?: IFileSystemWatchOptions): IFileSystemWatchSubscription;
|
|
36
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { File } from './file.js';
|
|
2
|
+
import { expoFileSystemNext } from './native-module.js';
|
|
3
|
+
import { Paths } from './paths.js';
|
|
4
|
+
import { FileSystemWatcher } from './watcher.js';
|
|
5
|
+
/**
|
|
6
|
+
* Represents a directory on the filesystem.
|
|
7
|
+
*
|
|
8
|
+
* A `Directory` instance can be created for any path, and does not need to exist on the
|
|
9
|
+
* filesystem during creation.
|
|
10
|
+
*/
|
|
11
|
+
export class Directory extends expoFileSystemNext.FileSystemDirectory {
|
|
12
|
+
static pickDirectoryAsync;
|
|
13
|
+
/**
|
|
14
|
+
* Creates an instance of a directory. It can be created for any path, and does not need to
|
|
15
|
+
* exist on the filesystem during creation.
|
|
16
|
+
*/
|
|
17
|
+
constructor(...uris) {
|
|
18
|
+
super(Paths.join(...uris));
|
|
19
|
+
this.validatePath();
|
|
20
|
+
}
|
|
21
|
+
/** Directory containing this directory. */
|
|
22
|
+
get parentDirectory() {
|
|
23
|
+
return new Directory(Paths.join(this.uri, '..'));
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Lists the contents of a directory. Calling this method if the parent directory does not
|
|
27
|
+
* exist will throw an error.
|
|
28
|
+
*/
|
|
29
|
+
list() {
|
|
30
|
+
return super
|
|
31
|
+
.listAsRecords()
|
|
32
|
+
.map(({ isDirectory, uri }) => isDirectory ? new Directory(uri) : new File(uri));
|
|
33
|
+
}
|
|
34
|
+
/** Directory name. */
|
|
35
|
+
get name() {
|
|
36
|
+
return Paths.basename(this.uri);
|
|
37
|
+
}
|
|
38
|
+
createFile(name, mimeType) {
|
|
39
|
+
return new File(super.createFile(name, mimeType).uri);
|
|
40
|
+
}
|
|
41
|
+
createDirectory(name) {
|
|
42
|
+
return new Directory(super.createDirectory(name).uri);
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Watches this directory for changes to its contents or the directory itself. On iOS,
|
|
46
|
+
* child changes are surfaced as a coarse-grained `modified` event on the directory itself, so
|
|
47
|
+
* filtering for child-level `created`, `deleted`, or `renamed` events is not reliable. The
|
|
48
|
+
* watcher automatically stops when the directory is deleted or renamed. To stop watching
|
|
49
|
+
* manually, call `remove()` on the returned subscription.
|
|
50
|
+
*/
|
|
51
|
+
watch(callback, options) {
|
|
52
|
+
return new FileSystemWatcher(this.uri, callback, options, (uri, isDirectory) => (isDirectory ? new Directory(uri) : new File(uri)));
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
Directory.pickDirectoryAsync = async function (initialUri) {
|
|
56
|
+
const directory = (await expoFileSystemNext.pickDirectoryAsync(initialUri))
|
|
57
|
+
.uri;
|
|
58
|
+
return new Directory(directory);
|
|
59
|
+
};
|