@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.
- package/dist/backends/backend.d.ts +19 -15
- package/dist/backends/backend.js +36 -19
- package/dist/backends/cow.d.ts +20 -30
- package/dist/backends/cow.js +83 -192
- package/dist/backends/fetch.d.ts +1 -0
- package/dist/backends/fetch.js +30 -30
- package/dist/backends/index.d.ts +1 -1
- package/dist/backends/index.js +1 -1
- package/dist/backends/memory.d.ts +5 -7
- package/dist/backends/memory.js +2 -3
- package/dist/backends/passthrough.d.ts +19 -23
- package/dist/backends/passthrough.js +98 -288
- package/dist/backends/port.d.ts +220 -0
- package/dist/backends/port.js +328 -0
- package/dist/backends/single_buffer.d.ts +59 -47
- package/dist/backends/single_buffer.js +468 -219
- package/dist/backends/store/fs.d.ts +25 -35
- package/dist/backends/store/fs.js +276 -315
- package/dist/backends/store/store.d.ts +10 -15
- package/dist/backends/store/store.js +11 -10
- package/dist/config.d.ts +3 -12
- package/dist/config.js +17 -19
- package/dist/context.d.ts +8 -21
- package/dist/context.js +33 -10
- package/dist/index.d.ts +2 -1
- package/dist/index.js +2 -1
- package/dist/internal/contexts.d.ts +63 -0
- package/dist/internal/contexts.js +15 -0
- package/dist/internal/credentials.d.ts +2 -11
- package/dist/internal/credentials.js +0 -19
- package/dist/internal/devices.d.ts +18 -80
- package/dist/internal/devices.js +103 -316
- package/dist/internal/error.d.ts +9 -204
- package/dist/internal/error.js +19 -288
- package/dist/internal/file_index.d.ts +1 -1
- package/dist/internal/file_index.js +11 -11
- package/dist/internal/filesystem.d.ts +51 -94
- package/dist/internal/filesystem.js +21 -20
- package/dist/internal/index.d.ts +1 -2
- package/dist/internal/index.js +1 -2
- package/dist/internal/index_fs.d.ts +12 -30
- package/dist/internal/index_fs.js +37 -69
- package/dist/internal/inode.d.ts +140 -24
- package/dist/internal/inode.js +515 -66
- package/dist/mixins/async.js +52 -112
- package/dist/mixins/mutexed.d.ts +19 -18
- package/dist/mixins/mutexed.js +62 -64
- package/dist/mixins/readonly.d.ts +7 -6
- package/dist/mixins/readonly.js +24 -18
- package/dist/mixins/sync.js +8 -8
- package/dist/{vfs/path.d.ts → path.d.ts} +3 -4
- package/dist/{vfs/path.js → path.js} +6 -9
- package/dist/polyfills.js +1 -1
- package/dist/readline.d.ts +134 -0
- package/dist/readline.js +623 -0
- package/dist/utils.d.ts +9 -37
- package/dist/utils.js +17 -85
- package/dist/vfs/acl.d.ts +42 -0
- package/dist/vfs/acl.js +268 -0
- package/dist/vfs/async.d.ts +9 -23
- package/dist/vfs/async.js +25 -27
- package/dist/vfs/config.d.ts +6 -18
- package/dist/vfs/config.js +8 -18
- package/dist/vfs/dir.d.ts +3 -3
- package/dist/vfs/dir.js +12 -12
- package/dist/vfs/file.d.ts +106 -0
- package/dist/vfs/file.js +244 -0
- package/dist/vfs/flags.d.ts +19 -0
- package/dist/vfs/flags.js +62 -0
- package/dist/vfs/index.d.ts +4 -10
- package/dist/vfs/index.js +4 -13
- package/dist/vfs/ioctl.d.ts +88 -0
- package/dist/vfs/ioctl.js +409 -0
- package/dist/vfs/promises.d.ts +81 -19
- package/dist/vfs/promises.js +404 -288
- package/dist/vfs/shared.d.ts +7 -37
- package/dist/vfs/shared.js +29 -85
- package/dist/{stats.d.ts → vfs/stats.d.ts} +14 -28
- package/dist/{stats.js → vfs/stats.js} +11 -66
- package/dist/vfs/streams.d.ts +1 -0
- package/dist/vfs/streams.js +32 -27
- package/dist/vfs/sync.d.ts +3 -3
- package/dist/vfs/sync.js +263 -260
- package/dist/vfs/watchers.d.ts +2 -2
- package/dist/vfs/watchers.js +12 -12
- package/dist/vfs/xattr.d.ts +116 -0
- package/dist/vfs/xattr.js +201 -0
- package/package.json +5 -3
- package/readme.md +1 -1
- package/scripts/test.js +2 -2
- package/tests/assignment.ts +1 -1
- package/tests/backend/config.worker.js +4 -1
- package/tests/backend/fetch.test.ts +3 -0
- package/tests/backend/port.test.ts +19 -33
- package/tests/backend/remote.worker.js +4 -1
- package/tests/backend/single-buffer.test.ts +53 -0
- package/tests/backend/single-buffer.worker.js +30 -0
- package/tests/common/context.test.ts +3 -3
- package/tests/common/handle.test.ts +17 -12
- package/tests/common/mutex.test.ts +9 -9
- package/tests/common/path.test.ts +1 -1
- package/tests/common/readline.test.ts +104 -0
- package/tests/common.ts +4 -19
- package/tests/fetch/fetch.ts +2 -2
- package/tests/fs/append.test.ts +4 -4
- package/tests/fs/directory.test.ts +25 -25
- package/tests/fs/errors.test.ts +15 -19
- package/tests/fs/links.test.ts +4 -3
- package/tests/fs/open.test.ts +4 -21
- package/tests/fs/permissions.test.ts +14 -18
- package/tests/fs/read.test.ts +10 -9
- package/tests/fs/readFile.test.ts +10 -26
- package/tests/fs/rename.test.ts +4 -9
- package/tests/fs/stat.test.ts +8 -8
- package/tests/fs/streams.test.ts +2 -11
- package/tests/fs/times.test.ts +7 -7
- package/tests/fs/truncate.test.ts +8 -36
- package/tests/fs/watch.test.ts +10 -10
- package/tests/fs/write.test.ts +77 -13
- package/tests/fs/xattr.test.ts +85 -0
- package/tests/logs.js +22 -0
- package/tests/setup/context.ts +1 -1
- package/tests/setup/index.ts +3 -3
- package/tests/setup/port.ts +7 -1
- package/dist/backends/port/fs.d.ts +0 -84
- package/dist/backends/port/fs.js +0 -151
- package/dist/backends/port/rpc.d.ts +0 -77
- package/dist/backends/port/rpc.js +0 -100
- package/dist/backends/store/simple.d.ts +0 -20
- package/dist/backends/store/simple.js +0 -13
- package/dist/internal/file.d.ts +0 -359
- package/dist/internal/file.js +0 -751
- package/dist/internal/log.d.ts +0 -133
- package/dist/internal/log.js +0 -218
- package/tests/fs/writeFile.test.ts +0 -70
|
@@ -4,7 +4,7 @@ import { fs } from '../common.js';
|
|
|
4
4
|
|
|
5
5
|
suite('Reading', () => {
|
|
6
6
|
test('Cannot read a file with an invalid encoding', () => {
|
|
7
|
-
assert.throws(() => fs.readFileSync('a.js', '
|
|
7
|
+
assert.throws(() => fs.readFileSync('a.js', 'wrong-encoding' as BufferEncoding));
|
|
8
8
|
});
|
|
9
9
|
|
|
10
10
|
test('Reading past the end of a file should not be an error', async () => {
|
|
@@ -12,9 +12,7 @@ suite('Reading', () => {
|
|
|
12
12
|
const { bytesRead } = await handle.read(new Uint8Array(10), 0, 10, 10000);
|
|
13
13
|
assert.equal(bytesRead, 0);
|
|
14
14
|
});
|
|
15
|
-
});
|
|
16
15
|
|
|
17
|
-
suite('Read and Unlink', () => {
|
|
18
16
|
const dir = 'test-readfile-unlink';
|
|
19
17
|
const file = 'test-readfile-unlink/test.bin';
|
|
20
18
|
const data = new Uint8Array(512).fill(42);
|
|
@@ -34,40 +32,26 @@ suite('Read and Unlink', () => {
|
|
|
34
32
|
await fs.promises.unlink(file);
|
|
35
33
|
await fs.promises.rmdir(dir);
|
|
36
34
|
});
|
|
37
|
-
});
|
|
38
|
-
|
|
39
|
-
suite('Read File Test', () => {
|
|
40
|
-
const fn = 'empty.txt';
|
|
41
35
|
|
|
42
|
-
|
|
43
|
-
const data: Uint8Array = await fs.promises.readFile(fn);
|
|
44
|
-
assert(data != undefined);
|
|
45
|
-
});
|
|
36
|
+
const fileName = 'empty.txt';
|
|
46
37
|
|
|
47
|
-
test('read file
|
|
48
|
-
|
|
49
|
-
assert.equal(
|
|
38
|
+
test('read file', async () => {
|
|
39
|
+
assert.equal((await fs.promises.readFile(fileName)).toString(), '');
|
|
40
|
+
assert.equal(fs.readFileSync(fileName).toString(), '');
|
|
50
41
|
});
|
|
51
42
|
|
|
52
|
-
test('read file
|
|
53
|
-
|
|
54
|
-
assert(
|
|
43
|
+
test('read file with utf-8 encoding', async () => {
|
|
44
|
+
assert.equal(await fs.promises.readFile(fileName, 'utf8'), '');
|
|
45
|
+
assert.equal(fs.readFileSync(fileName, 'utf8'), '');
|
|
55
46
|
});
|
|
56
47
|
|
|
57
|
-
test('read file with utf-8 encoding synchronously', () => {
|
|
58
|
-
const data: string = fs.readFileSync(fn, 'utf8');
|
|
59
|
-
assert.equal(data, '');
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
suite('fs file reading', () => {
|
|
64
48
|
test('read file synchronously and verify the content', () => {
|
|
65
49
|
const content = fs.readFileSync('elipses.txt', 'utf8');
|
|
66
50
|
|
|
51
|
+
assert.equal(content.length, 10000);
|
|
52
|
+
|
|
67
53
|
for (let i = 0; i < content.length; i++) {
|
|
68
54
|
assert.equal(content[i], '…');
|
|
69
55
|
}
|
|
70
|
-
|
|
71
|
-
assert.equal(content.length, 10000);
|
|
72
56
|
});
|
|
73
57
|
});
|
package/tests/fs/rename.test.ts
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
|
-
import { ErrnoError } from '../../dist/index.js';
|
|
4
3
|
import { fs } from '../common.js';
|
|
5
4
|
|
|
6
5
|
suite('Rename', () => {
|
|
@@ -77,10 +76,8 @@ suite('Rename', () => {
|
|
|
77
76
|
await fs.promises.mkdir(dir);
|
|
78
77
|
await fs.promises.writeFile(file, 'file contents go here');
|
|
79
78
|
|
|
80
|
-
await fs.promises.rename(file, dir)
|
|
81
|
-
|
|
82
|
-
assert.match(error.code, /EISDIR|EPERM/);
|
|
83
|
-
});
|
|
79
|
+
await assert.rejects(fs.promises.rename(file, dir), { code: 'EISDIR' });
|
|
80
|
+
assert.throws(() => fs.renameSync(file, dir), { code: 'EISDIR' });
|
|
84
81
|
});
|
|
85
82
|
|
|
86
83
|
test('rename directory inside itself', async () => {
|
|
@@ -89,9 +86,7 @@ suite('Rename', () => {
|
|
|
89
86
|
|
|
90
87
|
await fs.promises.mkdir(renDir1);
|
|
91
88
|
|
|
92
|
-
await fs.promises.rename(renDir1, renDir2)
|
|
93
|
-
|
|
94
|
-
assert.equal(error.code, 'EBUSY');
|
|
95
|
-
});
|
|
89
|
+
await assert.rejects(fs.promises.rename(renDir1, renDir2), { code: 'EBUSY' });
|
|
90
|
+
assert.throws(() => fs.renameSync(renDir1, renDir2), { code: 'EBUSY' });
|
|
96
91
|
});
|
|
97
92
|
});
|
package/tests/fs/stat.test.ts
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
|
-
import {
|
|
4
|
-
import { Stats } from '../../dist/stats.js';
|
|
3
|
+
import { defaultContext } from '../../dist/internal/contexts.js';
|
|
4
|
+
import { Stats } from '../../dist/vfs/stats.js';
|
|
5
5
|
import { fs } from '../common.js';
|
|
6
6
|
|
|
7
7
|
suite('Stats', () => {
|
|
8
8
|
const existing_file = 'x.txt';
|
|
9
9
|
|
|
10
|
-
test('stat empty path', () => {
|
|
11
|
-
assert.rejects(fs.promises.stat(''));
|
|
10
|
+
test('stat empty path', async () => {
|
|
11
|
+
await assert.rejects(fs.promises.stat(''));
|
|
12
12
|
});
|
|
13
13
|
|
|
14
14
|
test('stat directory', async () => {
|
|
@@ -40,7 +40,7 @@ suite('Stats', () => {
|
|
|
40
40
|
|
|
41
41
|
fs.writeFileSync(newFile, 'hello', { mode: 0o640 });
|
|
42
42
|
|
|
43
|
-
const prevCredentials = { ...credentials };
|
|
43
|
+
const prevCredentials = { ...defaultContext.credentials };
|
|
44
44
|
const uid = 33;
|
|
45
45
|
const nonRootCredentials = {
|
|
46
46
|
uid,
|
|
@@ -53,7 +53,7 @@ suite('Stats', () => {
|
|
|
53
53
|
|
|
54
54
|
fs.chownSync(newFile, 0, nonRootCredentials.gid); // creating with root-user so that non-root user can access
|
|
55
55
|
|
|
56
|
-
Object.assign(credentials, nonRootCredentials);
|
|
56
|
+
Object.assign(defaultContext.credentials, nonRootCredentials);
|
|
57
57
|
const stat = fs.statSync(newFile);
|
|
58
58
|
|
|
59
59
|
assert.equal(stat.gid, nonRootCredentials.gid);
|
|
@@ -63,13 +63,13 @@ suite('Stats', () => {
|
|
|
63
63
|
assert.equal(stat.hasAccess(fs.constants.X_OK), false);
|
|
64
64
|
// changing group
|
|
65
65
|
|
|
66
|
-
Object.assign(credentials, { ...nonRootCredentials, gid: 44 });
|
|
66
|
+
Object.assign(defaultContext.credentials, { ...nonRootCredentials, gid: 44 });
|
|
67
67
|
|
|
68
68
|
assert.equal(stat.hasAccess(fs.constants.R_OK), false);
|
|
69
69
|
assert.equal(stat.hasAccess(fs.constants.W_OK), false);
|
|
70
70
|
assert.equal(stat.hasAccess(fs.constants.X_OK), false);
|
|
71
71
|
|
|
72
|
-
Object.assign(credentials, prevCredentials);
|
|
72
|
+
Object.assign(defaultContext.credentials, prevCredentials);
|
|
73
73
|
});
|
|
74
74
|
|
|
75
75
|
test('stat file', async () => {
|
package/tests/fs/streams.test.ts
CHANGED
|
@@ -129,21 +129,12 @@ suite('Streams', () => {
|
|
|
129
129
|
test('FileHandle.createReadStream after close should give an error', async () => {
|
|
130
130
|
const fileHandle = await fs.promises.open(testFilePath, 'r');
|
|
131
131
|
await fileHandle.close();
|
|
132
|
-
|
|
133
|
-
const { promise, resolve, reject } = Promise.withResolvers();
|
|
134
|
-
setTimeout(resolve, 100);
|
|
135
|
-
stream.on('error', reject);
|
|
136
|
-
assert.rejects(promise);
|
|
132
|
+
assert.throws(() => fileHandle.createReadStream(), { code: 'EBADF' });
|
|
137
133
|
});
|
|
138
134
|
|
|
139
135
|
test('FileHandle.createWriteStream after close should give an error', async () => {
|
|
140
136
|
const fileHandle = await fs.promises.open(testFilePathWrite, 'w');
|
|
141
137
|
await fileHandle.close();
|
|
142
|
-
|
|
143
|
-
const { promise, resolve, reject } = Promise.withResolvers();
|
|
144
|
-
setTimeout(resolve, 100);
|
|
145
|
-
stream.on('error', reject);
|
|
146
|
-
assert.rejects(promise);
|
|
147
|
-
stream.write('Nuh-uh');
|
|
138
|
+
assert.throws(() => fileHandle.createWriteStream(), { code: 'EBADF' });
|
|
148
139
|
});
|
|
149
140
|
});
|
package/tests/fs/times.test.ts
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
+
import { Exception } from 'kerium';
|
|
1
2
|
import assert from 'node:assert/strict';
|
|
2
3
|
import { suite, test } from 'node:test';
|
|
3
4
|
import { wait } from 'utilium';
|
|
4
|
-
import {
|
|
5
|
-
import type { StatsLike } from '../../dist/stats.js';
|
|
5
|
+
import type { StatsLike } from '../../dist/vfs/stats.js';
|
|
6
6
|
import { fs } from '../common.js';
|
|
7
7
|
|
|
8
8
|
const path = 'x.txt';
|
|
@@ -19,7 +19,7 @@ export function unixTimestamps(stats: StatsLike<number>): Record<'atime' | 'mtim
|
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
suite('
|
|
22
|
+
suite('Times', () => {
|
|
23
23
|
async function runTest(atime: Date | number, mtime: Date | number): Promise<void> {
|
|
24
24
|
const times = {
|
|
25
25
|
atime: typeof atime == 'number' ? Math.floor(atime) : atime.getTime(),
|
|
@@ -30,8 +30,8 @@ suite('times', () => {
|
|
|
30
30
|
|
|
31
31
|
assert.deepEqual(unixTimestamps(await fs.promises.stat(path)), times);
|
|
32
32
|
|
|
33
|
-
await fs.promises.utimes('foobarbaz', atime, mtime).catch((error:
|
|
34
|
-
assert(error instanceof
|
|
33
|
+
await fs.promises.utimes('foobarbaz', atime, mtime).catch((error: Exception) => {
|
|
34
|
+
assert(error instanceof Exception);
|
|
35
35
|
assert.equal(error.code, 'ENOENT');
|
|
36
36
|
});
|
|
37
37
|
|
|
@@ -46,14 +46,14 @@ suite('times', () => {
|
|
|
46
46
|
try {
|
|
47
47
|
fs.utimesSync('foobarbaz', atime, mtime);
|
|
48
48
|
} catch (error: any) {
|
|
49
|
-
assert(error instanceof
|
|
49
|
+
assert(error instanceof Exception);
|
|
50
50
|
assert.equal(error.code, 'ENOENT');
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
try {
|
|
54
54
|
fs.futimesSync(-1, atime, mtime);
|
|
55
55
|
} catch (error: any) {
|
|
56
|
-
assert(error instanceof
|
|
56
|
+
assert(error instanceof Exception);
|
|
57
57
|
assert.equal(error.code, 'EBADF');
|
|
58
58
|
}
|
|
59
59
|
}
|
|
@@ -1,94 +1,66 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
3
|
import { fs } from '../common.js';
|
|
4
|
-
import type { FileHandle } from '../../dist/vfs/promises.js';
|
|
5
4
|
|
|
6
5
|
const path: string = 'truncate-file.txt',
|
|
7
6
|
size = 1024 * 16,
|
|
8
7
|
data = new Uint8Array(size).fill('x'.charCodeAt(0));
|
|
9
8
|
|
|
10
|
-
suite('
|
|
11
|
-
test('
|
|
9
|
+
suite('Truncating', () => {
|
|
10
|
+
test('Sync path functions', () => {
|
|
12
11
|
fs.writeFileSync(path, data);
|
|
13
12
|
assert.equal(fs.statSync(path).size, size);
|
|
14
|
-
});
|
|
15
13
|
|
|
16
|
-
test('truncate to 1024', () => {
|
|
17
14
|
fs.truncateSync(path, 1024);
|
|
18
15
|
assert.equal(fs.statSync(path).size, 1024);
|
|
19
|
-
});
|
|
20
16
|
|
|
21
|
-
test('truncate to 0', () => {
|
|
22
17
|
fs.truncateSync(path);
|
|
23
18
|
assert.equal(fs.statSync(path).size, 0);
|
|
24
|
-
});
|
|
25
19
|
|
|
26
|
-
test('write', () => {
|
|
27
20
|
fs.writeFileSync(path, data);
|
|
28
21
|
assert.equal(fs.statSync(path).size, size);
|
|
29
22
|
});
|
|
30
23
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
fd = fs.openSync(path, 'r+');
|
|
34
|
-
});
|
|
24
|
+
test('FD functions', () => {
|
|
25
|
+
const fd = fs.openSync(path, 'r+');
|
|
35
26
|
|
|
36
|
-
test('ftruncate to 1024', () => {
|
|
37
27
|
fs.ftruncateSync(fd, 1024);
|
|
38
28
|
assert.equal(fs.fstatSync(fd).size, 1024);
|
|
39
|
-
});
|
|
40
29
|
|
|
41
|
-
test('ftruncate to 0', () => {
|
|
42
30
|
fs.ftruncateSync(fd);
|
|
43
31
|
assert.equal(fs.fstatSync(fd).size, 0);
|
|
44
|
-
});
|
|
45
32
|
|
|
46
|
-
test('close fd', () => {
|
|
47
33
|
fs.closeSync(fd);
|
|
48
34
|
});
|
|
49
|
-
|
|
50
|
-
suite('Truncate, async', () => {
|
|
35
|
+
|
|
51
36
|
const statSize = async (path: string) => (await fs.promises.stat(path)).size;
|
|
52
37
|
|
|
53
|
-
test('
|
|
38
|
+
test('Async path functions', async () => {
|
|
54
39
|
await fs.promises.writeFile(path, data);
|
|
55
40
|
|
|
56
41
|
assert.equal(await statSize(path), 1024 * 16);
|
|
57
|
-
});
|
|
58
42
|
|
|
59
|
-
test('truncate to 1024', async () => {
|
|
60
43
|
await fs.promises.truncate(path, 1024);
|
|
61
44
|
assert.equal(await statSize(path), 1024);
|
|
62
|
-
});
|
|
63
45
|
|
|
64
|
-
test('truncate to 0', async () => {
|
|
65
46
|
await fs.promises.truncate(path);
|
|
66
47
|
assert.equal(await statSize(path), 0);
|
|
67
|
-
});
|
|
68
48
|
|
|
69
|
-
test('write', async () => {
|
|
70
49
|
await fs.promises.writeFile(path, data);
|
|
71
50
|
assert.equal(await statSize(path), size);
|
|
72
51
|
});
|
|
73
52
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
handle = await fs.promises.open(path, 'w');
|
|
77
|
-
});
|
|
53
|
+
test('FileHandle', async () => {
|
|
54
|
+
const handle = await fs.promises.open(path, 'w');
|
|
78
55
|
|
|
79
|
-
test('handle.truncate to 1024', async () => {
|
|
80
56
|
await handle.truncate(1024);
|
|
81
57
|
await handle.sync();
|
|
82
58
|
assert.equal(await statSize(path), 1024);
|
|
83
|
-
});
|
|
84
59
|
|
|
85
|
-
test('handle.truncate to 0', async () => {
|
|
86
60
|
await handle.truncate();
|
|
87
61
|
await handle.sync();
|
|
88
62
|
assert.equal(await statSize(path), 0);
|
|
89
|
-
});
|
|
90
63
|
|
|
91
|
-
test('close handle', async () => {
|
|
92
64
|
await handle.close();
|
|
93
65
|
});
|
|
94
66
|
});
|
package/tests/fs/watch.test.ts
CHANGED
|
@@ -11,8 +11,8 @@ await fs.promises.writeFile(testFile, 'Initial content');
|
|
|
11
11
|
/**
|
|
12
12
|
* @todo convert using watcher to void discards pending ES proposal
|
|
13
13
|
*/
|
|
14
|
-
await suite('Watch
|
|
15
|
-
test('
|
|
14
|
+
await suite('Watch', () => {
|
|
15
|
+
test('Events emitted on file change', async () => {
|
|
16
16
|
const { promise, resolve } = Promise.withResolvers<[string, string]>();
|
|
17
17
|
|
|
18
18
|
using watcher = fs.watch(testFile, (eventType, filename) => {
|
|
@@ -27,7 +27,7 @@ await suite('Watch Features', () => {
|
|
|
27
27
|
assert.equal(filename, 'test.txt');
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
-
test('
|
|
30
|
+
test('Events are emitted on delete', async () => {
|
|
31
31
|
using watcher = fs.watch(testFile, (eventType, filename) => {
|
|
32
32
|
assert.equal(eventType, 'rename');
|
|
33
33
|
assert.equal(filename, 'test.txt');
|
|
@@ -37,7 +37,7 @@ await suite('Watch Features', () => {
|
|
|
37
37
|
await fs.promises.unlink(testFile);
|
|
38
38
|
});
|
|
39
39
|
|
|
40
|
-
test('
|
|
40
|
+
test('Changes are detected with watchFile()', async () => {
|
|
41
41
|
const listener = (curr: Stats, prev: Stats) => {
|
|
42
42
|
assert(curr.mtimeMs != prev.mtimeMs);
|
|
43
43
|
fs.unwatchFile(testFile, listener);
|
|
@@ -49,7 +49,7 @@ await suite('Watch Features', () => {
|
|
|
49
49
|
await fs.promises.writeFile(testFile, 'Changed content');
|
|
50
50
|
});
|
|
51
51
|
|
|
52
|
-
test('
|
|
52
|
+
test('unwatchFile() works', async () => {
|
|
53
53
|
let changeDetected = false;
|
|
54
54
|
|
|
55
55
|
const listener = () => {
|
|
@@ -66,7 +66,7 @@ await suite('Watch Features', () => {
|
|
|
66
66
|
assert(!changeDetected);
|
|
67
67
|
});
|
|
68
68
|
|
|
69
|
-
test('
|
|
69
|
+
test('Directories can be watched', async () => {
|
|
70
70
|
using watcher = fs.watch(testDir, (eventType, filename) => {
|
|
71
71
|
assert.equal(eventType, 'change');
|
|
72
72
|
assert.equal(filename, 'newFile.txt');
|
|
@@ -75,7 +75,7 @@ await suite('Watch Features', () => {
|
|
|
75
75
|
await fs.promises.writeFile(testDir + '/newFile.txt', 'Content');
|
|
76
76
|
});
|
|
77
77
|
|
|
78
|
-
test('
|
|
78
|
+
test('File renames are detected', async () => {
|
|
79
79
|
const oldFileName = 'oldFile.txt';
|
|
80
80
|
const newFileName = 'newFile.txt';
|
|
81
81
|
const oldFile = testDir + '/' + oldFileName;
|
|
@@ -102,7 +102,7 @@ await suite('Watch Features', () => {
|
|
|
102
102
|
await Promise.all([newFileResolver.promise, oldFileResolver.promise]);
|
|
103
103
|
});
|
|
104
104
|
|
|
105
|
-
test('
|
|
105
|
+
test('File deletions are detected', async () => {
|
|
106
106
|
const tempFile = `${testDir}/tempFile.txt`;
|
|
107
107
|
|
|
108
108
|
await fs.promises.writeFile(tempFile, 'Temporary content');
|
|
@@ -115,7 +115,7 @@ await suite('Watch Features', () => {
|
|
|
115
115
|
await fs.promises.unlink(tempFile);
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
-
test('
|
|
118
|
+
test('File deletions are detected by promises API', async () => {
|
|
119
119
|
const tempFile = `${testDir}/tempFile.txt`;
|
|
120
120
|
|
|
121
121
|
await fs.promises.writeFile(tempFile, 'Temporary content');
|
|
@@ -135,7 +135,7 @@ await suite('Watch Features', () => {
|
|
|
135
135
|
await promise;
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
test('
|
|
138
|
+
test('File creations are detected recursively', async () => {
|
|
139
139
|
const subDir = `${testDir}/sub-dir`;
|
|
140
140
|
const tempFile = `${subDir}/tempFile.txt`;
|
|
141
141
|
await fs.promises.mkdir(subDir);
|
package/tests/fs/write.test.ts
CHANGED
|
@@ -1,27 +1,33 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path/posix';
|
|
2
4
|
import { suite, test } from 'node:test';
|
|
3
5
|
import { fs } from '../common.js';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
6
|
+
import { data as dataPath } from '../setup.js';
|
|
7
|
+
|
|
8
|
+
const fileName = 'write.txt';
|
|
9
|
+
const utf8example = readFileSync(join(dataPath, 'utf8.txt'), 'utf8');
|
|
10
|
+
|
|
11
|
+
suite('Writes', () => {
|
|
12
|
+
test('Using FileHandle and UTF-8', async () => {
|
|
7
13
|
const expected = 'ümlaut.';
|
|
8
14
|
|
|
9
|
-
const handle = await fs.promises.open(
|
|
15
|
+
const handle = await fs.promises.open(fileName, 'w', 0o644);
|
|
10
16
|
await handle.write('', 0, 'utf8');
|
|
11
17
|
const { bytesWritten } = await handle.write(expected, 0, 'utf8');
|
|
12
18
|
assert.equal(bytesWritten, Buffer.from(expected).length);
|
|
13
19
|
await handle.close();
|
|
14
20
|
|
|
15
|
-
const data = await fs.promises.readFile(
|
|
21
|
+
const data = await fs.promises.readFile(fileName, 'utf8');
|
|
16
22
|
assert.equal(data, expected);
|
|
17
23
|
|
|
18
|
-
await fs.promises.unlink(
|
|
24
|
+
await fs.promises.unlink(fileName);
|
|
19
25
|
});
|
|
20
26
|
|
|
21
|
-
test('
|
|
27
|
+
test('Using FileHandle with buffer', async () => {
|
|
22
28
|
const expected = Buffer.from('hello');
|
|
23
29
|
|
|
24
|
-
const handle = await fs.promises.open(
|
|
30
|
+
const handle = await fs.promises.open(fileName, 'w', 0o644);
|
|
25
31
|
|
|
26
32
|
const written = await handle.write(expected, 0, expected.length, null);
|
|
27
33
|
|
|
@@ -29,13 +35,13 @@ suite('write', () => {
|
|
|
29
35
|
|
|
30
36
|
await handle.close();
|
|
31
37
|
|
|
32
|
-
assert.deepEqual(await fs.promises.readFile(
|
|
38
|
+
assert.deepEqual(await fs.promises.readFile(fileName), expected);
|
|
33
39
|
|
|
34
|
-
await fs.promises.unlink(
|
|
40
|
+
await fs.promises.unlink(fileName);
|
|
35
41
|
});
|
|
36
42
|
|
|
37
|
-
test('
|
|
38
|
-
const fd = fs.openSync(
|
|
43
|
+
test('Using sync path functions', () => {
|
|
44
|
+
const fd = fs.openSync(fileName, 'w');
|
|
39
45
|
|
|
40
46
|
let written = fs.writeSync(fd, '');
|
|
41
47
|
assert.equal(written, 0);
|
|
@@ -48,6 +54,64 @@ suite('write', () => {
|
|
|
48
54
|
|
|
49
55
|
fs.closeSync(fd);
|
|
50
56
|
|
|
51
|
-
assert.equal(fs.readFileSync(
|
|
57
|
+
assert.equal(fs.readFileSync(fileName, 'utf8'), 'foobár');
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('Using promises API', async () => {
|
|
61
|
+
const filename = 'test.txt';
|
|
62
|
+
await fs.promises.writeFile(filename, utf8example);
|
|
63
|
+
const data = await fs.promises.readFile(filename);
|
|
64
|
+
assert.equal(data.length, Buffer.from(utf8example).length);
|
|
65
|
+
await fs.promises.unlink(filename);
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('Using promises API with buffer', async () => {
|
|
69
|
+
const filename = 'test2.txt';
|
|
70
|
+
const expected = Buffer.from(utf8example, 'utf8');
|
|
71
|
+
|
|
72
|
+
await fs.promises.writeFile(filename, expected);
|
|
73
|
+
const actual = await fs.promises.readFile(filename);
|
|
74
|
+
assert.equal(actual.length, expected.length);
|
|
75
|
+
|
|
76
|
+
await fs.promises.unlink(filename);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
test('Promises API with base64 data', async () => {
|
|
80
|
+
const data = readFileSync(join(dataPath, 'image.jpg'), 'base64');
|
|
81
|
+
|
|
82
|
+
const buffer = Buffer.from(data, 'base64');
|
|
83
|
+
const filePath = 'test.jpg';
|
|
84
|
+
|
|
85
|
+
await fs.promises.writeFile(filePath, buffer);
|
|
86
|
+
|
|
87
|
+
const read = await fs.promises.readFile(filePath, 'base64');
|
|
88
|
+
assert.equal(read, data);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('Using sync path functions with custom mode', () => {
|
|
92
|
+
const file = 'testWriteFileSync.txt';
|
|
93
|
+
const mode = 0o755;
|
|
94
|
+
|
|
95
|
+
fs.writeFileSync(file, '123', { mode });
|
|
96
|
+
|
|
97
|
+
const content = fs.readFileSync(file, 'utf8');
|
|
98
|
+
assert.equal(content, '123');
|
|
99
|
+
assert.equal(fs.statSync(file).mode & 0o777, mode);
|
|
100
|
+
|
|
101
|
+
fs.unlinkSync(file);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('Appending to a file synchronously with custom mode', () => {
|
|
105
|
+
const file = 'testAppendFileSync.txt';
|
|
106
|
+
const mode = 0o755;
|
|
107
|
+
|
|
108
|
+
fs.appendFileSync(file, 'abc', { mode });
|
|
109
|
+
|
|
110
|
+
const content = fs.readFileSync(file, { encoding: 'utf8' });
|
|
111
|
+
assert.equal(content, 'abc');
|
|
112
|
+
|
|
113
|
+
assert.equal(fs.statSync(file).mode & 0o777, mode);
|
|
114
|
+
|
|
115
|
+
fs.unlinkSync(file);
|
|
52
116
|
});
|
|
53
117
|
});
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Buffer } from 'buffer';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { suite, test } from 'node:test';
|
|
4
|
+
import { fs } from '../common.js';
|
|
5
|
+
|
|
6
|
+
suite('Extended Attributes', () => {
|
|
7
|
+
const testFile = 'xattr-test.txt';
|
|
8
|
+
const testValue = 'test value';
|
|
9
|
+
const testName = 'user.test';
|
|
10
|
+
|
|
11
|
+
test.before(() => fs.promises.writeFile(testFile, 'test content'));
|
|
12
|
+
test.after(() => fs.promises.unlink(testFile));
|
|
13
|
+
|
|
14
|
+
test('Non-user attribute set fails', async () => {
|
|
15
|
+
await assert.rejects(fs.xattr.set(testFile, 'system.test', 'value'), { code: 'ENOTSUP' });
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('set and get attributes', async () => {
|
|
19
|
+
await fs.xattr.set(testFile, testName, testValue);
|
|
20
|
+
const value = await fs.xattr.get(testFile, testName, { encoding: 'utf8' });
|
|
21
|
+
assert.equal(value, testValue);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test('get attributes with buffer encoding', async () => {
|
|
25
|
+
await fs.xattr.set(testFile, 'user.buffer', 'buffer value');
|
|
26
|
+
const buffer = await fs.xattr.get(testFile, 'user.buffer', { encoding: 'buffer' });
|
|
27
|
+
assert(buffer instanceof Uint8Array);
|
|
28
|
+
assert.equal(Buffer.from(buffer).toString(), 'buffer value');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('remove attributes', async () => {
|
|
32
|
+
await fs.xattr.set(testFile, 'user.to-remove', testValue);
|
|
33
|
+
await fs.xattr.remove(testFile, 'user.to-remove');
|
|
34
|
+
|
|
35
|
+
await assert.rejects(fs.xattr.get(testFile, 'user.to-remove', { encoding: 'utf8' }), { code: 'ENODATA' });
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('list attributes', async () => {
|
|
39
|
+
await fs.xattr.set(testFile, 'user.list1', 'value1');
|
|
40
|
+
await fs.xattr.set(testFile, 'user.list2', 'value2');
|
|
41
|
+
|
|
42
|
+
const attrs = await fs.xattr.list(testFile);
|
|
43
|
+
assert(attrs.includes('user.list1'));
|
|
44
|
+
assert(attrs.includes('user.list2'));
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('handle create and replace options', async () => {
|
|
48
|
+
const flagTestName = 'user.flag-test';
|
|
49
|
+
|
|
50
|
+
await fs.xattr.set(testFile, flagTestName, 'original', { create: true });
|
|
51
|
+
|
|
52
|
+
await assert.rejects(fs.xattr.set(testFile, flagTestName, 'new value', { create: true }), { code: 'EEXIST' });
|
|
53
|
+
|
|
54
|
+
await fs.xattr.set(testFile, flagTestName, 'updated', { replace: true });
|
|
55
|
+
const value = await fs.xattr.get(testFile, flagTestName, { encoding: 'utf8' });
|
|
56
|
+
assert.equal(value, 'updated');
|
|
57
|
+
|
|
58
|
+
await assert.rejects(fs.xattr.set(testFile, 'user.nonexistent', 'value', { replace: true }), { code: 'ENODATA' });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
test('file must exist', async () => {
|
|
62
|
+
await assert.rejects(fs.xattr.set('nonexistent-file.txt', testName, 'value'), { code: 'ENOENT' });
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('synchronous operations', () => {
|
|
66
|
+
const syncAttrName = 'user.sync-test';
|
|
67
|
+
|
|
68
|
+
fs.xattr.setSync(testFile, syncAttrName, testValue);
|
|
69
|
+
const value = fs.xattr.getSync(testFile, syncAttrName, { encoding: 'utf8' });
|
|
70
|
+
assert.equal(value, testValue);
|
|
71
|
+
|
|
72
|
+
fs.xattr.removeSync(testFile, syncAttrName);
|
|
73
|
+
|
|
74
|
+
assert.throws(() => fs.xattr.getSync(testFile, syncAttrName, { encoding: 'utf8' }), { code: 'ENODATA' });
|
|
75
|
+
|
|
76
|
+
const syncList1 = 'user.sync-list1';
|
|
77
|
+
const syncList2 = 'user.sync-list2';
|
|
78
|
+
fs.xattr.setSync(testFile, syncList1, 'value1');
|
|
79
|
+
fs.xattr.setSync(testFile, syncList2, 'value2');
|
|
80
|
+
|
|
81
|
+
const attrs = fs.xattr.listSync(testFile);
|
|
82
|
+
assert(attrs.includes(syncList1));
|
|
83
|
+
assert(attrs.includes(syncList2));
|
|
84
|
+
});
|
|
85
|
+
});
|
package/tests/logs.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { log } from 'kerium';
|
|
2
|
+
|
|
3
|
+
export function setupLogs(prefix) {
|
|
4
|
+
const { ZENFS_LOG_LEVEL } = process.env;
|
|
5
|
+
|
|
6
|
+
let level = log.Level.ERR;
|
|
7
|
+
|
|
8
|
+
if (ZENFS_LOG_LEVEL) {
|
|
9
|
+
const tmp = parseInt(ZENFS_LOG_LEVEL);
|
|
10
|
+
if (Number.isSafeInteger(tmp)) level = tmp;
|
|
11
|
+
else level = ZENFS_LOG_LEVEL;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
log.configure({
|
|
15
|
+
enabled: true,
|
|
16
|
+
format: log.fancy({ style: 'ansi', colorize: 'message' }),
|
|
17
|
+
dumpBacklog: true,
|
|
18
|
+
level,
|
|
19
|
+
stack: true,
|
|
20
|
+
output: (...msg) => (prefix ? console.error(prefix, ...msg) : console.error(...msg)),
|
|
21
|
+
});
|
|
22
|
+
}
|
package/tests/setup/context.ts
CHANGED
package/tests/setup/index.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { readFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path/posix';
|
|
3
|
-
import { configureSingle, InMemory, InMemoryStore, mounts,
|
|
3
|
+
import { configureSingle, CopyOnWrite, InMemory, InMemoryStore, mounts, Readonly, StoreFS } from '../../dist/index.js';
|
|
4
4
|
import { S_IFDIR } from '../../dist/vfs/constants.js';
|
|
5
5
|
import { copySync, data } from '../setup.js';
|
|
6
6
|
|
|
@@ -32,7 +32,7 @@ const readable = new MockFS();
|
|
|
32
32
|
await readable.ready();
|
|
33
33
|
|
|
34
34
|
await configureSingle({
|
|
35
|
-
backend:
|
|
35
|
+
backend: CopyOnWrite,
|
|
36
36
|
readable,
|
|
37
|
-
writable:
|
|
37
|
+
writable: { backend: InMemory, label: 'cow' },
|
|
38
38
|
});
|