@zenfs/core 1.9.3 → 1.9.5
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/fetch.d.ts +3 -0
- package/dist/backends/fetch.js +3 -0
- package/dist/backends/memory.d.ts +1 -1
- package/dist/backends/memory.js +1 -3
- package/dist/backends/overlay.d.ts +1 -0
- package/dist/backends/overlay.js +1 -0
- package/dist/backends/port/fs.d.ts +8 -2
- package/dist/backends/port/fs.js +7 -1
- package/dist/backends/port/rpc.d.ts +6 -2
- package/dist/backends/port/rpc.js +15 -4
- package/dist/backends/store/fs.d.ts +1 -4
- package/dist/backends/store/fs.js +1 -4
- package/dist/config.d.ts +0 -15
- package/dist/config.js +2 -5
- package/dist/internal/file_index.d.ts +1 -1
- package/dist/internal/file_index.js +1 -1
- package/dist/internal/index_fs.d.ts +1 -1
- package/dist/internal/index_fs.js +1 -1
- package/dist/utils.d.ts +1 -2
- package/dist/utils.js +10 -2
- package/dist/vfs/promises.d.ts +2 -2
- package/dist/vfs/promises.js +75 -84
- package/dist/vfs/shared.d.ts +10 -0
- package/dist/vfs/shared.js +1 -5
- package/dist/vfs/sync.d.ts +2 -2
- package/dist/vfs/sync.js +53 -53
- package/dist/vfs/types.d.ts +1 -13
- package/package.json +1 -1
- package/scripts/test.js +7 -4
- package/tests/backend/port.test.ts +11 -9
- package/tests/common/path.test.ts +12 -0
- package/tests/fs/links.test.ts +25 -0
- package/dist/vfs/cache.d.ts +0 -46
- package/dist/vfs/cache.js +0 -75
package/package.json
CHANGED
package/scripts/test.js
CHANGED
|
@@ -51,7 +51,7 @@ Output:
|
|
|
51
51
|
-h, --help Outputs this help message
|
|
52
52
|
-w, --verbose Output verbose messages
|
|
53
53
|
-q, --quiet Don't output normal messages
|
|
54
|
-
|
|
54
|
+
-l, --logs <level> Change the default log level for test output. Level can be a number or string
|
|
55
55
|
-N, --file-names Use full file paths for tests from setup files instead of the base name
|
|
56
56
|
-C, --ci Continuous integration (CI) mode. This interacts with the Github
|
|
57
57
|
Checks API for better test status. Requires @octokit/action
|
|
@@ -166,9 +166,12 @@ if (options.common) {
|
|
|
166
166
|
!options.quiet && console.log('Running common tests...');
|
|
167
167
|
const { pass, fail } = await status('Common tests');
|
|
168
168
|
try {
|
|
169
|
-
execSync(
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
execSync(
|
|
170
|
+
`tsx ${options.inspect ? 'inspect' : ''} ${options.force ? '--test-force-exit' : ''} --test --experimental-test-coverage 'tests/*.test.ts' 'tests/**/!(fs)/*.test.ts'`,
|
|
171
|
+
{
|
|
172
|
+
stdio: ['ignore', options.verbose ? 'inherit' : 'ignore', 'inherit'],
|
|
173
|
+
}
|
|
174
|
+
);
|
|
172
175
|
await pass();
|
|
173
176
|
} catch {
|
|
174
177
|
await fail();
|
|
@@ -2,6 +2,7 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
3
|
import { MessageChannel, Worker } from 'node:worker_threads';
|
|
4
4
|
import { Port, attachFS } from '../../dist/backends/port/fs.js';
|
|
5
|
+
import { waitOnline } from '../../dist/backends/port/rpc.js';
|
|
5
6
|
import type { InMemoryStore, StoreFS } from '../../dist/index.js';
|
|
6
7
|
import { ErrnoError, InMemory, configure, configureSingle, fs, resolveMountConfig } from '../../dist/index.js';
|
|
7
8
|
|
|
@@ -49,12 +50,13 @@ timeoutChannel.port1.unref();
|
|
|
49
50
|
// Test configuration
|
|
50
51
|
|
|
51
52
|
const configPort = new Worker(import.meta.dirname + '/config.worker.js');
|
|
53
|
+
await waitOnline(configPort);
|
|
52
54
|
|
|
53
55
|
await suite('Remote FS with resolveRemoteMount', () => {
|
|
54
56
|
const content = 'FS is in a port';
|
|
55
57
|
|
|
56
58
|
test('Configuration', async () => {
|
|
57
|
-
await configureSingle({ backend: Port, port: configPort, timeout:
|
|
59
|
+
await configureSingle({ backend: Port, port: configPort, timeout: 100 });
|
|
58
60
|
});
|
|
59
61
|
|
|
60
62
|
test('Write', async () => {
|
|
@@ -62,11 +64,11 @@ await suite('Remote FS with resolveRemoteMount', () => {
|
|
|
62
64
|
});
|
|
63
65
|
|
|
64
66
|
test('Read', async () => {
|
|
65
|
-
assert(
|
|
67
|
+
assert.equal(await fs.promises.readFile('/test', 'utf8'), content);
|
|
66
68
|
});
|
|
67
69
|
});
|
|
68
70
|
|
|
69
|
-
await configPort
|
|
71
|
+
await configPort.terminate();
|
|
70
72
|
configPort.unref();
|
|
71
73
|
|
|
72
74
|
// Test using a message channel
|
|
@@ -79,7 +81,7 @@ await suite('FS with MessageChannel', () => {
|
|
|
79
81
|
test('configuration', async () => {
|
|
80
82
|
tmpfs = await resolveMountConfig({ backend: InMemory, name: 'tmp' });
|
|
81
83
|
attachFS(channel.port2, tmpfs);
|
|
82
|
-
await configureSingle({ backend: Port, port: channel.port1, disableAsyncCache: true, timeout:
|
|
84
|
+
await configureSingle({ backend: Port, port: channel.port1, disableAsyncCache: true, timeout: 100 });
|
|
83
85
|
});
|
|
84
86
|
|
|
85
87
|
test('write', async () => {
|
|
@@ -88,12 +90,12 @@ await suite('FS with MessageChannel', () => {
|
|
|
88
90
|
|
|
89
91
|
test('remote content', () => {
|
|
90
92
|
fs.mount('/tmp', tmpfs);
|
|
91
|
-
assert(fs.readFileSync('/tmp/test', 'utf8')
|
|
93
|
+
assert.equal(fs.readFileSync('/tmp/test', 'utf8'), content);
|
|
92
94
|
fs.umount('/tmp');
|
|
93
95
|
});
|
|
94
96
|
|
|
95
97
|
test('read', async () => {
|
|
96
|
-
assert(
|
|
98
|
+
assert.equal(await fs.promises.readFile('/test', 'utf8'), content);
|
|
97
99
|
});
|
|
98
100
|
|
|
99
101
|
test('readFileSync should throw', () => {
|
|
@@ -101,6 +103,8 @@ await suite('FS with MessageChannel', () => {
|
|
|
101
103
|
});
|
|
102
104
|
});
|
|
103
105
|
|
|
106
|
+
channel.port1.close();
|
|
107
|
+
channel.port2.close();
|
|
104
108
|
channel.port1.unref();
|
|
105
109
|
channel.port2.unref();
|
|
106
110
|
|
|
@@ -112,7 +116,7 @@ await suite('Remote FS', () => {
|
|
|
112
116
|
const content = 'FS is in a port';
|
|
113
117
|
|
|
114
118
|
test('Configuration', async () => {
|
|
115
|
-
await configureSingle({ backend: Port, port: remotePort, timeout:
|
|
119
|
+
await configureSingle({ backend: Port, port: remotePort, timeout: 100 });
|
|
116
120
|
});
|
|
117
121
|
|
|
118
122
|
test('Write', async () => {
|
|
@@ -122,8 +126,6 @@ await suite('Remote FS', () => {
|
|
|
122
126
|
test('Read', async () => {
|
|
123
127
|
assert.equal(await fs.promises.readFile('/test', 'utf8'), content);
|
|
124
128
|
});
|
|
125
|
-
|
|
126
|
-
test('Cleanup', async () => {});
|
|
127
129
|
});
|
|
128
130
|
|
|
129
131
|
await remotePort.terminate();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
3
|
import { basename, dirname, extname, join, normalize, resolve } from '../../dist/vfs/path.js';
|
|
4
|
+
import * as fs from '../../dist/vfs/index.js';
|
|
4
5
|
|
|
5
6
|
suite('Path emulation', () => {
|
|
6
7
|
test('resolve', () => {
|
|
@@ -31,4 +32,15 @@ suite('Path emulation', () => {
|
|
|
31
32
|
assert.equal(extname('/path/to/file.txt'), '.txt');
|
|
32
33
|
assert.equal(extname('/path/to/file'), '');
|
|
33
34
|
});
|
|
35
|
+
|
|
36
|
+
test('file:// URL (string)', () => {
|
|
37
|
+
fs.writeFileSync('/example.txt', 'Yay');
|
|
38
|
+
assert.equal(fs.readFileSync('file:///example.txt', 'utf-8'), 'Yay');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('file:// URL (URL)', () => {
|
|
42
|
+
fs.writeFileSync('/example.txt', 'Yay');
|
|
43
|
+
const url = new URL('file:///example.txt');
|
|
44
|
+
assert.equal(fs.readFileSync(url, 'utf-8'), 'Yay');
|
|
45
|
+
});
|
|
34
46
|
});
|
package/tests/fs/links.test.ts
CHANGED
|
@@ -27,6 +27,31 @@ suite('Links', () => {
|
|
|
27
27
|
assert.equal(await fs.promises.readFile(target, 'utf-8'), await fs.promises.readFile(symlink, 'utf-8'));
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
test('nested symlinks', async () => {
|
|
31
|
+
// Create the real directory structure
|
|
32
|
+
const realDir = '/real-dir';
|
|
33
|
+
const realFile = '/real-dir/realfile.txt';
|
|
34
|
+
const fileContent = 'hello world';
|
|
35
|
+
await fs.promises.mkdir(realDir);
|
|
36
|
+
await fs.promises.writeFile(realFile, fileContent);
|
|
37
|
+
// Create first symlink (symlink-dir -> real-dir)
|
|
38
|
+
const symlinkDir = '/symlink-dir';
|
|
39
|
+
await fs.promises.symlink(realDir, symlinkDir);
|
|
40
|
+
const symfile = 'symfile.txt';
|
|
41
|
+
const symlinkFile = join(realDir, symfile);
|
|
42
|
+
// Create second symlink (symlink-dir -> real-dir)
|
|
43
|
+
await fs.promises.symlink(realFile, symlinkFile);
|
|
44
|
+
// Now access file through nested symlinks
|
|
45
|
+
const nestedPath = join(symlinkDir, symfile);
|
|
46
|
+
// Verify realpath resolution
|
|
47
|
+
const resolvedPath = await fs.promises.realpath(nestedPath);
|
|
48
|
+
assert.equal(resolvedPath, realFile);
|
|
49
|
+
// Verify content can be read through nested symlinks
|
|
50
|
+
const content = await fs.promises.readFile(nestedPath, 'utf8');
|
|
51
|
+
assert.notEqual(content, '/real-dir/realfile.txt');
|
|
52
|
+
assert.equal(content, fileContent);
|
|
53
|
+
});
|
|
54
|
+
|
|
30
55
|
test('unlink', async () => {
|
|
31
56
|
await fs.promises.unlink(symlink);
|
|
32
57
|
assert(!(await fs.promises.exists(symlink)));
|
package/dist/vfs/cache.d.ts
DELETED
|
@@ -1,46 +0,0 @@
|
|
|
1
|
-
import type { Stats } from '../stats.js';
|
|
2
|
-
/**
|
|
3
|
-
* Used for caching data
|
|
4
|
-
* @internal
|
|
5
|
-
*/
|
|
6
|
-
export declare class Cache<T> {
|
|
7
|
-
isEnabled: boolean;
|
|
8
|
-
protected sync: Map<string, T>;
|
|
9
|
-
protected async: Map<string, Promise<T>>;
|
|
10
|
-
/**
|
|
11
|
-
* Whether the data exists in the cache
|
|
12
|
-
*/
|
|
13
|
-
has(path: string): boolean;
|
|
14
|
-
/**
|
|
15
|
-
* Gets data from the cache, if is exists and the cache is enabled.
|
|
16
|
-
*/
|
|
17
|
-
get(path: string): T | undefined;
|
|
18
|
-
/**
|
|
19
|
-
* Adds data if the cache is enabled
|
|
20
|
-
*/
|
|
21
|
-
set(path: string, value: T): void;
|
|
22
|
-
/**
|
|
23
|
-
* Whether the data exists in the cache
|
|
24
|
-
*/
|
|
25
|
-
hasAsync(path: string): boolean;
|
|
26
|
-
/**
|
|
27
|
-
* Gets data from the cache, if it exists and the cache is enabled.
|
|
28
|
-
*/
|
|
29
|
-
getAsync(path: string): Promise<T> | undefined;
|
|
30
|
-
/**
|
|
31
|
-
* Adds data if the cache is enabled
|
|
32
|
-
*/
|
|
33
|
-
setAsync(path: string, value: Promise<T>): void;
|
|
34
|
-
/**
|
|
35
|
-
* Clears the cache if it is enabled
|
|
36
|
-
*/
|
|
37
|
-
clear(): void;
|
|
38
|
-
}
|
|
39
|
-
/**
|
|
40
|
-
* Used to cache
|
|
41
|
-
*/
|
|
42
|
-
export declare const stats: Cache<Stats>;
|
|
43
|
-
/**
|
|
44
|
-
* Used to cache realpath lookups
|
|
45
|
-
*/
|
|
46
|
-
export declare const paths: Cache<string>;
|
package/dist/vfs/cache.js
DELETED
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
/* Experimental caching */
|
|
2
|
-
/**
|
|
3
|
-
* Used for caching data
|
|
4
|
-
* @internal
|
|
5
|
-
*/
|
|
6
|
-
export class Cache {
|
|
7
|
-
constructor() {
|
|
8
|
-
this.isEnabled = false;
|
|
9
|
-
this.sync = new Map();
|
|
10
|
-
this.async = new Map();
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Whether the data exists in the cache
|
|
14
|
-
*/
|
|
15
|
-
has(path) {
|
|
16
|
-
return this.isEnabled && this.sync.has(path);
|
|
17
|
-
}
|
|
18
|
-
/**
|
|
19
|
-
* Gets data from the cache, if is exists and the cache is enabled.
|
|
20
|
-
*/
|
|
21
|
-
get(path) {
|
|
22
|
-
if (!this.isEnabled)
|
|
23
|
-
return;
|
|
24
|
-
return this.sync.get(path);
|
|
25
|
-
}
|
|
26
|
-
/**
|
|
27
|
-
* Adds data if the cache is enabled
|
|
28
|
-
*/
|
|
29
|
-
set(path, value) {
|
|
30
|
-
if (!this.isEnabled)
|
|
31
|
-
return;
|
|
32
|
-
this.sync.set(path, value);
|
|
33
|
-
this.async.set(path, Promise.resolve(value));
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Whether the data exists in the cache
|
|
37
|
-
*/
|
|
38
|
-
hasAsync(path) {
|
|
39
|
-
return this.isEnabled && this.async.has(path);
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Gets data from the cache, if it exists and the cache is enabled.
|
|
43
|
-
*/
|
|
44
|
-
getAsync(path) {
|
|
45
|
-
if (!this.isEnabled)
|
|
46
|
-
return;
|
|
47
|
-
return this.async.get(path);
|
|
48
|
-
}
|
|
49
|
-
/**
|
|
50
|
-
* Adds data if the cache is enabled
|
|
51
|
-
*/
|
|
52
|
-
setAsync(path, value) {
|
|
53
|
-
if (!this.isEnabled)
|
|
54
|
-
return;
|
|
55
|
-
this.async.set(path, value);
|
|
56
|
-
void value.then(v => this.sync.set(path, v));
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Clears the cache if it is enabled
|
|
60
|
-
*/
|
|
61
|
-
clear() {
|
|
62
|
-
if (!this.isEnabled)
|
|
63
|
-
return;
|
|
64
|
-
this.sync.clear();
|
|
65
|
-
this.async.clear();
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
/**
|
|
69
|
-
* Used to cache
|
|
70
|
-
*/
|
|
71
|
-
export const stats = new Cache();
|
|
72
|
-
/**
|
|
73
|
-
* Used to cache realpath lookups
|
|
74
|
-
*/
|
|
75
|
-
export const paths = new Cache();
|