@zenfs/core 1.3.6 → 1.4.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 (88) hide show
  1. package/dist/backends/memory.d.ts +4 -4
  2. package/dist/backends/memory.js +4 -4
  3. package/dist/backends/overlay.d.ts +5 -2
  4. package/dist/backends/overlay.js +7 -10
  5. package/dist/backends/port/fs.js +1 -4
  6. package/dist/config.js +4 -8
  7. package/dist/context.d.ts +32 -0
  8. package/dist/context.js +23 -0
  9. package/dist/credentials.d.ts +5 -5
  10. package/dist/credentials.js +10 -6
  11. package/dist/emulation/async.d.ts +90 -89
  12. package/dist/emulation/async.js +76 -75
  13. package/dist/emulation/dir.d.ts +3 -1
  14. package/dist/emulation/dir.js +6 -7
  15. package/dist/emulation/index.d.ts +1 -1
  16. package/dist/emulation/index.js +1 -1
  17. package/dist/emulation/promises.d.ts +50 -48
  18. package/dist/emulation/promises.js +78 -77
  19. package/dist/emulation/shared.d.ts +35 -8
  20. package/dist/emulation/shared.js +37 -11
  21. package/dist/emulation/sync.d.ts +63 -62
  22. package/dist/emulation/sync.js +72 -73
  23. package/dist/index.d.ts +1 -0
  24. package/dist/index.js +1 -0
  25. package/dist/stats.d.ts +2 -1
  26. package/dist/stats.js +5 -4
  27. package/package.json +3 -5
  28. package/scripts/test.js +78 -17
  29. package/tests/assignment.ts +1 -1
  30. package/tests/common/context.test.ts +19 -0
  31. package/tests/{devices.test.ts → common/devices.test.ts} +3 -3
  32. package/tests/{handle.test.ts → common/handle.test.ts} +1 -1
  33. package/tests/common/mounts.test.ts +36 -0
  34. package/tests/{mutex.test.ts → common/mutex.test.ts} +3 -3
  35. package/tests/common/path.test.ts +34 -0
  36. package/tests/common.ts +4 -3
  37. package/tests/fs/dir.test.ts +11 -11
  38. package/tests/fs/directory.test.ts +17 -17
  39. package/tests/fs/errors.test.ts +29 -39
  40. package/tests/fs/watch.test.ts +2 -2
  41. package/tests/setup/context.ts +9 -0
  42. package/tests/setup/cow+fetch.ts +1 -1
  43. package/tests/setup/memory.ts +1 -1
  44. package/tests/{setup/common.ts → setup.ts} +6 -5
  45. package/src/backends/backend.ts +0 -161
  46. package/src/backends/fetch.ts +0 -180
  47. package/src/backends/file_index.ts +0 -206
  48. package/src/backends/memory.ts +0 -45
  49. package/src/backends/overlay.ts +0 -560
  50. package/src/backends/port/fs.ts +0 -329
  51. package/src/backends/port/readme.md +0 -54
  52. package/src/backends/port/rpc.ts +0 -167
  53. package/src/backends/readme.md +0 -3
  54. package/src/backends/store/fs.ts +0 -667
  55. package/src/backends/store/readme.md +0 -9
  56. package/src/backends/store/simple.ts +0 -154
  57. package/src/backends/store/store.ts +0 -189
  58. package/src/config.ts +0 -227
  59. package/src/credentials.ts +0 -49
  60. package/src/devices.ts +0 -521
  61. package/src/emulation/async.ts +0 -834
  62. package/src/emulation/cache.ts +0 -86
  63. package/src/emulation/config.ts +0 -21
  64. package/src/emulation/constants.ts +0 -182
  65. package/src/emulation/dir.ts +0 -138
  66. package/src/emulation/index.ts +0 -8
  67. package/src/emulation/path.ts +0 -440
  68. package/src/emulation/promises.ts +0 -1140
  69. package/src/emulation/shared.ts +0 -172
  70. package/src/emulation/streams.ts +0 -34
  71. package/src/emulation/sync.ts +0 -863
  72. package/src/emulation/watchers.ts +0 -194
  73. package/src/error.ts +0 -307
  74. package/src/file.ts +0 -631
  75. package/src/filesystem.ts +0 -174
  76. package/src/index.ts +0 -35
  77. package/src/inode.ts +0 -128
  78. package/src/mixins/async.ts +0 -230
  79. package/src/mixins/index.ts +0 -5
  80. package/src/mixins/mutexed.ts +0 -257
  81. package/src/mixins/readonly.ts +0 -96
  82. package/src/mixins/shared.ts +0 -25
  83. package/src/mixins/sync.ts +0 -58
  84. package/src/polyfills.ts +0 -21
  85. package/src/stats.ts +0 -405
  86. package/src/utils.ts +0 -276
  87. package/tests/mounts.test.ts +0 -18
  88. package/tests/path.test.ts +0 -34
