@zenfs/core 1.11.4 → 2.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 (135) hide show
  1. package/dist/backends/backend.d.ts +19 -15
  2. package/dist/backends/backend.js +36 -19
  3. package/dist/backends/cow.d.ts +20 -30
  4. package/dist/backends/cow.js +83 -192
  5. package/dist/backends/fetch.d.ts +1 -0
  6. package/dist/backends/fetch.js +30 -30
  7. package/dist/backends/index.d.ts +1 -1
  8. package/dist/backends/index.js +1 -1
  9. package/dist/backends/memory.d.ts +5 -7
  10. package/dist/backends/memory.js +2 -3
  11. package/dist/backends/passthrough.d.ts +19 -23
  12. package/dist/backends/passthrough.js +98 -288
  13. package/dist/backends/port.d.ts +220 -0
  14. package/dist/backends/port.js +328 -0
  15. package/dist/backends/single_buffer.d.ts +59 -47
  16. package/dist/backends/single_buffer.js +468 -219
  17. package/dist/backends/store/fs.d.ts +25 -35
  18. package/dist/backends/store/fs.js +276 -315
  19. package/dist/backends/store/store.d.ts +10 -15
  20. package/dist/backends/store/store.js +11 -10
  21. package/dist/config.d.ts +3 -12
  22. package/dist/config.js +17 -19
  23. package/dist/context.d.ts +8 -21
  24. package/dist/context.js +33 -10
  25. package/dist/index.d.ts +2 -1
  26. package/dist/index.js +2 -1
  27. package/dist/internal/contexts.d.ts +63 -0
  28. package/dist/internal/contexts.js +15 -0
  29. package/dist/internal/credentials.d.ts +2 -11
  30. package/dist/internal/credentials.js +0 -19
  31. package/dist/internal/devices.d.ts +18 -80
  32. package/dist/internal/devices.js +103 -316
  33. package/dist/internal/error.d.ts +9 -204
  34. package/dist/internal/error.js +19 -288
  35. package/dist/internal/file_index.d.ts +1 -1
  36. package/dist/internal/file_index.js +11 -11
  37. package/dist/internal/filesystem.d.ts +51 -94
  38. package/dist/internal/filesystem.js +21 -20
  39. package/dist/internal/index.d.ts +1 -2
  40. package/dist/internal/index.js +1 -2
  41. package/dist/internal/index_fs.d.ts +12 -30
  42. package/dist/internal/index_fs.js +37 -69
  43. package/dist/internal/inode.d.ts +140 -24
  44. package/dist/internal/inode.js +515 -66
  45. package/dist/mixins/async.js +52 -112
  46. package/dist/mixins/mutexed.d.ts +19 -18
  47. package/dist/mixins/mutexed.js +62 -64
  48. package/dist/mixins/readonly.d.ts +7 -6
  49. package/dist/mixins/readonly.js +24 -18
  50. package/dist/mixins/sync.js +8 -8
  51. package/dist/{vfs/path.d.ts → path.d.ts} +3 -4
  52. package/dist/{vfs/path.js → path.js} +6 -9
  53. package/dist/polyfills.js +1 -1
  54. package/dist/readline.d.ts +134 -0
  55. package/dist/readline.js +623 -0
  56. package/dist/utils.d.ts +9 -37
  57. package/dist/utils.js +17 -85
  58. package/dist/vfs/acl.d.ts +42 -0
  59. package/dist/vfs/acl.js +268 -0
  60. package/dist/vfs/async.d.ts +9 -23
  61. package/dist/vfs/async.js +25 -27
  62. package/dist/vfs/config.d.ts +6 -18
  63. package/dist/vfs/config.js +8 -18
  64. package/dist/vfs/dir.d.ts +3 -3
  65. package/dist/vfs/dir.js +12 -12
  66. package/dist/vfs/file.d.ts +106 -0
  67. package/dist/vfs/file.js +244 -0
  68. package/dist/vfs/flags.d.ts +19 -0
  69. package/dist/vfs/flags.js +62 -0
  70. package/dist/vfs/index.d.ts +4 -10
  71. package/dist/vfs/index.js +4 -13
  72. package/dist/vfs/ioctl.d.ts +88 -0
  73. package/dist/vfs/ioctl.js +409 -0
  74. package/dist/vfs/promises.d.ts +81 -19
  75. package/dist/vfs/promises.js +404 -288
  76. package/dist/vfs/shared.d.ts +7 -37
  77. package/dist/vfs/shared.js +29 -85
  78. package/dist/{stats.d.ts → vfs/stats.d.ts} +14 -28
  79. package/dist/{stats.js → vfs/stats.js} +11 -66
  80. package/dist/vfs/streams.d.ts +1 -0
  81. package/dist/vfs/streams.js +32 -27
  82. package/dist/vfs/sync.d.ts +3 -3
  83. package/dist/vfs/sync.js +263 -260
  84. package/dist/vfs/watchers.d.ts +2 -2
  85. package/dist/vfs/watchers.js +12 -12
  86. package/dist/vfs/xattr.d.ts +116 -0
  87. package/dist/vfs/xattr.js +201 -0
  88. package/package.json +5 -3
  89. package/readme.md +1 -1
  90. package/scripts/test.js +2 -2
  91. package/tests/assignment.ts +1 -1
  92. package/tests/backend/config.worker.js +4 -1
  93. package/tests/backend/fetch.test.ts +3 -0
  94. package/tests/backend/port.test.ts +19 -33
  95. package/tests/backend/remote.worker.js +4 -1
  96. package/tests/backend/single-buffer.test.ts +53 -0
  97. package/tests/backend/single-buffer.worker.js +30 -0
  98. package/tests/common/context.test.ts +3 -3
  99. package/tests/common/handle.test.ts +17 -12
  100. package/tests/common/mutex.test.ts +9 -9
  101. package/tests/common/path.test.ts +1 -1
  102. package/tests/common/readline.test.ts +104 -0
  103. package/tests/common.ts +4 -19
  104. package/tests/fetch/fetch.ts +2 -2
  105. package/tests/fs/append.test.ts +4 -4
  106. package/tests/fs/directory.test.ts +25 -25
  107. package/tests/fs/errors.test.ts +15 -19
  108. package/tests/fs/links.test.ts +4 -3
  109. package/tests/fs/open.test.ts +4 -21
  110. package/tests/fs/permissions.test.ts +14 -18
  111. package/tests/fs/read.test.ts +10 -9
  112. package/tests/fs/readFile.test.ts +10 -26
  113. package/tests/fs/rename.test.ts +4 -9
  114. package/tests/fs/stat.test.ts +8 -8
  115. package/tests/fs/streams.test.ts +2 -11
  116. package/tests/fs/times.test.ts +7 -7
  117. package/tests/fs/truncate.test.ts +8 -36
  118. package/tests/fs/watch.test.ts +10 -10
  119. package/tests/fs/write.test.ts +77 -13
  120. package/tests/fs/xattr.test.ts +85 -0
  121. package/tests/logs.js +22 -0
  122. package/tests/setup/context.ts +1 -1
  123. package/tests/setup/index.ts +3 -3
  124. package/tests/setup/port.ts +7 -1
  125. package/dist/backends/port/fs.d.ts +0 -84
  126. package/dist/backends/port/fs.js +0 -151
  127. package/dist/backends/port/rpc.d.ts +0 -77
  128. package/dist/backends/port/rpc.js +0 -100
  129. package/dist/backends/store/simple.d.ts +0 -20
  130. package/dist/backends/store/simple.js +0 -13
  131. package/dist/internal/file.d.ts +0 -359
  132. package/dist/internal/file.js +0 -751
  133. package/dist/internal/log.d.ts +0 -133
  134. package/dist/internal/log.js +0 -218
  135. package/tests/fs/writeFile.test.ts +0 -70
