@zenfs/core 2.4.1 → 2.4.2

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 (99) hide show
  1. package/COPYING.md +18 -0
  2. package/{readme.md → README.md} +16 -74
  3. package/dist/backends/fetch.js +1 -1
  4. package/dist/backends/memory.js +1 -1
  5. package/dist/backends/passthrough.d.ts +1 -2
  6. package/dist/backends/single_buffer.js +1 -1
  7. package/dist/backends/store/fs.js +4 -4
  8. package/dist/config.js +15 -15
  9. package/dist/context.js +3 -2
  10. package/dist/index.d.ts +9 -3
  11. package/dist/index.js +9 -4
  12. package/dist/internal/contexts.d.ts +5 -4
  13. package/dist/internal/devices.js +1 -1
  14. package/dist/internal/error.d.ts +11 -2
  15. package/dist/internal/error.js +38 -2
  16. package/dist/internal/file_index.js +1 -1
  17. package/dist/internal/index.d.ts +1 -0
  18. package/dist/internal/index.js +2 -1
  19. package/dist/internal/index_fs.js +1 -1
  20. package/dist/internal/inode.d.ts +51 -2
  21. package/dist/internal/inode.js +18 -2
  22. package/dist/mixins/shared.js +1 -1
  23. package/dist/node/async.d.ts +278 -0
  24. package/dist/node/async.js +518 -0
  25. package/dist/node/compat.d.ts +4 -0
  26. package/dist/node/compat.js +6 -0
  27. package/dist/node/dir.d.ts +78 -0
  28. package/dist/node/dir.js +150 -0
  29. package/dist/node/index.d.ts +8 -0
  30. package/dist/node/index.js +8 -0
  31. package/dist/{vfs → node}/promises.d.ts +10 -66
  32. package/dist/{vfs → node}/promises.js +141 -478
  33. package/dist/{vfs → node}/stats.d.ts +0 -4
  34. package/dist/{vfs → node}/stats.js +1 -16
  35. package/dist/{vfs → node}/streams.js +2 -2
  36. package/dist/node/sync.d.ts +252 -0
  37. package/dist/node/sync.js +682 -0
  38. package/dist/node/types.d.ts +21 -0
  39. package/dist/utils.d.ts +1 -7
  40. package/dist/utils.js +0 -6
  41. package/dist/vfs/acl.js +1 -1
  42. package/dist/vfs/async.d.ts +22 -278
  43. package/dist/vfs/async.js +212 -501
  44. package/dist/vfs/dir.d.ts +5 -82
  45. package/dist/vfs/dir.js +5 -233
  46. package/dist/vfs/file.d.ts +52 -13
  47. package/dist/vfs/file.js +167 -25
  48. package/dist/vfs/flags.js +1 -1
  49. package/dist/vfs/index.d.ts +2 -5
  50. package/dist/vfs/index.js +2 -5
  51. package/dist/vfs/shared.d.ts +25 -1
  52. package/dist/vfs/shared.js +6 -4
  53. package/dist/vfs/sync.d.ts +17 -245
  54. package/dist/vfs/sync.js +129 -773
  55. package/dist/vfs/watchers.d.ts +1 -1
  56. package/dist/vfs/watchers.js +2 -2
  57. package/dist/vfs/xattr.js +1 -1
  58. package/eslint.shared.js +1 -0
  59. package/package.json +7 -5
  60. package/scripts/make-index.js +5 -29
  61. package/scripts/test.js +59 -51
  62. package/tests/backend/fetch.test.ts +2 -2
  63. package/tests/backend/port.test.ts +2 -3
  64. package/tests/backend/single-buffer.test.ts +1 -1
  65. package/tests/common/casefold.test.ts +1 -1
  66. package/tests/common/context.test.ts +11 -4
  67. package/tests/common/devices.test.ts +3 -3
  68. package/tests/common/handle.test.ts +4 -3
  69. package/tests/common/inode.test.ts +2 -2
  70. package/tests/common/mounts.test.ts +1 -3
  71. package/tests/common/mutex.test.ts +1 -3
  72. package/tests/common/path.test.ts +2 -2
  73. package/tests/common/readline.test.ts +1 -1
  74. package/tests/common.ts +5 -4
  75. package/tests/fetch/fetch.ts +1 -1
  76. package/tests/fs/dir.test.ts +3 -43
  77. package/tests/fs/directory.test.ts +4 -4
  78. package/tests/fs/errors.test.ts +2 -2
  79. package/tests/fs/links.test.ts +1 -1
  80. package/tests/fs/permissions.test.ts +3 -3
  81. package/tests/fs/read.test.ts +1 -1
  82. package/tests/fs/scaling.test.ts +1 -1
  83. package/tests/fs/stat.test.ts +1 -2
  84. package/tests/fs/times.test.ts +1 -1
  85. package/tests/fs/watch.test.ts +3 -2
  86. package/tests/setup/context.ts +1 -2
  87. package/tests/setup/cow.ts +1 -1
  88. package/tests/setup/index.ts +2 -2
  89. package/tests/setup/port.ts +1 -1
  90. package/tests/setup/single-buffer.ts +1 -1
  91. package/tests/setup.ts +4 -3
  92. package/dist/vfs/types.d.ts +0 -24
  93. package/tests/assignment.ts +0 -21
  94. /package/dist/{vfs/constants.d.ts → constants.d.ts} +0 -0
  95. /package/dist/{vfs/constants.js → constants.js} +0 -0
  96. /package/dist/{readline.d.ts → node/readline.d.ts} +0 -0
  97. /package/dist/{readline.js → node/readline.js} +0 -0
  98. /package/dist/{vfs → node}/streams.d.ts +0 -0
  99. /package/dist/{vfs → node}/types.js +0 -0
