@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/scripts/test.js CHANGED
@@ -1,16 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { execSync } from 'node:child_process';
4
- import { existsSync } from 'node:fs';
4
+ import { existsSync, mkdirSync, rmSync, globSync } from 'node:fs';
5
5
  import { join } from 'node:path';
6
6
  import { parseArgs } from 'node:util';
7
7
 
8
8
  const { values: options, positionals } = parseArgs({
9
9
  options: {
10
10
  help: { short: 'h', type: 'boolean', default: false },
11
- verbose: { type: 'boolean', default: false },
12
- test: { type: 'string' },
13
- forceExit: { short: 'f', type: 'boolean', default: false },
11
+ verbose: { short: 'w', type: 'boolean', default: false },
12
+ quiet: { short: 'q', type: 'boolean', default: false },
13
+ test: { short: 't', type: 'string' },
14
+ force: { short: 'f', type: 'boolean', default: false },
15
+ auto: { short: 'a', type: 'boolean', default: false },
16
+ build: { short: 'b', type: 'boolean', default: false },
17
+ common: { short: 'c', type: 'boolean', default: false },
14
18
  },
15
19
  allowPositionals: true,
16
20
  });
@@ -18,32 +22,89 @@ const { values: options, positionals } = parseArgs({
18
22
  if (options.help) {
19
23
  console.log(`zenfs-test [...options] <...paths>
20
24
 
21
- paths: The setup files to run tests on
25
+ Paths: The setup files to run tests on
22
26
 
23
- options:
24
- --help, -h Outputs this help message
25
- --verbose Output verbose messages
26
- --test Which test to run
27
- --forceExit Whether to use --test-force-exit
28
- `);
27
+ Options:
28
+ --help, -h Outputs this help message
29
+ --verbose,-w Output verbose messages
30
+ --quiet, -q Don't output normal messages
31
+ --test,-t <glob> Which test(s) to run
32
+ --force, -f Whether to use --test-force-exit
33
+ --auto, -a Automatically detect setup files
34
+ --build, -b Run the npm build script prior to running tests
35
+ --common, -c Also run tests not specific to any backend
36
+ `);
29
37
  process.exit();
30
38
  }
31
39
 
32
- if (options.verbose) console.debug('Forcing tests to exit (--test-force-exit)');
40
+ if (options.quiet && options.verbose) {
41
+ console.error('ERROR: Can not specify --verbose and --quiet');
42
+ process.exit(1);
43
+ }
44
+
45
+ options.verbose && options.force && console.debug('Forcing tests to exit (--test-force-exit)');
46
+
47
+ if (options.build) {
48
+ !options.quiet && console.log('Building...');
49
+ try {
50
+ execSync('npm run build');
51
+ } catch {
52
+ console.warn('Build failed, continuing without it.');
53
+ }
54
+ }
33
55
 
34
56
  if (!existsSync(join(import.meta.dirname, '../dist'))) {
35
- console.log('ERROR: Missing build. If you are using an installed package, please submit a bug report.');
57
+ console.error('ERROR: Missing build. If you are using an installed package, please submit a bug report.');
36
58
  process.exit(1);
37
59
  }
38
60
 
61
+ if (options.auto) {
62
+ let sum = 0;
63
+
64
+ for (const pattern of ['**/tests/setup/*.ts', '**/tests/setup-*.ts']) {
65
+ const files = await globSync(pattern);
66
+ sum += files.length;
67
+ positionals.push(...files);
68
+ }
69
+
70
+ !options.quiet && console.log(`Auto-detected ${sum} test setup files`);
71
+ }
72
+
73
+ const coverage = join(import.meta.dirname, '../.coverage');
74
+ if (existsSync(coverage)) rmSync(coverage, { recursive: true });
75
+ mkdirSync(coverage);
76
+ process.env.NODE_V8_COVERAGE = coverage;
77
+
78
+ if (options.common) {
79
+ !options.quiet && console.log('Running common tests...');
80
+ try {
81
+ execSync("tsx --test --experimental-test-coverage 'tests/**/!(fs)/*.test.ts'", {
82
+ stdio: ['ignore', options.verbose ? 'inherit' : 'ignore', 'inherit'],
83
+ });
84
+ } catch {
85
+ console.error('Common tests failed');
86
+ }
87
+ }
88
+
39
89
  const testsGlob = join(import.meta.dirname, `../tests/fs/${options.test || '*'}.test.ts`);
40
90
 
41
91
  for (const setupFile of positionals) {
42
- if (options.verbose) console.debug('Running tests for:', setupFile);
43
- process.env.SETUP = setupFile;
44
92
  if (!existsSync(setupFile)) {
45
- console.log('ERROR: Skipping non-existent file:', setupFile);
93
+ !options.quiet && console.warn('Skipping tests for non-existent setup file:', setupFile);
46
94
  continue;
47
95
  }
48
- execSync(['tsx --test --experimental-test-coverage', options.forceExit ? '--test-force-exit' : '', testsGlob, process.env.CMD].join(' '), { stdio: 'inherit' });
96
+
97
+ !options.quiet && console.debug('Running tests using setup:', setupFile);
98
+ process.env.SETUP = setupFile;
99
+
100
+ try {
101
+ execSync(['tsx --test --experimental-test-coverage', options.force ? '--test-force-exit' : '', testsGlob, process.env.CMD].join(' '), {
102
+ stdio: ['ignore', options.verbose ? 'inherit' : 'ignore', 'inherit'],
103
+ });
104
+ } catch {
105
+ !options.quiet && console.error('Tests failed:', setupFile);
106
+ }
49
107
  }
108
+
109
+ execSync('npx c8 report --reporter=text', { stdio: 'inherit' });
110
+ rmSync('.coverage', { recursive: true });
@@ -7,7 +7,7 @@
7
7
  Notes on omissions and exclusions:
8
8
  - __promisify__ is omitted as it is metadata
9
9
  - native is omitted as zenfs isn't native
10
- - ReadStream and WriteStream are excluded since they are polfilled from another module
10
+ - ReadStream and WriteStream are excluded since they are polyfilled from another module
11
11
  */
12
12
 
13
13
  import { fs as zen } from '../src/index.js';
@@ -0,0 +1,19 @@
1
+ import { suite, test } from 'node:test';
2
+ import assert from 'node:assert';
3
+ import { bindContext } from '../../dist/context.js';
4
+ import * as fs from '../../dist/emulation/index.js';
5
+
6
+ fs.mkdirSync('/new_root');
7
+ const c_fs = bindContext('/new_root');
8
+
9
+ suite('Context', () => {
10
+ test('create a file', () => {
11
+ c_fs.writeFileSync('/example.txt', 'not in real root!');
12
+ assert.deepEqual(fs.readdirSync('/'), ['new_root']);
13
+ assert.deepEqual(fs.readdirSync('/new_root'), ['example.txt']);
14
+ });
15
+
16
+ test('break-out fails', () => {
17
+ assert.deepEqual(c_fs.readdirSync('/../../'), ['example.txt']);
18
+ });
19
+ });
@@ -1,8 +1,8 @@
1
1
  import { suite, test } from 'node:test';
2
2
  import assert from 'node:assert';
3
- import { configure } from '../src/config.js';
4
- import * as fs from '../src/emulation/index.js';
5
- import { S_IFCHR, S_IFMT } from '../src/emulation/constants.js';
3
+ import { configure } from '../../dist/config.js';
4
+ import * as fs from '../../dist/emulation/index.js';
5
+ import { S_IFCHR, S_IFMT } from '../../dist/emulation/constants.js';
6
6
 
7
7
  await configure({
8
8
  addDevices: true,
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert';
2
2
  import { suite, test } from 'node:test';
3
- import { constants, type FileHandle, open } from '../src/emulation/promises.js';
3
+ import { constants, type FileHandle, open } from '../../dist/emulation/promises.js';
4
4
 
5
5
  const content = 'The cake is a lie',
6
6
  appended = '\nAnother lie';
@@ -0,0 +1,36 @@
1
+ import assert from 'node:assert';
2
+ import { suite, test } from 'node:test';
3
+ import { configure } from '../../dist/config.js';
4
+ import * as fs from '../../dist/emulation/index.js';
5
+ import { InMemory } from '../../dist/index.js';
6
+
7
+ suite('Mounts', () => {
8
+ test('Mount in nested directory', async () => {
9
+ await configure({
10
+ mounts: {
11
+ '/nested/dir': InMemory,
12
+ },
13
+ });
14
+
15
+ assert.deepStrictEqual(fs.readdirSync('/'), ['nested']);
16
+ assert.deepStrictEqual(fs.readdirSync('/nested'), ['dir']);
17
+
18
+ // cleanup
19
+ fs.umount('/nested/dir');
20
+ fs.rmSync('/nested', { recursive: true, force: true });
21
+ });
22
+
23
+ test('Race conditions', async () => {
24
+ await configure({
25
+ mounts: {
26
+ one: InMemory,
27
+ two: InMemory,
28
+ three: InMemory,
29
+ four: InMemory,
30
+ },
31
+ });
32
+
33
+ assert.equal(fs.mounts.size, 5); // 4 + default `/` mount
34
+ assert.equal(fs.readdirSync('/').length, 4);
35
+ });
36
+ });
@@ -1,7 +1,7 @@
1
1
  import { wait } from 'utilium';
2
- import { Mutexed } from '../src/mixins/mutexed.js';
3
- import { StoreFS } from '../src/backends/store/fs.js';
4
- import { InMemoryStore } from '../src/backends/memory.js';
2
+ import { Mutexed } from '../../dist/mixins/mutexed.js';
3
+ import { StoreFS } from '../../dist/backends/store/fs.js';
4
+ import { InMemoryStore } from '../../dist/backends/memory.js';
5
5
  import { suite, test } from 'node:test';
6
6
  import assert from 'node:assert';
7
7
 
@@ -0,0 +1,34 @@
1
+ import assert from 'node:assert';
2
+ import { suite, test } from 'node:test';
3
+ import { basename, dirname, extname, join, normalize, resolve } from '../../dist/emulation/path.js';
4
+
5
+ suite('Path emulation', () => {
6
+ test('resolve', () => {
7
+ assert.equal(resolve('somepath'), '/somepath');
8
+ assert.equal(resolve('/another', 'path'), '/another/path');
9
+ });
10
+
11
+ test('join', () => {
12
+ assert.equal(join('/path', 'to', 'file.txt'), '/path/to/file.txt');
13
+ assert.equal(join('/path/', 'to', '/file.txt'), '/path/to/file.txt');
14
+ });
15
+
16
+ test('normalize', () => {
17
+ assert.equal(normalize('/path/to/../file.txt'), '/path/file.txt');
18
+ assert.equal(normalize('/path/to/./file.txt'), '/path/to/file.txt');
19
+ });
20
+
21
+ test('basename', () => {
22
+ assert.equal(basename('/path/to/file.txt'), 'file.txt');
23
+ assert.equal(basename('/path/to/file.txt', '.txt'), 'file');
24
+ });
25
+
26
+ test('dirname', () => {
27
+ assert.equal(dirname('/path/to/file.txt'), '/path/to');
28
+ });
29
+
30
+ test('extname', () => {
31
+ assert.equal(extname('/path/to/file.txt'), '.txt');
32
+ assert.equal(extname('/path/to/file'), '');
33
+ });
34
+ });
package/tests/common.ts CHANGED
@@ -1,11 +1,12 @@
1
1
  import { join, resolve } from 'node:path';
2
- import { fs } from '../dist/index.js';
2
+ import { fs as defaultFS } from '../dist/index.js';
3
+ export type * from '../dist/emulation/index.js';
3
4
 
4
5
  const setupPath = resolve(process.env.SETUP || join(import.meta.dirname, 'setup/memory.ts'));
5
6
 
6
- await import(setupPath).catch(error => {
7
+ const setup = await import(setupPath).catch(error => {
7
8
  console.log('Failed to import test setup:');
8
9
  throw error;
9
10
  });
10
11
 
11
- export { fs };
12
+ export const fs = (setup.fs || defaultFS) as typeof defaultFS;
@@ -1,6 +1,6 @@
1
1
  import assert, { rejects } from 'node:assert';
2
2
  import { suite, test } from 'node:test';
3
- import { fs } from '../common.js';
3
+ import { fs, type Dirent } from '../common.js';
4
4
 
5
5
  const testFile = 'test-file.txt';
6
6
  fs.writeFileSync(testFile, 'Sample content');
@@ -59,7 +59,7 @@ suite('Dirent', () => {
59
59
 
60
60
  suite('Dir', () => {
61
61
  test('read()', async () => {
62
- const dir = new fs.Dir(testDirPath);
62
+ const dir = fs.opendirSync(testDirPath);
63
63
 
64
64
  const dirent1 = await dir.read();
65
65
  assert(dirent1 instanceof fs.Dirent);
@@ -76,7 +76,7 @@ suite('Dir', () => {
76
76
  });
77
77
 
78
78
  test('readSync()', () => {
79
- const dir = new fs.Dir(testDirPath);
79
+ const dir = fs.opendirSync(testDirPath);
80
80
 
81
81
  const dirent1 = dir.readSync();
82
82
  assert(dirent1 instanceof fs.Dirent);
@@ -93,20 +93,20 @@ suite('Dir', () => {
93
93
  });
94
94
 
95
95
  test('close()', async () => {
96
- const dir = new fs.Dir(testDirPath);
96
+ const dir = fs.opendirSync(testDirPath);
97
97
  await dir.close();
98
98
  rejects(dir.read(), 'Can not use closed Dir');
99
99
  });
100
100
 
101
101
  test('closeSync()', () => {
102
- const dir = new fs.Dir(testDirPath);
102
+ const dir = fs.opendirSync(testDirPath);
103
103
  dir.closeSync();
104
104
  assert.throws(() => dir.readSync(), 'Can not use closed Dir');
105
105
  });
106
106
 
107
107
  test('asynchronous iteration', async () => {
108
- const dir = new fs.Dir(testDirPath);
109
- const dirents: fs.Dirent[] = [];
108
+ const dir = fs.opendirSync(testDirPath);
109
+ const dirents: Dirent[] = [];
110
110
 
111
111
  for await (const dirent of dir) {
112
112
  dirents.push(dirent);
@@ -119,26 +119,26 @@ suite('Dir', () => {
119
119
  });
120
120
 
121
121
  test('read after directory is closed', async () => {
122
- const dir = new fs.Dir(testDirPath);
122
+ const dir = fs.opendirSync(testDirPath);
123
123
  await dir.close();
124
124
  await assert.rejects(dir.read(), 'Can not use closed Dir');
125
125
  });
126
126
 
127
127
  test('readSync after directory is closed', () => {
128
- const dir = new fs.Dir(testDirPath);
128
+ const dir = fs.opendirSync(testDirPath);
129
129
  dir.closeSync();
130
130
  assert.throws(() => dir.readSync(), 'Can not use closed Dir');
131
131
  });
132
132
 
133
133
  test('close multiple times', async () => {
134
- const dir = new fs.Dir(testDirPath);
134
+ const dir = fs.opendirSync(testDirPath);
135
135
  await dir.close();
136
136
  await dir.close(); // Should not throw an error
137
137
  assert(dir['closed']);
138
138
  });
139
139
 
140
140
  test('closeSync multiple times', () => {
141
- const dir = new fs.Dir(testDirPath);
141
+ const dir = fs.opendirSync(testDirPath);
142
142
  dir.closeSync();
143
143
  dir.closeSync(); // Should not throw an error
144
144
  assert(dir['closed']);
@@ -40,27 +40,27 @@ suite('Directories', () => {
40
40
  });
41
41
 
42
42
  test('mkdir, recursive', async () => {
43
- assert((await fs.promises.mkdir('/recursiveP/A/B', { recursive: true, mode: 0o755 })) == '/recursiveP');
44
- assert((await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o777 })) == '/recursiveP/A/B/C');
45
- assert((await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o700 })) == undefined);
43
+ assert.equal(await fs.promises.mkdir('/recursiveP/A/B', { recursive: true, mode: 0o755 }), '/recursiveP');
44
+ assert.equal(await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o777 }), '/recursiveP/A/B/C');
45
+ assert.equal(await fs.promises.mkdir('/recursiveP/A/B/C/D', { recursive: true, mode: 0o700 }), undefined);
46
46
 
47
- assert((await fs.promises.stat('/recursiveP')).mode == (fs.constants.S_IFDIR | 0o755));
48
- assert((await fs.promises.stat('/recursiveP/A')).mode == (fs.constants.S_IFDIR | 0o755));
49
- assert((await fs.promises.stat('/recursiveP/A/B')).mode == (fs.constants.S_IFDIR | 0o755));
50
- assert((await fs.promises.stat('/recursiveP/A/B/C')).mode == (fs.constants.S_IFDIR | 0o777));
51
- assert((await fs.promises.stat('/recursiveP/A/B/C/D')).mode == (fs.constants.S_IFDIR | 0o777));
47
+ assert.equal((await fs.promises.stat('/recursiveP')).mode, fs.constants.S_IFDIR | 0o755);
48
+ assert.equal((await fs.promises.stat('/recursiveP/A')).mode, fs.constants.S_IFDIR | 0o755);
49
+ assert.equal((await fs.promises.stat('/recursiveP/A/B')).mode, fs.constants.S_IFDIR | 0o755);
50
+ assert.equal((await fs.promises.stat('/recursiveP/A/B/C')).mode, fs.constants.S_IFDIR | 0o777);
51
+ assert.equal((await fs.promises.stat('/recursiveP/A/B/C/D')).mode, fs.constants.S_IFDIR | 0o777);
52
52
  });
53
53
 
54
54
  test('mkdirSync, recursive', () => {
55
- assert(fs.mkdirSync('/recursiveS/A/B', { recursive: true, mode: 0o755 }) === '/recursiveS');
56
- assert(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o777 }) === '/recursiveS/A/B/C');
57
- assert(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o700 }) === undefined);
58
-
59
- assert(fs.statSync('/recursiveS').mode == (fs.constants.S_IFDIR | 0o755));
60
- assert(fs.statSync('/recursiveS/A').mode == (fs.constants.S_IFDIR | 0o755));
61
- assert(fs.statSync('/recursiveS/A/B').mode == (fs.constants.S_IFDIR | 0o755));
62
- assert(fs.statSync('/recursiveS/A/B/C').mode == (fs.constants.S_IFDIR | 0o777));
63
- assert(fs.statSync('/recursiveS/A/B/C/D').mode == (fs.constants.S_IFDIR | 0o777));
55
+ assert.equal(fs.mkdirSync('/recursiveS/A/B', { recursive: true, mode: 0o755 }), '/recursiveS');
56
+ assert.equal(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o777 }), '/recursiveS/A/B/C');
57
+ assert.equal(fs.mkdirSync('/recursiveS/A/B/C/D', { recursive: true, mode: 0o700 }), undefined);
58
+
59
+ assert.equal(fs.statSync('/recursiveS').mode, fs.constants.S_IFDIR | 0o755);
60
+ assert.equal(fs.statSync('/recursiveS/A').mode, fs.constants.S_IFDIR | 0o755);
61
+ assert.equal(fs.statSync('/recursiveS/A/B').mode, fs.constants.S_IFDIR | 0o755);
62
+ assert.equal(fs.statSync('/recursiveS/A/B/C').mode, fs.constants.S_IFDIR | 0o777);
63
+ assert.equal(fs.statSync('/recursiveS/A/B/C/D').mode, fs.constants.S_IFDIR | 0o777);
64
64
  });
65
65
 
66
66
  test('readdirSync without permission', () => {
@@ -1,23 +1,10 @@
1
1
  import assert from 'node:assert';
2
- import { suite, test } from 'node:test';
3
- import { ErrnoError } from '../../dist/error.js';
2
+ import test, { suite } from 'node:test';
3
+ import type { ErrnoError } from '../../dist/error.js';
4
4
  import { fs } from '../common.js';
5
5
 
6
6
  const existingFile = '/exit.js';
7
7
 
8
- async function expectError(fn: (...args: any[]) => unknown, path: string, ...args: any[]) {
9
- let error: ErrnoError | undefined;
10
- try {
11
- await fn(path, ...args);
12
- } catch (err: any) {
13
- assert(err instanceof ErrnoError);
14
- error = err;
15
- }
16
- assert(error);
17
- assert.equal(error.path, path);
18
- assert(error.message.includes(path));
19
- }
20
-
21
8
  suite('Error messages', () => {
22
9
  const path = '/non-existent';
23
10
 
@@ -26,28 +13,31 @@ suite('Error messages', () => {
26
13
  assert.equal(error.bufferSize(), 4 + JSON.stringify(error.toJSON()).length);
27
14
  });
28
15
 
29
- test('stat', () => expectError(fs.promises.stat, path));
30
- test('mkdir', () => expectError(fs.promises.mkdir, existingFile, 0o666));
31
- test('rmdir', () => expectError(fs.promises.rmdir, path));
32
- test('rmdir', () => expectError(fs.promises.rmdir, existingFile));
33
- test('rename', () => expectError(fs.promises.rename, path, 'foo'));
34
- test('open', () => expectError(fs.promises.open, path, 'r'));
35
- test('readdir', () => expectError(fs.promises.readdir, path));
36
- test('unlink', () => expectError(fs.promises.unlink, path));
37
- test('link', () => expectError(fs.promises.link, path, 'foo'));
38
- test('chmod', () => expectError(fs.promises.chmod, path, 0o666));
39
- test('lstat', () => expectError(fs.promises.lstat, path));
40
- test('readlink', () => expectError(fs.promises.readlink, path));
41
- test('statSync', () => expectError(fs.statSync, path));
42
- test('mkdirSync', () => expectError(fs.mkdirSync, existingFile, 0o666));
43
- test('rmdirSync', () => expectError(fs.rmdirSync, path));
44
- test('rmdirSync', () => expectError(fs.rmdirSync, existingFile));
45
- test('renameSync', () => expectError(fs.renameSync, path, 'foo'));
46
- test('openSync', () => expectError(fs.openSync, path, 'r'));
47
- test('readdirSync', () => expectError(fs.readdirSync, path));
48
- test('unlinkSync', () => expectError(fs.unlinkSync, path));
49
- test('linkSync', () => expectError(fs.linkSync, path, 'foo'));
50
- test('chmodSync', () => expectError(fs.chmodSync, path, 0o666));
51
- test('lstatSync', () => expectError(fs.lstatSync, path));
52
- test('readlinkSync', () => expectError(fs.readlinkSync, path));
16
+ const missing = { path, message: new RegExp(path) };
17
+ const existing = { path: existingFile, message: new RegExp(existingFile) };
18
+
19
+ test('stat', () => assert.rejects(() => fs.promises.stat(path), missing));
20
+ test('mkdir', () => assert.rejects(() => fs.promises.mkdir(existingFile, 0o666), existing));
21
+ test('rmdir', () => assert.rejects(() => fs.promises.rmdir(path), missing));
22
+ test('rmdir', () => assert.rejects(() => fs.promises.rmdir(existingFile), existing));
23
+ test('rename', () => assert.rejects(() => fs.promises.rename(path, 'foo'), missing));
24
+ test('open', () => assert.rejects(() => fs.promises.open(path, 'r'), missing));
25
+ test('readdir', () => assert.rejects(() => fs.promises.readdir(path), missing));
26
+ test('unlink', () => assert.rejects(() => fs.promises.unlink(path), missing));
27
+ test('link', () => assert.rejects(() => fs.promises.link(path, 'foo'), missing));
28
+ test('chmod', () => assert.rejects(() => fs.promises.chmod(path, 0o666), missing));
29
+ test('lstat', () => assert.rejects(() => fs.promises.lstat(path), missing));
30
+ test('readlink', () => assert.rejects(() => fs.promises.readlink(path), missing));
31
+ test('statSync', () => assert.throws(() => fs.statSync(path), missing));
32
+ test('mkdirSync', () => assert.throws(() => fs.mkdirSync(existingFile, 0o666), existing));
33
+ test('rmdirSync', () => assert.throws(() => fs.rmdirSync(path), missing));
34
+ test('rmdirSync', () => assert.throws(() => fs.rmdirSync(existingFile), existing));
35
+ test('renameSync', () => assert.throws(() => fs.renameSync(path, 'foo'), missing));
36
+ test('openSync', () => assert.throws(() => fs.openSync(path, 'r'), missing));
37
+ test('readdirSync', () => assert.throws(() => fs.readdirSync(path), missing));
38
+ test('unlinkSync', () => assert.throws(() => fs.unlinkSync(path), missing));
39
+ test('linkSync', () => assert.throws(() => fs.linkSync(path, 'foo'), missing));
40
+ test('chmodSync', () => assert.throws(() => fs.chmodSync(path, 0o666), missing));
41
+ test('lstatSync', () => assert.throws(() => fs.lstatSync(path), missing));
42
+ test('readlinkSync', () => assert.throws(() => fs.readlinkSync(path), missing));
53
43
  });
@@ -1,6 +1,6 @@
1
1
  import assert from 'node:assert';
2
2
  import { suite, test } from 'node:test';
3
- import { fs } from '../common.js';
3
+ import { fs, type Stats } from '../common.js';
4
4
 
5
5
  const testDir = '/test-watch-dir';
6
6
  const testFile = `${testDir}/test.txt`;
@@ -33,7 +33,7 @@ suite('Watch Features', () => {
33
33
  });
34
34
 
35
35
  test('fs.watchFile should detect changes to a file', async () => {
36
- const listener = (curr: fs.Stats, prev: fs.Stats) => {
36
+ const listener = (curr: Stats, prev: Stats) => {
37
37
  assert(curr.mtimeMs != prev.mtimeMs);
38
38
  fs.unwatchFile(testFile, listener);
39
39
  };
@@ -0,0 +1,9 @@
1
+ import { bindContext } from '../../dist/context.js';
2
+ import { fs as _fs } from '../../dist/index.js';
3
+ import { copy, data } from '../setup.js';
4
+
5
+ _fs.mkdirSync('/new_root');
6
+
7
+ export const fs = bindContext('/new_root');
8
+
9
+ copy(data, fs);
@@ -3,7 +3,7 @@ import { readFileSync } from 'node:fs';
3
3
  import { createServer } from 'node:http';
4
4
  import { join } from 'node:path';
5
5
  import { configureSingle, Fetch, InMemory, Overlay } from '../../dist/index.js';
6
- import { data, tmp } from './common.js';
6
+ import { data, tmp } from '../setup.js';
7
7
 
8
8
  const port = 26514,
9
9
  index = tmp + '/index.json';
@@ -1,3 +1,3 @@
1
- import { copy, data } from './common.js';
1
+ import { copy, data } from '../setup.js';
2
2
 
3
3
  copy(data);
@@ -1,16 +1,17 @@
1
1
  import { join, relative } from 'node:path';
2
2
  import { statSync, readFileSync, readdirSync, existsSync, mkdirSync } from 'node:fs';
3
- import { fs } from '../../dist/index.js';
3
+ import { fs as _fs } from '../dist/index.js';
4
+ import type { BoundContext } from '../dist/context.js';
4
5
 
5
- export const data = join(import.meta.dirname, '../data');
6
+ export const data = join(import.meta.dirname, 'data');
6
7
 
7
- export const tmp = join(import.meta.dirname, '../tmp');
8
+ export const tmp = join(import.meta.dirname, 'tmp');
8
9
 
9
10
  if (!existsSync(tmp)) {
10
11
  mkdirSync(tmp);
11
12
  }
12
13
 
13
- export function copy(_path: string) {
14
+ export function copy(_path: string, fs: typeof _fs | BoundContext = _fs) {
14
15
  const path = relative(data, _path) || '/';
15
16
  const stats = statSync(_path);
16
17
 
@@ -23,6 +24,6 @@ export function copy(_path: string) {
23
24
  fs.mkdirSync(path);
24
25
  }
25
26
  for (const file of readdirSync(_path)) {
26
- copy(join(_path, file));
27
+ copy(join(_path, file), fs);
27
28
  }
28
29
  }