@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.
Files changed (95) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +262 -0
  3. package/build/angular/index.d.ts +1 -0
  4. package/build/angular/index.js +1 -0
  5. package/build/core/file-system.d.ts +169 -0
  6. package/build/core/file-system.js +404 -0
  7. package/build/core/index.d.ts +2 -0
  8. package/build/core/index.js +2 -0
  9. package/build/core/native-module.d.ts +32 -0
  10. package/build/core/native-module.js +2 -0
  11. package/build/core/types.d.ts +104 -0
  12. package/build/core/types.js +15 -0
  13. package/build/next/directory.d.ts +36 -0
  14. package/build/next/directory.js +59 -0
  15. package/build/next/file.d.ts +71 -0
  16. package/build/next/file.js +207 -0
  17. package/build/next/index.d.ts +5 -0
  18. package/build/next/index.js +5 -0
  19. package/build/next/native-module.d.ts +109 -0
  20. package/build/next/native-module.js +2 -0
  21. package/build/next/network-tasks.d.ts +118 -0
  22. package/build/next/network-tasks.js +333 -0
  23. package/build/next/node-path.d.ts +16 -0
  24. package/build/next/node-path.js +353 -0
  25. package/build/next/path-utilities.d.ts +42 -0
  26. package/build/next/path-utilities.js +110 -0
  27. package/build/next/paths.d.ts +22 -0
  28. package/build/next/paths.js +43 -0
  29. package/build/next/streams.d.ts +25 -0
  30. package/build/next/streams.js +55 -0
  31. package/build/next/types.d.ts +152 -0
  32. package/build/next/types.js +19 -0
  33. package/build/next/url-utilities.d.ts +3 -0
  34. package/build/next/url-utilities.js +62 -0
  35. package/build/next/watcher.d.ts +19 -0
  36. package/build/next/watcher.js +64 -0
  37. package/build-ngc/angular/index.d.ts +1 -0
  38. package/build-ngc/angular/index.js +2 -0
  39. package/build-ngc/angular/index.js.map +1 -0
  40. package/build-ngc/next/directory.d.ts +36 -0
  41. package/build-ngc/next/directory.js +60 -0
  42. package/build-ngc/next/directory.js.map +1 -0
  43. package/build-ngc/next/file.d.ts +71 -0
  44. package/build-ngc/next/file.js +208 -0
  45. package/build-ngc/next/file.js.map +1 -0
  46. package/build-ngc/next/index.d.ts +5 -0
  47. package/build-ngc/next/index.js +6 -0
  48. package/build-ngc/next/index.js.map +1 -0
  49. package/build-ngc/next/native-module.d.ts +109 -0
  50. package/build-ngc/next/native-module.js +3 -0
  51. package/build-ngc/next/native-module.js.map +1 -0
  52. package/build-ngc/next/network-tasks.d.ts +118 -0
  53. package/build-ngc/next/network-tasks.js +334 -0
  54. package/build-ngc/next/network-tasks.js.map +1 -0
  55. package/build-ngc/next/node-path.d.ts +16 -0
  56. package/build-ngc/next/node-path.js +354 -0
  57. package/build-ngc/next/node-path.js.map +1 -0
  58. package/build-ngc/next/path-utilities.d.ts +42 -0
  59. package/build-ngc/next/path-utilities.js +111 -0
  60. package/build-ngc/next/path-utilities.js.map +1 -0
  61. package/build-ngc/next/paths.d.ts +22 -0
  62. package/build-ngc/next/paths.js +44 -0
  63. package/build-ngc/next/paths.js.map +1 -0
  64. package/build-ngc/next/streams.d.ts +25 -0
  65. package/build-ngc/next/streams.js +56 -0
  66. package/build-ngc/next/streams.js.map +1 -0
  67. package/build-ngc/next/types.d.ts +152 -0
  68. package/build-ngc/next/types.js +20 -0
  69. package/build-ngc/next/types.js.map +1 -0
  70. package/build-ngc/next/url-utilities.d.ts +3 -0
  71. package/build-ngc/next/url-utilities.js +63 -0
  72. package/build-ngc/next/url-utilities.js.map +1 -0
  73. package/build-ngc/next/watcher.d.ts +19 -0
  74. package/build-ngc/next/watcher.js +65 -0
  75. package/build-ngc/next/watcher.js.map +1 -0
  76. package/native-link.json +17 -0
  77. package/package.json +154 -0
  78. package/src/angular/index.ts +1 -0
  79. package/src/core/file-system.ts +634 -0
  80. package/src/core/index.ts +2 -0
  81. package/src/core/native-module.ts +99 -0
  82. package/src/core/types.ts +135 -0
  83. package/src/next/directory.ts +83 -0
  84. package/src/next/file.ts +318 -0
  85. package/src/next/index.ts +5 -0
  86. package/src/next/native-module.ts +188 -0
  87. package/src/next/network-tasks.ts +456 -0
  88. package/src/next/node-path.ts +362 -0
  89. package/src/next/path-utilities.ts +144 -0
  90. package/src/next/paths.ts +52 -0
  91. package/src/next/streams-ambient.d.ts +34 -0
  92. package/src/next/streams.ts +81 -0
  93. package/src/next/types.ts +194 -0
  94. package/src/next/url-utilities.ts +70 -0
  95. package/src/next/watcher.ts +106 -0
