@tauri-apps/plugin-fs 2.0.1 → 2.0.3

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/dist-js/index.cjs CHANGED
@@ -18,16 +18,29 @@ var core = require('@tauri-apps/api/core');
18
18
  *
19
19
  * The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns.
20
20
  *
21
- * The scope configuration is an array of glob patterns describing folder paths that are allowed.
22
- * For instance, this scope configuration only allows accessing files on the
23
- * *databases* folder of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
21
+ * The scope configuration is an array of glob patterns describing file/directory paths that are allowed.
22
+ * For instance, this scope configuration allows **all** enabled `fs` APIs to (only) access files in the
23
+ * *databases* directory of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
24
24
  * ```json
25
25
  * {
26
- * "plugins": {
27
- * "fs": {
28
- * "scope": ["$APPDATA/databases/*"]
26
+ * "permissions": [
27
+ * {
28
+ * "identifier": "fs:scope",
29
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
29
30
  * }
30
- * }
31
+ * ]
32
+ * }
33
+ * ```
34
+ *
35
+ * Scopes can also be applied to specific `fs` APIs by using the API's identifier instead of `fs:scope`:
36
+ * ```json
37
+ * {
38
+ * "permissions": [
39
+ * {
40
+ * "identifier": "fs:allow-exists",
41
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
42
+ * }
43
+ * ]
31
44
  * }
32
45
  * ```
33
46
  *
@@ -60,8 +73,6 @@ var core = require('@tauri-apps/api/core');
60
73
  *
61
74
  * Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
62
75
  *
63
- * Note that this scope applies to **all** APIs on this module.
64
- *
65
76
  * @module
66
77
  */
67
78
  exports.SeekMode = void 0;
@@ -99,6 +110,7 @@ function fromBytes(buffer) {
99
110
  const size = bytes.byteLength;
100
111
  let x = 0;
101
112
  for (let i = 0; i < size; i++) {
113
+ // eslint-disable-next-line security/detect-object-injection
102
114
  const byte = bytes[i];
103
115
  x *= 0x100;
104
116
  x += byte;
@@ -250,11 +262,11 @@ class FileHandle extends core.Resource {
250
262
  });
251
263
  }