package/dist/vfs/file.js CHANGED
@@ -1,14 +1,13 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
2
  import { UV, withErrno } from 'kerium';
3
+ import * as c from '../constants.js';
3
4
  import { defaultContext } from '../internal/contexts.js';
4
- import { InodeFlags, isBlockDevice, isCharacterDevice } from '../internal/inode.js';
5
+ import { _chown, InodeFlags, isBlockDevice, isCharacterDevice } from '../internal/inode.js';
5
6
  import '../polyfills.js';
6
- import * as c from './constants.js';
7
- import { _chown } from './stats.js';
8
7
  /**
9
8
  * @internal
10
9
  */
11
- export class SyncHandle {
10
+ export class Handle {
12
11
  context;
13
12
  path;
14
13
  fs;
@@ -43,6 +42,9 @@ export class SyncHandle {
43
42
  * Whether the file is open or closed
44
43
  */
45
44
  closed = false;
45
+ get isClosed() {
46
+ return this.closed;
47
+ }
46
48
  /**
47
49
  * Creates a file with `path` and, optionally, the given contents.
48
50
  * Note that, if contents is specified, it will be mutated by the file.
@@ -55,13 +57,13 @@ export class SyncHandle {
55
57
  this.flag = flag;
56
58
  this.inode = inode;
57
59
  }
58
- [Symbol.dispose]() {
59
- this.close();
60
- }
61
60
  get _isSync() {
62
61
  return !!(this.flag & c.O_SYNC || this.inode.flags & InodeFlags.Sync || this.fs.attributes.has('sync'));
63
62
  }
64
- sync() {
63
+ [Symbol.dispose]() {
64
+ this.closeSync();
65
+ }
66
+ syncSync() {
65
67
  if (this.closed)
66
68
  throw UV('EBADF', 'sync', this.path);
67
69
  if (!this.dirty)
@@ -73,13 +75,153 @@ export class SyncHandle {
73
75
  /**
74
76
  * Default implementation maps to `syncSync`.
75
77
  */
78
+ datasyncSync() {
79
+ return this.syncSync();
80
+ }
81
+ closeSync() {
82
+ if (this.closed)
83
+ throw UV('EBADF', 'close', this.path);
84
+ this.syncSync();
85
+ this.disposeSync();
86
+ }
87
+ /**
88
+ * Cleans up. This will *not* sync the file data to the FS
89
+ */
90
+ disposeSync(force) {
91
+ if (this.closed)
92
+ throw UV('EBADF', 'close', this.path);
93
+ if (this.dirty && !force)
94
+ throw UV('EBUSY', 'close', this.path);
95
+ this.closed = true;
96
+ }
97
+ truncateSync(length) {
98
+ if (length < 0)
99
+ throw UV('EINVAL', 'truncate', this.path);
100
+ if (this.closed)
101
+ throw UV('EBADF', 'truncate', this.path);
102
+ if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR))
103
+ throw UV('EBADF', 'truncate', this.path);
104
+ if (this.fs.attributes.has('readonly'))
105
+ throw UV('EROFS', 'truncate', this.path);
106
+ if (this.inode.flags & InodeFlags.Immutable)
107
+ throw UV('EPERM', 'truncate', this.path);
108
+ this.dirty = true;
109
+ this.inode.mtimeMs = Date.now();
110
+ this.inode.size = length;
111
+ this.inode.ctimeMs = Date.now();
112
+ if (this._isSync)
113
+ this.syncSync();
114
+ }
115
+ /**
116
+ * Write buffer to the file.
117
+ * @param buffer Uint8Array containing the data to write to the file.
118
+ * @param offset Offset in the buffer to start reading data from.
119
+ * @param length The amount of bytes to write to the file.
120
+ * @param position Offset from the beginning of the file where this data should be written.
121
+ * If position is null, the data will be written at the current position.
122
+ * @returns bytes written
123
+ */
124
+ writeSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
125
+ if (this.closed)
126
+ throw UV('EBADF', 'write', this.path);
127
+ if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR))
128
+ throw UV('EBADF', 'write', this.path);
129
+ if (this.fs.attributes.has('readonly'))
130
+ throw UV('EROFS', 'write', this.path);
131
+ if (this.inode.flags & InodeFlags.Immutable)
132
+ throw UV('EPERM', 'write', this.path);
133
+ this.dirty = true;
134
+ const end = position + length;
135
+ const slice = buffer.subarray(offset, offset + length);
136
+ if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size)
137
+ this.inode.size = end;
138
+ this.inode.mtimeMs = Date.now();
139
+ this.inode.ctimeMs = Date.now();
140
+ this._position = position + slice.byteLength;
141
+ this.fs.writeSync(this.internalPath, slice, position);
142
+ if (this._isSync)
143
+ this.syncSync();
144
+ return slice.byteLength;
145
+ }
146
+ /**
147
+ * Read data from the file.
148
+ * @param buffer The buffer that the data will be written to.
149
+ * @param offset The offset within the buffer where writing will start.
150
+ * @param length An integer specifying the number of bytes to read.
151
+ * @param position An integer specifying where to begin reading from in the file.
152
+ * If position is null, data will be read from the current file position.
153
+ * @returns number of bytes written
154
+ */
155
+ readSync(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
156
+ if (this.closed)
157
+ throw UV('EBADF', 'read', this.path);
158
+ if (this.flag & c.O_WRONLY)
159
+ throw UV('EBADF', 'read', this.path);
160
+ if (!(this.inode.flags & InodeFlags.NoAtime) && !this.fs.attributes.has('no_atime')) {
161
+ this.dirty = true;
162
+ this.inode.atimeMs = Date.now();
163
+ }
164
+ let end = position + length;
165
+ if (!isCharacterDevice(this.inode) && !isBlockDevice(this.inode) && end > this.inode.size) {
166
+ end = position + Math.max(this.inode.size - position, 0);
167
+ }
168
+ this._position = end;
169
+ const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
170
+ this.fs.readSync(this.internalPath, uint8.subarray(offset, offset + length), position, end);
171
+ if (this._isSync)
172
+ this.syncSync();
173
+ return end - position;
174
+ }
175
+ chmodSync(mode) {
176
+ if (this.closed)
177
+ throw UV('EBADF', 'chmod', this.path);
178
+ this.dirty = true;
179
+ this.inode.mode = (this.inode.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
180
+ if (this._isSync || mode > c.S_IFMT)
181
+ this.syncSync();
182
+ }
183
+ chownSync(uid, gid) {
184
+ if (this.closed)
185
+ throw UV('EBADF', 'chmod', this.path);
186
+ this.dirty = true;
187
+ _chown(this.inode, uid, gid);
188
+ if (this._isSync)
189
+ this.syncSync();
190
+ }
191
+ /**
192
+ * Change the file timestamps of the file.
193
+ */
194
+ utimesSync(atime, mtime) {
195
+ if (this.closed)
196
+ throw UV('EBADF', 'utimes', this.path);
197
+ this.dirty = true;
198
+ this.inode.atimeMs = atime;
199
+ this.inode.mtimeMs = mtime;
200
+ if (this._isSync)
201
+ this.syncSync();
202
+ }
203
+ async [Symbol.asyncDispose]() {
204
+ await this.close();
205
+ }
206
+ async sync() {
207
+ if (this.closed)
208
+ throw UV('EBADF', 'sync', this.path);
209
+ if (!this.dirty)
210
+ return;
211
+ if (!this.fs.attributes.has('no_write'))
212
+ await this.fs.touch(this.internalPath, this.inode);
213
+ this.dirty = false;
214
+ }
215
+ /**
216
+ * Default implementation maps to `sync`.
217
+ */
76
218
  datasync() {
77
219
  return this.sync();
78
220
  }
79
- close() {
221
+ async close() {
80
222
  if (this.closed)
81
223
  throw UV('EBADF', 'close', this.path);
82
- this.sync();
224
+ await this.sync();
83
225
  this.dispose();
84
226
  }
85
227
  /**
@@ -97,7 +239,7 @@ export class SyncHandle {
97
239
  throw UV('EBADF', 'stat', this.path);
98
240
  return this.inode;
99
241
  }
100
- truncate(length) {
242
+ async truncate(length) {
101
243
  if (length < 0)
102
244
  throw UV('EINVAL', 'truncate', this.path);
103
245
  if (this.closed)
@@ -113,7 +255,7 @@ export class SyncHandle {
113
255
  this.inode.size = length;
114
256
  this.inode.ctimeMs = Date.now();
115
257
  if (this._isSync)
116
- this.sync();
258
+ await this.sync();
117
259
  }
118
260
  /**
119
261
  * Write buffer to the file.
@@ -124,7 +266,7 @@ export class SyncHandle {
124
266
  * If position is null, the data will be written at the current position.
125
267
  * @returns bytes written
126
268
  */
127
- write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
269
+ async write(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
128
270
  if (this.closed)
129
271
  throw UV('EBADF', 'write', this.path);
130
272
  if (!(this.flag & c.O_WRONLY || this.flag & c.O_RDWR))
@@ -141,9 +283,9 @@ export class SyncHandle {
141
283
  this.inode.mtimeMs = Date.now();
142
284
  this.inode.ctimeMs = Date.now();
143
285
  this._position = position + slice.byteLength;
144
- this.fs.writeSync(this.internalPath, slice, position);
286
+ await this.fs.write(this.internalPath, slice, position);
145
287
  if (this._isSync)
146
- this.sync();
288
+ await this.sync();
147
289
  return slice.byteLength;
148
290
  }
149
291
  /**
@@ -155,7 +297,7 @@ export class SyncHandle {
155
297
  * If position is null, data will be read from the current file position.
156
298
  * @returns number of bytes written
157
299
  */
158
- read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
300
+ async read(buffer, offset = 0, length = buffer.byteLength - offset, position = this.position) {
159
301
  if (this.closed)
160
302
  throw UV('EBADF', 'read', this.path);
161
303
  if (this.flag & c.O_WRONLY)
@@ -170,38 +312,38 @@ export class SyncHandle {
170
312
  }
171
313
  this._position = end;
172
314
  const uint8 = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
173
- this.fs.readSync(this.internalPath, uint8.subarray(offset, offset + length), position, end);
315
+ await this.fs.read(this.internalPath, uint8.subarray(offset, offset + length), position, end);
174
316
  if (this._isSync)
175
- this.sync();
317
+ await this.sync();
176
318
  return end - position;
177
319
  }
178
- chmod(mode) {
320
+ async chmod(mode) {
179
321
  if (this.closed)
180
322
  throw UV('EBADF', 'chmod', this.path);
181
323
  this.dirty = true;
182
324
  this.inode.mode = (this.inode.mode & (mode > c.S_IFMT ? ~c.S_IFMT : c.S_IFMT)) | mode;
183
325
  if (this._isSync || mode > c.S_IFMT)
184
- this.sync();
326
+ await this.sync();
185
327
  }
186
- chown(uid, gid) {
328
+ async chown(uid, gid) {
187
329
  if (this.closed)
188
- throw UV('EBADF', 'chmod', this.path);
330
+ throw UV('EBADF', 'chown', this.path);
189
331
  this.dirty = true;
190
332
  _chown(this.inode, uid, gid);
191
333
  if (this._isSync)
192
- this.sync();
334
+ await this.sync();
193
335
  }
194
336
  /**
195
337
  * Change the file timestamps of the file.
196
338
  */
197
- utimes(atime, mtime) {
339
+ async utimes(atime, mtime) {
198
340
  if (this.closed)
199
341
  throw UV('EBADF', 'utimes', this.path);
200
342
  this.dirty = true;
201
343
  this.inode.atimeMs = atime;
202
344
  this.inode.mtimeMs = mtime;
203
345
  if (this._isSync)
204
- this.sync();
346
+ await this.sync();
205
347
  }
206
348
  /**
207
349
  * Create a stream for reading the file.
package/dist/vfs/flags.js CHANGED
@@ -1,6 +1,6 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
2
  import { withErrno } from 'kerium';
3
- import * as c from './constants.js';
3
+ import * as c from '../constants.js';
4
4
  export const pattern = /[rwasx]{1,2}\+?/;
5
5
  /**
6
6
  * @internal @hidden
@@ -1,10 +1,7 @@
1
1
  export * from './async.js';
2
- export * as constants from './constants.js';
3
2
  export * from './dir.js';
3
+ export * from './file.js';
4
4
  export * from './ioctl.js';
5
- export * as promises from './promises.js';
6
5
  export { chroot, mount, umount } from './shared.js';
7
- export { BigIntStatsFs, Stats, StatsFs } from './stats.js';
8
- export * from './streams.js';
9
- export * from './sync.js';
6
+ export * as sync from './sync.js';
10
7
  export * as xattr from './xattr.js';
package/dist/vfs/index.js CHANGED
@@ -1,11 +1,8 @@
1
1
  // SPDX-License-Identifier: LGPL-3.0-or-later
2
2
  export * from './async.js';
3
- export * as constants from './constants.js';
4
3
  export * from './dir.js';
4
+ export * from './file.js';
5
5
  export * from './ioctl.js';
6
- export * as promises from './promises.js';
7
6
  export { chroot, mount, umount } from './shared.js';
8
- export { BigIntStatsFs, Stats, StatsFs } from './stats.js';
9
- export * from './streams.js';
10
- export * from './sync.js';
7
+ export * as sync from './sync.js';
11
8
  export * as xattr from './xattr.js';
@@ -2,6 +2,7 @@ import type * as fs from 'node:fs';
2
2
  import type { V_Context } from '../context.js';
3
3
  import type { FileSystem } from '../internal/filesystem.js';
4
4
  import type { InodeLike } from '../internal/inode.js';
5
+ import { type ExceptionExtra } from 'kerium';
5
6
  import { type AbsolutePath } from '../path.js';
6
7
  /**
7
8
  * @internal @hidden
@@ -46,7 +47,7 @@ export interface ResolvedPath extends ResolvedMount {
46
47
  * Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
47
48
  * @internal @hidden
48
49
  */
49
- export declare function resolveMount(path: string, ctx: V_Context): ResolvedMount;
50
+ export declare function resolveMount(path: string, ctx: V_Context, extra?: ExceptionExtra): ResolvedMount;
50
51
  /**
51
52
  * @internal @hidden
52
53
  */
@@ -56,3 +57,26 @@ export declare function _statfs<const T extends boolean>(fs: FileSystem, bigint?
56
57
  * @category Backends and Configuration
57
58
  */
58
59
  export declare function chroot(this: V_Context, path: string): void;
60
+ export type FileContents = ArrayBufferView | string;
61
+ /**
62
+ * @internal @hidden Used for the internal `_open` functions
63
+ */
64
+ export interface OpenOptions {
65
+ flag: fs.OpenMode;
66
+ mode?: fs.Mode | null;
67
+ /**
68
+ * If true, do not resolve symlinks
69
+ */
70
+ preserveSymlinks?: boolean;
71
+ /**
72
+ * If true, allows opening directories
73
+ */
74
+ allowDirectory?: boolean;
75
+ }
76
+ export interface MkdirOptions {
77
+ mode?: number;
78
+ recursive?: boolean;
79
+ }
80
+ export interface ReaddirOptions {
81
+ recursive?: boolean;
82
+ }
@@ -3,11 +3,12 @@
3
3
  import { Errno, Exception, UV, withErrno } from 'kerium';
4
4
  import { alert, debug, err, info, notice, warn } from 'kerium/log';
5
5
  import { InMemory } from '../backends/memory.js';
6
+ import { size_max } from '../constants.js';
6
7
  import { defaultContext } from '../internal/contexts.js';
8
+ import { credentialsAllowRoot } from '../internal/credentials.js';
9
+ import { withExceptionContext } from '../internal/error.js';
7
10
  import { join, resolve } from '../path.js';
8
11
  import { normalizePath } from '../utils.js';
9
- import { size_max } from './constants.js';
10
- import { credentialsAllowRoot } from '../internal/credentials.js';
11
12
  /**
12
13
  * The map of mount points
13
14
  * @category Backends and Configuration
@@ -51,8 +52,9 @@ export function umount(mountPoint) {
51
52
  * Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
52
53
  * @internal @hidden
53
54
  */
54
- export function resolveMount(path, ctx) {
55
+ export function resolveMount(path, ctx, extra) {
55
56
  const root = ctx?.root || defaultContext.root;
57
+ const _exceptionContext = { path, ...extra };
56
58
  path = normalizePath(join(root, path));
57
59
  const sortedMounts = [...mounts].sort((a, b) => (a[0].length > b[0].length ? -1 : 1)); // descending order of the string length
58
60
  for (const [mountPoint, fs] of sortedMounts) {
@@ -67,7 +69,7 @@ export function resolveMount(path, ctx) {
67
69
  path = path.toLowerCase();
68
70
  if (case_fold === 'upper')
69
71
  path = path.toUpperCase();
70
- return { fs, path, mountPoint, root };
72
+ return { fs: withExceptionContext(fs, _exceptionContext), path, mountPoint, root };
71
73
  }
72
74
  throw alert(new Exception(Errno.EIO, 'No file system for ' + path));
73
75
  }