@react-native-gpui/fs 0.0.0-stage → 0.0.1-alpha.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/src/index.ts ADDED
@@ -0,0 +1,769 @@
1
+ /**
2
+ * `@react-native-gpui/fs`: a Node-shaped file API for the desktop app.
3
+ *
4
+ * The native module underneath is one TurboModule on its own thread with
5
+ * `ArrayBuffer` arguments and plain-object results. Everything Node adds on top
6
+ * of that — encodings, `Stats` and `Dirent` instances, callback forms, the
7
+ * `fs/promises` object — lives in this package's JS layer, so the bridge stays a
8
+ * byte mover and the API stays recognisably Node's.
9
+ *
10
+ * The default export is the `node:fs` object; `dialog`, `paths` and `watch` are
11
+ * named exports because they have no `fs` counterpart to hang off.
12
+ */
13
+
14
+ import { constants } from './constants';
15
+ import NativeFs from './NativeFs';
16
+ import {
17
+ Dirent,
18
+ FileHandle,
19
+ Stats,
20
+ promises,
21
+ readFileImpl,
22
+ type MkdirOptions,
23
+ type OpenOptions,
24
+ type RmOptions,
25
+ type WriteOptions,
26
+ } from './promises';
27
+ import {
28
+ decodeString,
29
+ flagFrom,
30
+ missingPath,
31
+ modeFrom,
32
+ normalizeEncoding,
33
+ relativeEntryName,
34
+ toArrayBuffer,
35
+ toPath,
36
+ withContext,
37
+ type BinaryData,
38
+ type Encoding,
39
+ type NodeError,
40
+ } from './shared';
41
+ import { watch } from './watch';
42
+
43
+ export type {
44
+ CpOptions,
45
+ MkdirOptions,
46
+ OpenOptions,
47
+ ReaddirOptions,
48
+ RmOptions,
49
+ TimeLike,
50
+ WriteOptions,
51
+ } from './promises';
52
+ export type { BinaryData, Encoding, NodeError } from './shared';
53
+ export type {
54
+ FSWatcher,
55
+ WatchEventType,
56
+ WatchListener,
57
+ WatchOptions,
58
+ } from './watch';
59
+ export { Dirent, Stats, FileHandle, promises, constants, watch };
60
+
61
+ /* -------------------------------------------------------------------------- */
62
+ /* Dialogs */
63
+ /* -------------------------------------------------------------------------- */
64
+
65
+ export interface DialogFilter {
66
+ name: string;
67
+ extensions: Array<string>;
68
+ }
69
+
70
+ export interface OpenDialogOptions {
71
+ title?: string;
72
+ defaultPath?: string;
73
+ multiple?: boolean;
74
+ directory?: boolean;
75
+ filters?: Array<DialogFilter>;
76
+ }
77
+
78
+ export interface SaveDialogOptions {
79
+ title?: string;
80
+ defaultPath?: string;
81
+ defaultName?: string;
82
+ filters?: Array<DialogFilter>;
83
+ }
84
+
85
+ /**
86
+ * The native pickers.
87
+ *
88
+ * Cancelling is not an error: `open` resolves with `[]` and `save` with `null`,
89
+ * which is what the design specifies and what libraries pairing the two expect.
90
+ */
91
+ export const dialog = {
92
+ async open(options: OpenDialogOptions = {}): Promise<string[]> {
93
+ return NativeFs.showOpenDialog({
94
+ title: options.title,
95
+ defaultPath:
96
+ options.defaultPath === undefined
97
+ ? undefined
98
+ : toPath(options.defaultPath),
99
+ multiple: options.multiple ?? false,
100
+ directory: options.directory ?? false,
101
+ filters: options.filters ?? [],
102
+ });
103
+ },
104
+
105
+ async save(options: SaveDialogOptions = {}): Promise<string | null> {
106
+ return NativeFs.showSaveDialog({
107
+ title: options.title,
108
+ defaultPath:
109
+ options.defaultPath === undefined
110
+ ? undefined
111
+ : toPath(options.defaultPath),
112
+ defaultName: options.defaultName,
113
+ filters: options.filters ?? [],
114
+ });
115
+ },
116
+ };
117
+
118
+ /* -------------------------------------------------------------------------- */
119
+ /* Known paths */
120
+ /* -------------------------------------------------------------------------- */
121
+
122
+ /** The well-known directories, as the native side reports them. */
123
+ export interface KnownPaths {
124
+ home: string;
125
+ documents: string;
126
+ downloads: string;
127
+ desktop: string;
128
+ temp: string;
129
+ appData: string;
130
+ appCache: string;
131
+ appLogs: string;
132
+ }
133
+
134
+ const KNOWN_PATH_NAMES = [
135
+ 'home',
136
+ 'documents',
137
+ 'downloads',
138
+ 'desktop',
139
+ 'temp',
140
+ 'appData',
141
+ 'appCache',
142
+ 'appLogs',
143
+ ] as const;
144
+
145
+ let cachedPaths: KnownPaths | null = null;
146
+
147
+ /**
148
+ * Reads the known paths once and remembers them.
149
+ *
150
+ * Lazy rather than at import time so that importing this package cannot fail: the
151
+ * native module is only asked when an app actually names a directory, and the
152
+ * native side creates the per-app folders on that first read.
153
+ */
154
+ function knownPaths(): KnownPaths {
155
+ if (cachedPaths === null) {
156
+ cachedPaths = NativeFs.getPaths();
157
+ }
158
+ return cachedPaths;
159
+ }
160
+
161
+ /**
162
+ * `paths.home`, `paths.documents` and the rest.
163
+ *
164
+ * Each property is a getter over one cached native read, so destructuring works
165
+ * and the object can be exported before the module has been asked anything.
166
+ */
167
+ export const paths: KnownPaths = Object.defineProperties(
168
+ {},
169
+ Object.fromEntries(
170
+ KNOWN_PATH_NAMES.map(name => [
171
+ name,
172
+ { enumerable: true, get: () => knownPaths()[name] },
173
+ ]),
174
+ ),
175
+ ) as KnownPaths;
176
+
177
+ /* -------------------------------------------------------------------------- */
178
+ /* Sync forms */
179
+ /* -------------------------------------------------------------------------- */
180
+
181
+ function readFileSync(path: string): Uint8Array;
182
+ function readFileSync(
183
+ path: string,
184
+ options: Encoding | { encoding: Encoding },
185
+ ): string;
186
+ function readFileSync(
187
+ path: string,
188
+ options?: Encoding | { encoding?: Encoding | null } | null,
189
+ ): Uint8Array | string {
190
+ const target = toPath(path);
191
+ const encoding =
192
+ typeof options === 'string' ? options : (options?.encoding ?? null);
193
+ try {
194
+ if (encoding === null) {
195
+ return new Uint8Array(NativeFs.readFileSync(target));
196
+ }
197
+ const normalized = normalizeEncoding(encoding) ?? 'utf8';
198
+ if (normalized === 'utf8') {
199
+ // The dedicated text method decodes in Rust instead of handing JS bytes
200
+ // to decode a second time.
201
+ return NativeFs.readTextFileSync(target);
202
+ }
203
+ return decodeString(
204
+ new Uint8Array(NativeFs.readFileSync(target)),
205
+ normalized,
206
+ );
207
+ } catch (error) {
208
+ throw withContext(error, 'open', target);
209
+ }
210
+ }
211
+
212
+ function writeFileSync(
213
+ path: string,
214
+ data: BinaryData,
215
+ options?: WriteOptions | Encoding,
216
+ ): void {
217
+ const target = toPath(path);
218
+ const opts: WriteOptions =
219
+ typeof options === 'string' ? { encoding: options } : (options ?? {});
220
+ const flag = flagFrom(
221
+ opts.flag,
222
+ constants.O_WRONLY | constants.O_CREAT | constants.O_TRUNC,
223
+ );
224
+ const mode = modeFrom(opts.mode);
225
+ try {
226
+ if (typeof data === 'string') {
227
+ const encoding = normalizeEncoding(opts.encoding ?? 'utf8') ?? 'utf8';
228
+ if (encoding === 'utf8') {
229
+ NativeFs.writeTextFileSync(target, data, flag, mode);
230
+ return;
231
+ }
232
+ NativeFs.writeFileSync(target, toArrayBuffer(data, encoding), flag, mode);
233
+ return;
234
+ }
235
+ NativeFs.writeFileSync(target, toArrayBuffer(data), flag, mode);
236
+ } catch (error) {
237
+ throw withContext(error, 'open', target);
238
+ }
239
+ }
240
+
241
+ function appendFileSync(
242
+ path: string,
243
+ data: BinaryData,
244
+ options?: WriteOptions | Encoding,
245
+ ): void {
246
+ const opts: WriteOptions =
247
+ typeof options === 'string' ? { encoding: options } : (options ?? {});
248
+ writeFileSync(path, data, {
249
+ ...opts,
250
+ flag:
251
+ opts.flag ?? constants.O_WRONLY | constants.O_CREAT | constants.O_APPEND,
252
+ });
253
+ }
254
+
255
+ function existsSync(path: string): boolean {
256
+ try {
257
+ return NativeFs.existsSync(toPath(path));
258
+ } catch {
259
+ // `existsSync` answers a question rather than reporting a failure, so every
260
+ // error — a permission problem as much as a missing path — is a `false`.
261
+ return false;
262
+ }
263
+ }
264
+
265
+ function statSync(path: string): Stats {
266
+ const target = toPath(path);
267
+ try {
268
+ const result = NativeFs.statSync(target, true);
269
+ if (result === null) {
270
+ throw missingPath(target, 'stat');
271
+ }
272
+ return new Stats(result);
273
+ } catch (error) {
274
+ throw withContext(error, 'stat', target);
275
+ }
276
+ }
277
+
278
+ function lstatSync(path: string): Stats {
279
+ const target = toPath(path);
280
+ try {
281
+ const result = NativeFs.statSync(target, false);
282
+ if (result === null) {
283
+ throw missingPath(target, 'lstat');
284
+ }
285
+ return new Stats(result);
286
+ } catch (error) {
287
+ throw withContext(error, 'lstat', target);
288
+ }
289
+ }
290
+
291
+ function readdirSync(
292
+ path: string,
293
+ options?: { withFileTypes?: false; recursive?: boolean } | null,
294
+ ): string[];
295
+ function readdirSync(
296
+ path: string,
297
+ options: { withFileTypes: true; recursive?: boolean },
298
+ ): Dirent[];
299
+ function readdirSync(
300
+ path: string,
301
+ options?: { withFileTypes?: boolean; recursive?: boolean } | null,
302
+ ): string[] | Dirent[] {
303
+ const target = toPath(path);
304
+ try {
305
+ const recursive = options?.recursive ?? false;
306
+ const entries = NativeFs.readdirSync(target, recursive);
307
+ return options?.withFileTypes
308
+ ? entries.map(entry => new Dirent(entry))
309
+ : entries.map(entry =>
310
+ recursive
311
+ ? relativeEntryName(target, entry.parentPath, entry.name)
312
+ : entry.name,
313
+ );
314
+ } catch (error) {
315
+ throw withContext(error, 'scandir', target);
316
+ }
317
+ }
318
+
319
+ function mkdirSync(path: string, options?: MkdirOptions | number): void {
320
+ const target = toPath(path);
321
+ const opts: MkdirOptions =
322
+ typeof options === 'number' ? { mode: options } : (options ?? {});
323
+ try {
324
+ NativeFs.mkdirSync(
325
+ target,
326
+ opts.recursive ?? false,
327
+ modeFrom(opts.mode, 0o777),
328
+ );
329
+ } catch (error) {
330
+ throw withContext(error, 'mkdir', target);
331
+ }
332
+ }
333
+
334
+ function rmSync(path: string, options?: RmOptions): void {
335
+ const target = toPath(path);
336
+ try {
337
+ NativeFs.rmSync(
338
+ target,
339
+ options?.recursive ?? false,
340
+ options?.force ?? false,
341
+ );
342
+ } catch (error) {
343
+ throw withContext(error, 'rm', target);
344
+ }
345
+ }
346
+
347
+ function renameSync(from: string, to: string): void {
348
+ const source = toPath(from);
349
+ const destination = toPath(to);
350
+ try {
351
+ NativeFs.renameSync(source, destination);
352
+ } catch (error) {
353
+ const wrapped = withContext(error, 'rename', source);
354
+ // Node's rename error names both ends; the second is `dest`, not `path`.
355
+ if (wrapped.dest === undefined) {
356
+ wrapped.dest = destination;
357
+ }
358
+ throw wrapped;
359
+ }
360
+ }
361
+
362
+ function copyFileSync(from: string, to: string): void {
363
+ const source = toPath(from);
364
+ const destination = toPath(to);
365
+ try {
366
+ NativeFs.copyFileSync(source, destination);
367
+ } catch (error) {
368
+ const wrapped = withContext(error, 'copyfile', source);
369
+ if (wrapped.dest === undefined) {
370
+ wrapped.dest = destination;
371
+ }
372
+ throw wrapped;
373
+ }
374
+ }
375
+
376
+ /* -------------------------------------------------------------------------- */
377
+ /* Callback forms */
378
+ /* -------------------------------------------------------------------------- */
379
+
380
+ /**
381
+ * The callback forms are wrappers over the promise implementations, as the
382
+ * design says, so there is one implementation of each operation.
383
+ *
384
+ * The trailing argument is the callback in every form; the optional argument
385
+ * before it is an options object (or an encoding, or a mode) when it is not a
386
+ * function. That is the whole of Node's overloading convention here.
387
+ */
388
+
389
+ type NodeCallback<T> = (error: NodeError | null, value?: T) => void;
390
+
391
+ function callbackOf<T>(args: unknown[]): NodeCallback<T> {
392
+ const callback = args[args.length - 1];
393
+ if (typeof callback !== 'function') {
394
+ throw new TypeError('A callback function is required');
395
+ }
396
+ return callback as NodeCallback<T>;
397
+ }
398
+
399
+ /** The options argument, if the caller supplied one rather than the callback. */
400
+ function before<T>(args: unknown[], index: number, fallback: T): T {
401
+ const value = args[index];
402
+ return value === undefined || typeof value === 'function'
403
+ ? fallback
404
+ : (value as T);
405
+ }
406
+
407
+ /**
408
+ * Reports a settled promise the Node way.
409
+ *
410
+ * A rejection that has a callback is delivered to it; nothing is thrown, because
411
+ * a throw from here would escape the caller's stack rather than the callback.
412
+ */
413
+ function settle<T>(promise: Promise<T>, callback: NodeCallback<T>): void {
414
+ promise.then(
415
+ value => callback(null, value),
416
+ (error: NodeError) => callback(error),
417
+ );
418
+ }
419
+
420
+ function readFile(
421
+ path: string,
422
+ callback: NodeCallback<Uint8Array | string>,
423
+ ): void;
424
+ function readFile(
425
+ path: string,
426
+ options: Encoding | { encoding?: Encoding | null } | null,
427
+ callback: NodeCallback<Uint8Array | string>,
428
+ ): void;
429
+ function readFile(path: string, ...args: unknown[]): void {
430
+ // The promise overloads keep the result precise; the callback form goes
431
+ // through the coarse alias because at that point the encoding is a runtime
432
+ // value and either shape is possible.
433
+ const options = before(args, 0, undefined) as
434
+ Encoding | { encoding?: Encoding | null } | null | undefined;
435
+ settle(readFileImpl(path, options), callbackOf(args));
436
+ }
437
+
438
+ function writeFile(
439
+ path: string,
440
+ data: BinaryData,
441
+ callback: NodeCallback<void>,
442
+ ): void;
443
+ function writeFile(
444
+ path: string,
445
+ data: BinaryData,
446
+ options: WriteOptions | Encoding | null,
447
+ callback: NodeCallback<void>,
448
+ ): void;
449
+ function writeFile(path: string, data: BinaryData, ...args: unknown[]): void {
450
+ settle(
451
+ promises.writeFile(
452
+ path,
453
+ data,
454
+ before(args, 0, undefined) as WriteOptions | Encoding | undefined,
455
+ ),
456
+ callbackOf(args),
457
+ );
458
+ }
459
+
460
+ function appendFile(
461
+ path: string,
462
+ data: BinaryData,
463
+ callback: NodeCallback<void>,
464
+ ): void;
465
+ function appendFile(
466
+ path: string,
467
+ data: BinaryData,
468
+ options: WriteOptions | Encoding | null,
469
+ callback: NodeCallback<void>,
470
+ ): void;
471
+ function appendFile(path: string, data: BinaryData, ...args: unknown[]): void {
472
+ settle(
473
+ promises.appendFile(
474
+ path,
475
+ data,
476
+ before(args, 0, undefined) as WriteOptions | Encoding | undefined,
477
+ ),
478
+ callbackOf(args),
479
+ );
480
+ }
481
+
482
+ function readdir(
483
+ path: string,
484
+ callback: NodeCallback<string[] | Dirent[]>,
485
+ ): void;
486
+ function readdir(
487
+ path: string,
488
+ options: { withFileTypes?: boolean; recursive?: boolean } | null,
489
+ callback: NodeCallback<string[] | Dirent[]>,
490
+ ): void;
491
+ function readdir(path: string, ...args: unknown[]): void {
492
+ const options = before(args, 0, null) as {
493
+ withFileTypes?: boolean;
494
+ recursive?: boolean;
495
+ } | null;
496
+ const pending: Promise<string[] | Dirent[]> = options?.withFileTypes
497
+ ? promises.readdir(path, { ...options, withFileTypes: true })
498
+ : promises.readdir(path, {
499
+ recursive: options?.recursive,
500
+ withFileTypes: false,
501
+ });
502
+ settle(pending, callbackOf(args));
503
+ }
504
+
505
+ function stat(path: string, callback: NodeCallback<Stats>): void;
506
+ function stat(path: string, ...args: unknown[]): void {
507
+ settle(promises.stat(path), callbackOf([...args]));
508
+ }
509
+
510
+ function lstat(path: string, callback: NodeCallback<Stats>): void;
511
+ function lstat(path: string, ...args: unknown[]): void {
512
+ settle(promises.lstat(path), callbackOf([...args]));
513
+ }
514
+
515
+ function mkdir(path: string, callback: NodeCallback<string | undefined>): void;
516
+ function mkdir(
517
+ path: string,
518
+ options: MkdirOptions | number | null,
519
+ callback: NodeCallback<string | undefined>,
520
+ ): void;
521
+ function mkdir(path: string, ...args: unknown[]): void {
522
+ settle(
523
+ promises.mkdir(
524
+ path,
525
+ before(args, 0, undefined) as MkdirOptions | number | undefined,
526
+ ),
527
+ callbackOf(args),
528
+ );
529
+ }
530
+
531
+ function rm(path: string, callback: NodeCallback<void>): void;
532
+ function rm(
533
+ path: string,
534
+ options: RmOptions | null,
535
+ callback: NodeCallback<void>,
536
+ ): void;
537
+ function rm(path: string, ...args: unknown[]): void {
538
+ settle(
539
+ promises.rm(path, before(args, 0, undefined) as RmOptions | undefined),
540
+ callbackOf(args),
541
+ );
542
+ }
543
+
544
+ function rename(from: string, to: string, callback: NodeCallback<void>): void;
545
+ function rename(from: string, to: string, ...args: unknown[]): void {
546
+ settle(promises.rename(from, to), callbackOf([...args]));
547
+ }
548
+
549
+ function copyFile(from: string, to: string, callback: NodeCallback<void>): void;
550
+ function copyFile(
551
+ from: string,
552
+ to: string,
553
+ mode: number,
554
+ callback: NodeCallback<void>,
555
+ ): void;
556
+ function copyFile(from: string, to: string, ...args: unknown[]): void {
557
+ settle(
558
+ promises.copyFile(
559
+ from,
560
+ to,
561
+ before(args, 0, undefined) as number | undefined,
562
+ ),
563
+ callbackOf(args),
564
+ );
565
+ }
566
+
567
+ function cp(from: string, to: string, callback: NodeCallback<void>): void;
568
+ function cp(
569
+ from: string,
570
+ to: string,
571
+ options: { recursive?: boolean } | null,
572
+ callback: NodeCallback<void>,
573
+ ): void;
574
+ function cp(from: string, to: string, ...args: unknown[]): void {
575
+ settle(
576
+ promises.cp(
577
+ from,
578
+ to,
579
+ before(args, 0, undefined) as { recursive?: boolean } | undefined,
580
+ ),
581
+ callbackOf(args),
582
+ );
583
+ }
584
+
585
+ function access(path: string, callback: NodeCallback<void>): void;
586
+ function access(path: string, mode: number, callback: NodeCallback<void>): void;
587
+ function access(path: string, ...args: unknown[]): void {
588
+ settle(
589
+ promises.access(path, before(args, 0, undefined) as number | undefined),
590
+ callbackOf(args),
591
+ );
592
+ }
593
+
594
+ function realpath(path: string, callback: NodeCallback<string>): void;
595
+ function realpath(path: string, ...args: unknown[]): void {
596
+ settle(promises.realpath(path), callbackOf([...args]));
597
+ }
598
+
599
+ function readlink(path: string, callback: NodeCallback<string>): void;
600
+ function readlink(path: string, ...args: unknown[]): void {
601
+ settle(promises.readlink(path), callbackOf([...args]));
602
+ }
603
+
604
+ function symlink(
605
+ target: string,
606
+ path: string,
607
+ callback: NodeCallback<void>,
608
+ ): void;
609
+ function symlink(
610
+ target: string,
611
+ path: string,
612
+ type: string,
613
+ callback: NodeCallback<void>,
614
+ ): void;
615
+ function symlink(target: string, path: string, ...args: unknown[]): void {
616
+ settle(
617
+ promises.symlink(
618
+ target,
619
+ path,
620
+ before(args, 0, undefined) as 'file' | 'dir' | 'junction' | undefined,
621
+ ),
622
+ callbackOf(args),
623
+ );
624
+ }
625
+
626
+ function truncate(path: string, callback: NodeCallback<void>): void;
627
+ function truncate(
628
+ path: string,
629
+ length: number,
630
+ callback: NodeCallback<void>,
631
+ ): void;
632
+ function truncate(path: string, ...args: unknown[]): void {
633
+ settle(
634
+ promises.truncate(path, before(args, 0, undefined) as number | undefined),
635
+ callbackOf(args),
636
+ );
637
+ }
638
+
639
+ function utimes(
640
+ path: string,
641
+ atime: Date | number | string,
642
+ mtime: Date | number | string,
643
+ callback: NodeCallback<void>,
644
+ ): void;
645
+ function utimes(
646
+ path: string,
647
+ atime: Date | number | string,
648
+ mtime: Date | number | string,
649
+ ...args: unknown[]
650
+ ): void {
651
+ settle(promises.utimes(path, atime, mtime), callbackOf([...args]));
652
+ }
653
+
654
+ function chmod(
655
+ path: string,
656
+ mode: number | string,
657
+ callback: NodeCallback<void>,
658
+ ): void;
659
+ function chmod(path: string, mode: number | string, ...args: unknown[]): void {
660
+ settle(promises.chmod(path, mode), callbackOf([...args]));
661
+ }
662
+
663
+ function mkdtemp(prefix: string, callback: NodeCallback<string>): void;
664
+ function mkdtemp(
665
+ prefix: string,
666
+ options: Encoding | { encoding?: Encoding },
667
+ callback: NodeCallback<string>,
668
+ ): void;
669
+ function mkdtemp(prefix: string, ...args: unknown[]): void {
670
+ settle(
671
+ promises.mkdtemp(
672
+ prefix,
673
+ before(args, 0, undefined) as
674
+ Encoding | { encoding?: Encoding } | undefined,
675
+ ),
676
+ callbackOf(args),
677
+ );
678
+ }
679
+
680
+ function open(path: string, callback: NodeCallback<FileHandle>): void;
681
+ function open(
682
+ path: string,
683
+ flags: string | number | OpenOptions,
684
+ callback: NodeCallback<FileHandle>,
685
+ ): void;
686
+ function open(
687
+ path: string,
688
+ flags: string | number | OpenOptions,
689
+ mode: number | string,
690
+ callback: NodeCallback<FileHandle>,
691
+ ): void;
692
+ function open(path: string, ...args: unknown[]): void {
693
+ const flags = before(args, 0, undefined) as
694
+ string | number | OpenOptions | undefined;
695
+ const mode = before(args, 1, undefined) as number | string | undefined;
696
+ settle(
697
+ mode === undefined
698
+ ? promises.open(path, flags)
699
+ : promises.open(path, flags, mode),
700
+ callbackOf(args),
701
+ );
702
+ }
703
+
704
+ function reveal(path: string, callback: NodeCallback<void>): void;
705
+ function reveal(path: string, ...args: unknown[]): void {
706
+ settle(promises.reveal(path), callbackOf([...args]));
707
+ }
708
+
709
+ function openExternal(path: string, callback: NodeCallback<void>): void;
710
+ function openExternal(path: string, ...args: unknown[]): void {
711
+ settle(promises.openExternal(path), callbackOf([...args]));
712
+ }
713
+
714
+ /* -------------------------------------------------------------------------- */
715
+ /* The `fs` object */
716
+ /* -------------------------------------------------------------------------- */
717
+
718
+ /**
719
+ * Node's `fs`, as far as this platform implements it.
720
+ *
721
+ * The callback forms wrap the promise implementations; the sync forms call the
722
+ * bridge's JS-thread methods directly; `promises` is the same object shape Node
723
+ * exposes as `fs.promises`.
724
+ */
725
+ const fs = {
726
+ constants,
727
+ promises,
728
+
729
+ readFile,
730
+ writeFile,
731
+ appendFile,
732
+ readdir,
733
+ stat,
734
+ lstat,
735
+ mkdir,
736
+ rm,
737
+ rename,
738
+ copyFile,
739
+ cp,
740
+ access,
741
+ realpath,
742
+ readlink,
743
+ symlink,
744
+ truncate,
745
+ utimes,
746
+ chmod,
747
+ mkdtemp,
748
+ open,
749
+ reveal,
750
+ openExternal,
751
+
752
+ readFileSync,
753
+ writeFileSync,
754
+ appendFileSync,
755
+ existsSync,
756
+ statSync,
757
+ lstatSync,
758
+ readdirSync,
759
+ mkdirSync,
760
+ rmSync,
761
+ renameSync,
762
+ copyFileSync,
763
+
764
+ Stats,
765
+ Dirent,
766
+ FileHandle,
767
+ };
768
+
769
+ export default fs;