252
264
  /**
253
- * Writes `p.byteLength` bytes from `p` to the underlying data stream. It
254
- * resolves to the number of bytes written from `p` (`0` <= `n` <=
255
- * `p.byteLength`) or reject with the error encountered that caused the
265
+ * Writes `data.byteLength` bytes from `data` to the underlying data stream. It
266
+ * resolves to the number of bytes written from `data` (`0` <= `n` <=
267
+ * `data.byteLength`) or reject with the error encountered that caused the
256
268
  * write to stop early. `write()` must reject with a non-null error if
257
- * would resolve to `n` < `p.byteLength`. `write()` must not modify the
269
+ * would resolve to `n` < `data.byteLength`. `write()` must not modify the
258
270
  * slice data, even temporarily.
259
271
  *
260
272
  * @example
@@ -432,10 +444,12 @@ async function readTextFile(path, options) {
432
444
  if (path instanceof URL && path.protocol !== 'file:') {
433
445
  throw new TypeError('Must be a file URL.');
434
446
  }
435
- return await core.invoke('plugin:fs|read_text_file', {
447
+ const arr = await core.invoke('plugin:fs|read_text_file', {
436
448
  path: path instanceof URL ? path.toString() : path,
437
449
  options
438
450
  });
451
+ const bytes = arr instanceof ArrayBuffer ? arr : Uint8Array.from(arr);
452
+ return new TextDecoder().decode(bytes);
439
453
  }
440
454
  /**
441
455
  * Returns an async {@linkcode AsyncIterableIterator} over the lines of a file as UTF-8 string.
@@ -467,12 +481,22 @@ async function readTextFileLines(path, options) {
467
481
  options
468
482
  });
469
483
  }
470
- const [line, done] = await core.invoke('plugin:fs|read_text_file_lines_next', { rid: this.rid });
471
- // an iteration is over, reset rid for next iteration
472
- if (done)
484
+ const arr = await core.invoke('plugin:fs|read_text_file_lines_next', { rid: this.rid });
485
+ const bytes = arr instanceof ArrayBuffer ? new Uint8Array(arr) : Uint8Array.from(arr);
486
+ // Rust side will never return an empty array for this command and
487
+ // ensure there is at least one elements there.
488
+ //
489
+ // This is an optimization to include whether we finished iteration or not (1 or 0)
490
+ // at the end of returned array to avoid serialization overhead of separate values.
491
+ const done = bytes[bytes.byteLength - 1] === 1;
492
+ if (done) {
493
+ // a full iteration is over, reset rid for next iteration
473
494
  this.rid = null;
495
+ return { value: null, done };
496
+ }
497
+ const line = new TextDecoder().decode(bytes.slice(0, bytes.byteLength));
474
498
  return {
475
- value: done ? '' : line,
499
+ value: line,
476
500
  done
477
501
  };
478
502
  },
@@ -616,12 +640,21 @@ async function writeFile(path, data, options) {
616
640
  if (path instanceof URL && path.protocol !== 'file:') {
617
641
  throw new TypeError('Must be a file URL.');
618
642
  }
619
- await core.invoke('plugin:fs|write_file', data, {
620
- headers: {
621
- path: encodeURIComponent(path instanceof URL ? path.toString() : path),
622
- options: JSON.stringify(options)
643
+ if (data instanceof ReadableStream) {
644
+ const file = await open(path, options);
645
+ for await (const chunk of data) {
646
+ await file.write(chunk);
623
647
  }
624
- });
648
+ await file.close();
649
+ }
650
+ else {
651
+ await core.invoke('plugin:fs|write_file', data, {
652
+ headers: {
653
+ path: encodeURIComponent(path instanceof URL ? path.toString() : path),
654
+ options: JSON.stringify(options)
655
+ }
656
+ });
657
+ }
625
658
  }
626
659
  /**
627
660
  * Writes UTF-8 string `data` to the given `path`, by default creating a new file if needed, else overwriting.
@@ -641,7 +674,7 @@ async function writeTextFile(path, data, options) {
641
674
  const encoder = new TextEncoder();
642
675
  await core.invoke('plugin:fs|write_text_file', encoder.encode(data), {
643
676
  headers: {
644
- path: path instanceof URL ? path.toString() : path,
677
+ path: encodeURIComponent(path instanceof URL ? path.toString() : path),
645
678
  options: JSON.stringify(options)
646
679
  }
647
680
  });
@@ -725,6 +758,29 @@ async function watchImmediate(paths, cb, options) {
725
758
  void unwatch(rid);
726
759
  };
727
760
  }
761
+ /**
762
+ * Get the size of a file or directory. For files, the `stat` functions can be used as well.
763
+ *
764
+ * If `path` is a directory, this function will recursively iterate over every file and every directory inside of `path` and therefore will be very time consuming if used on larger directories.
765
+ *
766
+ * @example
767
+ * ```typescript
768
+ * import { size, BaseDirectory } from '@tauri-apps/plugin-fs';
769
+ * // Get the size of the `$APPDATA/tauri` directory.
770
+ * const dirSize = await size('tauri', { baseDir: BaseDirectory.AppData });
771
+ * console.log(dirSize); // 1024
772
+ * ```
773
+ *
774
+ * @since 2.1.0
775
+ */
776
+ async function size(path) {
777
+ if (path instanceof URL && path.protocol !== 'file:') {
778
+ throw new TypeError('Must be a file URL.');
779
+ }
780
+ return await core.invoke('plugin:fs|size', {
781
+ path: path instanceof URL ? path.toString() : path
782
+ });
783
+ }
728
784
 
