@themartiancompany/opfs 1.8.11
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/COPYING +674 -0
- package/README.cn.md +301 -0
- package/README.md +241 -0
- package/dist/main.cjs +1840 -0
- package/dist/main.cjs.map +1 -0
- package/dist/main.mjs +1751 -0
- package/dist/main.mjs.map +1 -0
- package/dist/types.d.ts +920 -0
- package/dist/types.d.ts.map +1 -0
- package/docs/README.md +115 -0
- package/docs/classes/SyncMessenger.md +42 -0
- package/docs/functions/appendFile.md +29 -0
- package/docs/functions/appendFileSync.md +26 -0
- package/docs/functions/assertAbsolutePath.md +29 -0
- package/docs/functions/assertFileUrl.md +29 -0
- package/docs/functions/connectSyncAgent.md +25 -0
- package/docs/functions/copy.md +35 -0
- package/docs/functions/copySync.md +30 -0
- package/docs/functions/createFile.md +27 -0
- package/docs/functions/createFileSync.md +25 -0
- package/docs/functions/deleteTemp.md +23 -0
- package/docs/functions/deleteTempSync.md +19 -0
- package/docs/functions/downloadFile.md +58 -0
- package/docs/functions/emptyDir.md +28 -0
- package/docs/functions/emptyDirSync.md +25 -0
- package/docs/functions/exists.md +29 -0
- package/docs/functions/existsSync.md +26 -0
- package/docs/functions/generateTempPath.md +27 -0
- package/docs/functions/getFileDataByHandle.md +27 -0
- package/docs/functions/getSyncMessenger.md +22 -0
- package/docs/functions/isDirectoryHandle.md +27 -0
- package/docs/functions/isFileHandle.md +27 -0
- package/docs/functions/isFileHandleLike.md +27 -0
- package/docs/functions/isOPFSSupported.md +21 -0
- package/docs/functions/isTempPath.md +27 -0
- package/docs/functions/mkTemp.md +28 -0
- package/docs/functions/mkTempSync.md +25 -0
- package/docs/functions/mkdir.md +27 -0
- package/docs/functions/mkdirSync.md +25 -0
- package/docs/functions/move.md +34 -0
- package/docs/functions/moveSync.md +30 -0
- package/docs/functions/pruneTemp.md +28 -0
- package/docs/functions/pruneTempSync.md +25 -0
- package/docs/functions/readBlobFile.md +28 -0
- package/docs/functions/readBlobFileSync.md +25 -0
- package/docs/functions/readDir.md +28 -0
- package/docs/functions/readDirSync.md +26 -0
- package/docs/functions/readFile.md +132 -0
- package/docs/functions/readFileSync.md +70 -0
- package/docs/functions/readJsonFile.md +35 -0
- package/docs/functions/readJsonFileSync.md +31 -0
- package/docs/functions/readTextFile.md +28 -0
- package/docs/functions/readTextFileSync.md +25 -0
- package/docs/functions/remove.md +27 -0
- package/docs/functions/removeSync.md +25 -0
- package/docs/functions/setSyncMessenger.md +26 -0
- package/docs/functions/startSyncAgent.md +21 -0
- package/docs/functions/stat.md +27 -0
- package/docs/functions/statSync.md +25 -0
- package/docs/functions/toFileSystemHandleLike.md +27 -0
- package/docs/functions/unzip.md +32 -0
- package/docs/functions/unzipFromUrl.md +36 -0
- package/docs/functions/unzipSync.md +26 -0
- package/docs/functions/uploadFile.md +33 -0
- package/docs/functions/writeFile.md +32 -0
- package/docs/functions/writeFileSync.md +30 -0
- package/docs/functions/zip.md +65 -0
- package/docs/functions/zipFromUrl.md +63 -0
- package/docs/functions/zipSync.md +55 -0
- package/docs/interfaces/CopyOptions.md +17 -0
- package/docs/interfaces/DownloadFileTempResponse.md +18 -0
- package/docs/interfaces/ErrorLike.md +18 -0
- package/docs/interfaces/ExistsOptions.md +18 -0
- package/docs/interfaces/FileLike.md +21 -0
- package/docs/interfaces/FileSystemFileHandleLike.md +25 -0
- package/docs/interfaces/FileSystemHandleLike.md +22 -0
- package/docs/interfaces/MoveOptions.md +17 -0
- package/docs/interfaces/ReadDirEntry.md +18 -0
- package/docs/interfaces/ReadDirEntrySync.md +18 -0
- package/docs/interfaces/ReadDirOptions.md +17 -0
- package/docs/interfaces/ReadOptions.md +17 -0
- package/docs/interfaces/SyncAgentOptions.md +19 -0
- package/docs/interfaces/TempOptions.md +19 -0
- package/docs/interfaces/UploadRequestInit.md +21 -0
- package/docs/interfaces/WriteOptions.md +19 -0
- package/docs/interfaces/ZipOptions.md +17 -0
- package/docs/type-aliases/FileEncoding.md +15 -0
- package/docs/type-aliases/FsRequestInit.md +15 -0
- package/docs/type-aliases/ReadFileContent.md +15 -0
- package/docs/type-aliases/WriteFileContent.md +15 -0
- package/docs/type-aliases/WriteSyncFileContent.md +16 -0
- package/docs/variables/CURRENT_DIR.md +15 -0
- package/docs/variables/NOT_FOUND_ERROR.md +18 -0
- package/docs/variables/ROOT_DIR.md +15 -0
- package/docs/variables/TMP_DIR.md +15 -0
- package/package.json +141 -0
- package/src/fs/assertions.ts +63 -0
- package/src/fs/constants.ts +63 -0
- package/src/fs/defines.ts +352 -0
- package/src/fs/helpers.ts +338 -0
- package/src/fs/opfs_core.ts +413 -0
- package/src/fs/opfs_download.ts +174 -0
- package/src/fs/opfs_ext.ts +504 -0
- package/src/fs/opfs_tmp.ts +131 -0
- package/src/fs/opfs_unzip.ts +168 -0
- package/src/fs/opfs_upload.ts +126 -0
- package/src/fs/opfs_zip.ts +314 -0
- package/src/fs/support.ts +36 -0
- package/src/fs/utils.ts +176 -0
- package/src/mod.ts +41 -0
- package/src/worker/helpers.ts +168 -0
- package/src/worker/opfs_worker.ts +298 -0
- package/src/worker/opfs_worker_adapter.ts +666 -0
- package/src/worker/shared.ts +400 -0
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
/** ----------------------------------------------------------------------
|
|
4
|
+
* Copyright ©
|
|
5
|
+
* Jiang Jie
|
|
6
|
+
* 2024, 2025
|
|
7
|
+
* Pellegrino Prevete
|
|
8
|
+
* 2025
|
|
9
|
+
*
|
|
10
|
+
* All rights reserved
|
|
11
|
+
* ----------------------------------------------------------------------
|
|
12
|
+
*
|
|
13
|
+
* This program is free software: you can redistribute it and/or modify
|
|
14
|
+
* it under the terms of the GNU General Public License as published by
|
|
15
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
16
|
+
* (at your option) any later version.
|
|
17
|
+
*
|
|
18
|
+
* This program is distributed in the hope that it will be useful,
|
|
19
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21
|
+
* GNU General Public License for more details.
|
|
22
|
+
*
|
|
23
|
+
* You should have received a copy of the GNU General Public License
|
|
24
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { basename,
|
|
28
|
+
dirname,
|
|
29
|
+
join } from '@std/path/posix';
|
|
30
|
+
import { Err,
|
|
31
|
+
Ok,
|
|
32
|
+
RESULT_VOID,
|
|
33
|
+
type AsyncIOResult,
|
|
34
|
+
type AsyncVoidIOResult } from 'happy-rusty';
|
|
35
|
+
import { assertAbsolutePath } from './assertions.ts';
|
|
36
|
+
import { NOT_FOUND_ERROR } from './constants.ts';
|
|
37
|
+
import type { ReadDirEntry,
|
|
38
|
+
ReadDirOptions,
|
|
39
|
+
ReadFileContent,
|
|
40
|
+
ReadOptions,
|
|
41
|
+
WriteFileContent,
|
|
42
|
+
WriteOptions } from './defines.ts';
|
|
43
|
+
import { getDirHandle,
|
|
44
|
+
getFileHandle,
|
|
45
|
+
isNotFoundError,
|
|
46
|
+
isRootPath } from './helpers.ts';
|
|
47
|
+
import { isDirectoryHandle } from './utils.ts';
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Creates a new file at the specified path same as `touch`.
|
|
51
|
+
*
|
|
52
|
+
* @param filePath - The path of the file to create.
|
|
53
|
+
* @returns A promise that resolves to an `AsyncIOResult` indicating whether the file was successfully created.
|
|
54
|
+
*/
|
|
55
|
+
export async function
|
|
56
|
+
createFile(
|
|
57
|
+
filePath:
|
|
58
|
+
string):
|
|
59
|
+
AsyncVoidIOResult {
|
|
60
|
+
assertAbsolutePath(
|
|
61
|
+
filePath);
|
|
62
|
+
const
|
|
63
|
+
fileHandleRes =
|
|
64
|
+
await getFileHandle(
|
|
65
|
+
filePath,
|
|
66
|
+
{ create:
|
|
67
|
+
true });
|
|
68
|
+
return fileHandleRes.and(
|
|
69
|
+
RESULT_VOID);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Creates a new directory at the specified path same as `mkdir -p`.
|
|
74
|
+
*
|
|
75
|
+
* @param dirPath - The path where the new directory will be created.
|
|
76
|
+
* @returns A promise that resolves to an `AsyncIOResult` indicating whether the directory was successfully created.
|
|
77
|
+
*/
|
|
78
|
+
export async function
|
|
79
|
+
mkdir(
|
|
80
|
+
dirPath:
|
|
81
|
+
string):
|
|
82
|
+
AsyncVoidIOResult {
|
|
83
|
+
assertAbsolutePath(
|
|
84
|
+
dirPath);
|
|
85
|
+
const
|
|
86
|
+
dirHandleRes =
|
|
87
|
+
await getDirHandle(
|
|
88
|
+
dirPath,
|
|
89
|
+
{ create:
|
|
90
|
+
true,
|
|
91
|
+
});
|
|
92
|
+
return dirHandleRes.and(
|
|
93
|
+
RESULT_VOID);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Reads the contents of a directory at the specified path.
|
|
98
|
+
*
|
|
99
|
+
* @param dirPath - The path of the directory to read.
|
|
100
|
+
* @param options - Options of readdir.
|
|
101
|
+
* @returns A promise that resolves to an `AsyncIOResult` containing an async iterable iterator over the entries of the directory.
|
|
102
|
+
*/
|
|
103
|
+
export async function
|
|
104
|
+
readDir(
|
|
105
|
+
dirPath:
|
|
106
|
+
string,
|
|
107
|
+
options?:
|
|
108
|
+
ReadDirOptions):
|
|
109
|
+
AsyncIOResult<AsyncIterableIterator<ReadDirEntry>> {
|
|
110
|
+
assertAbsolutePath(
|
|
111
|
+
dirPath);
|
|
112
|
+
const
|
|
113
|
+
dirHandleRes =
|
|
114
|
+
await getDirHandle(
|
|
115
|
+
dirPath);
|
|
116
|
+
async function*
|
|
117
|
+
read(
|
|
118
|
+
dirHandle:
|
|
119
|
+
FileSystemDirectoryHandle,
|
|
120
|
+
subDirPath:
|
|
121
|
+
string):
|
|
122
|
+
AsyncIterableIterator<ReadDirEntry> {
|
|
123
|
+
const
|
|
124
|
+
entries =
|
|
125
|
+
dirHandle.entries();
|
|
126
|
+
for await ( const [ name,
|
|
127
|
+
handle ] of entries) {
|
|
128
|
+
// relative path from `dirPath`
|
|
129
|
+
const
|
|
130
|
+
path =
|
|
131
|
+
subDirPath === dirPath ? name : join(
|
|
132
|
+
subDirPath,
|
|
133
|
+
name);
|
|
134
|
+
yield {
|
|
135
|
+
path,
|
|
136
|
+
handle,
|
|
137
|
+
};
|
|
138
|
+
if ( isDirectoryHandle(
|
|
139
|
+
handle) && options?.recursive ) {
|
|
140
|
+
yield* read(
|
|
141
|
+
await dirHandle.getDirectoryHandle(
|
|
142
|
+
name),
|
|
143
|
+
path);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return dirHandleRes.andThen(
|
|
148
|
+
x => Ok(
|
|
149
|
+
read(
|
|
150
|
+
x,
|
|
151
|
+
dirPath)));
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Reads the content of a file at the specified path as a File.
|
|
156
|
+
*
|
|
157
|
+
* @param filePath - The path of the file to read.
|
|
158
|
+
* @param options - Read options specifying the 'blob' encoding.
|
|
159
|
+
* @returns A promise that resolves to an `AsyncIOResult` containing the file content as a File.
|
|
160
|
+
*/
|
|
161
|
+
export function
|
|
162
|
+
readFile(
|
|
163
|
+
filePath:
|
|
164
|
+
string,
|
|
165
|
+
options:
|
|
166
|
+
ReadOptions & { encoding:
|
|
167
|
+
'blob'; }):
|
|
168
|
+
AsyncIOResult<File>;
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Reads the content of a file at the specified path as a string.
|
|
172
|
+
*
|
|
173
|
+
* @param filePath - The path of the file to read.
|
|
174
|
+
* @param options - Read options specifying the 'utf8' encoding.
|
|
175
|
+
* @returns A promise that resolves to an `AsyncIOResult` containing the file content as a string.
|
|
176
|
+
*/
|
|
177
|
+
export function
|
|
178
|
+
readFile(
|
|
179
|
+
filePath:
|
|
180
|
+
string,
|
|
181
|
+
options:
|
|
182
|
+
ReadOptions & { encoding:
|
|
183
|
+
'utf8'; }):
|
|
184
|
+
AsyncIOResult<string>;
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Reads the content of a file at the specified path as an ArrayBuffer by default.
|
|
188
|
+
*
|
|
189
|
+
* @param filePath - The path of the file to read.
|
|
190
|
+
* @param options - Read options specifying the 'binary' encoding.
|
|
191
|
+
* @returns A promise that resolves to an `AsyncIOResult` containing the file content as an ArrayBuffer.
|
|
192
|
+
*/
|
|
193
|
+
export function
|
|
194
|
+
readFile(
|
|
195
|
+
filePath:
|
|
196
|
+
string,
|
|
197
|
+
options?:
|
|
198
|
+
ReadOptions & { encoding:
|
|
199
|
+
'binary'; }):
|
|
200
|
+
AsyncIOResult<ArrayBuffer>;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Reads the content of a file at the specified path with the specified options.
|
|
204
|
+
*
|
|
205
|
+
* @template T The type of the content to read from the file.
|
|
206
|
+
* @param filePath - The path of the file to read.
|
|
207
|
+
* @param options - Optional read options.
|
|
208
|
+
* @returns A promise that resolves to an `AsyncIOResult` containing the file content.
|
|
209
|
+
*/
|
|
210
|
+
export async function
|
|
211
|
+
readFile<T extends ReadFileContent>(
|
|
212
|
+
filePath:
|
|
213
|
+
string,
|
|
214
|
+
options?:
|
|
215
|
+
ReadOptions):
|
|
216
|
+
AsyncIOResult<T> {
|
|
217
|
+
assertAbsolutePath(
|
|
218
|
+
filePath);
|
|
219
|
+
const
|
|
220
|
+
fileHandleRes =
|
|
221
|
+
await getFileHandle(
|
|
222
|
+
filePath);
|
|
223
|
+
return fileHandleRes.andThenAsync(
|
|
224
|
+
async fileHandle => {
|
|
225
|
+
const
|
|
226
|
+
file =
|
|
227
|
+
await fileHandle.getFile();
|
|
228
|
+
switch ( options?.encoding ) {
|
|
229
|
+
case 'blob': {
|
|
230
|
+
return Ok(file as unknown as T);
|
|
231
|
+
}
|
|
232
|
+
case 'utf8': {
|
|
233
|
+
const
|
|
234
|
+
text =
|
|
235
|
+
await file.text();
|
|
236
|
+
return Ok(text as unknown as T);
|
|
237
|
+
}
|
|
238
|
+
default: {
|
|
239
|
+
const
|
|
240
|
+
data =
|
|
241
|
+
await file.arrayBuffer();
|
|
242
|
+
return Ok(data as unknown as T);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Removes a file or directory at the specified path same as `rm -rf`.
|
|
250
|
+
*
|
|
251
|
+
* @param path - The path of the file or directory to remove.
|
|
252
|
+
* @returns A promise that resolves to an `AsyncIOResult` indicating whether the file or directory was successfully removed.
|
|
253
|
+
*/
|
|
254
|
+
export async function
|
|
255
|
+
remove(
|
|
256
|
+
path:
|
|
257
|
+
string):
|
|
258
|
+
AsyncVoidIOResult {
|
|
259
|
+
assertAbsolutePath(
|
|
260
|
+
path);
|
|
261
|
+
const
|
|
262
|
+
dirPath =
|
|
263
|
+
dirname(
|
|
264
|
+
path);
|
|
265
|
+
const
|
|
266
|
+
childName =
|
|
267
|
+
basename(
|
|
268
|
+
path);
|
|
269
|
+
const
|
|
270
|
+
dirHandleRes =
|
|
271
|
+
await getDirHandle(
|
|
272
|
+
dirPath);
|
|
273
|
+
return ( await dirHandleRes.andThenAsync(
|
|
274
|
+
async (
|
|
275
|
+
dirHandle):
|
|
276
|
+
AsyncVoidIOResult => {
|
|
277
|
+
try {
|
|
278
|
+
// root
|
|
279
|
+
if ( isRootPath(
|
|
280
|
+
dirPath) &&
|
|
281
|
+
isRootPath(
|
|
282
|
+
childName) ) {
|
|
283
|
+
// TODO ts not support yet
|
|
284
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
285
|
+
await ( dirHandle as any ).remove(
|
|
286
|
+
{ recursive:
|
|
287
|
+
true });
|
|
288
|
+
} else {
|
|
289
|
+
await dirHandle.removeEntry(
|
|
290
|
+
childName,
|
|
291
|
+
{ recursive:
|
|
292
|
+
true });
|
|
293
|
+
}
|
|
294
|
+
} catch (
|
|
295
|
+
_error) {
|
|
296
|
+
return Err(_error as DOMException);
|
|
297
|
+
}
|
|
298
|
+
return RESULT_VOID;
|
|
299
|
+
})).orElse<Error>(
|
|
300
|
+
_error => {
|
|
301
|
+
// not found as success
|
|
302
|
+
return isNotFoundError(
|
|
303
|
+
_error) ? RESULT_VOID : Err(
|
|
304
|
+
_error);
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* Retrieves the status of a file or directory at the specified path.
|
|
310
|
+
*
|
|
311
|
+
* @param path - The path of the file or directory to retrieve status for.
|
|
312
|
+
* @returns A promise that resolves to an `AsyncIOResult` containing the `FileSystemHandle`.
|
|
313
|
+
*/
|
|
314
|
+
export async function
|
|
315
|
+
stat(
|
|
316
|
+
path:
|
|
317
|
+
string):
|
|
318
|
+
AsyncIOResult<FileSystemHandle> {
|
|
319
|
+
assertAbsolutePath(
|
|
320
|
+
path);
|
|
321
|
+
const
|
|
322
|
+
dirPath =
|
|
323
|
+
dirname(
|
|
324
|
+
path);
|
|
325
|
+
const
|
|
326
|
+
childName =
|
|
327
|
+
basename(
|
|
328
|
+
path);
|
|
329
|
+
const
|
|
330
|
+
dirHandleRes =
|
|
331
|
+
await getDirHandle(
|
|
332
|
+
dirPath);
|
|
333
|
+
if ( !childName ) {
|
|
334
|
+
// root
|
|
335
|
+
return dirHandleRes;
|
|
336
|
+
}
|
|
337
|
+
return dirHandleRes.andThenAsync(
|
|
338
|
+
async dirHandle => {
|
|
339
|
+
// currently only rely on traversal inspection
|
|
340
|
+
for await ( const [ name,
|
|
341
|
+
handle] of dirHandle.entries() ) {
|
|
342
|
+
if ( name === childName ) {
|
|
343
|
+
return Ok(
|
|
344
|
+
handle);
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
const
|
|
348
|
+
err =
|
|
349
|
+
new Error(
|
|
350
|
+
`${ NOT_FOUND_ERROR }: '${ childName }' ` +
|
|
351
|
+
`does not exist. Full path is '${ path }'.`);
|
|
352
|
+
err.name =
|
|
353
|
+
NOT_FOUND_ERROR;
|
|
354
|
+
return Err(
|
|
355
|
+
err);
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
/**
|
|
360
|
+
* Writes content to a file at the specified path.
|
|
361
|
+
*
|
|
362
|
+
* @param filePath - The path of the file to write to.
|
|
363
|
+
* @param contents - The content to write to the file.
|
|
364
|
+
* @param options - Optional write options.
|
|
365
|
+
* @returns A promise that resolves to an `AsyncIOResult` indicating whether the file was successfully written.
|
|
366
|
+
*/
|
|
367
|
+
export async function
|
|
368
|
+
writeFile(
|
|
369
|
+
filePath:
|
|
370
|
+
string,
|
|
371
|
+
contents:
|
|
372
|
+
WriteFileContent,
|
|
373
|
+
options?:
|
|
374
|
+
WriteOptions):
|
|
375
|
+
AsyncVoidIOResult {
|
|
376
|
+
assertAbsolutePath(
|
|
377
|
+
filePath);
|
|
378
|
+
// create as default
|
|
379
|
+
const { append = false, create = true } = options ?? {};
|
|
380
|
+
const
|
|
381
|
+
fileHandleRes =
|
|
382
|
+
await getFileHandle(
|
|
383
|
+
filePath,
|
|
384
|
+
{ create });
|
|
385
|
+
return fileHandleRes.andThenAsync(
|
|
386
|
+
async fileHandle => {
|
|
387
|
+
const
|
|
388
|
+
writable =
|
|
389
|
+
await fileHandle.createWritable(
|
|
390
|
+
{ keepExistingData:
|
|
391
|
+
append });
|
|
392
|
+
const
|
|
393
|
+
params:
|
|
394
|
+
WriteParams =
|
|
395
|
+
{ type:
|
|
396
|
+
'write',
|
|
397
|
+
data:
|
|
398
|
+
contents,
|
|
399
|
+
};
|
|
400
|
+
// append?
|
|
401
|
+
if ( append ) {
|
|
402
|
+
const
|
|
403
|
+
{ size } =
|
|
404
|
+
await fileHandle.getFile();
|
|
405
|
+
params.position =
|
|
406
|
+
size;
|
|
407
|
+
}
|
|
408
|
+
await writable.write(
|
|
409
|
+
params);
|
|
410
|
+
await writable.close();
|
|
411
|
+
return RESULT_VOID;
|
|
412
|
+
});
|
|
413
|
+
}
|
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
2
|
+
|
|
3
|
+
/** ----------------------------------------------------------------------
|
|
4
|
+
* Copyright ©
|
|
5
|
+
* Jiang Jie
|
|
6
|
+
* 2024, 2025
|
|
7
|
+
* Pellegrino Prevete
|
|
8
|
+
* 2025
|
|
9
|
+
*
|
|
10
|
+
* All rights reserved
|
|
11
|
+
* ----------------------------------------------------------------------
|
|
12
|
+
*
|
|
13
|
+
* This program is free software: you can redistribute it and/or modify
|
|
14
|
+
* it under the terms of the GNU General Public License as published by
|
|
15
|
+
* the Free Software Foundation, either version 3 of the License, or
|
|
16
|
+
* (at your option) any later version.
|
|
17
|
+
*
|
|
18
|
+
* This program is distributed in the hope that it will be useful,
|
|
19
|
+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
20
|
+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
21
|
+
* GNU General Public License for more details.
|
|
22
|
+
*
|
|
23
|
+
* You should have received a copy of the GNU General Public License
|
|
24
|
+
* along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { fetchT,
|
|
28
|
+
type FetchResponse,
|
|
29
|
+
type FetchTask } from '@happy-ts/fetch-t';
|
|
30
|
+
import { extname } from '@std/path/posix';
|
|
31
|
+
import { Err,
|
|
32
|
+
Ok } from 'happy-rusty';
|
|
33
|
+
import { assertAbsolutePath,
|
|
34
|
+
assertFileUrl } from './assertions.ts';
|
|
35
|
+
import type { DownloadFileTempResponse,
|
|
36
|
+
FsRequestInit } from './defines.ts';
|
|
37
|
+
import { createAbortError } from './helpers.ts';
|
|
38
|
+
import { writeFile } from './opfs_core.ts';
|
|
39
|
+
import { generateTempPath } from './utils.ts';
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Downloads a file from a URL and saves it to a temporary file.
|
|
43
|
+
* The returned response will contain the temporary file path.
|
|
44
|
+
*
|
|
45
|
+
* @param fileUrl - The URL of the file to download.
|
|
46
|
+
* @param requestInit - Optional request initialization parameters.
|
|
47
|
+
* @returns A task that can be aborted and contains the result of the download.
|
|
48
|
+
*/
|
|
49
|
+
export function
|
|
50
|
+
downloadFile(
|
|
51
|
+
fileUrl:
|
|
52
|
+
string,
|
|
53
|
+
requestInit?:
|
|
54
|
+
FsRequestInit):
|
|
55
|
+
FetchTask<DownloadFileTempResponse>;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Downloads a file from a URL and saves it to the specified path.
|
|
59
|
+
*
|
|
60
|
+
* @param fileUrl - The URL of the file to download.
|
|
61
|
+
* @param filePath - The path where the downloaded file will be saved.
|
|
62
|
+
* @param requestInit - Optional request initialization parameters.
|
|
63
|
+
* @returns A task that can be aborted and contains the result of the download.
|
|
64
|
+
*/
|
|
65
|
+
export function
|
|
66
|
+
downloadFile(
|
|
67
|
+
fileUrl:
|
|
68
|
+
string,
|
|
69
|
+
filePath:
|
|
70
|
+
string,
|
|
71
|
+
requestInit?:
|
|
72
|
+
FsRequestInit):
|
|
73
|
+
FetchTask<Response>;
|
|
74
|
+
export function
|
|
75
|
+
downloadFile(
|
|
76
|
+
fileUrl:
|
|
77
|
+
string,
|
|
78
|
+
filePath?:
|
|
79
|
+
string | FsRequestInit,
|
|
80
|
+
requestInit?:
|
|
81
|
+
FsRequestInit):
|
|
82
|
+
FetchTask<Response | DownloadFileTempResponse> {
|
|
83
|
+
let
|
|
84
|
+
aborted:
|
|
85
|
+
boolean,
|
|
86
|
+
saveToTemp;
|
|
87
|
+
assertFileUrl(
|
|
88
|
+
fileUrl);
|
|
89
|
+
saveToTemp =
|
|
90
|
+
false;
|
|
91
|
+
if ( typeof filePath === 'string' ) {
|
|
92
|
+
assertAbsolutePath(
|
|
93
|
+
filePath);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
requestInit =
|
|
97
|
+
filePath;
|
|
98
|
+
// save to a temporary file, reserve the extension
|
|
99
|
+
filePath =
|
|
100
|
+
generateTempPath(
|
|
101
|
+
{ extname:
|
|
102
|
+
extname(
|
|
103
|
+
fileUrl) });
|
|
104
|
+
saveToTemp =
|
|
105
|
+
true;
|
|
106
|
+
}
|
|
107
|
+
aborted =
|
|
108
|
+
false;
|
|
109
|
+
const
|
|
110
|
+
fetchTask =
|
|
111
|
+
fetchT(
|
|
112
|
+
fileUrl,
|
|
113
|
+
{ redirect:
|
|
114
|
+
'follow',
|
|
115
|
+
...requestInit,
|
|
116
|
+
abortable:
|
|
117
|
+
true });
|
|
118
|
+
const
|
|
119
|
+
response =
|
|
120
|
+
( async ():
|
|
121
|
+
FetchResponse<Response> => {
|
|
122
|
+
const
|
|
123
|
+
responseRes =
|
|
124
|
+
await fetchTask.response;
|
|
125
|
+
return responseRes.andThenAsync(
|
|
126
|
+
async response => {
|
|
127
|
+
const
|
|
128
|
+
blob =
|
|
129
|
+
await response.blob();
|
|
130
|
+
// maybe aborted
|
|
131
|
+
if ( aborted ) {
|
|
132
|
+
return Err(
|
|
133
|
+
createAbortError());
|
|
134
|
+
}
|
|
135
|
+
const
|
|
136
|
+
writeRes =
|
|
137
|
+
await writeFile(filePath, blob);
|
|
138
|
+
return writeRes.and(
|
|
139
|
+
Ok(response));
|
|
140
|
+
});
|
|
141
|
+
})();
|
|
142
|
+
return {
|
|
143
|
+
abort(
|
|
144
|
+
reason?:
|
|
145
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
146
|
+
any):
|
|
147
|
+
void {
|
|
148
|
+
aborted =
|
|
149
|
+
true;
|
|
150
|
+
fetchTask.abort(
|
|
151
|
+
reason);
|
|
152
|
+
},
|
|
153
|
+
get aborted():
|
|
154
|
+
boolean {
|
|
155
|
+
return aborted;
|
|
156
|
+
},
|
|
157
|
+
get response():
|
|
158
|
+
FetchResponse<Response | DownloadFileTempResponse> {
|
|
159
|
+
return saveToTemp ?
|
|
160
|
+
response.then(
|
|
161
|
+
res => {
|
|
162
|
+
return res.map<DownloadFileTempResponse>(
|
|
163
|
+
rawResponse => {
|
|
164
|
+
return {
|
|
165
|
+
tempFilePath:
|
|
166
|
+
filePath,
|
|
167
|
+
rawResponse,
|
|
168
|
+
};
|
|
169
|
+
});
|
|
170
|
+
}) :
|
|
171
|
+
response;
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
}
|