@@ -0,0 +1,99 @@
1
+ import {
2
+ requireOptionalNativeModule,
3
+ type EventSubscription,
4
+ } from 'expo-modules-core';
5
+
6
+ import type {
7
+ IFileSystemDeletingOptions,
8
+ IFileSystemDownloadOptions,
9
+ IFileSystemDownloadResult,
10
+ IFileSystemFileInfo,
11
+ IFileSystemInfoOptions,
12
+ IFileSystemMakeDirectoryOptions,
13
+ IFileSystemRelocatingOptions,
14
+ IFileSystemRequestDirectoryPermissionsResult,
15
+ IFileSystemUploadOptions,
16
+ IFileSystemUploadResult,
17
+ IFileSystemWritingOptions,
18
+ } from './types';
19
+
20
+ export type INativeFileSystemModule = {
21
+ documentDirectory: string | null;
22
+ cacheDirectory: string | null;
23
+ bundleDirectory: string | null;
24
+
25
+ getInfoAsync?: (
26
+ fileUri: string,
27
+ options: IFileSystemInfoOptions,
28
+ ) => Promise<IFileSystemFileInfo>;
29
+ readAsStringAsync?: (
30
+ fileUri: string,
31
+ options: Record<string, unknown>,
32
+ ) => Promise<string>;
33
+ getContentUriAsync?: (fileUri: string) => Promise<string>;
34
+ writeAsStringAsync?: (
35
+ fileUri: string,
36
+ contents: string,
37
+ options: IFileSystemWritingOptions,
38
+ ) => Promise<void>;
39
+ deleteAsync?: (
40
+ fileUri: string,
41
+ options: IFileSystemDeletingOptions,
42
+ ) => Promise<void>;
43
+ moveAsync?: (options: IFileSystemRelocatingOptions) => Promise<void>;
44
+ copyAsync?: (options: IFileSystemRelocatingOptions) => Promise<void>;
45
+ makeDirectoryAsync?: (
46
+ fileUri: string,
47
+ options: IFileSystemMakeDirectoryOptions,
48
+ ) => Promise<void>;
49
+ readDirectoryAsync?: (fileUri: string) => Promise<string[]>;
50
+ getFreeDiskStorageAsync?: () => Promise<number>;
51
+ getTotalDiskCapacityAsync?: () => Promise<number>;
52
+ downloadAsync?: (
53
+ uri: string,
54
+ fileUri: string,
55
+ options: IFileSystemDownloadOptions,
56
+ ) => Promise<IFileSystemDownloadResult>;
57
+ uploadAsync?: (
58
+ url: string,
59
+ fileUri: string,
60
+ options: IFileSystemUploadOptions,
61
+ ) => Promise<IFileSystemUploadResult>;
62
+ networkTaskCancelAsync?: (uuid: string) => Promise<void>;
63
+ addListener?: (
64
+ eventName: string,
65
+ callback: (event: unknown) => void,
66
+ ) => EventSubscription;
67
+ uploadTaskStartAsync?: (
68
+ url: string,
69
+ fileUri: string,
70
+ uuid: string,
71
+ options: IFileSystemUploadOptions,
72
+ ) => Promise<IFileSystemUploadResult | undefined | null>;
73
+ downloadResumableStartAsync?: (
74
+ url: string,
75
+ fileUri: string,
76
+ uuid: string,
77
+ options: IFileSystemDownloadOptions,
78
+ resumeData?: string,
79
+ ) => Promise<IFileSystemDownloadResult | undefined>;
80
+ downloadResumablePauseAsync?: (
81
+ uuid: string,
82
+ ) => Promise<{ resumeData?: string } | undefined>;
83
+ requestDirectoryPermissionsAsync?: (
84
+ initialFileUrl: string | null,
85
+ ) => Promise<IFileSystemRequestDirectoryPermissionsResult>;
86
+ readSAFDirectoryAsync?: (dirUri: string) => Promise<string[]>;
87
+ makeSAFDirectoryAsync?: (
88
+ parentUri: string,
89
+ dirName: string,
90
+ ) => Promise<string>;
91
+ createSAFFileAsync?: (
92
+ parentUri: string,
93
+ fileName: string,
94
+ mimeType: string,
95
+ ) => Promise<string>;
96
+ };
97
+
98
+ export const expoFileSystem =
99
+ requireOptionalNativeModule<INativeFileSystemModule>('ExponentFileSystem');
@@ -0,0 +1,135 @@
1
+ export enum FileSystemSessionType {
2
+ BACKGROUND = 0,
3
+ FOREGROUND = 1,
4
+ }
5
+
6
+ export enum FileSystemUploadType {
7
+ BINARY_CONTENT = 0,
8
+ MULTIPART = 1,
9
+ }
10
+
11
+ export type IFileSystemDownloadOptions = {
12
+ md5?: boolean;
13
+ cache?: boolean;
14
+ headers?: Record<string, string>;
15
+ sessionType?: FileSystemSessionType;
16
+ };
17
+
18
+ export type IFileSystemHttpResult = {
19
+ headers: Record<string, string>;
20
+ status: number;
21
+ mimeType: string | null;
22
+ };
23
+
24
+ export type IFileSystemDownloadResult = IFileSystemHttpResult & {
25
+ uri: string;
26
+ md5?: string;
27
+ };
28
+
29
+ export type IFileSystemAcceptedUploadHttpMethod = 'POST' | 'PUT' | 'PATCH';
30
+
31
+ export type IFileSystemUploadOptionsBinary = {
32
+ uploadType?: FileSystemUploadType;
33
+ };
34
+
35
+ export type IFileSystemUploadOptionsMultipart = {
36
+ uploadType: FileSystemUploadType;
37
+ fieldName?: string;
38
+ mimeType?: string;
39
+ parameters?: Record<string, string>;
40
+ };
41
+
42
+ export type IFileSystemUploadOptions = (
43
+ IFileSystemUploadOptionsBinary | IFileSystemUploadOptionsMultipart
44
+ ) & {
45
+ headers?: Record<string, string>;
46
+ httpMethod?: IFileSystemAcceptedUploadHttpMethod;
47
+ sessionType?: FileSystemSessionType;
48
+ };
49
+
50
+ export type IFileSystemUploadResult = IFileSystemHttpResult & {
51
+ body: string;
52
+ };
53
+
54
+ export type IFileSystemNetworkTaskProgressCallback<
55
+ T extends IFileSystemDownloadProgressData | IFileSystemUploadProgressData,
56
+ > = (data: T) => void;
57
+
58
+ export type IFileSystemDownloadProgressData = {
59
+ totalBytesWritten: number;
60
+ totalBytesExpectedToWrite: number;
61
+ };
62
+
63
+ export type IFileSystemUploadProgressData = {
64
+ totalBytesSent: number;
65
+ totalBytesExpectedToSend: number;
66
+ };
67
+
68
+ export type IFileSystemDownloadPauseState = {
69
+ url: string;
70
+ fileUri: string;
71
+ options: IFileSystemDownloadOptions;
72
+ resumeData?: string;
73
+ };
74
+
75
+ export type IFileSystemFileInfo =
76
+ | {
77
+ exists: true;
78
+ uri: string;
79
+ size: number;
80
+ isDirectory: boolean;
81
+ modificationTime: number;
82
+ md5?: string;
83
+ }
84
+ | {
85
+ exists: false;
86
+ uri: string;
87
+ isDirectory: false;
88
+ };
89
+
90
+ export enum FileSystemEncodingType {
91
+ UTF8 = 'utf8',
92
+ Base64 = 'base64',
93
+ }
94
+
95
+ export type IFileSystemReadingOptions = {
96
+ encoding?: FileSystemEncodingType | 'utf8' | 'base64';
97
+ position?: number;
98
+ length?: number;
99
+ };
100
+
101
+ export type IFileSystemWritingOptions = {
102
+ encoding?: FileSystemEncodingType | 'utf8' | 'base64';
103
+ append?: boolean;
104
+ };
105
+
106
+ export type IFileSystemDeletingOptions = {
107
+ idempotent?: boolean;
108
+ };
109
+
110
+ export type IFileSystemInfoOptions = {
111
+ md5?: boolean;
112
+ };
113
+
114
+ export type IFileSystemRelocatingOptions = {
115
+ from: string;
116
+ to: string;
117
+ };
118
+
119
+ export type IFileSystemMakeDirectoryOptions = {
120
+ intermediates?: boolean;
121
+ };
122
+
123
+ export type IFileSystemProgressEvent<T> = {
124
+ uuid: string;
125
+ data: T;
126
+ };
127
+
128
+ export type IFileSystemRequestDirectoryPermissionsResult =
129
+ | {
130
+ granted: false;
131
+ }
132
+ | {
133
+ granted: true;
134
+ directoryUri: string;
135
+ };
@@ -0,0 +1,83 @@
1
+ import { File } from './file';
2
+ import { expoFileSystemNext } from './native-module';
3
+ import { Paths } from './paths';
4
+ import { FileSystemWatcher } from './watcher';
5
+ import type {
6
+ IFileSystemWatchEvent,
7
+ IFileSystemWatchOptions,
8
+ IFileSystemWatchSubscription,
9
+ } from './types';
10
+
11
+ /**
12
+ * Represents a directory on the filesystem.
13
+ *
14
+ * A `Directory` instance can be created for any path, and does not need to exist on the
15
+ * filesystem during creation.
16
+ */
17
+ export class Directory extends expoFileSystemNext.FileSystemDirectory {
18
+ static pickDirectoryAsync: (initialUri?: string) => Promise<Directory>;
19
+
20
+ /**
21
+ * Creates an instance of a directory. It can be created for any path, and does not need to
22
+ * exist on the filesystem during creation.
23
+ */
24
+ constructor(...uris: (string | File | Directory)[]) {
25
+ super(Paths.join(...uris));
26
+ this.validatePath();
27
+ }
28
+
29
+ /** Directory containing this directory. */
30
+ get parentDirectory() {
31
+ return new Directory(Paths.join(this.uri, '..'));
32
+ }
33
+
34
+ /**
35
+ * Lists the contents of a directory. Calling this method if the parent directory does not
36
+ * exist will throw an error.
37
+ */
38
+ override list(): (Directory | File)[] {
39
+ return super
40
+ .listAsRecords()
41
+ .map(({ isDirectory, uri }) =>
42
+ isDirectory ? new Directory(uri) : new File(uri),
43
+ );
44
+ }
45
+
46
+ /** Directory name. */
47
+ get name() {
48
+ return Paths.basename(this.uri);
49
+ }
50
+
51
+ override createFile(name: string, mimeType: string | null): File {
52
+ return new File(super.createFile(name, mimeType).uri);
53
+ }
54
+
55
+ override createDirectory(name: string): Directory {
56
+ return new Directory(super.createDirectory(name).uri);
57
+ }
58
+
59
+ /**
60
+ * Watches this directory for changes to its contents or the directory itself. On iOS,
61
+ * child changes are surfaced as a coarse-grained `modified` event on the directory itself, so
62
+ * filtering for child-level `created`, `deleted`, or `renamed` events is not reliable. The
63
+ * watcher automatically stops when the directory is deleted or renamed. To stop watching
64
+ * manually, call `remove()` on the returned subscription.
65
+ */
66
+ watch(
67
+ callback: (event: IFileSystemWatchEvent<File | Directory>) => void,
68
+ options?: IFileSystemWatchOptions,
69
+ ): IFileSystemWatchSubscription {
70
+ return new FileSystemWatcher<File | Directory>(
71
+ this.uri,
72
+ callback,
73
+ options,
74
+ (uri, isDirectory) => (isDirectory ? new Directory(uri) : new File(uri)),
75
+ );
76
+ }
77
+ }
78
+
79
+ Directory.pickDirectoryAsync = async function (initialUri?: string) {
80
+ const directory = (await expoFileSystemNext.pickDirectoryAsync(initialUri))
81
+ .uri;
82
+ return new Directory(directory);
83
+ };
@@ -0,0 +1,318 @@
1
+ import { uuid, type EventSubscription } from 'expo-modules-core';
2
+
3
+ import { Directory } from './directory';
4
+ import { expoFileSystemNext } from './native-module';
5
+ import { DownloadTask, UploadTask } from './network-tasks';
6
+ import { Paths } from './paths';
7
+ import {
8
+ FileSystemReadableStreamSource,
9
+ FileSystemWritableSink,
10
+ } from './streams';
11
+ import { FileSystemWatcher } from './watcher';
12
+ import {
13
+ FileMode,
14
+ type IFileSystemDownloadOptions,
15
+ type IFileSystemDownloadProgress,
16
+ type IFileSystemDownloadTaskOptions,
17
+ type IFileSystemUploadOptions,
18
+ type IFileSystemUploadResult,
19
+ type IFileSystemWatchEvent,
20
+ type IFileSystemWatchOptions,
21
+ type IFileSystemWatchSubscription,
22
+ type IPickFileOptions,
23
+ type IPickMultipleFilesOptions,
24
+ type IPickMultipleFilesResult,
25
+ type IPickSingleFileOptions,
26
+ type IPickSingleFileResult,
27
+ } from './types';
28
+
29
+ function createAbortError(reason?: unknown): Error {
30
+ const message =
31
+ typeof reason === 'string' ? reason : 'The operation was aborted.';
32
+ const error = new Error(message);
33
+ error.name = 'AbortError';
34
+ return error;
35
+ }
36
+
37
+ function parsePickFileOptions(
38
+ initialUriOrOptions?: string | IPickFileOptions,
39
+ mimeType?: string,
40
+ ): { options: IPickFileOptions; usingOldAPI: boolean } {
41
+ if (typeof initialUriOrOptions === 'object') {
42
+ return { options: initialUriOrOptions, usingOldAPI: false };
43
+ }
44
+ return {
45
+ options: {
46
+ initialUri: initialUriOrOptions,
47
+ mimeTypes: mimeType,
48
+ multipleFiles: false,
49
+ },
50
+ usingOldAPI:
51
+ mimeType !== undefined || typeof initialUriOrOptions === 'string',
52
+ };
53
+ }
54
+
55
+ /**
56
+ * Represents a file on the filesystem.
57
+ *
58
+ * A `File` instance can be created for any path, and does not need to exist on the filesystem
59
+ * during creation. The constructor accepts an array of strings that are joined to create the
60
+ * file URI. The first argument can also be a `Directory` instance (like `Paths.cache`) or a
61
+ * `File` instance (which creates a new reference to the same file).
62
+ */
63
+ export class File extends expoFileSystemNext.FileSystemFile {
64
+ /**
65
+ * Downloads a file from the network.
66
+ *
67
+ * On Android, the response body streams directly into the target file. If the download fails
68
+ * after it starts, a partially written file may remain at the destination. On iOS, the
69
+ * download first completes in a temporary location and the file is moved into place only
70
+ * after success, so no file is left behind when the request fails.
71
+ */
72
+ static downloadFileAsync: (
73
+ url: string,
74
+ destination: Directory | File,
75
+ options?: IFileSystemDownloadOptions,
76
+ ) => Promise<File>;
77
+
78
+ /**
79
+ * Opens the system file picker for selecting a single file.
80
+ */
81
+ static pickFileAsync(
82
+ options?: IPickSingleFileOptions,
83
+ ): Promise<IPickSingleFileResult>;
84
+ /**
85
+ * Opens the system file picker for selecting multiple files.
86
+ */
87
+ static pickFileAsync(
88
+ options?: IPickMultipleFilesOptions,
89
+ ): Promise<IPickMultipleFilesResult>;
90
+ /**
91
+ * @deprecated Use `pickFileAsync({initialUri, mimeTypes: mimeType})` instead.
92
+ */
93
+ static pickFileAsync(
94
+ initialUri?: string,
95
+ mimeType?: string,
96
+ ): Promise<File | File[]>;
97
+ static async pickFileAsync(
98
+ initialUriOrOptions?: string | IPickFileOptions,
99
+ mimeType?: string,
100
+ ): Promise<File | File[] | IPickSingleFileResult | IPickMultipleFilesResult> {
101
+ const { options, usingOldAPI } = parsePickFileOptions(
102
+ initialUriOrOptions,
103
+ mimeType,
104
+ );
105
+ try {
106
+ if (options.multipleFiles) {
107
+ const files = await expoFileSystemNext.pickFileAsync(options);
108
+ return {
109
+ result: files.map(file => new File(file.uri)),
110
+ canceled: false,
111
+ };
112
+ }
113
+ const file = await expoFileSystemNext.pickFileAsync(options);
114
+ if (usingOldAPI) {
115
+ return new File(file.uri);
116
+ }
117
+ return {
118
+ result: new File(file.uri),
119
+ canceled: false,
120
+ };
121
+ } catch (error) {
122
+ if (usingOldAPI) {
123
+ throw error;
124
+ }
125
+ return {
126
+ result: null,
127
+ canceled: true,
128
+ };
129
+ }
130
+ }
131
+
132
+ /**
133
+ * Creates an instance of a file. It can be created for any path, and does not need to exist on
134
+ * the filesystem during creation.
135
+ */
136
+ constructor(...uris: (string | File | Directory)[]) {
137
+ super(Paths.join(...uris));
138
+ this.validatePath();
139
+ }
140
+
141
+ /** Directory containing the file. */
142
+ get parentDirectory() {
143
+ return new Directory(Paths.dirname(this.uri));
144
+ }
145
+
146
+ /** File extension, e.g. `.png`. */
147
+ get extension() {
148
+ return Paths.extname(this.uri);
149
+ }
150
+
151
+ /** File name. Includes the extension. */
152
+ get name() {
153
+ return Paths.basename(this.uri);
154
+ }
155
+
156
+ readableStream() {
157
+ return new ReadableStream(
158
+ new FileSystemReadableStreamSource(super.open(FileMode.ReadOnly)),
159
+ );
160
+ }
161
+
162
+ writableStream() {
163
+ return new WritableStream<Uint8Array>(
164
+ new FileSystemWritableSink(super.open(FileMode.WriteOnly)),
165
+ );
166
+ }
167
+
168
+ async arrayBuffer(): Promise<ArrayBuffer> {
169
+ const bytes = await this.bytes();
170
+ return bytes.buffer;
171
+ }
172
+
173
+ async json(): Promise<unknown> {
174
+ return JSON.parse(await this.text());
175
+ }
176
+
177
+ async formData(): ReturnType<Response['formData']> {
178
+ return new Response(await this.arrayBuffer(), {
179
+ headers: this.type ? { 'Content-Type': this.type } : undefined,
180
+ }).formData();
181
+ }
182
+
183
+ stream(): ReadableStream<Uint8Array<ArrayBuffer>> {
184
+ return this.readableStream();
185
+ }
186
+
187
+ slice(start?: number, end?: number, contentType?: string): Blob {
188
+ // React Native's own ambient `Blob` declaration (react-native/src/types/globals.d.ts) is
189
+ // narrower than its runtime: `blobParts` is typed as `(Blob | string)[]` only (the real
190
+ // native implementation accepts raw bytes), and `BlobOptions` requires both `type` and
191
+ // `lastModified` even though `lastModified` is a `File`-only concept the Web `Blob` spec
192
+ // doesn't have at all. Cast at this one call — RN's declared contract, not our logic, is
193
+ // what's incomplete; `lastModified` is supplied only to satisfy the type and is not read by
194
+ // any real `Blob` implementation.
195
+ const bytes = this.bytesSync().slice(start, end) as unknown as string;
196
+ return new Blob([bytes], {
197
+ type: contentType ?? '',
198
+ lastModified: Date.now(),
199
+ });
200
+ }
201
+
202
+ /**
203
+ * Uploads this file to a server and starts the request immediately.
204
+ */
205
+ upload(
206
+ url: string,
207
+ options?: IFileSystemUploadOptions,
208
+ ): Promise<IFileSystemUploadResult> {
209
+ return new UploadTask(this, url, options).uploadAsync();
210
+ }
211
+
212
+ /**
213
+ * Creates an upload task for this file without starting it.
214
+ */
215
+ createUploadTask(
216
+ url: string,
217
+ options?: IFileSystemUploadOptions,
218
+ ): UploadTask {
219
+ return new UploadTask(this, url, options);
220
+ }
221
+
222
+ /**
223
+ * Creates a download task without starting it.
224
+ */
225
+ static createDownloadTask(
226
+ url: string,
227
+ destination: File | Directory,
228
+ options?: IFileSystemDownloadTaskOptions,
229
+ ): DownloadTask {
230
+ return new DownloadTask(url, destination, options);
231
+ }
232
+
233
+ /**
234
+ * Watches this file for changes on the filesystem. The watcher automatically stops when the
235
+ * file is deleted or renamed. To stop watching manually, call `remove()` on the returned
236
+ * subscription.
237
+ */
238
+ watch(
239
+ callback: (event: IFileSystemWatchEvent<File>) => void,
240
+ options?: IFileSystemWatchOptions,
241
+ ): IFileSystemWatchSubscription {
242
+ return new FileSystemWatcher<File>(
243
+ this.uri,
244
+ callback,
245
+ options,
246
+ uri => new File(uri),
247
+ );
248
+ }
249
+ }
250
+
251
+ // Cannot use `static` keyword in class declaration because of a runtime error (matches upstream's
252
+ // own comment — assigning after the class body avoids whatever native interop issue upstream ran
253
+ // into with `SharedObject`-extending classes).
254
+ File.downloadFileAsync = async function downloadFileAsync(
255
+ url: string,
256
+ to: File | Directory,
257
+ options?: IFileSystemDownloadOptions,
258
+ ) {
259
+ const needsUuid = options?.onProgress || options?.signal;
260
+ const downloadUuid = needsUuid ? uuid.v4() : undefined;
261
+
262
+ let subscription: EventSubscription | undefined;
263
+ let abortHandler: (() => void) | undefined;
264
+ let lastProgress: IFileSystemDownloadProgress | undefined;
265
+
266
+ try {
267
+ if (options?.signal?.aborted) {
268
+ throw createAbortError(options.signal.reason);
269
+ }
270
+
271
+ if (downloadUuid && options?.onProgress) {
272
+ subscription = expoFileSystemNext.addListener(
273
+ 'downloadProgress',
274
+ event => {
275
+ if (event.uuid === downloadUuid) {
276
+ lastProgress = event.data;
277
+ options.onProgress!(lastProgress);
278
+ }
279
+ },
280
+ );
281
+ }
282
+
283
+ if (downloadUuid && options?.signal) {
284
+ abortHandler = () => {
285
+ expoFileSystemNext.cancelDownloadAsync(downloadUuid);
286
+ };
287
+ options.signal.addEventListener('abort', abortHandler, { once: true });
288
+ }
289
+
290
+ const outputURI = await expoFileSystemNext.downloadFileAsync(
291
+ url,
292
+ to,
293
+ options,
294
+ downloadUuid,
295
+ );
296
+ const file = new File(outputURI);
297
+ const fileSize = file.size ?? 0;
298
+ if (
299
+ options?.onProgress &&
300
+ fileSize > 0 &&
301
+ (lastProgress?.bytesWritten !== fileSize ||
302
+ lastProgress?.totalBytes !== fileSize)
303
+ ) {
304
+ options.onProgress({ bytesWritten: fileSize, totalBytes: fileSize });
305
+ }
306
+ return file;
307
+ } catch (error) {
308
+ if (options?.signal?.aborted) {
309
+ throw createAbortError(options.signal.reason);
310
+ }
311
+ throw error;
312
+ } finally {
313
+ subscription?.remove();
314
+ if (abortHandler && options?.signal) {
315
+ options.signal.removeEventListener('abort', abortHandler);
316
+ }
317
+ }
318
+ };
@@ -0,0 +1,5 @@
1
+ export { File } from './file';
2
+ export { Directory } from './directory';
3
+ export { Paths } from './paths';
4
+ export { UploadTask, DownloadTask } from './network-tasks';
5
+ export * from './types';