729
785
  Object.defineProperty(exports, "BaseDirectory", {
730
786
  enumerable: true,
@@ -743,6 +799,7 @@ exports.readTextFile = readTextFile;
743
799
  exports.readTextFileLines = readTextFileLines;
744
800
  exports.remove = remove;
745
801
  exports.rename = rename;
802
+ exports.size = size;
746
803
  exports.stat = stat;
747
804
  exports.truncate = truncate;
748
805
  exports.watch = watch;
@@ -10,16 +10,29 @@
10
10
  *
11
11
  * The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns.
12
12
  *
13
- * The scope configuration is an array of glob patterns describing folder paths that are allowed.
14
- * For instance, this scope configuration only allows accessing files on the
15
- * *databases* folder of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
13
+ * The scope configuration is an array of glob patterns describing file/directory paths that are allowed.
14
+ * For instance, this scope configuration allows **all** enabled `fs` APIs to (only) access files in the
15
+ * *databases* directory of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
16
16
  * ```json
17
17
  * {
18
- * "plugins": {
19
- * "fs": {
20
- * "scope": ["$APPDATA/databases/*"]
18
+ * "permissions": [
19
+ * {
20
+ * "identifier": "fs:scope",
21
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
21
22
  * }
22
- * }
23
+ * ]
24
+ * }
25
+ * ```
26
+ *
27
+ * Scopes can also be applied to specific `fs` APIs by using the API's identifier instead of `fs:scope`:
28
+ * ```json
29
+ * {
30
+ * "permissions": [
31
+ * {
32
+ * "identifier": "fs:allow-exists",
33
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
34
+ * }
35
+ * ]
23
36
  * }
24
37
  * ```
25
38
  *
@@ -52,8 +65,6 @@
52
65
  *
53
66
  * Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
54
67
  *
55
- * Note that this scope applies to **all** APIs on this module.
56
- *
57
68
  * @module
58
69
  */
59
70
  import { BaseDirectory } from '@tauri-apps/api/path';
@@ -304,11 +315,11 @@ declare class FileHandle extends Resource {
304
315
  */
305
316
  truncate(len?: number): Promise<void>;
306
317
  /**
307
- * Writes `p.byteLength` bytes from `p` to the underlying data stream. It
308
- * resolves to the number of bytes written from `p` (`0` <= `n` <=
309
- * `p.byteLength`) or reject with the error encountered that caused the
318
+ * Writes `data.byteLength` bytes from `data` to the underlying data stream. It
319
+ * resolves to the number of bytes written from `data` (`0` <= `n` <=
320
+ * `data.byteLength`) or reject with the error encountered that caused the
310
321
  * write to stop early. `write()` must reject with a non-null error if
311
- * would resolve to `n` < `p.byteLength`. `write()` must not modify the
322
+ * would resolve to `n` < `data.byteLength`. `write()` must not modify the
312
323
  * slice data, even temporarily.
313
324
  *
314
325
  * @example
@@ -692,7 +703,7 @@ interface WriteFileOptions {
692
703
  *
693
704
  * @since 2.0.0
694
705
  */
695
- declare function writeFile(path: string | URL, data: Uint8Array, options?: WriteFileOptions): Promise<void>;
706
+ declare function writeFile(path: string | URL, data: Uint8Array | ReadableStream<Uint8Array>, options?: WriteFileOptions): Promise<void>;
696
707
  /**
697
708
  * Writes UTF-8 string `data` to the given `path`, by default creating a new file if needed, else overwriting.
698
709
  @example
@@ -831,5 +842,21 @@ declare function watch(paths: string | string[] | URL | URL[], cb: (event: Watch
831
842
  * @since 2.0.0
832
843
  */
833
844
  declare function watchImmediate(paths: string | string[] | URL | URL[], cb: (event: WatchEvent) => void, options?: WatchOptions): Promise<UnwatchFn>;
845
+ /**
846
+ * Get the size of a file or directory. For files, the `stat` functions can be used as well.
847
+ *
848
+ * If `path` is a directory, this function will recursively iterate over every file and every directory inside of `path` and therefore will be very time consuming if used on larger directories.
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * import { size, BaseDirectory } from '@tauri-apps/plugin-fs';
853
+ * // Get the size of the `$APPDATA/tauri` directory.
854
+ * const dirSize = await size('tauri', { baseDir: BaseDirectory.AppData });
855
+ * console.log(dirSize); // 1024
856
+ * ```
857
+ *
858
+ * @since 2.1.0
859
+ */
860
+ declare function size(path: string | URL): Promise<number>;
834
861
  export type { CreateOptions, OpenOptions, CopyFileOptions, MkdirOptions, DirEntry, ReadDirOptions, ReadFileOptions, RemoveOptions, RenameOptions, StatOptions, TruncateOptions, WriteFileOptions, ExistsOptions, FileInfo, WatchOptions, DebouncedWatchOptions, WatchEvent, WatchEventKind, WatchEventKindAccess, WatchEventKindCreate, WatchEventKindModify, WatchEventKindRemove, UnwatchFn };
835
- export { BaseDirectory, FileHandle, create, open, copyFile, mkdir, readDir, readFile, readTextFile, readTextFileLines, remove, rename, SeekMode, stat, lstat, truncate, writeFile, writeTextFile, exists, watch, watchImmediate };
862
+ export { BaseDirectory, FileHandle, create, open, copyFile, mkdir, readDir, readFile, readTextFile, readTextFileLines, remove, rename, SeekMode, stat, lstat, truncate, writeFile, writeTextFile, exists, watch, watchImmediate, size };
package/dist-js/index.js CHANGED
@@ -16,16 +16,29 @@ import { Resource, invoke, Channel } from '@tauri-apps/api/core';
16
16
  *
17
17
  * The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns.
18
18
  *
19
- * The scope configuration is an array of glob patterns describing folder paths that are allowed.
20
- * For instance, this scope configuration only allows accessing files on the
21
- * *databases* folder of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
19
+ * The scope configuration is an array of glob patterns describing file/directory paths that are allowed.
20
+ * For instance, this scope configuration allows **all** enabled `fs` APIs to (only) access files in the
21
+ * *databases* directory of the {@link https://v2.tauri.app/reference/javascript/api/namespacepath/#appdatadir | `$APPDATA` directory}:
22
22
  * ```json
23
23
  * {
24
- * "plugins": {
25
- * "fs": {
26
- * "scope": ["$APPDATA/databases/*"]
24
+ * "permissions": [
25
+ * {
26
+ * "identifier": "fs:scope",
27
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
27
28
  * }
28
- * }
29
+ * ]
30
+ * }
31
+ * ```
32
+ *
33
+ * Scopes can also be applied to specific `fs` APIs by using the API's identifier instead of `fs:scope`:
34
+ * ```json
35
+ * {
36
+ * "permissions": [
37
+ * {
38
+ * "identifier": "fs:allow-exists",
39
+ * "allow": [{ "path": "$APPDATA/databases/*" }]
40
+ * }
41
+ * ]
29
42
  * }
30
43
  * ```
31
44
  *
@@ -58,8 +71,6 @@ import { Resource, invoke, Channel } from '@tauri-apps/api/core';
58
71
  *
59
72
  * Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
60
73
  *
61
- * Note that this scope applies to **all** APIs on this module.
62
- *
63
74
  * @module
64
75
  */
65
76
  var SeekMode;
@@ -97,6 +108,7 @@ function fromBytes(buffer) {
97
108
  const size = bytes.byteLength;
98
109
  let x = 0;
99
110
  for (let i = 0; i < size; i++) {
111
+ // eslint-disable-next-line security/detect-object-injection
100
112
  const byte = bytes[i];
101
113
  x *= 0x100;
102
114
  x += byte;
@@ -248,11 +260,11 @@ class FileHandle extends Resource {
248
260
  });
249
261
  }
250
262
  /**
251
- * Writes `p.byteLength` bytes from `p` to the underlying data stream. It
252
- * resolves to the number of bytes written from `p` (`0` <= `n` <=
253
- * `p.byteLength`) or reject with the error encountered that caused the
263
+ * Writes `data.byteLength` bytes from `data` to the underlying data stream. It
264
+ * resolves to the number of bytes written from `data` (`0` <= `n` <=
265
+ * `data.byteLength`) or reject with the error encountered that caused the
254
266
  * write to stop early. `write()` must reject with a non-null error if
255
- * would resolve to `n` < `p.byteLength`. `write()` must not modify the
267
+ * would resolve to `n` < `data.byteLength`. `write()` must not modify the
256
268
  * slice data, even temporarily.
257
269
  *
258
270
  * @example
@@ -430,10 +442,12 @@ async function readTextFile(path, options) {
430
442
  if (path instanceof URL && path.protocol !== 'file:') {
431
443
  throw new TypeError('Must be a file URL.');
432
444
  }
433
- return await invoke('plugin:fs|read_text_file', {
445
+ const arr = await invoke('plugin:fs|read_text_file', {
434
446
  path: path instanceof URL ? path.toString() : path,
435
447
  options
436
448
  });
449
+ const bytes = arr instanceof ArrayBuffer ? arr : Uint8Array.from(arr);
450
+ return new TextDecoder().decode(bytes);
437
451
  }
438
452
  /**
439
453
  * Returns an async {@linkcode AsyncIterableIterator} over the lines of a file as UTF-8 string.
@@ -465,12 +479,22 @@ async function readTextFileLines(path, options) {
465
479
  options
466
480
  });
467
481
  }
468
- const [line, done] = await invoke('plugin:fs|read_text_file_lines_next', { rid: this.rid });
469
- // an iteration is over, reset rid for next iteration
470
- if (done)
482
+ const arr = await invoke('plugin:fs|read_text_file_lines_next', { rid: this.rid });
483
+ const bytes = arr instanceof ArrayBuffer ? new Uint8Array(arr) : Uint8Array.from(arr);
484
+ // Rust side will never return an empty array for this command and
485
+ // ensure there is at least one elements there.
486
+ //
487
+ // This is an optimization to include whether we finished iteration or not (1 or 0)
488
+ // at the end of returned array to avoid serialization overhead of separate values.
489
+ const done = bytes[bytes.byteLength - 1] === 1;
490
+ if (done) {
491
+ // a full iteration is over, reset rid for next iteration
471
492
  this.rid = null;
493
+ return { value: null, done };
494
+ }
495
+ const line = new TextDecoder().decode(bytes.slice(0, bytes.byteLength));
472
496
  return {
473
- value: done ? '' : line,
497
+ value: line,
474
498
  done
475
499
  };
476
500
  },
@@ -614,12 +638,21 @@ async function writeFile(path, data, options) {
614
638
  if (path instanceof URL && path.protocol !== 'file:') {
615
639
  throw new TypeError('Must be a file URL.');
616
640
  }
617
- await invoke('plugin:fs|write_file', data, {
618
- headers: {
619
- path: encodeURIComponent(path instanceof URL ? path.toString() : path),
620
- options: JSON.stringify(options)
641
+ if (data instanceof ReadableStream) {
642
+ const file = await open(path, options);
643
+ for await (const chunk of data) {
644
+ await file.write(chunk);
621
645
  }
622
- });
646
+ await file.close();
647
+ }
648
+ else {
649
+ await invoke('plugin:fs|write_file', data, {
650
+ headers: {
651
+ path: encodeURIComponent(path instanceof URL ? path.toString() : path),
652
+ options: JSON.stringify(options)
653
+ }
654
+ });
655
+ }
623
656
  }
624
657
  /**
625
658
  * Writes UTF-8 string `data` to the given `path`, by default creating a new file if needed, else overwriting.
@@ -639,7 +672,7 @@ async function writeTextFile(path, data, options) {
639
672
  const encoder = new TextEncoder();
640
673
  await invoke('plugin:fs|write_text_file', encoder.encode(data), {
641
674
  headers: {
642
- path: path instanceof URL ? path.toString() : path,
675
+ path: encodeURIComponent(path instanceof URL ? path.toString() : path),
643
676
  options: JSON.stringify(options)
644
677
  }
645
678
  });
@@ -723,5 +756,28 @@ async function watchImmediate(paths, cb, options) {
723
756
  void unwatch(rid);
724
757
  };
725
758
  }
759
+ /**
760
+ * Get the size of a file or directory. For files, the `stat` functions can be used as well.
761
+ *
762
+ * If `path` is a directory, this function will recursively iterate over every file and every directory inside of `path` and therefore will be very time consuming if used on larger directories.
763
+ *
764
+ * @example
765
+ * ```typescript
766
+ * import { size, BaseDirectory } from '@tauri-apps/plugin-fs';
767
+ * // Get the size of the `$APPDATA/tauri` directory.
768
+ * const dirSize = await size('tauri', { baseDir: BaseDirectory.AppData });
769
+ * console.log(dirSize); // 1024
770
+ * ```
771
+ *
772
+ * @since 2.1.0
773
+ */
774
+ async function size(path) {
775
+ if (path instanceof URL && path.protocol !== 'file:') {
776
+ throw new TypeError('Must be a file URL.');
777
+ }
778
+ return await invoke('plugin:fs|size', {
779
+ path: path instanceof URL ? path.toString() : path
780
+ });
781
+ }
726
782
 
727
- export { FileHandle, SeekMode, copyFile, create, exists, lstat, mkdir, open, readDir, readFile, readTextFile, readTextFileLines, remove, rename, stat, truncate, watch, watchImmediate, writeFile, writeTextFile };
783
+ export { FileHandle, SeekMode, copyFile, create, exists, lstat, mkdir, open, readDir, readFile, readTextFile, readTextFileLines, remove, rename, size, stat, truncate, watch, watchImmediate, writeFile, writeTextFile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tauri-apps/plugin-fs",
3
- "version": "2.0.1",
3
+ "version": "2.0.3",
4
4
  "description": "Access the file system.",
5
5
  "license": "MIT OR Apache-2.0",
6
6
  "authors": [