@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.
Files changed (114) hide show
  1. package/COPYING +674 -0
  2. package/README.cn.md +301 -0
  3. package/README.md +241 -0
  4. package/dist/main.cjs +1840 -0
  5. package/dist/main.cjs.map +1 -0
  6. package/dist/main.mjs +1751 -0
  7. package/dist/main.mjs.map +1 -0
  8. package/dist/types.d.ts +920 -0
  9. package/dist/types.d.ts.map +1 -0
  10. package/docs/README.md +115 -0
  11. package/docs/classes/SyncMessenger.md +42 -0
  12. package/docs/functions/appendFile.md +29 -0
  13. package/docs/functions/appendFileSync.md +26 -0
  14. package/docs/functions/assertAbsolutePath.md +29 -0
  15. package/docs/functions/assertFileUrl.md +29 -0
  16. package/docs/functions/connectSyncAgent.md +25 -0
  17. package/docs/functions/copy.md +35 -0
  18. package/docs/functions/copySync.md +30 -0
  19. package/docs/functions/createFile.md +27 -0
  20. package/docs/functions/createFileSync.md +25 -0
  21. package/docs/functions/deleteTemp.md +23 -0
  22. package/docs/functions/deleteTempSync.md +19 -0
  23. package/docs/functions/downloadFile.md +58 -0
  24. package/docs/functions/emptyDir.md +28 -0
  25. package/docs/functions/emptyDirSync.md +25 -0
  26. package/docs/functions/exists.md +29 -0
  27. package/docs/functions/existsSync.md +26 -0
  28. package/docs/functions/generateTempPath.md +27 -0
  29. package/docs/functions/getFileDataByHandle.md +27 -0
  30. package/docs/functions/getSyncMessenger.md +22 -0
  31. package/docs/functions/isDirectoryHandle.md +27 -0
  32. package/docs/functions/isFileHandle.md +27 -0
  33. package/docs/functions/isFileHandleLike.md +27 -0
  34. package/docs/functions/isOPFSSupported.md +21 -0
  35. package/docs/functions/isTempPath.md +27 -0
  36. package/docs/functions/mkTemp.md +28 -0
  37. package/docs/functions/mkTempSync.md +25 -0
  38. package/docs/functions/mkdir.md +27 -0
  39. package/docs/functions/mkdirSync.md +25 -0
  40. package/docs/functions/move.md +34 -0
  41. package/docs/functions/moveSync.md +30 -0
  42. package/docs/functions/pruneTemp.md +28 -0
  43. package/docs/functions/pruneTempSync.md +25 -0
  44. package/docs/functions/readBlobFile.md +28 -0
  45. package/docs/functions/readBlobFileSync.md +25 -0
  46. package/docs/functions/readDir.md +28 -0
  47. package/docs/functions/readDirSync.md +26 -0
  48. package/docs/functions/readFile.md +132 -0
  49. package/docs/functions/readFileSync.md +70 -0
  50. package/docs/functions/readJsonFile.md +35 -0
  51. package/docs/functions/readJsonFileSync.md +31 -0
  52. package/docs/functions/readTextFile.md +28 -0
  53. package/docs/functions/readTextFileSync.md +25 -0
  54. package/docs/functions/remove.md +27 -0
  55. package/docs/functions/removeSync.md +25 -0
  56. package/docs/functions/setSyncMessenger.md +26 -0
  57. package/docs/functions/startSyncAgent.md +21 -0
  58. package/docs/functions/stat.md +27 -0
  59. package/docs/functions/statSync.md +25 -0
  60. package/docs/functions/toFileSystemHandleLike.md +27 -0
  61. package/docs/functions/unzip.md +32 -0
  62. package/docs/functions/unzipFromUrl.md +36 -0
  63. package/docs/functions/unzipSync.md +26 -0
  64. package/docs/functions/uploadFile.md +33 -0
  65. package/docs/functions/writeFile.md +32 -0
  66. package/docs/functions/writeFileSync.md +30 -0
  67. package/docs/functions/zip.md +65 -0
  68. package/docs/functions/zipFromUrl.md +63 -0
  69. package/docs/functions/zipSync.md +55 -0
  70. package/docs/interfaces/CopyOptions.md +17 -0
  71. package/docs/interfaces/DownloadFileTempResponse.md +18 -0
  72. package/docs/interfaces/ErrorLike.md +18 -0
  73. package/docs/interfaces/ExistsOptions.md +18 -0
  74. package/docs/interfaces/FileLike.md +21 -0
  75. package/docs/interfaces/FileSystemFileHandleLike.md +25 -0
  76. package/docs/interfaces/FileSystemHandleLike.md +22 -0
  77. package/docs/interfaces/MoveOptions.md +17 -0
  78. package/docs/interfaces/ReadDirEntry.md +18 -0
  79. package/docs/interfaces/ReadDirEntrySync.md +18 -0
  80. package/docs/interfaces/ReadDirOptions.md +17 -0
  81. package/docs/interfaces/ReadOptions.md +17 -0
  82. package/docs/interfaces/SyncAgentOptions.md +19 -0
  83. package/docs/interfaces/TempOptions.md +19 -0
  84. package/docs/interfaces/UploadRequestInit.md +21 -0
  85. package/docs/interfaces/WriteOptions.md +19 -0
  86. package/docs/interfaces/ZipOptions.md +17 -0
  87. package/docs/type-aliases/FileEncoding.md +15 -0
  88. package/docs/type-aliases/FsRequestInit.md +15 -0
  89. package/docs/type-aliases/ReadFileContent.md +15 -0
  90. package/docs/type-aliases/WriteFileContent.md +15 -0
  91. package/docs/type-aliases/WriteSyncFileContent.md +16 -0
  92. package/docs/variables/CURRENT_DIR.md +15 -0
  93. package/docs/variables/NOT_FOUND_ERROR.md +18 -0
  94. package/docs/variables/ROOT_DIR.md +15 -0
  95. package/docs/variables/TMP_DIR.md +15 -0
  96. package/package.json +141 -0
  97. package/src/fs/assertions.ts +63 -0
  98. package/src/fs/constants.ts +63 -0
  99. package/src/fs/defines.ts +352 -0
  100. package/src/fs/helpers.ts +338 -0
  101. package/src/fs/opfs_core.ts +413 -0
  102. package/src/fs/opfs_download.ts +174 -0
  103. package/src/fs/opfs_ext.ts +504 -0
  104. package/src/fs/opfs_tmp.ts +131 -0
  105. package/src/fs/opfs_unzip.ts +168 -0
  106. package/src/fs/opfs_upload.ts +126 -0
  107. package/src/fs/opfs_zip.ts +314 -0
  108. package/src/fs/support.ts +36 -0
  109. package/src/fs/utils.ts +176 -0
  110. package/src/mod.ts +41 -0
  111. package/src/worker/helpers.ts +168 -0
  112. package/src/worker/opfs_worker.ts +298 -0
  113. package/src/worker/opfs_worker_adapter.ts +666 -0
  114. package/src/worker/shared.ts +400 -0