package/src/devices.ts DELETED
@@ -1,521 +0,0 @@
1
- /*
2
- This is a great resource: https://www.kernel.org/doc/html/latest/admin-guide/devices.html
3
- */
4
-
5
- import type { FileReadResult } from 'node:fs/promises';
6
- import { InMemoryStore } from './backends/memory.js';
7
- import { StoreFS } from './backends/store/fs.js';
8
- import { S_IFBLK, S_IFCHR } from './emulation/constants.js';
9
- import { Errno, ErrnoError } from './error.js';
10
- import { File } from './file.js';
11
- import type { StatsLike } from './stats.js';
12
- import { Stats } from './stats.js';
13
- import { basename, dirname } from './emulation/path.js';
14
-
15
- /**
16
- * A device
17
- * @todo Maybe add major/minor number or some other device information, like a UUID?
18
- * @privateRemarks
19
- * UUIDs were considered, however they don't make sense without an easy mechanism for persistance
20
- */
21
- export interface Device<TData = any> {
22
- /**
23
- * The device's driver
24
- */
25
- driver: DeviceDriver;
26
-
27
- /**
28
- * Which inode the device is assigned
29
- */
30
- ino: bigint;
31
-
32
- /**
33
- * Data associated with a device.
34
- * This is meant to be used by device drivers.
35
- * @experimental
36
- */
37
- data: TData;
38
-
39
- /**
40
- * Major device number
41
- * @experimental
42
- */
43
- major: number;
44
-
45
- /**
46
- * Minor device number
47
- * @experimental
48
- */
49
- minor: number;
50
- }
51
-
52
- /**
53
- * A device driver
54
- */
55
- export interface DeviceDriver<TData = any> {
56
- /**
57
- * The name of the device driver
58
- */
59
- name: string;
60
-
61
- /**
62
- * Whether the device is buffered (a "block" device) or unbuffered (a "character" device)
63
- * @default false
64
- */
65
- isBuffered?: boolean;
66
-
67
- /**
68
- * Initializes a new device.
69
- * @returns `Device.data`
70
- * @experimental
71
- */
72
- init?(ino: bigint): {
73
- data?: TData;
74
- minor?: number;
75
- major?: number;
76
- };
77
-
78
- /**
79
- * Synchronously read from the device
80
- * @group File operations
81
- */
82
- read(file: DeviceFile, buffer: ArrayBufferView, offset?: number, length?: number, position?: number): number;
83
-
84
- /**
85
- * Synchronously write to the device
86
- * @group File operations
87
- */
88
- write(file: DeviceFile, buffer: Uint8Array, offset: number, length: number, position?: number): number;
89
-
90
- /**
91
- * Sync the device
92
- * @group File operations
93
- */
94
- sync?(file: DeviceFile): void;
95
-
96
- /**
97
- * Close the device
98
- * @group File operations
99
- */
100
- close?(file: DeviceFile): void;
101
- }
102
-
103
- /**
104
- * The base class for device files
105
- * This class only does some simple things:
106
- * It implements `truncate` using `write` and it has non-device methods throw.
107
- * It is up to device drivers to implement the rest of the functionality.
108
- */
109
- export class DeviceFile extends File {
110
- public position = 0;
111
-
112
- public constructor(
113
- public fs: DeviceFS,
114
- path: string,
115
- public readonly device: Device
116
- ) {
117
- super(fs, path);
118
- }
119
-
120
- public get driver(): DeviceDriver {
121
- return this.device.driver;
122
- }
123
-
124
- protected get stats(): Partial<StatsLike> {
125
- return { mode: (this.driver.isBuffered ? S_IFBLK : S_IFCHR) | 0o666 };
126
- }
127
-
128
- public async stat(): Promise<Stats> {
129
- return Promise.resolve(new Stats(this.stats));
130
- }
131
-
132
- public statSync(): Stats {
133
- return new Stats(this.stats);
134
- }
135
-
136
- public readSync(buffer: ArrayBufferView, offset?: number, length?: number, position?: number): number {
137
- return this.driver.read(this, buffer, offset, length, position);
138
- }
139
-
140
- // eslint-disable-next-line @typescript-eslint/require-await
141
- public async read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number): Promise<FileReadResult<TBuffer>> {
142
- return { bytesRead: this.readSync(buffer, offset, length), buffer };
143
- }
144
-
145
- public writeSync(buffer: Uint8Array, offset = 0, length = buffer.length, position?: number): number {
146
- return this.driver.write(this, buffer, offset, length, position);
147
- }
148
-
149
- // eslint-disable-next-line @typescript-eslint/require-await
150
- public async write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number> {
151
- return this.writeSync(buffer, offset, length, position);
152
- }
153
-
154
- public async truncate(length: number): Promise<void> {
155
- const { size } = await this.stat();
156
-
157
- const buffer = new Uint8Array(length > size ? length - size : 0);
158
-
159
- await this.write(buffer, 0, buffer.length, length > size ? size : length);
160
- }
161
-
162
- public truncateSync(length: number): void {
163
- const { size } = this.statSync();
164
-
165
- const buffer = new Uint8Array(length > size ? length - size : 0);
166
-
167
- this.writeSync(buffer, 0, buffer.length, length > size ? size : length);
168
- }
169
-
170
- public closeSync(): void {
171
- this.driver.close?.(this);
172
- }
173
-
174
- public close(): Promise<void> {
175
- this.closeSync();
176
- return Promise.resolve();
177
- }
178
-
179
- public syncSync(): void {
180
- this.driver.sync?.(this);
181
- }
182
-
183
- public sync(): Promise<void> {
184
- this.syncSync();
185
- return Promise.resolve();
186
- }
187
-
188
- public chown(): Promise<void> {
189
- throw ErrnoError.With('ENOTSUP', this.path, 'chown');
190
- }
191
-
192
- public chownSync(): void {
193
- throw ErrnoError.With('ENOTSUP', this.path, 'chown');
194
- }
195
-
196
- public chmod(): Promise<void> {
197
- throw ErrnoError.With('ENOTSUP', this.path, 'chmod');
198
- }
199
-
200
- public chmodSync(): void {
201
- throw ErrnoError.With('ENOTSUP', this.path, 'chmod');
202
- }
203
-
204
- public utimes(): Promise<void> {
205
- throw ErrnoError.With('ENOTSUP', this.path, 'utimes');
206
- }
207
-
208
- public utimesSync(): void {
209
- throw ErrnoError.With('ENOTSUP', this.path, 'utimes');
210
- }
211
-
212
- public _setType(): Promise<void> {
213
- throw ErrnoError.With('ENOTSUP', this.path, '_setType');
214
- }
215
-
216
- public _setTypeSync(): void {
217
- throw ErrnoError.With('ENOTSUP', this.path, '_setType');
218
- }
219
- }
220
-
221
- /**
222
- * A temporary file system that manages and interfaces with devices
223
- */
224
- export class DeviceFS extends StoreFS<InMemoryStore> {
225
- protected readonly devices = new Map<string, Device>();
226
-
227
- /**
228
- * Creates a new device at `path` relative to the `DeviceFS` root.
229
- */
230
- public createDevice<TData = any>(path: string, driver: DeviceDriver<TData>): Device<TData | Record<string, never>> {
231
- if (this.existsSync(path)) {
232
- throw ErrnoError.With('EEXIST', path, 'mknod');
233
- }
234
- let ino = 1n;
235
- while (this.store.has(ino)) ino++;
236
- const dev = {
237
- driver,
238
- ino,
239
- data: {},
240
- minor: 0,
241
- major: 0,
242
- ...driver.init?.(ino),
243
- };
244
- this.devices.set(path, dev);
245
- return dev;
246
- }
247
-
248
- /**
249
- * Adds default devices
250
- */
251
- public addDefaults(): void {
252
- this.createDevice('/null', nullDevice);
253
- this.createDevice('/zero', zeroDevice);
254
- this.createDevice('/full', fullDevice);
255
- this.createDevice('/random', randomDevice);
256
- }
257
-
258
- public constructor() {
259
- super(new InMemoryStore('devfs'));
260
- }
261
-
262
- public async rename(oldPath: string, newPath: string): Promise<void> {
263
- if (this.devices.has(oldPath)) {
264
- throw ErrnoError.With('EPERM', oldPath, 'rename');
265
- }
266
- if (this.devices.has(newPath)) {
267
- throw ErrnoError.With('EEXIST', newPath, 'rename');
268
- }
269
- return super.rename(oldPath, newPath);
270
- }
271
-
272
- public renameSync(oldPath: string, newPath: string): void {
273
- if (this.devices.has(oldPath)) {
274
- throw ErrnoError.With('EPERM', oldPath, 'rename');
275
- }
276
- if (this.devices.has(newPath)) {
277
- throw ErrnoError.With('EEXIST', newPath, 'rename');
278
- }
279
- return super.renameSync(oldPath, newPath);
280
- }
281
-
282
- public async stat(path: string): Promise<Stats> {
283
- if (this.devices.has(path)) {
284
- await using file = await this.openFile(path, 'r');
285
- return file.stat();
286
- }
287
- return super.stat(path);
288
- }
289
-
290
- public statSync(path: string): Stats {
291
- if (this.devices.has(path)) {
292
- using file = this.openFileSync(path, 'r');
293
- return file.statSync();
294
- }
295
- return super.statSync(path);
296
- }
297
-
298
- public async openFile(path: string, flag: string): Promise<File> {
299
- if (this.devices.has(path)) {
300
- return new DeviceFile(this, path, this.devices.get(path)!);
301
- }
302
- return await super.openFile(path, flag);
303
- }
304
-
305
- public openFileSync(path: string, flag: string): File {
306
- if (this.devices.has(path)) {
307
- return new DeviceFile(this, path, this.devices.get(path)!);
308
- }
309
- return super.openFileSync(path, flag);
310
- }
311
-
312
- public async createFile(path: string, flag: string, mode: number): Promise<File> {
313
- if (this.devices.has(path)) {
314
- throw ErrnoError.With('EEXIST', path, 'createFile');
315
- }
316
- return super.createFile(path, flag, mode);
317
- }
318
-
319
- public createFileSync(path: string, flag: string, mode: number): File {
320
- if (this.devices.has(path)) {
321
- throw ErrnoError.With('EEXIST', path, 'createFile');
322
- }
323
- return super.createFileSync(path, flag, mode);
324
- }
325
-
326
- public async unlink(path: string): Promise<void> {
327
- if (this.devices.has(path)) {
328
- throw ErrnoError.With('EPERM', path, 'unlink');
329
- }
330
- return super.unlink(path);
331
- }
332
-
333
- public unlinkSync(path: string): void {
334
- if (this.devices.has(path)) {
335
- throw ErrnoError.With('EPERM', path, 'unlink');
336
- }
337
- return super.unlinkSync(path);
338
- }
339
-
340
- public async rmdir(path: string): Promise<void> {
341
- return super.rmdir(path);
342
- }
343
-
344
- public rmdirSync(path: string): void {
345
- return super.rmdirSync(path);
346
- }
347
-
348
- public async mkdir(path: string, mode: number): Promise<void> {
349
- if (this.devices.has(path)) {
350
- throw ErrnoError.With('EEXIST', path, 'mkdir');
351
- }
352
- return super.mkdir(path, mode);
353
- }
354
-
355
- public mkdirSync(path: string, mode: number): void {
356
- if (this.devices.has(path)) {
357
- throw ErrnoError.With('EEXIST', path, 'mkdir');
358
- }
359
- return super.mkdirSync(path, mode);
360
- }
361
-
362
- public async readdir(path: string): Promise<string[]> {
363
- const entries = await super.readdir(path);
364
- for (const dev of this.devices.keys()) {
365
- if (dirname(dev) == path) {
366
- entries.push(basename(dev));
367
- }
368
- }
369
- return entries;
370
- }
371
-
372
- public readdirSync(path: string): string[] {
373
- const entries = super.readdirSync(path);
374
- for (const dev of this.devices.keys()) {
375
- if (dirname(dev) == path) {
376
- entries.push(basename(dev));
377
- }
378
- }
379
- return entries;
380
- }
381
-
382
- public async link(target: string, link: string): Promise<void> {
383
- if (this.devices.has(target)) {
384
- throw ErrnoError.With('EPERM', target, 'rmdir');
385
- }
386
- if (this.devices.has(link)) {
387
- throw ErrnoError.With('EEXIST', link, 'link');
388
- }
389
- return super.link(target, link);
390
- }
391
-
392
- public linkSync(target: string, link: string): void {
393
- if (this.devices.has(target)) {
394
- throw ErrnoError.With('EPERM', target, 'rmdir');
395
- }
396
- if (this.devices.has(link)) {
397
- throw ErrnoError.With('EEXIST', link, 'link');
398
- }
399
- return super.linkSync(target, link);
400
- }
401
-
402
- public async sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
403
- if (this.devices.has(path)) {
404
- throw new ErrnoError(Errno.EINVAL, 'Attempted to sync a device incorrectly (bug)', path, 'sync');
405
- }
406
- return super.sync(path, data, stats);
407
- }
408
-
409
- public syncSync(path: string, data: Uint8Array, stats: Readonly<Stats>): void {
410
- if (this.devices.has(path)) {
411
- throw new ErrnoError(Errno.EINVAL, 'Attempted to sync a device incorrectly (bug)', path, 'sync');
412
- }
413
- return super.syncSync(path, data, stats);
414
- }
415
- }
416
-
417
- function defaultWrite(file: DeviceFile, buffer: Uint8Array, offset: number, length: number): number {
418
- file.position += length;
419
- return length;
420
- }
421
-
422
- /**
423
- * Simulates the `/dev/null` device.
424
- * - Reads return 0 bytes (EOF).
425
- * - Writes discard data, advancing the file position.
426
- * @experimental
427
- */
428
- export const nullDevice: DeviceDriver = {
429
- name: 'null',
430
- init() {
431
- return { major: 1, minor: 3 };
432
- },
433
- read(): number {
434
- return 0;
435
- },
436
- write: defaultWrite,
437
- };
438
-
439
- /**
440
- * Simulates the `/dev/zero` device
441
- * Provides an infinite stream of zeroes when read.
442
- * Discards any data written to it.
443
- *
444
- * - Reads fill the buffer with zeroes.
445
- * - Writes discard data but update the file position.
446
- * - Provides basic file metadata, treating it as a character device.
447
- * @experimental
448
- */
449
- export const zeroDevice: DeviceDriver = {
450
- name: 'zero',
451
- init() {
452
- return { major: 1, minor: 5 };
453
- },
454
- read(file: DeviceFile, buffer: ArrayBufferView, offset = 0, length = buffer.byteLength): number {
455
- const data = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
456
- for (let i = offset; i < offset + length; i++) {
457
- data[i] = 0;
458
- }
459
- file.position += length;
460
- return length;
461
- },
462
- write: defaultWrite,
463
- };
464
-
465
- /**
466
- * Simulates the `/dev/full` device.
467
- * - Reads behave like `/dev/zero` (returns zeroes).
468
- * - Writes always fail with ENOSPC (no space left on device).
469
- * @experimental
470
- */
471
- export const fullDevice: DeviceDriver = {
472
- name: 'full',
473
- init() {
474
- return { major: 1, minor: 7 };
475
- },
476
- read(file: DeviceFile, buffer: ArrayBufferView, offset = 0, length = buffer.byteLength): number {
477
- const data = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
478
- for (let i = offset; i < offset + length; i++) {
479
- data[i] = 0;
480
- }
481
- file.position += length;
482
- return length;
483
- },
484
-
485
- write(file: DeviceFile): number {
486
- throw ErrnoError.With('ENOSPC', file.path, 'write');
487
- },
488
- };
489
-
490
- /**
491
- * Simulates the `/dev/random` device.
492
- * - Reads return random bytes.
493
- * - Writes discard data, advancing the file position.
494
- * @experimental
495
- */
496
- export const randomDevice: DeviceDriver = {
497
- name: 'random',
498
- init() {
499
- return { major: 1, minor: 8 };
500
- },
501
- read(file: DeviceFile, buffer: ArrayBufferView, offset = 0, length = buffer.byteLength): number {
502
- const data = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
503
- for (let i = offset; i < offset + length; i++) {
504
- data[i] = Math.floor(Math.random() * 256);
505
- }
506
- file.position += length;
507
- return length;
508
- },
509
- write: defaultWrite,
510
- };
511
-
512
- /**
513
- * Shortcuts for importing.
514
- * @experimental
515
- */
516
- export default {
517
- null: nullDevice,
518
- zero: zeroDevice,
519
- full: fullDevice,
520
- random: randomDevice,
521
- };