package/dist/vfs/async.js CHANGED
@@ -1,10 +1,9 @@
1
1
  import { Buffer } from 'buffer';
2
- import { Errno, ErrnoError } from '../internal/error.js';
3
- import { BigIntStats } from '../stats.js';
2
+ import { UV, withErrno } from 'kerium';
4
3
  import { normalizeMode, normalizePath } from '../utils.js';
5
4
  import { R_OK } from './constants.js';
6
5
  import * as promises from './promises.js';
7
- import { fd2file, fdMap } from './shared.js';
6
+ import { BigIntStats } from './stats.js';
8
7
  import { ReadStream, WriteStream } from './streams.js';
9
8
  import { FSWatcher, StatWatcher } from './watchers.js';
10
9
  const nop = () => { };
@@ -31,7 +30,7 @@ rename;
31
30
  /**
32
31
  * Test whether or not `path` exists by checking with the file system.
33
32
  * Then call the callback argument with either true or false.
34
- * @deprecated Use {@link stat} or {@link access} instead.
33
+ * According to Node.js: deprecated Use {@link stat} or {@link access} instead.
35
34
  */
36
35
  export function exists(path, cb = nop) {
37
36
  promises.exists
@@ -108,39 +107,39 @@ export function appendFile(filename, data, cbEncOpts, cb = nop) {
108
107
  appendFile;
109
108
  export function fstat(fd, options, cb = nop) {
110
109
  cb = typeof options == 'function' ? options : cb;
111
- fd2file(fd)
110
+ new promises.FileHandle(this, fd)
112
111
  .stat()
113
112
  .then(stats => cb(undefined, typeof options == 'object' && (options === null || options === void 0 ? void 0 : options.bigint) ? new BigIntStats(stats) : stats))
114
113
  .catch(cb);
115
114
  }
116
115
  fstat;
117
116
  export function close(fd, cb = nop) {
118
- const close = fd2file(fd).close();
119
- fdMap.delete(fd);
120
- close.then(() => cb()).catch(cb);
117
+ new promises.FileHandle(this, fd)
118
+ .close()
119
+ .then(() => cb())
120
+ .catch(cb);
121
121
  }
122
122
  close;
123
123
  export function ftruncate(fd, lenOrCB, cb = nop) {
124
124
  const length = typeof lenOrCB === 'number' ? lenOrCB : 0;
125
125
  cb = typeof lenOrCB === 'function' ? lenOrCB : cb;
126
- const file = fd2file(fd);
127
- if (length < 0) {
128
- throw new ErrnoError(Errno.EINVAL);
129
- }
126
+ const file = new promises.FileHandle(this, fd);
127
+ if (length < 0)
128
+ throw withErrno('EINVAL');
130
129
  file.truncate(length)
131
130
  .then(() => cb())
132
131
  .catch(cb);
133
132
  }
134
133
  ftruncate;
135
134
  export function fsync(fd, cb = nop) {
136
- fd2file(fd)
135
+ new promises.FileHandle(this, fd)
137
136
  .sync()
138
137
  .then(() => cb())
139
138
  .catch(cb);
140
139
  }
141
140
  fsync;
142
141
  export function fdatasync(fd, cb = nop) {
143
- fd2file(fd)
142
+ new promises.FileHandle(this, fd)
144
143
  .datasync()
145
144
  .then(() => cb())
146
145
  .catch(cb);
@@ -148,7 +147,7 @@ export function fdatasync(fd, cb = nop) {
148
147
  fdatasync;
149
148
  export function write(fd, data, cbPosOff, cbLenEnc, cbPosEnc, cb = nop) {
150
149
  let buffer, offset, length, position, encoding;
151
- const handle = new promises.FileHandle(fd, this);
150
+ const handle = new promises.FileHandle(this, fd);
152
151
  if (typeof data === 'string') {
153
152
  // Signature 1: (fd, string, [position?, [encoding?]], cb?)
154
153
  encoding = 'utf8';
@@ -166,7 +165,7 @@ export function write(fd, data, cbPosOff, cbLenEnc, cbPosEnc, cb = nop) {
166
165
  default:
167
166
  // ...try to find the callback and get out of here!
168
167
  cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb);
169
- cb(new ErrnoError(Errno.EINVAL, 'Invalid arguments'));
168
+ cb(withErrno('EINVAL'));
170
169
  return;
171
170
  }
172
171
  buffer = Buffer.from(data);
@@ -202,21 +201,21 @@ write;
202
201
  * @param cb The number is the number of bytes read
203
202
  */
204
203
  export function read(fd, buffer, offset, length, position, cb = nop) {
205
- new promises.FileHandle(fd, this)
204
+ new promises.FileHandle(this, fd)
206
205
  .read(buffer, offset, length, position)
207
206
  .then(({ bytesRead, buffer }) => cb(undefined, bytesRead, buffer))
208
207
  .catch(cb);
209
208
  }
210
209
  read;
211
210
  export function fchown(fd, uid, gid, cb = nop) {
212
- new promises.FileHandle(fd, this)
211
+ new promises.FileHandle(this, fd)
213
212
  .chown(uid, gid)
214
213
  .then(() => cb())
215
214
  .catch(cb);
216
215
  }
217
216
  fchown;
218
217
  export function fchmod(fd, mode, cb) {
219
- new promises.FileHandle(fd, this)
218
+ new promises.FileHandle(this, fd)
220
219
  .chmod(mode)
221
220
  .then(() => cb())
222
221
  .catch(cb);
@@ -226,7 +225,7 @@ fchmod;
226
225
  * Change the file timestamps of a file referenced by the supplied file descriptor.
227
226
  */
228
227
  export function futimes(fd, atime, mtime, cb = nop) {
229
- new promises.FileHandle(fd, this)
228
+ new promises.FileHandle(this, fd)
230
229
  .utimes(atime, mtime)
231
230
  .then(() => cb())
232
231
  .catch(cb);
@@ -350,14 +349,13 @@ export function access(path, cbMode, cb = nop) {
350
349
  access;
351
350
  const statWatchers = new Map();
352
351
  export function watchFile(path, options, listener) {
353
- const normalizedPath = normalizePath(path.toString());
352
+ const normalizedPath = normalizePath(path);
354
353
  const opts = typeof options != 'function' ? options : {};
355
354
  if (typeof options == 'function') {
356
355
  listener = options;
357
356
  }
358
- if (!listener) {
359
- throw new ErrnoError(Errno.EINVAL, 'No listener specified', path.toString(), 'watchFile');
360
- }
357
+ if (!listener)
358
+ throw UV('EINVAL', 'watch', path.toString());
361
359
  if (statWatchers.has(normalizedPath)) {
362
360
  const entry = statWatchers.get(normalizedPath);
363
361
  if (entry) {
@@ -388,7 +386,7 @@ watchFile;
388
386
  * @param listener Optional listener to remove.
389
387
  */
390
388
  export function unwatchFile(path, listener = nop) {
391
- const normalizedPath = normalizePath(path.toString());
389
+ const normalizedPath = normalizePath(path);
392
390
  const entry = statWatchers.get(normalizedPath);
393
391
  if (entry) {
394
392
  if (listener && listener !== nop) {
@@ -465,7 +463,7 @@ export function copyFile(src, dest, flags, callback = nop) {
465
463
  copyFile;
466
464
  export function readv(fd, buffers, position, cb = nop) {
467
465
  cb = typeof position === 'function' ? position : cb;
468
- new promises.FileHandle(fd, this)
466
+ new promises.FileHandle(this, fd)
469
467
  .readv(buffers, typeof position === 'function' ? undefined : position)
470
468
  .then(({ buffers, bytesRead }) => cb(undefined, bytesRead, buffers))
471
469
  .catch(cb);
@@ -473,7 +471,7 @@ export function readv(fd, buffers, position, cb = nop) {
473
471
  readv;
474
472
  export function writev(fd, buffers, position, cb = nop) {
475
473
  cb = typeof position === 'function' ? position : cb;
476
- new promises.FileHandle(fd, this)
474
+ new promises.FileHandle(this, fd)
477
475
  .writev(buffers, typeof position === 'function' ? undefined : position)
478
476
  .then(({ buffers, bytesWritten }) => cb(undefined, bytesWritten, buffers))
479
477
  .catch(cb);
@@ -1,18 +1,6 @@
1
- export declare const config: {
2
- /**
3
- * Whether to perform access checks
4
- */
5
- checkAccess: boolean;
6
- /**
7
- * Whether to mark a file as dirty after updating its `atime` when read from
8
- */
9
- updateOnRead: boolean;
10
- /**
11
- * Whether to immediately sync when files are changed
12
- */
13
- syncImmediately: boolean;
14
- /**
15
- * If a file's buffer is not large enough to store content when writing and the buffer can't be resized, reuse the buffer passed to write()
16
- */
17
- unsafeBufferReplace: boolean;
18
- };
1
+ /** Whether to perform access checks */
2
+ export declare let checkAccess: boolean;
3
+ /**
4
+ * @internal @hidden
5
+ */
6
+ export declare function _setAccessChecks(value: boolean): void;
@@ -1,18 +1,8 @@
1
- export const config = {
2
- /**
3
- * Whether to perform access checks
4
- */
5
- checkAccess: true,
6
- /**
7
- * Whether to mark a file as dirty after updating its `atime` when read from
8
- */
9
- updateOnRead: true,
10
- /**
11
- * Whether to immediately sync when files are changed
12
- */
13
- syncImmediately: true,
14
- /**
15
- * If a file's buffer is not large enough to store content when writing and the buffer can't be resized, reuse the buffer passed to write()
16
- */
17
- unsafeBufferReplace: false,
18
- };
1
+ /** Whether to perform access checks */
2
+ export let checkAccess = true;
3
+ /**
4
+ * @internal @hidden
5
+ */
6
+ export function _setAccessChecks(value) {
7
+ checkAccess = value;
8
+ }
package/dist/vfs/dir.d.ts CHANGED
@@ -1,12 +1,12 @@
1
1
  import type { Dir as _Dir, Dirent as _Dirent } from 'node:fs';
2
2
  import type { V_Context } from '../context.js';
3
- import type { Stats } from '../stats.js';
3
+ import type { InodeLike } from '../internal/inode.js';
4
4
  import type { Callback } from '../utils.js';
5
5
  export declare class Dirent implements _Dirent {
6
6
  path: string;
7
- protected stats: Stats;
7
+ protected stats: InodeLike;
8
8
  get name(): string;
9
- constructor(path: string, stats: Stats);
9
+ constructor(path: string, stats: InodeLike);
10
10
  get parentPath(): string;
11
11
  isFile(): boolean;
12
12
  isDirectory(): boolean;
package/dist/vfs/dir.js CHANGED
@@ -1,5 +1,6 @@
1
- import { Errno, ErrnoError } from '../internal/error.js';
2
- import { basename } from './path.js';
1
+ import { withErrno } from 'kerium';
2
+ import { isBlockDevice, isCharacterDevice, isDirectory, isFIFO, isFile, isSocket, isSymbolicLink } from '../internal/inode.js';
3
+ import { basename } from '../path.js';
3
4
  import { readdir } from './promises.js';
4
5
  import { readdirSync } from './sync.js';
5
6
  export class Dirent {
@@ -14,25 +15,25 @@ export class Dirent {
14
15
  return this.path;
15
16
  }
16
17
  isFile() {
17
- return this.stats.isFile();
18
+ return isFile(this.stats);
18
19
  }
19
20
  isDirectory() {
20
- return this.stats.isDirectory();
21
+ return isDirectory(this.stats);
21
22
  }
22
23
  isBlockDevice() {
23
- return this.stats.isBlockDevice();
24
+ return isBlockDevice(this.stats);
24
25
  }
25
26
  isCharacterDevice() {
26
- return this.stats.isCharacterDevice();
27
+ return isCharacterDevice(this.stats);
27
28
  }
28
29
  isSymbolicLink() {
29
- return this.stats.isSymbolicLink();
30
+ return isSymbolicLink(this.stats);
30
31
  }
31
32
  isFIFO() {
32
- return this.stats.isFIFO();
33
+ return isFIFO(this.stats);
33
34
  }
34
35
  isSocket() {
35
- return this.stats.isSocket();
36
+ return isSocket(this.stats);
36
37
  }
37
38
  }
38
39
  /**
@@ -40,9 +41,8 @@ export class Dirent {
40
41
  */
41
42
  export class Dir {
42
43
  checkClosed() {
43
- if (this.closed) {
44
- throw new ErrnoError(Errno.EBADF, 'Can not use closed Dir');
45
- }
44
+ if (this.closed)
45
+ throw withErrno('EBADF', 'Can not use closed Dir');
46
46
  }
47
47
  constructor(path, context) {
48
48
  this.path = path;
@@ -0,0 +1,106 @@
1
+ import type { V_Context } from '../context.js';
2
+ import type { FileSystem, StreamOptions } from '../internal/filesystem.js';
3
+ import { type InodeLike } from '../internal/inode.js';
4
+ import '../polyfills.js';
5
+ /** @hidden */
6
+ export interface FileReadResult<T extends ArrayBufferView> {
7
+ bytesRead: number;
8
+ buffer: T;
9
+ }
10
+ /**
11
+ * @internal
12
+ */
13
+ export declare class SyncHandle {
14
+ readonly context: V_Context;
15
+ readonly path: string;
16
+ readonly fs: FileSystem;
17
+ readonly internalPath: string;
18
+ readonly flag: number;
19
+ readonly inode: InodeLike;
20
+ protected _buffer?: Uint8Array;
21
+ /**
22
+ * Current position
23
+ */
24
+ protected _position: number;
25
+ /**
26
+ * Get the current file position.
27
+ *
28
+ * We emulate the following bug mentioned in the Node documentation:
29
+ *
30
+ * On Linux, positional writes don't work when the file is opened in append mode.
31
+ * The kernel ignores the position argument and always appends the data to the end of the file.
32
+ * @returns The current file position.
33
+ */
34
+ get position(): number;
35
+ set position(value: number);
36
+ /**
37
+ * Whether the file has changes which have not been written to the FS
38
+ */
39
+ protected dirty: boolean;
40
+ /**
41
+ * Whether the file is open or closed
42
+ */
43
+ protected closed: boolean;
44
+ /**
45
+ * Creates a file with `path` and, optionally, the given contents.
46
+ * Note that, if contents is specified, it will be mutated by the file.
47
+ */
48
+ constructor(context: V_Context, path: string, fs: FileSystem, internalPath: string, flag: number, inode: InodeLike);
49
+ [Symbol.dispose](): void;
50
+ private get _isSync();
51
+ sync(): void;
52
+ /**
53
+ * Default implementation maps to `syncSync`.
54
+ */
55
+ datasync(): void;
56
+ close(): void;
57
+ /**
58
+ * Cleans up. This will *not* sync the file data to the FS
59
+ */
60
+ protected dispose(force?: boolean): void;
61
+ stat(): InodeLike;
62
+ truncate(length: number): void;
63
+ /**
64
+ * Write buffer to the file.
65
+ * @param buffer Uint8Array containing the data to write to the file.
66
+ * @param offset Offset in the buffer to start reading data from.
67
+ * @param length The amount of bytes to write to the file.
68
+ * @param position Offset from the beginning of the file where this data should be written.
69
+ * If position is null, the data will be written at the current position.
70
+ * @returns bytes written
71
+ */
72
+ write(buffer: Uint8Array, offset?: number, length?: number, position?: number): number;
73
+ /**
74
+ * Read data from the file.
75
+ * @param buffer The buffer that the data will be written to.
76
+ * @param offset The offset within the buffer where writing will start.
77
+ * @param length An integer specifying the number of bytes to read.
78
+ * @param position An integer specifying where to begin reading from in the file.
79
+ * If position is null, data will be read from the current file position.
80
+ * @returns number of bytes written
81
+ */
82
+ read(buffer: ArrayBufferView, offset?: number, length?: number, position?: number): number;
83
+ chmod(mode: number): void;
84
+ chown(uid: number, gid: number): void;
85
+ /**
86
+ * Change the file timestamps of the file.
87
+ */
88
+ utimes(atime: number, mtime: number): void;
89
+ /**
90
+ * Create a stream for reading the file.
91
+ */
92
+ streamRead(options: StreamOptions): ReadableStream;
93
+ /**
94
+ * Create a stream for writing the file.
95
+ */
96
+ streamWrite(options: StreamOptions): WritableStream;
97
+ }
98
+ /**
99
+ * @internal @hidden
100
+ */
101
+ export declare function toFD(file: SyncHandle): number;
102
+ /**
103
+ * @internal @hidden
104
+ */
105
+ export declare function fromFD($: V_Context, fd: number): SyncHandle;
106
+ export declare function deleteFD($: V_Context, fd: number): boolean;
@@ -0,0 +1,244 @@
1
+ import { UV, withErrno } from 'kerium';
2
+ import { defaultContext } from '../internal/contexts.js';
3
+ import { InodeFlags, isBlockDevice, isCharacterDevice } from '../internal/inode.js';
4
+ import '../polyfills.js';
5
+ import * as c from './constants.js';
6
+ import { _chown } from './stats.js';
7
+ /**
8
+ * @internal
9
+ */
10
+ export class SyncHandle {
11
+ /**
12
+ * Get the current file position.
13
+ *
14
+ * We emulate the following bug mentioned in the Node documentation:
15
+ *
16
+ * On Linux, positional writes don't work when the file is opened in append mode.
17
+ * The kernel ignores the position argument and always appends the data to the end of the file.
18
+ * @returns The current file position.
19
+ */
20
+ get position() {
21
+ return this.flag & c.O_APPEND ? this.inode.size : this._position;
22
+ }
23
+ set position(value) {
24
+ this._position = value;
25
+ }
26
+ /**
27
+ * Creates a file with `path` and, optionally, the given contents.
28
+ * Note that, if contents is specified, it will be mutated by the file.
29
+ */
30
+ constructor(context, path, fs, internalPath, flag, inode) {
31
+ this.context = context;
32
+ this.path = path;
33
+ this.fs = fs;
34
+ this.internalPath = internalPath;
35
+ this.flag = flag;
36
+ this.inode = inode;
37
+ /**
38
+ * Current position
39
+ */
40
+ this._position = 0;
41
+ /**
42
+ * Whether the file has changes which have not been written to the FS
43
+ */
44
+ this.dirty = false;
45
+ /**
46
+ * Whether the file is open or closed
47
+ */
48
+ this.closed = false;
49
+ }
50
+ [Symbol.dispose]() {
51
+ this.close();
52
+ }
53
+ get _isSync() {
54
+ return !!(this.flag & c.O_SYNC || this.inode.flags & InodeFlags.Sync || this.fs.attributes.has('sync'));
55
+ }
56
+ sync() {
57
+ if (this.closed)
58
+ throw UV('EBADF', 'sync', this.path);
59
+ if (!this.dirty)
60
+ return;
61
+ if (!this.fs.attributes.has('no_write'))
62
+ this.fs.touchSync(this.internalPath, this.inode);
63
+ this.dirty = false;
64
+ }
65
+ /**
66
+ * Default implementation maps to `syncSync`.
67
+ */
68
+ datasync() {
69
+ return this.sync();
70
+ }
71
+ close() {
72
+ if (this.closed)
73
+ throw UV('EBADF', 'close', this.path);
74
+ this.sync();
75
+ this.dispose();
76
+ }
77
+ /**
78
+ * Cleans up. This will *not* sync the file data to the FS
79
+ */
80
+ dispose(force) {
81
+ if (this.closed)
82
+ throw UV('EBADF', 'close', this.path);
83
+ if (this.dirty && !force)
84
+ throw UV('EBUSY', 'close', this.path);
85
+ this.closed = true;
86
+ }
87
+ stat() {
88
+ if (this.closed)
89
+ throw UV('EBADF', 'stat', this.path);
90
+ return this.inode;
91
+ }
92
+ truncate(length) {
93
+ if (length < 0)
94
+ throw UV('EINVAL', 'truncate', this.path);
95
+ if (this.closed)
96
+ throw UV('EBADF', 'truncate', this.path);
97
+ if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR))
98
+ throw UV('EBADF', 'truncate', this.path);
99
+ if (this.fs.attributes.has('readonly'))
100
+ throw UV('EROFS', 'truncate', this.path);
101
+ if (this.inode.flags & InodeFlags.Immutable)
102
+ throw UV('EPERM', 'truncate', this.path);
103
+ this.dirty = true;
104
+ this.inode.mtimeMs = Date.now();
105
+ this.inode.size = length;
106
+ this.inode.ctimeMs = Date.now();
107
+ if (this._isSync)
108
+ this.sync();
109
+ }
110
+ /**
111
+ * Write buffer to the file.
112
+ * @param buffer Uint8Array containing the data to write to the file.
113
+ * @param offset Offset in the buffer to start reading data from.
114
+ * @param length The amount of bytes to write to the file.
115
+ * @param position Offset from the beginning of the file where this data should be written.
116
+ * If position is null, the data will be written at the current position.
117
+ * @returns bytes written
118
+ */
119
+ write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
120
+ if (this.closed)
121
+ throw UV('EBADF', 'write', this.path);
122
+ if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR))
123
+ throw UV('EBADF', 'write', this.path);
124
+ if (this.fs.attributes.has('readonly'))
125
+ throw UV('EROFS', 'write', this.path);
126
+ if (this.inode.flags & InodeFlags.Immutable)
127
+ throw UV('EPERM', 'write', this.path);
128
+ this.dirty = true;
129
+ const end = position + length;
130
+ const slice = buffer.subarray(offset, offset + length);
131
+ if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size)
132
+ this.inode.size = end;
133
+ this.inode.mtimeMs = Date.now();
134
+ this.inode.ctimeMs = Date.now();
135
+ this._position = position + slice.byteLength;
136
+ this.fs.writeSync(this.internalPath, slice, position);
137
+ if (this._isSync)
138
+ this.sync();
139
+ return slice.byteLength;
140
+ }
141
+ /**
142
+ * Read data from the file.
143
+ * @param buffer The buffer that the data will be written to.
144
+ * @param offset The offset within the buffer where writing will start.
145
+ * @param length An integer specifying the number of bytes to read.
146
+ * @param position An integer specifying where to begin reading from in the file.
147
+ * If position is null, data will be read from the current file position.
148
+ * @returns number of bytes written
149
+ */
150
+ read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
151
+ if (this.closed)
152
+ throw UV('EBADF', 'read', this.path);
153
+ if (this.flag & c.O_WRONLY)
154
+ throw UV('EBADF', 'read', this.path);
155
+ if (!(this.inode.flags & InodeFlags.NoAtime) && !this.fs.attributes.has('no_atime')) {
156
+ this.dirty = true;
157
+ this.inode.atimeMs = Date.now();
158
+ }
159
+ let end = position + length;
160
+ if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size) {
161
+ end = position + Math.max(this.inode.size - position, 0);
162
+ }
163
+ this._position = end;
164
+ const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
165
+ this.fs.readSync(this.internalPath, uint8.subarray(offset, offset + length), position, end);
166
+ if (this._isSync)
167
+ this.sync();
168
+ return end - position;
169
+ }
170
+ chmod(mode) {
171
+ if (this.closed)
172
+ throw UV('EBADF', 'chmod', this.path);
173
+ this.dirty = true;
174
+ this.inode.mode = (this.inode.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
175
+ if (this._isSync || mode > c.S_IFMT)
176
+ this.sync();
177
+ }
178
+ chown(uid, gid) {
179
+ if (this.closed)
180
+ throw UV('EBADF', 'chmod', this.path);
181
+ this.dirty = true;
182
+ _chown(this.inode, uid, gid);
183
+ if (this._isSync)
184
+ this.sync();
185
+ }
186
+ /**
187
+ * Change the file timestamps of the file.
188
+ */
189
+ utimes(atime, mtime) {
190
+ if (this.closed)
191
+ throw UV('EBADF', 'utimes', this.path);
192
+ this.dirty = true;
193
+ this.inode.atimeMs = atime;
194
+ this.inode.mtimeMs = mtime;
195
+ if (this._isSync)
196
+ this.sync();
197
+ }
198
+ /**
199
+ * Create a stream for reading the file.
200
+ */
201
+ streamRead(options) {
202
+ if (this.closed)
203
+ throw UV('EBADF', 'streamRead', this.path);
204
+ return this.fs.streamRead(this.internalPath, options);
205
+ }
206
+ /**
207
+ * Create a stream for writing the file.
208
+ */
209
+ streamWrite(options) {
210
+ if (this.closed)
211
+ throw UV('EBADF', 'write', this.path);
212
+ if (this.inode.flags & InodeFlags.Immutable)
213
+ throw UV('EPERM', 'write', this.path);
214
+ if (this.fs.attributes.has('readonly'))
215
+ throw UV('EROFS', 'write', this.path);
216
+ return this.fs.streamWrite(this.internalPath, options);
217
+ }
218
+ }
219
+ // descriptors
220
+ /**
221
+ * @internal @hidden
222
+ */
223
+ export function toFD(file) {
224
+ var _a, _b;
225
+ const map = (_b = (_a = file.context) === null || _a === void 0 ? void 0 : _a.descriptors) !== null && _b !== void 0 ? _b : defaultContext.descriptors;
226
+ const fd = Math.max(map.size ? Math.max(...map.keys()) + 1 : 0, 4);
227
+ map.set(fd, file);
228
+ return fd;
229
+ }
230
+ /**
231
+ * @internal @hidden
232
+ */
233
+ export function fromFD($, fd) {
234
+ var _a;
235
+ const map = (_a = $ === null || $ === void 0 ? void 0 : $.descriptors) !== null && _a !== void 0 ? _a : defaultContext.descriptors;
236
+ const value = map.get(fd);
237
+ if (!value)
238
+ throw withErrno('EBADF');
239
+ return value;
240
+ }
241
+ export function deleteFD($, fd) {
242
+ var _a;
243
+ return ((_a = $ === null || $ === void 0 ? void 0 : $.descriptors) !== null && _a !== void 0 ? _a : defaultContext.descriptors).delete(fd);
244
+ }
@@ -0,0 +1,19 @@
1
+ export declare const pattern: RegExp;
2
+ /**
3
+ * @internal @hidden
4
+ */
5
+ export declare function parse(flag: string | number): number;
6
+ /**
7
+ * @internal @hidden
8
+ */
9
+ export declare function toString(flag: number): string;
10
+ /**
11
+ * @internal @hidden
12
+ */
13
+ export declare function toNumber(flag: string): number;
14
+ /**
15
+ * Parses a flag as a mode (W_OK, R_OK, and/or X_OK)
16
+ * @param flag the flag to parse
17
+ * @internal @hidden
18
+ */
19
+ export declare function toMode(flag: number): number;