@@ -0,0 +1,666 @@
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 { Err,
28
+ Ok,
29
+ type IOResult,
30
+ type VoidIOResult } from 'happy-rusty';
31
+ import invariant from 'tiny-invariant';
32
+ import type { CopyOptions,
33
+ ExistsOptions,
34
+ FileLike,
35
+ FileSystemHandleLike,
36
+ MoveOptions,
37
+ ReadDirEntrySync,
38
+ ReadDirOptions,
39
+ ReadFileContent,
40
+ ReadOptions,
41
+ SyncAgentOptions,
42
+ TempOptions,
43
+ WriteOptions,
44
+ WriteSyncFileContent,
45
+ ZipOptions } from '../fs/defines.ts';
46
+ import { deserializeError,
47
+ setGlobalOpTimeout } from './helpers.ts';
48
+ import { callWorkerFromMain,
49
+ decodeFromBuffer,
50
+ decodeToString,
51
+ encodeToBuffer,
52
+ SyncMessenger,
53
+ WorkerAsyncOp } from './shared.ts';
54
+
55
+ /**
56
+ * Cache the messenger instance.
57
+ */
58
+ let
59
+ messenger:
60
+ SyncMessenger;
61
+
62
+ /**
63
+ * Communicate with worker.
64
+ * @param options - SyncAgentOptions
65
+ * @returns
66
+ */
67
+ export
68
+ function
69
+ connectSyncAgent(
70
+ options:
71
+ SyncAgentOptions):
72
+ Promise<void> {
73
+ if ( typeof window === 'undefined' ) {
74
+ throw new Error(
75
+ 'Only can use in main thread');
76
+ }
77
+ if ( messenger ) {
78
+ throw new Error(
79
+ 'Main messenger already started');
80
+ }
81
+ return new Promise(
82
+ resolve => {
83
+ const
84
+ { worker,
85
+ bufferLength = 1024 * 1024,
86
+ opTimeout = 1000 } =
87
+ options;
88
+ // check parameters
89
+ invariant(
90
+ worker instanceof Worker ||
91
+ worker instanceof URL ||
92
+ ( typeof worker === 'string' &&
93
+ worker),
94
+ () => 'Worker must be Worker or valid URL(string).');
95
+ invariant(
96
+ bufferLength > 16 &&
97
+ bufferLength % 4 === 0,
98
+ () => 'bufferLength must be a multiple of 4.')
99
+ invariant(
100
+ Number.isInteger(
101
+ opTimeout) &&
102
+ opTimeout > 0,
103
+ () => 'opTimeout must be integer and greater than 0.');
104
+ setGlobalOpTimeout(
105
+ opTimeout);
106
+ const
107
+ workerAdapter =
108
+ worker instanceof Worker ?
109
+ worker :
110
+ new Worker(
111
+ worker);
112
+ const
113
+ sab =
114
+ new SharedArrayBuffer(
115
+ bufferLength);
116
+ workerAdapter.addEventListener(
117
+ 'message',
118
+ (event:
119
+ MessageEvent<boolean>) => {
120
+ if ( event.data ) {
121
+ messenger =
122
+ new SyncMessenger(
123
+ sab);
124
+ resolve();
125
+ }
126
+ });
127
+ workerAdapter.postMessage(
128
+ sab);
129
+ });
130
+ }
131
+
132
+ /**
133
+ * Get messenger instance.
134
+ * Use `setSyncMessenger` to pass the messenger to other environments for sharing.
135
+ *
136
+ * @returns SyncMessenger instance.
137
+ */
138
+ export
139
+ function
140
+ getSyncMessenger():
141
+ SyncMessenger {
142
+ return messenger;
143
+ }
144
+
145
+ /**
146
+ * Set messenger instance.
147
+ * Use this method to share messenger with other environments.
148
+ *
149
+ * @param syncMessenger - SyncMessenger instance.
150
+ */
151
+ export
152
+ function
153
+ setSyncMessenger(
154
+ syncMessenger:
155
+ SyncMessenger):
156
+ void {
157
+ invariant(
158
+ syncMessenger != null,
159
+ () => 'syncMessenger is null or undefined.');
160
+ messenger =
161
+ syncMessenger;
162
+ }
163
+
164
+ /**
165
+ * Call worker I/O operation.
166
+ * @param op - I/O operation enum.
167
+ * @param args - I/O operation arguments.
168
+ * @returns - I/O operation result.
169
+ */
170
+ // The linter here shows not to work well as it doesnt
171
+ // support ignoring blocks.
172
+ // https://eslint.org/docs/latest/use/configure/rules
173
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
174
+ function callWorkerOp<T>(op: WorkerAsyncOp, ...args: any[]): IOResult<T> {
175
+ if ( !messenger ) {
176
+ // too early
177
+ return Err(
178
+ new Error(
179
+ 'Worker not initialized. Come back later.'));
180
+ }
181
+ const
182
+ request =
183
+ [ op,
184
+ ...args ];
185
+ const
186
+ requestData =
187
+ encodeToBuffer(
188
+ request);
189
+ try {
190
+ const
191
+ response =
192
+ callWorkerFromMain(
193
+ messenger,
194
+ requestData);
195
+ const
196
+ decodedResponse =
197
+ decodeFromBuffer(
198
+ response) as [ Error, T ];
199
+ const
200
+ _error =
201
+ decodedResponse[
202
+ 0];
203
+ const
204
+ result:
205
+ IOResult<T> =
206
+ _error ?
207
+ Err(
208
+ deserializeError(
209
+ _error)) :
210
+ Ok(
211
+ ( decodedResponse[
212
+ 1] ?? undefined ) as T);
213
+ return result;
214
+ }
215
+ catch (
216
+ _error) {
217
+ return Err(
218
+ _error as Error);
219
+ }
220
+ }
221
+
222
+ /**
223
+ * Sync version of `createFile`.
224
+ */
225
+ export
226
+ function
227
+ createFileSync(
228
+ filePath:
229
+ string):
230
+ VoidIOResult {
231
+ return callWorkerOp(
232
+ WorkerAsyncOp.createFile,
233
+ filePath);
234
+ }
235
+
236
+ /**
237
+ * Sync version of `mkdir`.
238
+ */
239
+ export
240
+ function
241
+ mkdirSync(
242
+ dirPath:
243
+ string):
244
+ VoidIOResult {
245
+ return callWorkerOp(
246
+ WorkerAsyncOp.mkdir,
247
+ dirPath);
248
+ }
249
+
250
+ /**
251
+ * Sync version of `move`.
252
+ */
253
+ export
254
+ function
255
+ moveSync(
256
+ srcPath:
257
+ string,
258
+ destPath:
259
+ string,
260
+ options?:
261
+ MoveOptions):
262
+ VoidIOResult {
263
+ return callWorkerOp(
264
+ WorkerAsyncOp.move,
265
+ srcPath,
266
+ destPath,
267
+ options);
268
+ }
269
+
270
+ /**
271
+ * Sync version of `readDir`.
272
+ */
273
+ export
274
+ function
275
+ readDirSync(
276
+ dirPath:
277
+ string,
278
+ options?:
279
+ ReadDirOptions):
280
+ IOResult<ReadDirEntrySync[]> {
281
+ return callWorkerOp(
282
+ WorkerAsyncOp.readDir,
283
+ dirPath,
284
+ options);
285
+ }
286
+
287
+ /**
288
+ * Sync version of `readFile`.
289
+ */
290
+ export
291
+ function
292
+ readFileSync(
293
+ filePath:
294
+ string,
295
+ options:
296
+ ReadOptions &
297
+ { encoding:
298
+ 'blob'; }):
299
+ IOResult<FileLike>;
300
+ export
301
+ function
302
+ readFileSync(
303
+ filePath:
304
+ string,
305
+ options:
306
+ ReadOptions &
307
+ { encoding:
308
+ 'utf8'; }):
309
+ IOResult<string>;
310
+ export
311
+ function
312
+ readFileSync(
313
+ filePath:
314
+ string,
315
+ options?:
316
+ ReadOptions &
317
+ { encoding:
318
+ 'binary'; }):
319
+ IOResult<ArrayBuffer>;
320
+ export
321
+ function
322
+ readFileSync<T extends ReadFileContent>(
323
+ filePath:
324
+ string,
325
+ options?:
326
+ ReadOptions):
327
+ IOResult<T> {
328
+ const
329
+ res:
330
+ IOResult<FileLike> =
331
+ callWorkerOp(
332
+ WorkerAsyncOp.readBlobFile,
333
+ filePath);
334
+ return res.map(
335
+ file => {
336
+ // actually data is number array
337
+ const
338
+ u8a =
339
+ new Uint8Array(
340
+ file.data);
341
+ file.data =
342
+ u8a.buffer.slice(
343
+ u8a.byteOffset,
344
+ u8a.byteOffset + u8a.byteLength);
345
+ switch (
346
+ options?.encoding) {
347
+ case 'blob': {
348
+ return file as unknown as T;
349
+ }
350
+ case 'utf8': {
351
+ return decodeToString(new Uint8Array(file.data)) as unknown as T;
352
+ }
353
+ default: {
354
+ return file.data as unknown as T;
355
+ }
356
+ }
357
+ });
358
+ }
359
+
360
+ /**
361
+ * Sync version of `remove`.
362
+ */
363
+ export
364
+ function
365
+ removeSync(
366
+ path:
367
+ string):
368
+ VoidIOResult {
369
+ return callWorkerOp(
370
+ WorkerAsyncOp.remove,
371
+ path);
372
+ }
373
+
374
+ /**
375
+ * Sync version of `stat`.
376
+ */
377
+ export
378
+ function
379
+ statSync(
380
+ path:
381
+ string):
382
+ IOResult<FileSystemHandleLike> {
383
+ return callWorkerOp(
384
+ WorkerAsyncOp.stat,
385
+ path);
386
+ }
387
+
388
+ /**
389
+ * Serialize contents to an byte array or a string
390
+ * that can be sent to worker.
391
+ * @param contents
392
+ * @returns
393
+ */
394
+ function
395
+ serializeWriteContents(
396
+ contents:
397
+ WriteSyncFileContent):
398
+ number[] |
399
+ string {
400
+ return contents instanceof ArrayBuffer ?
401
+ [ ...new Uint8Array(
402
+ contents) ] :
403
+ ArrayBuffer.isView(
404
+ contents) ?
405
+ [ ...new Uint8Array(
406
+ contents.buffer) ] :
407
+ contents;
408
+ }
409
+
410
+ /**
411
+ * Sync version of `writeFile`.
412
+ */
413
+ export
414
+ function
415
+ writeFileSync(
416
+ filePath:
417
+ string,
418
+ contents:
419
+ WriteSyncFileContent,
420
+ options?:
421
+ WriteOptions):
422
+ VoidIOResult {
423
+ return callWorkerOp(
424
+ WorkerAsyncOp.writeFile,
425
+ filePath,
426
+ serializeWriteContents(
427
+ contents),
428
+ options);
429
+ }
430
+
431
+ /**
432
+ * Sync version of `appendFile`.
433
+ */
434
+ export
435
+ function
436
+ appendFileSync(
437
+ filePath:
438
+ string,
439
+ contents:
440
+ WriteSyncFileContent):
441
+ VoidIOResult {
442
+ return callWorkerOp(
443
+ WorkerAsyncOp.appendFile,
444
+ filePath,
445
+ serializeWriteContents(
446
+ contents));
447
+ }
448
+
449
+ /**
450
+ * Sync version of `copy`.
451
+ */
452
+ export
453
+ function
454
+ copySync(
455
+ srcPath:
456
+ string,
457
+ destPath:
458
+ string,
459
+ options?:
460
+ CopyOptions):
461
+ VoidIOResult {
462
+ return callWorkerOp(
463
+ WorkerAsyncOp.copy,
464
+ srcPath,
465
+ destPath,
466
+ options);
467
+ }
468
+
469
+ /**
470
+ * Sync version of `emptyDir`.
471
+ */
472
+ export
473
+ function
474
+ emptyDirSync(
475
+ dirPath:
476
+ string):
477
+ VoidIOResult {
478
+ return callWorkerOp(
479
+ WorkerAsyncOp.emptyDir,
480
+ dirPath);
481
+ }
482
+
483
+ /**
484
+ * Sync version of `exists`.
485
+ */
486
+ export
487
+ function
488
+ existsSync(
489
+ path:
490
+ string,
491
+ options?:
492
+ ExistsOptions):
493
+ IOResult<boolean> {
494
+ return callWorkerOp(
495
+ WorkerAsyncOp.exists,
496
+ path,
497
+ options);
498
+ }
499
+
500
+ /**
501
+ * Sync version of `deleteTemp`.
502
+ */
503
+ export
504
+ function
505
+ deleteTempSync():
506
+ VoidIOResult {
507
+ return callWorkerOp(
508
+ WorkerAsyncOp.deleteTemp);
509
+ }
510
+
511
+ /**
512
+ * Sync version of `mkTemp`.
513
+ */
514
+ export
515
+ function
516
+ mkTempSync(
517
+ options?:
518
+ TempOptions):
519
+ IOResult<string> {
520
+ return callWorkerOp(
521
+ WorkerAsyncOp.mkTemp,
522
+ options);
523
+ }
524
+
525
+ /**
526
+ * Sync version of `pruneTemp`.
527
+ */
528
+ export
529
+ function
530
+ pruneTempSync(
531
+ expired:
532
+ Date):
533
+ VoidIOResult {
534
+ return callWorkerOp(
535
+ WorkerAsyncOp.pruneTemp,
536
+ expired);
537
+ }
538
+
539
+ /**
540
+ * Sync version of `readBlobFile`.
541
+ */
542
+ export
543
+ function
544
+ readBlobFileSync(
545
+ filePath:
546
+ string):
547
+ IOResult<FileLike> {
548
+ return readFileSync(
549
+ filePath, {
550
+ encoding:
551
+ 'blob' });
552
+ }
553
+
554
+ /**
555
+ * Sync version of `readJsonFile`.
556
+ */
557
+ export
558
+ function
559
+ readJsonFileSync<T>(
560
+ filePath:
561
+ string):
562
+ IOResult<T> {
563
+ return readTextFileSync(
564
+ filePath).andThen(
565
+ contents => {
566
+ try {
567
+ return Ok(
568
+ JSON.parse(
569
+ contents));
570
+ }
571
+ catch (
572
+ _error) {
573
+ return Err(
574
+ _error as Error);
575
+ }
576
+ });
577
+ }
578
+
579
+ /**
580
+ * Sync version of `readTextFile`.
581
+ */
582
+ export
583
+ function
584
+ readTextFileSync(
585
+ filePath:
586
+ string):
587
+ IOResult<string> {
588
+ return readFileSync(
589
+ filePath,
590
+ { encoding:
591
+ 'utf8' });
592
+ }
593
+
594
+ /**
595
+ * Sync version of `unzip`.
596
+ */
597
+ export
598
+ function
599
+ unzipSync(
600
+ zipFilePath:
601
+ string,
602
+ targetPath:
603
+ string):
604
+ VoidIOResult {
605
+ return callWorkerOp(
606
+ WorkerAsyncOp.unzip,
607
+ zipFilePath,
608
+ targetPath);
609
+ }
610
+
611
+ /**
612
+ * Sync version of `zip`.
613
+ */
614
+ export
615
+ function
616
+ zipSync(
617
+ sourcePath:
618
+ string,
619
+ zipFilePath:
620
+ string,
621
+ options?:
622
+ ZipOptions):
623
+ VoidIOResult;
624
+
625
+ /**
626
+ * Sync version of `zip`.
627
+ */
628
+ export
629
+ function
630
+ zipSync(
631
+ sourcePath:
632
+ string,
633
+ options?:
634
+ ZipOptions):
635
+ IOResult<Uint8Array>;
636
+
637
+ /**
638
+ * Sync version of `zip`.
639
+ */
640
+ export
641
+ function
642
+ zipSync<T>(
643
+ sourcePath:
644
+ string,
645
+ zipFilePath?:
646
+ string |
647
+ ZipOptions,
648
+ options?:
649
+ ZipOptions):
650
+ IOResult<T> {
651
+ const
652
+ res =
653
+ callWorkerOp(
654
+ WorkerAsyncOp.zip,
655
+ sourcePath,
656
+ zipFilePath,
657
+ options) as IOResult<number[]> |
658
+ VoidIOResult;
659
+ return res.map(
660
+ data => {
661
+ return ( data ?
662
+ new Uint8Array(
663
+ data) :
664
+ data) as T;
665
+ });
666
+ }