@zenfs/core 1.7.2 → 1.8.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.js +3 -4
- package/dist/backends/fetch.d.ts +17 -18
- package/dist/backends/fetch.js +95 -58
- package/dist/backends/index.d.ts +2 -1
- package/dist/backends/index.js +2 -1
- package/dist/backends/memory.d.ts +1 -1
- package/dist/backends/overlay.d.ts +8 -14
- package/dist/backends/overlay.js +38 -31
- package/dist/backends/passthrough.d.ts +8 -3
- package/dist/backends/passthrough.js +148 -4
- package/dist/backends/port/fs.d.ts +15 -49
- package/dist/backends/port/fs.js +28 -116
- package/dist/backends/port/rpc.d.ts +13 -6
- package/dist/backends/port/rpc.js +9 -7
- package/dist/backends/store/file_index.d.ts +38 -0
- package/dist/backends/store/file_index.js +76 -0
- package/dist/backends/store/fs.d.ts +39 -34
- package/dist/backends/store/fs.js +407 -238
- package/dist/backends/store/index_fs.d.ts +34 -0
- package/dist/backends/store/index_fs.js +67 -0
- package/dist/backends/store/inode.d.ts +26 -8
- package/dist/backends/store/inode.js +92 -91
- package/dist/backends/store/simple.d.ts +20 -20
- package/dist/backends/store/simple.js +3 -4
- package/dist/backends/store/store.d.ts +12 -12
- package/dist/backends/store/store.js +4 -6
- package/dist/devices.d.ts +44 -21
- package/dist/devices.js +110 -55
- package/dist/file.d.ts +111 -7
- package/dist/file.js +324 -92
- package/dist/filesystem.d.ts +44 -4
- package/dist/mixins/async.js +12 -6
- package/dist/mixins/mutexed.d.ts +8 -3
- package/dist/mixins/mutexed.js +57 -1
- package/dist/mixins/readonly.d.ts +17 -16
- package/dist/mixins/readonly.js +6 -0
- package/dist/mixins/sync.d.ts +1 -1
- package/dist/stats.d.ts +12 -6
- package/dist/stats.js +14 -6
- package/dist/utils.d.ts +23 -3
- package/dist/utils.js +58 -10
- package/dist/vfs/async.js +1 -1
- package/dist/vfs/constants.d.ts +2 -2
- package/dist/vfs/constants.js +2 -2
- package/dist/vfs/dir.js +3 -1
- package/dist/vfs/index.js +4 -1
- package/dist/vfs/promises.js +33 -13
- package/dist/vfs/shared.js +2 -0
- package/dist/vfs/sync.js +25 -13
- package/dist/vfs/types.d.ts +15 -0
- package/eslint.shared.js +1 -0
- package/package.json +2 -3
- package/readme.md +2 -2
- package/scripts/test.js +73 -11
- package/tests/common/mutex.test.ts +1 -1
- package/tests/fetch/run.sh +16 -0
- package/tests/fetch/server.ts +49 -0
- package/tests/fetch/setup.ts +13 -0
- package/tests/fs/read.test.ts +10 -10
- package/tests/fs/times.test.ts +2 -2
- package/tests/fs/write.test.ts +6 -11
- package/tests/setup/index.ts +38 -0
- package/tests/setup/port.ts +15 -0
- package/dist/backends/file_index.d.ts +0 -63
- package/dist/backends/file_index.js +0 -163
- package/tests/common/async.test.ts +0 -31
- package/tests/setup/cow+fetch.ts +0 -45
- /package/tests/fs/{appendFile.test.ts → append.test.ts} +0 -0
|
@@ -1,63 +0,0 @@
|
|
|
1
|
-
import { NoSyncFile } from '../file.js';
|
|
2
|
-
import { FileSystem } from '../filesystem.js';
|
|
3
|
-
import type { StatsLike } from '../stats.js';
|
|
4
|
-
import { Stats } from '../stats.js';
|
|
5
|
-
/**
|
|
6
|
-
* An Index in JSON form
|
|
7
|
-
* @internal
|
|
8
|
-
*/
|
|
9
|
-
export interface IndexData {
|
|
10
|
-
version: number;
|
|
11
|
-
entries: Record<string, StatsLike<number>>;
|
|
12
|
-
}
|
|
13
|
-
export declare const version = 1;
|
|
14
|
-
/**
|
|
15
|
-
* An index of files
|
|
16
|
-
* @internal
|
|
17
|
-
*/
|
|
18
|
-
export declare class Index extends Map<string, Stats> {
|
|
19
|
-
/**
|
|
20
|
-
* Convenience method
|
|
21
|
-
*/
|
|
22
|
-
files(): Map<string, Stats>;
|
|
23
|
-
/**
|
|
24
|
-
* Converts the index to JSON
|
|
25
|
-
*/
|
|
26
|
-
toJSON(): IndexData;
|
|
27
|
-
/**
|
|
28
|
-
* Converts the index to a string
|
|
29
|
-
*/
|
|
30
|
-
toString(): string;
|
|
31
|
-
/**
|
|
32
|
-
* Returns the files in the directory `dir`.
|
|
33
|
-
* This is expensive so it is only called once per directory.
|
|
34
|
-
*/
|
|
35
|
-
protected dirEntries(dir: string): string[];
|
|
36
|
-
/**
|
|
37
|
-
* Loads the index from JSON data
|
|
38
|
-
*/
|
|
39
|
-
fromJSON(json: IndexData): void;
|
|
40
|
-
/**
|
|
41
|
-
* Parses an index from a string
|
|
42
|
-
*/
|
|
43
|
-
static parse(data: string): Index;
|
|
44
|
-
}
|
|
45
|
-
declare const IndexFS_base: import("../index.js").Mixin<typeof FileSystem, import("../mixins/readonly.js").ReadonlyMixin>;
|
|
46
|
-
export declare abstract class IndexFS extends IndexFS_base {
|
|
47
|
-
private indexData;
|
|
48
|
-
protected index: Index;
|
|
49
|
-
protected _isInitialized: boolean;
|
|
50
|
-
ready(): Promise<void>;
|
|
51
|
-
constructor(indexData: IndexData | Promise<IndexData>);
|
|
52
|
-
reloadFiles(): Promise<void>;
|
|
53
|
-
reloadFilesSync(): void;
|
|
54
|
-
stat(path: string): Promise<Stats>;
|
|
55
|
-
statSync(path: string): Stats;
|
|
56
|
-
openFile(path: string, flag: string): Promise<NoSyncFile<this>>;
|
|
57
|
-
openFileSync(path: string, flag: string): NoSyncFile<this>;
|
|
58
|
-
readdir(path: string): Promise<string[]>;
|
|
59
|
-
readdirSync(path: string): string[];
|
|
60
|
-
protected abstract getData(path: string, stats: Stats): Promise<Uint8Array>;
|
|
61
|
-
protected abstract getDataSync(path: string, stats: Stats): Uint8Array;
|
|
62
|
-
}
|
|
63
|
-
export {};
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
/* Note: this file is named file_index.ts because Typescript has special behavior regarding index.ts which can't be disabled. */
|
|
2
|
-
import { isJSON } from 'utilium';
|
|
3
|
-
import { Errno, ErrnoError } from '../error.js';
|
|
4
|
-
import { NoSyncFile, isWriteable } from '../file.js';
|
|
5
|
-
import { FileSystem } from '../filesystem.js';
|
|
6
|
-
import { Readonly } from '../mixins/readonly.js';
|
|
7
|
-
import { Stats } from '../stats.js';
|
|
8
|
-
import { decodeUTF8, encodeUTF8 } from '../utils.js';
|
|
9
|
-
import { basename, dirname } from '../vfs/path.js';
|
|
10
|
-
export const version = 1;
|
|
11
|
-
/**
|
|
12
|
-
* An index of files
|
|
13
|
-
* @internal
|
|
14
|
-
*/
|
|
15
|
-
export class Index extends Map {
|
|
16
|
-
/**
|
|
17
|
-
* Convenience method
|
|
18
|
-
*/
|
|
19
|
-
files() {
|
|
20
|
-
const files = new Map();
|
|
21
|
-
for (const [path, stats] of this) {
|
|
22
|
-
if (stats.isFile()) {
|
|
23
|
-
files.set(path, stats);
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
return files;
|
|
27
|
-
}
|
|
28
|
-
/**
|
|
29
|
-
* Converts the index to JSON
|
|
30
|
-
*/
|
|
31
|
-
toJSON() {
|
|
32
|
-
return {
|
|
33
|
-
version,
|
|
34
|
-
entries: Object.fromEntries(this),
|
|
35
|
-
};
|
|
36
|
-
}
|
|
37
|
-
/**
|
|
38
|
-
* Converts the index to a string
|
|
39
|
-
*/
|
|
40
|
-
toString() {
|
|
41
|
-
return JSON.stringify(this.toJSON());
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Returns the files in the directory `dir`.
|
|
45
|
-
* This is expensive so it is only called once per directory.
|
|
46
|
-
*/
|
|
47
|
-
dirEntries(dir) {
|
|
48
|
-
const entries = [];
|
|
49
|
-
for (const entry of this.keys()) {
|
|
50
|
-
if (dirname(entry) == dir) {
|
|
51
|
-
entries.push(basename(entry));
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
return entries;
|
|
55
|
-
}
|
|
56
|
-
/**
|
|
57
|
-
* Loads the index from JSON data
|
|
58
|
-
*/
|
|
59
|
-
fromJSON(json) {
|
|
60
|
-
if (json.version != version) {
|
|
61
|
-
throw new ErrnoError(Errno.EINVAL, 'Index version mismatch');
|
|
62
|
-
}
|
|
63
|
-
this.clear();
|
|
64
|
-
for (const [path, data] of Object.entries(json.entries)) {
|
|
65
|
-
const stats = new Stats(data);
|
|
66
|
-
if (stats.isDirectory()) {
|
|
67
|
-
stats.fileData = encodeUTF8(JSON.stringify(this.dirEntries(path)));
|
|
68
|
-
}
|
|
69
|
-
this.set(path, stats);
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
/**
|
|
73
|
-
* Parses an index from a string
|
|
74
|
-
*/
|
|
75
|
-
static parse(data) {
|
|
76
|
-
if (!isJSON(data)) {
|
|
77
|
-
throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
|
|
78
|
-
}
|
|
79
|
-
const json = JSON.parse(data);
|
|
80
|
-
const index = new Index();
|
|
81
|
-
index.fromJSON(json);
|
|
82
|
-
return index;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
export class IndexFS extends Readonly(FileSystem) {
|
|
86
|
-
async ready() {
|
|
87
|
-
await super.ready();
|
|
88
|
-
if (this._isInitialized) {
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
this.index.fromJSON(await this.indexData);
|
|
92
|
-
this._isInitialized = true;
|
|
93
|
-
}
|
|
94
|
-
constructor(indexData) {
|
|
95
|
-
super();
|
|
96
|
-
this.indexData = indexData;
|
|
97
|
-
this.index = new Index();
|
|
98
|
-
this._isInitialized = false;
|
|
99
|
-
}
|
|
100
|
-
async reloadFiles() {
|
|
101
|
-
for (const [path, stats] of this.index.files()) {
|
|
102
|
-
delete stats.fileData;
|
|
103
|
-
stats.fileData = await this.getData(path, stats);
|
|
104
|
-
}
|
|
105
|
-
}
|
|
106
|
-
reloadFilesSync() {
|
|
107
|
-
for (const [path, stats] of this.index.files()) {
|
|
108
|
-
delete stats.fileData;
|
|
109
|
-
stats.fileData = this.getDataSync(path, stats);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
stat(path) {
|
|
113
|
-
return Promise.resolve(this.statSync(path));
|
|
114
|
-
}
|
|
115
|
-
statSync(path) {
|
|
116
|
-
if (!this.index.has(path)) {
|
|
117
|
-
throw ErrnoError.With('ENOENT', path, 'stat');
|
|
118
|
-
}
|
|
119
|
-
return this.index.get(path);
|
|
120
|
-
}
|
|
121
|
-
async openFile(path, flag) {
|
|
122
|
-
if (isWriteable(flag)) {
|
|
123
|
-
// You can't write to files on this file system.
|
|
124
|
-
throw new ErrnoError(Errno.EPERM, path);
|
|
125
|
-
}
|
|
126
|
-
// Check if the path exists, and is a file.
|
|
127
|
-
const stats = this.index.get(path);
|
|
128
|
-
if (!stats) {
|
|
129
|
-
throw ErrnoError.With('ENOENT', path, 'openFile');
|
|
130
|
-
}
|
|
131
|
-
return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : await this.getData(path, stats));
|
|
132
|
-
}
|
|
133
|
-
openFileSync(path, flag) {
|
|
134
|
-
if (isWriteable(flag)) {
|
|
135
|
-
// You can't write to files on this file system.
|
|
136
|
-
throw new ErrnoError(Errno.EPERM, path);
|
|
137
|
-
}
|
|
138
|
-
// Check if the path exists, and is a file.
|
|
139
|
-
const stats = this.index.get(path);
|
|
140
|
-
if (!stats) {
|
|
141
|
-
throw ErrnoError.With('ENOENT', path, 'openFile');
|
|
142
|
-
}
|
|
143
|
-
return new NoSyncFile(this, path, flag, stats, stats.isDirectory() ? stats.fileData : this.getDataSync(path, stats));
|
|
144
|
-
}
|
|
145
|
-
readdir(path) {
|
|
146
|
-
return Promise.resolve(this.readdirSync(path));
|
|
147
|
-
}
|
|
148
|
-
readdirSync(path) {
|
|
149
|
-
// Check if it exists.
|
|
150
|
-
const stats = this.index.get(path);
|
|
151
|
-
if (!stats) {
|
|
152
|
-
throw ErrnoError.With('ENOENT', path, 'readdir');
|
|
153
|
-
}
|
|
154
|
-
const content = JSON.parse(decodeUTF8(stats.fileData));
|
|
155
|
-
if (!Array.isArray(content)) {
|
|
156
|
-
throw ErrnoError.With('ENODATA', path, 'readdir');
|
|
157
|
-
}
|
|
158
|
-
if (!content.every(item => typeof item == 'string')) {
|
|
159
|
-
throw ErrnoError.With('ENODATA', path, 'readdir');
|
|
160
|
-
}
|
|
161
|
-
return content;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
@@ -1,31 +0,0 @@
|
|
|
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/vfs/index.js';
|
|
5
|
-
import { InMemoryStore, StoreFS } from '../../dist/index.js';
|
|
6
|
-
import { Async } from '../../dist/mixins/async.js';
|
|
7
|
-
|
|
8
|
-
class ExampleAsyncFS extends Async(StoreFS) {
|
|
9
|
-
_sync = new StoreFS(new InMemoryStore('cache'));
|
|
10
|
-
|
|
11
|
-
public constructor() {
|
|
12
|
-
super(new InMemoryStore('test'));
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
const asyncFS = new ExampleAsyncFS();
|
|
17
|
-
|
|
18
|
-
await configure({ mounts: { '/': asyncFS } });
|
|
19
|
-
|
|
20
|
-
suite('Async Mixin', () => {
|
|
21
|
-
test('async -> cache syncing', async () => {
|
|
22
|
-
await fs.promises.writeFile('test', 'test');
|
|
23
|
-
assert.strictEqual(fs.readFileSync('test', 'utf8'), 'test');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
test('cache -> async syncing', async () => {
|
|
27
|
-
fs.unlinkSync('test');
|
|
28
|
-
await asyncFS.queueDone();
|
|
29
|
-
assert(!(await fs.promises.exists('test')));
|
|
30
|
-
});
|
|
31
|
-
});
|
package/tests/setup/cow+fetch.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { createServer } from 'node:http';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import { configureSingle, Fetch, InMemory, Overlay } from '../../dist/index.js';
|
|
6
|
-
import { data, tmp } from '../setup.js';
|
|
7
|
-
|
|
8
|
-
const port = 26514,
|
|
9
|
-
index = tmp + '/index.json';
|
|
10
|
-
|
|
11
|
-
const statusCodes = {
|
|
12
|
-
ENOENT: 404,
|
|
13
|
-
};
|
|
14
|
-
|
|
15
|
-
execSync(`npm exec make-index -- ${data} --output ${index} --quiet`, { stdio: 'inherit' });
|
|
16
|
-
|
|
17
|
-
const server = createServer((request, response) => {
|
|
18
|
-
const path = request.url == '/.index.json' ? index : join(data, request.url?.slice(1) || '');
|
|
19
|
-
try {
|
|
20
|
-
response.statusCode = 200;
|
|
21
|
-
response.end(readFileSync(path));
|
|
22
|
-
} catch (e: any) {
|
|
23
|
-
response.statusCode = statusCodes[e.code as keyof typeof statusCodes] || 400;
|
|
24
|
-
response.end();
|
|
25
|
-
}
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
server
|
|
29
|
-
.once('error', (error: NodeJS.ErrnoException) => {
|
|
30
|
-
if (error.code == 'EADDRINUSE') return;
|
|
31
|
-
throw error;
|
|
32
|
-
})
|
|
33
|
-
.listen(port)
|
|
34
|
-
.unref();
|
|
35
|
-
|
|
36
|
-
const baseUrl = 'http://localhost:' + port;
|
|
37
|
-
|
|
38
|
-
await configureSingle({
|
|
39
|
-
backend: Overlay,
|
|
40
|
-
readable: Fetch.create({
|
|
41
|
-
baseUrl,
|
|
42
|
-
index: baseUrl + '/.index.json',
|
|
43
|
-
}),
|
|
44
|
-
writable: InMemory.create({ name: 'cow' }),
|
|
45
|
-
});
|
|
File without changes
|