@zenfs/core 1.8.8 → 1.9.1
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 +1 -1
- package/dist/backends/backend.js +7 -4
- package/dist/backends/fetch.d.ts +23 -32
- package/dist/backends/fetch.js +94 -134
- package/dist/backends/index.d.ts +1 -4
- package/dist/backends/index.js +1 -4
- package/dist/backends/memory.d.ts +7 -5
- package/dist/backends/memory.js +6 -4
- package/dist/backends/overlay.d.ts +4 -5
- package/dist/backends/overlay.js +16 -20
- package/dist/backends/passthrough.d.ts +3 -3
- package/dist/backends/passthrough.js +4 -6
- package/dist/backends/port/fs.d.ts +4 -5
- package/dist/backends/port/fs.js +7 -12
- package/dist/backends/port/rpc.d.ts +1 -1
- package/dist/backends/port/rpc.js +15 -13
- package/dist/backends/store/fs.d.ts +51 -40
- package/dist/backends/store/fs.js +347 -241
- package/dist/backends/store/map.d.ts +41 -0
- package/dist/backends/store/map.js +45 -0
- package/dist/backends/store/simple.d.ts +10 -58
- package/dist/backends/store/simple.js +8 -115
- package/dist/backends/store/store.d.ts +111 -44
- package/dist/backends/store/store.js +230 -38
- package/dist/config.d.ts +7 -3
- package/dist/config.js +17 -14
- package/dist/context.d.ts +1 -1
- package/dist/context.js +1 -1
- package/dist/index.d.ts +1 -5
- package/dist/index.js +1 -5
- package/dist/{devices.d.ts → internal/devices.d.ts} +4 -4
- package/dist/{devices.js → internal/devices.js} +18 -14
- package/dist/{file.d.ts → internal/file.d.ts} +3 -2
- package/dist/{file.js → internal/file.js} +17 -12
- package/dist/{backends/store → internal}/file_index.d.ts +13 -3
- package/dist/{backends/store → internal}/file_index.js +28 -5
- package/dist/{filesystem.d.ts → internal/filesystem.d.ts} +99 -32
- package/dist/internal/filesystem.js +83 -0
- package/dist/internal/index.d.ts +9 -0
- package/dist/internal/index.js +9 -0
- package/dist/internal/index_fs.d.ts +56 -0
- package/dist/internal/index_fs.js +188 -0
- package/dist/{backends/store → internal}/inode.d.ts +6 -1
- package/dist/{backends/store → internal}/inode.js +14 -6
- package/dist/internal/log.d.ts +132 -0
- package/dist/internal/log.js +177 -0
- package/dist/mixins/async.d.ts +2 -2
- package/dist/mixins/async.js +19 -16
- package/dist/mixins/mutexed.d.ts +9 -3
- package/dist/mixins/mutexed.js +22 -3
- package/dist/mixins/readonly.d.ts +2 -2
- package/dist/mixins/readonly.js +4 -3
- package/dist/mixins/shared.d.ts +1 -1
- package/dist/mixins/sync.d.ts +2 -2
- package/dist/stats.d.ts +2 -3
- package/dist/stats.js +7 -5
- package/dist/utils.d.ts +2 -15
- package/dist/utils.js +10 -47
- package/dist/vfs/async.d.ts +2 -2
- package/dist/vfs/async.js +3 -3
- package/dist/vfs/dir.js +1 -1
- package/dist/vfs/promises.d.ts +6 -6
- package/dist/vfs/promises.js +54 -49
- package/dist/vfs/shared.d.ts +3 -3
- package/dist/vfs/shared.js +16 -10
- package/dist/vfs/streams.js +1 -1
- package/dist/vfs/sync.d.ts +1 -2
- package/dist/vfs/sync.js +14 -15
- package/dist/vfs/types.d.ts +1 -0
- package/dist/vfs/watchers.d.ts +5 -1
- package/dist/vfs/watchers.js +16 -19
- package/package.json +3 -3
- package/readme.md +12 -12
- package/scripts/test.js +15 -3
- package/tests/backend/fetch.test.ts +49 -0
- package/tests/backend/port.test.ts +130 -0
- package/tests/common/context.test.ts +9 -4
- package/tests/common.ts +21 -3
- package/tests/data/image.jpg +0 -0
- package/tests/data/utf8.txt +1 -0
- package/tests/fetch/config.js +40 -0
- package/tests/fetch/fetch.ts +20 -0
- package/tests/fetch/run.sh +3 -3
- package/tests/fetch/{server.ts → server.js} +15 -11
- package/tests/fs/directory.test.ts +1 -1
- package/tests/fs/errors.test.ts +1 -1
- package/tests/fs/links.test.ts +1 -1
- package/tests/fs/open.test.ts +1 -1
- package/tests/fs/permissions.test.ts +2 -3
- package/tests/fs/rename.test.ts +1 -1
- package/tests/fs/stat.test.ts +1 -1
- package/tests/fs/times.test.ts +1 -1
- package/tests/fs/watch.test.ts +21 -22
- package/tests/fs/writeFile.test.ts +8 -7
- package/tests/readme.md +3 -3
- package/tests/setup/_overlay.ts +7 -0
- package/tests/setup/context.ts +2 -2
- package/tests/setup/index.ts +3 -3
- package/tests/setup/memory.ts +2 -2
- package/tests/setup/port.ts +2 -2
- package/tests/setup.ts +25 -5
- package/tests/tsconfig.json +3 -2
- package/dist/backends/store/index_fs.d.ts +0 -34
- package/dist/backends/store/index_fs.js +0 -67
- package/dist/filesystem.js +0 -52
- package/tests/fetch/cow+fetch.ts +0 -13
- package/tests/port/channel.test.ts +0 -39
- package/tests/port/config.test.ts +0 -30
- package/tests/port/remote.test.ts +0 -32
- package/tests/port/timeout.test.ts +0 -48
- /package/dist/{credentials.d.ts → internal/credentials.d.ts} +0 -0
- /package/dist/{credentials.js → internal/credentials.js} +0 -0
- /package/dist/{error.d.ts → internal/error.d.ts} +0 -0
- /package/dist/{error.js → internal/error.js} +0 -0
- /package/tests/{port → backend}/config.worker.js +0 -0
- /package/tests/{port → backend}/remote.worker.js +0 -0
package/dist/vfs/watchers.d.ts
CHANGED
|
@@ -41,6 +41,7 @@ export declare class FSWatcher<T extends string | Buffer = string | Buffer> exte
|
|
|
41
41
|
error: [error: Error];
|
|
42
42
|
}> implements fs.FSWatcher {
|
|
43
43
|
readonly options: fs.WatchOptions;
|
|
44
|
+
protected readonly realpath: string;
|
|
44
45
|
constructor(context: V_Context, path: string, options: fs.WatchOptions);
|
|
45
46
|
close(): void;
|
|
46
47
|
[Symbol.dispose](): void;
|
|
@@ -71,5 +72,8 @@ export declare class StatWatcher extends Watcher<{
|
|
|
71
72
|
}
|
|
72
73
|
export declare function addWatcher(path: string, watcher: FSWatcher): void;
|
|
73
74
|
export declare function removeWatcher(path: string, watcher: FSWatcher): void;
|
|
74
|
-
|
|
75
|
+
/**
|
|
76
|
+
* @internal @hidden
|
|
77
|
+
*/
|
|
78
|
+
export declare function emitChange(context: V_Context, eventType: fs.WatchEventType, filename: string): void;
|
|
75
79
|
export {};
|
package/dist/vfs/watchers.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { EventEmitter } from 'eventemitter3';
|
|
2
|
-
import { ErrnoError } from '../error.js';
|
|
2
|
+
import { ErrnoError } from '../internal/error.js';
|
|
3
3
|
import { isStatsEqual } from '../stats.js';
|
|
4
4
|
import { normalizePath } from '../utils.js';
|
|
5
|
-
import {
|
|
5
|
+
import { dirname, join, relative } from './path.js';
|
|
6
6
|
import { statSync } from './sync.js';
|
|
7
7
|
/**
|
|
8
8
|
* Base class for file system watchers.
|
|
@@ -59,11 +59,12 @@ export class FSWatcher extends Watcher {
|
|
|
59
59
|
constructor(context, path, options) {
|
|
60
60
|
super(context, path);
|
|
61
61
|
this.options = options;
|
|
62
|
-
|
|
62
|
+
this.realpath = (context === null || context === void 0 ? void 0 : context.root) ? join(context.root, path) : path;
|
|
63
|
+
addWatcher(this.realpath, this);
|
|
63
64
|
}
|
|
64
65
|
close() {
|
|
65
66
|
super.emit('close');
|
|
66
|
-
removeWatcher(this.
|
|
67
|
+
removeWatcher(this.realpath, this);
|
|
67
68
|
}
|
|
68
69
|
[Symbol.dispose]() {
|
|
69
70
|
this.close();
|
|
@@ -134,28 +135,24 @@ export function removeWatcher(path, watcher) {
|
|
|
134
135
|
}
|
|
135
136
|
}
|
|
136
137
|
}
|
|
137
|
-
|
|
138
|
+
/**
|
|
139
|
+
* @internal @hidden
|
|
140
|
+
*/
|
|
141
|
+
export function emitChange(context, eventType, filename) {
|
|
138
142
|
var _a;
|
|
143
|
+
if (context)
|
|
144
|
+
filename = join((_a = context.root) !== null && _a !== void 0 ? _a : '/', filename);
|
|
139
145
|
filename = normalizePath(filename);
|
|
140
|
-
// Notify watchers on the specific file
|
|
141
|
-
if (watchers.has(filename)) {
|
|
142
|
-
for (const watcher of watchers.get(filename)) {
|
|
143
|
-
watcher.emit('change', eventType, basename(filename));
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
146
|
// Notify watchers on parent directories if they are watching recursively
|
|
147
147
|
let parent = filename, normalizedFilename;
|
|
148
|
-
while (
|
|
148
|
+
while (normalizedFilename != '/') {
|
|
149
149
|
normalizedFilename = parent;
|
|
150
150
|
parent = dirname(parent);
|
|
151
|
-
|
|
151
|
+
const parentWatchers = watchers.get(parent);
|
|
152
|
+
if (!parentWatchers)
|
|
152
153
|
continue;
|
|
153
|
-
for (const watcher of
|
|
154
|
-
|
|
155
|
-
const root = (_a = watcher._context) === null || _a === void 0 ? void 0 : _a.root;
|
|
156
|
-
const contextPath = root && filename.startsWith(root) ? filename.slice(root.length) : filename;
|
|
157
|
-
const relativePath = contextPath.slice(parent.length + (parent == '/' ? 0 : 1));
|
|
158
|
-
watcher.emit('change', eventType, relativePath);
|
|
154
|
+
for (const watcher of parentWatchers) {
|
|
155
|
+
watcher.emit('change', eventType, relative(parent, filename));
|
|
159
156
|
}
|
|
160
157
|
}
|
|
161
158
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zenfs/core",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.1",
|
|
4
4
|
"description": "A filesystem, anywhere",
|
|
5
5
|
"funding": {
|
|
6
6
|
"type": "individual",
|
|
@@ -58,7 +58,7 @@
|
|
|
58
58
|
"format": "prettier --write .",
|
|
59
59
|
"format:check": "prettier --check .",
|
|
60
60
|
"lint": "eslint src tests",
|
|
61
|
-
"test": "npx zenfs-test --clean
|
|
61
|
+
"test": "npx zenfs-test --clean; npx zenfs-test -abcfp; tests/fetch/run.sh; npx zenfs-test --report",
|
|
62
62
|
"build": "tsc -p tsconfig.json",
|
|
63
63
|
"build:docs": "typedoc",
|
|
64
64
|
"dev": "npm run build -- --watch",
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"buffer": "^6.0.3",
|
|
71
71
|
"eventemitter3": "^5.0.1",
|
|
72
72
|
"readable-stream": "^4.5.2",
|
|
73
|
-
"utilium": "^1.
|
|
73
|
+
"utilium": "^1.2.10"
|
|
74
74
|
},
|
|
75
75
|
"devDependencies": {
|
|
76
76
|
"@eslint/js": "^9.8.0",
|
package/readme.md
CHANGED
|
@@ -6,11 +6,11 @@ ZenFS is a cross-platform library that emulates the [NodeJS filesystem API](http
|
|
|
6
6
|
|
|
7
7
|
ZenFS is modular and extensible. The core includes some built-in backends:
|
|
8
8
|
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
9
|
+
- `InMemory`: Stores files in-memory. This is cleared when the runtime ends (e.g. a user navigating away from a web page or a Node process exiting)
|
|
10
|
+
- `Overlay`: Use a read-only file system as read-write by overlaying a writable file system on top of it. ([copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write))
|
|
11
|
+
- `Fetch`: Downloads files over HTTP with the `fetch` API
|
|
12
|
+
- `Port`: Interacts with a remote over a `MessagePort`-like interface (e.g. a worker)
|
|
13
|
+
- `Passthrough`: Use an existing `node:fs` interface with ZenFS
|
|
14
14
|
|
|
15
15
|
ZenFS supports a number of other backends. Many are provided as separate packages under `@zenfs`. More backends can be defined by separate libraries by extending the `FileSystem` class and providing a `Backend` object.
|
|
16
16
|
|
|
@@ -207,18 +207,18 @@ A huge thank you to [ flag will automatically detect any setup scripts matching `tests/setup/*` or `tests/setup-*.ts`. If you do, you'll need to include the `c8` dependency for coverage.
|
|
222
222
|
|
|
223
223
|
### BrowserFS Fork
|
|
224
224
|
|
package/scripts/test.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { execSync } from 'node:child_process';
|
|
4
4
|
import { existsSync, globSync, mkdirSync, rmSync } from 'node:fs';
|
|
5
|
-
import { join, parse } from 'node:path';
|
|
5
|
+
import { join, parse, basename } from 'node:path';
|
|
6
6
|
import { parseArgs } from 'node:util';
|
|
7
7
|
|
|
8
8
|
const { values: options, positionals } = parseArgs({
|
|
@@ -11,6 +11,7 @@ const { values: options, positionals } = parseArgs({
|
|
|
11
11
|
help: { short: 'h', type: 'boolean', default: false },
|
|
12
12
|
verbose: { short: 'w', type: 'boolean', default: false },
|
|
13
13
|
quiet: { short: 'q', type: 'boolean', default: false },
|
|
14
|
+
log: { short: 'l', type: 'string', default: '' },
|
|
14
15
|
'file-names': { short: 'N', type: 'boolean', default: false },
|
|
15
16
|
ci: { short: 'C', type: 'boolean', default: false },
|
|
16
17
|
|
|
@@ -50,6 +51,7 @@ Output:
|
|
|
50
51
|
-h, --help Outputs this help message
|
|
51
52
|
-w, --verbose Output verbose messages
|
|
52
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
|
|
53
55
|
-N, --file-names Use full file paths for tests from setup files instead of the base name
|
|
54
56
|
-C, --ci Continuous integration (CI) mode. This interacts with the Github
|
|
55
57
|
Checks API for better test status. Requires @octokit/action
|
|
@@ -68,6 +70,7 @@ if (options.quiet && options.verbose) {
|
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
process.env.NODE_V8_COVERAGE = options.coverage;
|
|
73
|
+
process.env.ZENFS_LOG_LEVEL = options.log;
|
|
71
74
|
|
|
72
75
|
if (options.clean) {
|
|
73
76
|
rmSync(options.coverage, { recursive: true, force: true });
|
|
@@ -143,6 +146,10 @@ async function status(name) {
|
|
|
143
146
|
if (!options.quiet) console.log(`${color('passed', 32)}: ${name} ${time()}`);
|
|
144
147
|
if (options.ci) await ci.completeCheck(name, 'success');
|
|
145
148
|
},
|
|
149
|
+
async skip() {
|
|
150
|
+
if (!options.quiet) console.log(`${color('skipped', 33)}: ${name} ${time()}`);
|
|
151
|
+
if (options.ci) await ci.completeCheck(name, 'skipped');
|
|
152
|
+
},
|
|
146
153
|
async fail() {
|
|
147
154
|
console.error(`${color('failed', '1;31')}: ${name} ${time()}`);
|
|
148
155
|
if (options.ci) await ci.completeCheck(name, 'failure');
|
|
@@ -182,12 +189,17 @@ for (const setupFile of positionals) {
|
|
|
182
189
|
|
|
183
190
|
!options.quiet && console.log('Running tests:', name);
|
|
184
191
|
|
|
185
|
-
const { pass, fail } = await status(name);
|
|
192
|
+
const { pass, fail, skip } = await status(name);
|
|
193
|
+
|
|
194
|
+
if (basename(setupFile).startsWith('_')) {
|
|
195
|
+
await skip();
|
|
196
|
+
continue;
|
|
197
|
+
}
|
|
186
198
|
|
|
187
199
|
try {
|
|
188
200
|
execSync(
|
|
189
201
|
[
|
|
190
|
-
'tsx',
|
|
202
|
+
'tsx --trace-deprecation',
|
|
191
203
|
options.inspect ? 'inspect' : '',
|
|
192
204
|
'--test --experimental-test-coverage',
|
|
193
205
|
options.force ? '--test-force-exit' : '',
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { suite, test } from 'node:test';
|
|
4
|
+
import { Worker } from 'node:worker_threads';
|
|
5
|
+
import { Fetch, configureSingle, fs } from '../../dist/index.js';
|
|
6
|
+
import { baseUrl, defaultEntries, indexPath, whenServerReady } from '../fetch/config.js';
|
|
7
|
+
|
|
8
|
+
const server = new Worker(join(import.meta.dirname, '../fetch/server.js'));
|
|
9
|
+
|
|
10
|
+
await whenServerReady();
|
|
11
|
+
|
|
12
|
+
await suite('Fetch with `disableAsyncCache`', () => {
|
|
13
|
+
test('Configuration', async () => {
|
|
14
|
+
await configureSingle({
|
|
15
|
+
backend: Fetch,
|
|
16
|
+
disableAsyncCache: true,
|
|
17
|
+
remoteWrite: true,
|
|
18
|
+
baseUrl,
|
|
19
|
+
index: baseUrl + indexPath,
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('Read and write file', async () => {
|
|
24
|
+
await fs.promises.writeFile('/example', 'test');
|
|
25
|
+
|
|
26
|
+
const contents = await fs.promises.readFile('/example', 'utf8');
|
|
27
|
+
|
|
28
|
+
assert.equal(contents, 'test');
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test('Make new directory', async () => {
|
|
32
|
+
await fs.promises.mkdir('/duck');
|
|
33
|
+
const stats = await fs.promises.stat('/duck');
|
|
34
|
+
assert(stats.isDirectory());
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
test('Read directory', async () => {
|
|
38
|
+
const entries = await fs.promises.readdir('/');
|
|
39
|
+
|
|
40
|
+
assert.deepEqual(entries, [...defaultEntries, 'example', 'duck']);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
test('Uncached synchronous operations throw', () => {
|
|
44
|
+
assert.throws(() => fs.readFileSync('/x.txt', 'utf8'), { code: 'EAGAIN' });
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
await server.terminate();
|
|
49
|
+
server.unref();
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { suite, test } from 'node:test';
|
|
3
|
+
import { MessageChannel, Worker } from 'node:worker_threads';
|
|
4
|
+
import { Port, attachFS } from '../../dist/backends/port/fs.js';
|
|
5
|
+
import type { InMemoryStore, StoreFS } from '../../dist/index.js';
|
|
6
|
+
import { ErrnoError, InMemory, configure, configureSingle, fs, resolveMountConfig } from '../../dist/index.js';
|
|
7
|
+
|
|
8
|
+
// Tests a mis-configured `Port` using a MessageChannel
|
|
9
|
+
|
|
10
|
+
const timeoutChannel = new MessageChannel();
|
|
11
|
+
timeoutChannel.port2.unref();
|
|
12
|
+
|
|
13
|
+
await suite('Timeout', { timeout: 1000 }, () => {
|
|
14
|
+
test('Misconfiguration', async () => {
|
|
15
|
+
let error: ErrnoError;
|
|
16
|
+
try {
|
|
17
|
+
await configure({
|
|
18
|
+
mounts: {
|
|
19
|
+
'/tmp-timeout': { backend: InMemory, name: 'tmp' },
|
|
20
|
+
'/port': { backend: Port, port: timeoutChannel.port1, timeout: 100 },
|
|
21
|
+
},
|
|
22
|
+
});
|
|
23
|
+
} catch (e) {
|
|
24
|
+
assert(e instanceof ErrnoError);
|
|
25
|
+
error = e;
|
|
26
|
+
}
|
|
27
|
+
assert(error! instanceof ErrnoError);
|
|
28
|
+
assert.equal(error.code, 'EIO');
|
|
29
|
+
assert(error.message.includes('RPC Failed'));
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('Remote not attached', async () => {
|
|
33
|
+
let error: ErrnoError;
|
|
34
|
+
try {
|
|
35
|
+
await configureSingle({ backend: Port, port: timeoutChannel.port1, timeout: 100 });
|
|
36
|
+
await fs.promises.writeFile('/test', 'anything');
|
|
37
|
+
} catch (e) {
|
|
38
|
+
assert(e instanceof ErrnoError);
|
|
39
|
+
error = e;
|
|
40
|
+
}
|
|
41
|
+
assert(error! instanceof ErrnoError);
|
|
42
|
+
assert.equal(error.code, 'EIO');
|
|
43
|
+
assert(error.message.includes('RPC Failed'));
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
timeoutChannel.port1.unref();
|
|
48
|
+
|
|
49
|
+
// Test configuration
|
|
50
|
+
|
|
51
|
+
const configPort = new Worker(import.meta.dirname + '/config.worker.js');
|
|
52
|
+
|
|
53
|
+
await suite('Remote FS with resolveRemoteMount', () => {
|
|
54
|
+
const content = 'FS is in a port';
|
|
55
|
+
|
|
56
|
+
test('Configuration', async () => {
|
|
57
|
+
await configureSingle({ backend: Port, port: configPort, timeout: 500 });
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('Write', async () => {
|
|
61
|
+
await fs.promises.writeFile('/test', content);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('Read', async () => {
|
|
65
|
+
assert((await fs.promises.readFile('/test', 'utf8')) === content);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
await configPort?.terminate();
|
|
70
|
+
configPort.unref();
|
|
71
|
+
|
|
72
|
+
// Test using a message channel
|
|
73
|
+
|
|
74
|
+
const channel = new MessageChannel(),
|
|
75
|
+
content = 'FS is in a port';
|
|
76
|
+
let tmpfs: StoreFS<InMemoryStore>;
|
|
77
|
+
|
|
78
|
+
await suite('FS with MessageChannel', () => {
|
|
79
|
+
test('configuration', async () => {
|
|
80
|
+
tmpfs = await resolveMountConfig({ backend: InMemory, name: 'tmp' });
|
|
81
|
+
attachFS(channel.port2, tmpfs);
|
|
82
|
+
await configureSingle({ backend: Port, port: channel.port1, disableAsyncCache: true, timeout: 250 });
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test('write', async () => {
|
|
86
|
+
await fs.promises.writeFile('/test', content);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('remote content', () => {
|
|
90
|
+
fs.mount('/tmp', tmpfs);
|
|
91
|
+
assert(fs.readFileSync('/tmp/test', 'utf8') == content);
|
|
92
|
+
fs.umount('/tmp');
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test('read', async () => {
|
|
96
|
+
assert((await fs.promises.readFile('/test', 'utf8')) === content);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
test('readFileSync should throw', () => {
|
|
100
|
+
assert.throws(() => fs.readFileSync('/test', 'utf8'), { code: 'ENOTSUP' });
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
channel.port1.unref();
|
|
105
|
+
channel.port2.unref();
|
|
106
|
+
|
|
107
|
+
// Test using a worker
|
|
108
|
+
|
|
109
|
+
const remotePort = new Worker(import.meta.dirname + '/remote.worker.js');
|
|
110
|
+
|
|
111
|
+
await suite('Remote FS', () => {
|
|
112
|
+
const content = 'FS is in a port';
|
|
113
|
+
|
|
114
|
+
test('Configuration', async () => {
|
|
115
|
+
await configureSingle({ backend: Port, port: remotePort, timeout: 500 });
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
test('Write', async () => {
|
|
119
|
+
await fs.promises.writeFile('/test', content);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
test('Read', async () => {
|
|
123
|
+
assert.equal(await fs.promises.readFile('/test', 'utf8'), content);
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
test('Cleanup', async () => {});
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
await remotePort.terminate();
|
|
130
|
+
remotePort.unref();
|
|
@@ -2,6 +2,7 @@ import { suite, test } from 'node:test';
|
|
|
2
2
|
import assert from 'node:assert/strict';
|
|
3
3
|
import { bindContext } from '../../dist/context.js';
|
|
4
4
|
import * as fs from '../../dist/vfs/index.js';
|
|
5
|
+
import { canary } from 'utilium';
|
|
5
6
|
|
|
6
7
|
fs.mkdirSync('/ctx');
|
|
7
8
|
const { fs: ctx } = bindContext('/ctx');
|
|
@@ -38,19 +39,23 @@ suite('Context', () => {
|
|
|
38
39
|
});
|
|
39
40
|
|
|
40
41
|
test('watch should consider context', async () => {
|
|
41
|
-
let lastFile: string,
|
|
42
|
+
let lastFile: string | null = null,
|
|
42
43
|
events = 0;
|
|
43
44
|
const watcher = ctx.promises.watch('/', { recursive: true });
|
|
44
45
|
|
|
45
|
-
|
|
46
|
+
const silence = canary();
|
|
47
|
+
const promise = (async () => {
|
|
46
48
|
for await (const event of watcher) {
|
|
47
|
-
lastFile = event.filename
|
|
49
|
+
lastFile = event.filename;
|
|
48
50
|
if (++events == 2) return;
|
|
49
51
|
}
|
|
50
52
|
})();
|
|
53
|
+
silence();
|
|
51
54
|
await ctx.promises.writeFile('/xpto.txt', 'in real root');
|
|
52
|
-
assert.equal(lastFile
|
|
55
|
+
assert.equal(lastFile, 'xpto.txt');
|
|
53
56
|
await ctx.promises.unlink('/xpto.txt');
|
|
54
57
|
assert.equal(lastFile, 'xpto.txt');
|
|
58
|
+
await watcher.return!();
|
|
59
|
+
await promise;
|
|
55
60
|
});
|
|
56
61
|
});
|
package/tests/common.ts
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
1
|
import { join, resolve } from 'node:path';
|
|
2
|
-
import { fs as defaultFS } from '../dist/index.js';
|
|
3
|
-
export type * from '../dist/
|
|
2
|
+
import { fs as defaultFS, log } from '../dist/index.js';
|
|
3
|
+
export type * from '../dist/index.js';
|
|
4
4
|
|
|
5
|
-
const
|
|
5
|
+
const { ZENFS_LOG_LEVEL, SETUP } = process.env;
|
|
6
|
+
|
|
7
|
+
let level: log.Level | (typeof log.levels)[log.Level] = log.Level.CRIT;
|
|
8
|
+
|
|
9
|
+
if (ZENFS_LOG_LEVEL) {
|
|
10
|
+
const tmp = parseInt(ZENFS_LOG_LEVEL);
|
|
11
|
+
if (Number.isSafeInteger(tmp)) level = tmp;
|
|
12
|
+
else level = ZENFS_LOG_LEVEL as (typeof log.levels)[log.Level];
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
log.configure({
|
|
16
|
+
enabled: true,
|
|
17
|
+
format: log.formats.ansi_message,
|
|
18
|
+
dumpBacklog: true,
|
|
19
|
+
level,
|
|
20
|
+
output: console.error,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const setupPath = resolve(SETUP || join(import.meta.dirname, 'setup/memory.ts'));
|
|
6
24
|
|
|
7
25
|
const setup = await import(setupPath).catch(error => {
|
|
8
26
|
console.log('Failed to import test setup:');
|
|
Binary file
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
南越国是前203年至前111年存在于岭南地区的一个国家,国都位于番禺,疆域包括今天中国的广东、广西两省区的大部份地区,福建省、湖南、贵州、云南的一小部份地区和越南的北部。南越国是秦朝灭亡后,由南海郡尉赵佗于前203年起兵兼并桂林郡和象郡后建立。前196年和前179年,南越国曾先后两次名义上臣属于西汉,成为西汉的“外臣”。前112年,南越国末代君主赵建德与西汉发生战争,被汉武帝于前111年所灭。南越国共存在93年,历经五代君主。南越国是岭南地区的第一个有记载的政权国家,采用封建制和郡县制并存的制度,它的建立保证了秦末乱世岭南地区社会秩序的稳定,有效的改善了岭南地区落后的政治、经济现状。
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { createConnection } from 'node:net';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
|
|
5
|
+
// Copied from setup.ts
|
|
6
|
+
|
|
7
|
+
export const data = join(import.meta.dirname, '../data');
|
|
8
|
+
|
|
9
|
+
export const defaultEntries = readdirSync(data);
|
|
10
|
+
|
|
11
|
+
export const tmp = join(import.meta.dirname, '../tmp');
|
|
12
|
+
|
|
13
|
+
if (!existsSync(tmp)) mkdirSync(tmp);
|
|
14
|
+
|
|
15
|
+
export const port = 26514;
|
|
16
|
+
|
|
17
|
+
export const baseUrl = 'http://localhost:' + port;
|
|
18
|
+
|
|
19
|
+
export const indexPath = '/.index.json';
|
|
20
|
+
|
|
21
|
+
export function whenServerReady() {
|
|
22
|
+
const { promise, resolve, reject } = Promise.withResolvers();
|
|
23
|
+
|
|
24
|
+
const timeout = setTimeout(reject, 10_000);
|
|
25
|
+
|
|
26
|
+
const interval = setInterval(() => {
|
|
27
|
+
const socket = createConnection(port)
|
|
28
|
+
.on('connect', () => {
|
|
29
|
+
socket.end();
|
|
30
|
+
clearTimeout(timeout);
|
|
31
|
+
clearInterval(interval);
|
|
32
|
+
resolve();
|
|
33
|
+
})
|
|
34
|
+
.on('error', () => {
|
|
35
|
+
// ignore
|
|
36
|
+
});
|
|
37
|
+
}, 250);
|
|
38
|
+
|
|
39
|
+
return promise;
|
|
40
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { configure, Fetch } from '../../dist/index.js';
|
|
2
|
+
import { baseUrl } from './config.js';
|
|
3
|
+
import * as log from '../../dist/internal/log.js';
|
|
4
|
+
|
|
5
|
+
await configure({
|
|
6
|
+
mounts: {
|
|
7
|
+
'/': {
|
|
8
|
+
backend: Fetch,
|
|
9
|
+
baseUrl,
|
|
10
|
+
index: baseUrl + '/.index.json',
|
|
11
|
+
},
|
|
12
|
+
},
|
|
13
|
+
log: {
|
|
14
|
+
enabled: true,
|
|
15
|
+
output: console.error,
|
|
16
|
+
format: log.formats.ansi_message,
|
|
17
|
+
level: log.Level.INFO,
|
|
18
|
+
dumpBacklog: true,
|
|
19
|
+
},
|
|
20
|
+
});
|
package/tests/fetch/run.sh
CHANGED
|
@@ -3,14 +3,14 @@
|
|
|
3
3
|
# Credit: Dave Dopson, https://stackoverflow.com/a/246128/17637456
|
|
4
4
|
SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd )
|
|
5
5
|
|
|
6
|
-
|
|
6
|
+
node $SCRIPT_DIR/server.js &
|
|
7
7
|
PID=$!
|
|
8
8
|
|
|
9
9
|
echo "Waiting for server to start..."
|
|
10
10
|
until nc -z localhost 26514; do
|
|
11
|
-
sleep 0.
|
|
11
|
+
sleep 0.25
|
|
12
12
|
done
|
|
13
13
|
|
|
14
|
-
npx zenfs-test $SCRIPT_DIR/
|
|
14
|
+
npx zenfs-test $SCRIPT_DIR/fetch.ts --preserve --force "$@"
|
|
15
15
|
|
|
16
16
|
kill $PID
|
|
@@ -2,10 +2,7 @@ import { execSync } from 'node:child_process';
|
|
|
2
2
|
import { readFileSync } from 'node:fs';
|
|
3
3
|
import { createServer } from 'node:http';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
|
-
import { data, tmp } from '
|
|
6
|
-
|
|
7
|
-
// If you change the port please update the setup file as well
|
|
8
|
-
const port = 26514;
|
|
5
|
+
import { data, port, tmp, indexPath } from './config.js';
|
|
9
6
|
|
|
10
7
|
const statusCodes = {
|
|
11
8
|
ENOENT: 404,
|
|
@@ -13,7 +10,7 @@ const statusCodes = {
|
|
|
13
10
|
|
|
14
11
|
try {
|
|
15
12
|
execSync(`npm exec make-index -- ${data} --output ${tmp}/index.json --quiet`, { stdio: 'inherit' });
|
|
16
|
-
} catch (e
|
|
13
|
+
} catch (e) {
|
|
17
14
|
if (e.signal == 'SIGINT') {
|
|
18
15
|
console.log('Aborted whilst creating index.');
|
|
19
16
|
process.exit(0);
|
|
@@ -24,20 +21,27 @@ try {
|
|
|
24
21
|
}
|
|
25
22
|
|
|
26
23
|
const server = createServer((request, response) => {
|
|
27
|
-
|
|
24
|
+
response.statusCode = 200;
|
|
25
|
+
const { url = '', method } = request;
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @todo Test POST/DELETE
|
|
29
|
+
*/
|
|
30
|
+
if (method != 'GET') {
|
|
31
|
+
response.end();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
28
34
|
|
|
29
35
|
if (url == '/.ping') {
|
|
30
|
-
response.statusCode = 200;
|
|
31
36
|
response.end('pong');
|
|
32
37
|
return;
|
|
33
38
|
}
|
|
34
39
|
|
|
35
|
-
const path = url ==
|
|
40
|
+
const path = url == indexPath ? join(tmp, 'index.json') : join(data, url.slice(1) || '');
|
|
36
41
|
try {
|
|
37
|
-
response.statusCode = 200;
|
|
38
42
|
response.end(readFileSync(path));
|
|
39
|
-
} catch (e
|
|
40
|
-
response.statusCode = statusCodes[e.code
|
|
43
|
+
} catch (e) {
|
|
44
|
+
response.statusCode = statusCodes[e.code] || 400;
|
|
41
45
|
response.end();
|
|
42
46
|
}
|
|
43
47
|
});
|
package/tests/fs/errors.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import test, { suite } from 'node:test';
|
|
3
|
-
import type { ErrnoError } from '../../dist/error.js';
|
|
4
3
|
import { fs } from '../common.js';
|
|
4
|
+
import type { ErrnoError } from '../../dist/index.js';
|
|
5
5
|
|
|
6
6
|
const existingFile = '/exit.js';
|
|
7
7
|
|
package/tests/fs/links.test.ts
CHANGED
|
@@ -2,7 +2,7 @@ import assert from 'node:assert/strict';
|
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
3
|
import { join } from '../../dist/vfs/path.js';
|
|
4
4
|
import { fs } from '../common.js';
|
|
5
|
-
import type { ErrnoError } from '../../dist/
|
|
5
|
+
import type { ErrnoError } from '../../dist/index.js';
|
|
6
6
|
|
|
7
7
|
suite('Links', () => {
|
|
8
8
|
const target = '/a1.js',
|
package/tests/fs/open.test.ts
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
import assert from 'node:assert/strict';
|
|
2
2
|
import { suite, test } from 'node:test';
|
|
3
|
-
import { credentials } from '../../dist/
|
|
3
|
+
import { credentials, ErrnoError } from '../../dist/index.js';
|
|
4
|
+
import { encodeUTF8 } from '../../dist/utils.js';
|
|
4
5
|
import { R_OK, W_OK, X_OK } from '../../dist/vfs/constants.js';
|
|
5
6
|
import { join } from '../../dist/vfs/path.js';
|
|
6
|
-
import { ErrnoError } from '../../dist/error.js';
|
|
7
|
-
import { encodeUTF8 } from '../../dist/utils.js';
|
|
8
7
|
import { fs } from '../common.js';
|
|
9
8
|
|
|
10
9
|
const asyncMode = 0o777;
|
package/tests/fs/rename.test.ts
CHANGED