@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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zenfs/core",
3
- "version": "1.9.3",
3
+ "version": "1.9.5",
4
4
  "description": "A filesystem, anywhere",
5
5
  "funding": {
6
6
  "type": "individual",
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
- -l, --logs <level> Change the default log level for test output. Level can be a number or string
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(`tsx ${options.inspect ? 'inspect' : ''} --test --experimental-test-coverage 'tests/*.test.ts' 'tests/**/!(fs)/*.test.ts'`, {
170
- stdio: ['ignore', options.verbose ? 'inherit' : 'ignore', 'inherit'],
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: 500 });
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((await fs.promises.readFile('/test', 'utf8')) === content);
67
+ assert.equal(await fs.promises.readFile('/test', 'utf8'), content);
66
68
  });
67
69
  });
68
70
 
69
- await configPort?.terminate();
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: 250 });
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') == content);
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((await fs.promises.readFile('/test', 'utf8')) === content);
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: 500 });
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
  });
@@ -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)));
@@ -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();