@zenfs/core 1.7.2 → 1.8.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.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 +7 -2
- package/dist/backends/overlay.js +32 -9
- package/dist/backends/passthrough.d.ts +4 -0
- package/dist/backends/passthrough.js +128 -0
- package/dist/backends/port/fs.d.ts +9 -44
- package/dist/backends/port/fs.js +93 -116
- package/dist/backends/port/rpc.d.ts +8 -5
- 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 +55 -34
- package/dist/backends/store/fs.js +417 -233
- 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 +11 -10
- package/dist/devices.js +15 -11
- package/dist/file.d.ts +111 -7
- package/dist/file.js +319 -71
- package/dist/filesystem.d.ts +22 -4
- package/dist/mixins/mutexed.d.ts +7 -2
- package/dist/mixins/mutexed.js +56 -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 +17 -3
- package/dist/utils.js +32 -10
- 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 +31 -11
- package/dist/vfs/shared.js +2 -0
- package/dist/vfs/sync.js +25 -13
- package/dist/vfs/types.d.ts +15 -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/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,53 +1,16 @@
|
|
|
1
|
-
import type
|
|
2
|
-
import type { ExtractProperties } from 'utilium';
|
|
1
|
+
import { type ExtractProperties } from 'utilium';
|
|
3
2
|
import type { MountConfiguration } from '../../config.js';
|
|
4
3
|
import type { CreationOptions, FileSystemMetadata } from '../../filesystem.js';
|
|
5
4
|
import type { Backend, FilesystemOf } from '../backend.js';
|
|
6
5
|
import { File } from '../../file.js';
|
|
7
6
|
import { FileSystem } from '../../filesystem.js';
|
|
8
7
|
import { Stats } from '../../stats.js';
|
|
8
|
+
import type { Inode, InodeLike } from '../store/inode.js';
|
|
9
9
|
import * as RPC from './rpc.js';
|
|
10
|
-
type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;
|
|
11
|
-
type FileMethod = keyof FileMethods;
|
|
12
|
-
/** @internal */
|
|
13
|
-
export interface FileRequest<TMethod extends FileMethod = FileMethod> extends RPC.Request {
|
|
14
|
-
fd: number;
|
|
15
|
-
scope: 'file';
|
|
16
|
-
method: TMethod;
|
|
17
|
-
args: Parameters<FileMethods[TMethod]>;
|
|
18
|
-
}
|
|
19
|
-
export declare class PortFile extends File {
|
|
20
|
-
fs: PortFS;
|
|
21
|
-
readonly fd: number;
|
|
22
|
-
position: number;
|
|
23
|
-
constructor(fs: PortFS, fd: number, path: string, position: number);
|
|
24
|
-
rpc<const T extends FileMethod>(method: T, ...args: Parameters<FileMethods[T]>): Promise<Awaited<ReturnType<FileMethods[T]>>>;
|
|
25
|
-
protected _throwNoSync(syscall: string): never;
|
|
26
|
-
stat(): Promise<Stats>;
|
|
27
|
-
statSync(): Stats;
|
|
28
|
-
truncate(len: number): Promise<void>;
|
|
29
|
-
truncateSync(): void;
|
|
30
|
-
write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number>;
|
|
31
|
-
writeSync(): number;
|
|
32
|
-
read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>>;
|
|
33
|
-
readSync(): number;
|
|
34
|
-
chown(uid: number, gid: number): Promise<void>;
|
|
35
|
-
chownSync(): void;
|
|
36
|
-
chmod(mode: number): Promise<void>;
|
|
37
|
-
chmodSync(): void;
|
|
38
|
-
utimes(atime: Date, mtime: Date): Promise<void>;
|
|
39
|
-
utimesSync(): void;
|
|
40
|
-
_setTypeSync(): void;
|
|
41
|
-
close(): Promise<void>;
|
|
42
|
-
closeSync(): void;
|
|
43
|
-
sync(): Promise<void>;
|
|
44
|
-
syncSync(): void;
|
|
45
|
-
}
|
|
46
10
|
type FSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<any> | FileSystemMetadata>;
|
|
47
11
|
type FSMethod = keyof FSMethods;
|
|
48
12
|
/** @internal */
|
|
49
13
|
export interface FSRequest<TMethod extends FSMethod = FSMethod> extends RPC.Request {
|
|
50
|
-
scope: 'fs';
|
|
51
14
|
method: TMethod;
|
|
52
15
|
args: Parameters<FSMethods[TMethod]>;
|
|
53
16
|
}
|
|
@@ -61,7 +24,7 @@ declare const PortFS_base: import("../../index.js").Mixin<typeof FileSystem, imp
|
|
|
61
24
|
export declare class PortFS extends PortFS_base {
|
|
62
25
|
readonly options: RPC.Options;
|
|
63
26
|
readonly port: RPC.Port;
|
|
64
|
-
|
|
27
|
+
/**`
|
|
65
28
|
* @hidden
|
|
66
29
|
*/
|
|
67
30
|
_sync: import("../index.js").StoreFS<import("../memory.js").InMemoryStore>;
|
|
@@ -74,7 +37,7 @@ export declare class PortFS extends PortFS_base {
|
|
|
74
37
|
ready(): Promise<void>;
|
|
75
38
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
76
39
|
stat(path: string): Promise<Stats>;
|
|
77
|
-
sync(path: string, data: Uint8Array, stats: Readonly<
|
|
40
|
+
sync(path: string, data: Uint8Array | undefined, stats: Readonly<InodeLike | Inode>): Promise<void>;
|
|
78
41
|
openFile(path: string, flag: string): Promise<File>;
|
|
79
42
|
createFile(path: string, flag: string, mode: number, options: CreationOptions): Promise<File>;
|
|
80
43
|
unlink(path: string): Promise<void>;
|
|
@@ -83,11 +46,13 @@ export declare class PortFS extends PortFS_base {
|
|
|
83
46
|
readdir(path: string): Promise<string[]>;
|
|
84
47
|
exists(path: string): Promise<boolean>;
|
|
85
48
|
link(srcpath: string, dstpath: string): Promise<void>;
|
|
49
|
+
read(path: string, offset: number, length: number): Promise<Uint8Array>;
|
|
50
|
+
write(path: string, buffer: Uint8Array, offset: number): Promise<void>;
|
|
86
51
|
}
|
|
87
52
|
/** @internal */
|
|
88
|
-
export
|
|
89
|
-
|
|
90
|
-
|
|
53
|
+
export declare function handleRequest(port: RPC.Port, fs: FileSystem & {
|
|
54
|
+
_descriptors?: Map<number, File>;
|
|
55
|
+
}, request: FSRequest): Promise<void>;
|
|
91
56
|
export declare function attachFS(port: RPC.Port, fs: FileSystem): void;
|
|
92
57
|
export declare function detachFS(port: RPC.Port, fs: FileSystem): void;
|
|
93
58
|
declare const _Port: {
|
package/dist/backends/port/fs.js
CHANGED
|
@@ -1,3 +1,57 @@
|
|
|
1
|
+
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
|
+
if (value !== null && value !== void 0) {
|
|
3
|
+
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
4
|
+
var dispose, inner;
|
|
5
|
+
if (async) {
|
|
6
|
+
if (!Symbol.asyncDispose) throw new TypeError("Symbol.asyncDispose is not defined.");
|
|
7
|
+
dispose = value[Symbol.asyncDispose];
|
|
8
|
+
}
|
|
9
|
+
if (dispose === void 0) {
|
|
10
|
+
if (!Symbol.dispose) throw new TypeError("Symbol.dispose is not defined.");
|
|
11
|
+
dispose = value[Symbol.dispose];
|
|
12
|
+
if (async) inner = dispose;
|
|
13
|
+
}
|
|
14
|
+
if (typeof dispose !== "function") throw new TypeError("Object not disposable.");
|
|
15
|
+
if (inner) dispose = function() { try { inner.call(this); } catch (e) { return Promise.reject(e); } };
|
|
16
|
+
env.stack.push({ value: value, dispose: dispose, async: async });
|
|
17
|
+
}
|
|
18
|
+
else if (async) {
|
|
19
|
+
env.stack.push({ async: true });
|
|
20
|
+
}
|
|
21
|
+
return value;
|
|
22
|
+
};
|
|
23
|
+
var __disposeResources = (this && this.__disposeResources) || (function (SuppressedError) {
|
|
24
|
+
return function (env) {
|
|
25
|
+
function fail(e) {
|
|
26
|
+
env.error = env.hasError ? new SuppressedError(e, env.error, "An error was suppressed during disposal.") : e;
|
|
27
|
+
env.hasError = true;
|
|
28
|
+
}
|
|
29
|
+
var r, s = 0;
|
|
30
|
+
function next() {
|
|
31
|
+
while (r = env.stack.pop()) {
|
|
32
|
+
try {
|
|
33
|
+
if (!r.async && s === 1) return s = 0, env.stack.push(r), Promise.resolve().then(next);
|
|
34
|
+
if (r.dispose) {
|
|
35
|
+
var result = r.dispose.call(r.value);
|
|
36
|
+
if (r.async) return s |= 2, Promise.resolve(result).then(next, function(e) { fail(e); return next(); });
|
|
37
|
+
}
|
|
38
|
+
else s |= 1;
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
fail(e);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (s === 1) return env.hasError ? Promise.reject(env.error) : Promise.resolve();
|
|
45
|
+
if (env.hasError) throw env.error;
|
|
46
|
+
}
|
|
47
|
+
return next();
|
|
48
|
+
};
|
|
49
|
+
})(typeof SuppressedError === "function" ? SuppressedError : function (error, suppressed, message) {
|
|
50
|
+
var e = new Error(message);
|
|
51
|
+
return e.name = "SuppressedError", e.error = error, e.suppressed = suppressed, e;
|
|
52
|
+
});
|
|
53
|
+
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
54
|
+
import { pick } from 'utilium';
|
|
1
55
|
import { resolveMountConfig } from '../../config.js';
|
|
2
56
|
import { Errno, ErrnoError } from '../../error.js';
|
|
3
57
|
import { File } from '../../file.js';
|
|
@@ -6,83 +60,6 @@ import { Async } from '../../mixins/async.js';
|
|
|
6
60
|
import { Stats } from '../../stats.js';
|
|
7
61
|
import { InMemory } from '../memory.js';
|
|
8
62
|
import * as RPC from './rpc.js';
|
|
9
|
-
export class PortFile extends File {
|
|
10
|
-
constructor(fs, fd, path, position) {
|
|
11
|
-
super(fs, path);
|
|
12
|
-
this.fs = fs;
|
|
13
|
-
this.fd = fd;
|
|
14
|
-
this.position = position;
|
|
15
|
-
}
|
|
16
|
-
rpc(method, ...args) {
|
|
17
|
-
return RPC.request({
|
|
18
|
-
scope: 'file',
|
|
19
|
-
fd: this.fd,
|
|
20
|
-
method,
|
|
21
|
-
args,
|
|
22
|
-
}, this.fs.options);
|
|
23
|
-
}
|
|
24
|
-
_throwNoSync(syscall) {
|
|
25
|
-
throw new ErrnoError(Errno.ENOTSUP, 'Synchronous operations not supported on PortFile', this.path, syscall);
|
|
26
|
-
}
|
|
27
|
-
async stat() {
|
|
28
|
-
return new Stats(await this.rpc('stat'));
|
|
29
|
-
}
|
|
30
|
-
statSync() {
|
|
31
|
-
this._throwNoSync('stat');
|
|
32
|
-
}
|
|
33
|
-
truncate(len) {
|
|
34
|
-
return this.rpc('truncate', len);
|
|
35
|
-
}
|
|
36
|
-
truncateSync() {
|
|
37
|
-
this._throwNoSync('truncate');
|
|
38
|
-
}
|
|
39
|
-
write(buffer, offset, length, position) {
|
|
40
|
-
return this.rpc('write', buffer, offset, length, position);
|
|
41
|
-
}
|
|
42
|
-
writeSync() {
|
|
43
|
-
this._throwNoSync('write');
|
|
44
|
-
}
|
|
45
|
-
async read(buffer, offset, length, position) {
|
|
46
|
-
const result = await this.rpc('read', buffer, offset, length, position);
|
|
47
|
-
return result;
|
|
48
|
-
}
|
|
49
|
-
readSync() {
|
|
50
|
-
this._throwNoSync('read');
|
|
51
|
-
}
|
|
52
|
-
chown(uid, gid) {
|
|
53
|
-
return this.rpc('chown', uid, gid);
|
|
54
|
-
}
|
|
55
|
-
chownSync() {
|
|
56
|
-
this._throwNoSync('chown');
|
|
57
|
-
}
|
|
58
|
-
chmod(mode) {
|
|
59
|
-
return this.rpc('chmod', mode);
|
|
60
|
-
}
|
|
61
|
-
chmodSync() {
|
|
62
|
-
this._throwNoSync('chmod');
|
|
63
|
-
}
|
|
64
|
-
utimes(atime, mtime) {
|
|
65
|
-
return this.rpc('utimes', atime, mtime);
|
|
66
|
-
}
|
|
67
|
-
utimesSync() {
|
|
68
|
-
this._throwNoSync('utimes');
|
|
69
|
-
}
|
|
70
|
-
_setTypeSync() {
|
|
71
|
-
this._throwNoSync('_setType');
|
|
72
|
-
}
|
|
73
|
-
close() {
|
|
74
|
-
return this.rpc('close');
|
|
75
|
-
}
|
|
76
|
-
closeSync() {
|
|
77
|
-
this._throwNoSync('close');
|
|
78
|
-
}
|
|
79
|
-
sync() {
|
|
80
|
-
return this.rpc('sync');
|
|
81
|
-
}
|
|
82
|
-
syncSync() {
|
|
83
|
-
this._throwNoSync('sync');
|
|
84
|
-
}
|
|
85
|
-
}
|
|
86
63
|
/**
|
|
87
64
|
* PortFS lets you access an FS instance that is running in a port, or the other way around.
|
|
88
65
|
*
|
|
@@ -96,7 +73,7 @@ export class PortFS extends Async(FileSystem) {
|
|
|
96
73
|
constructor(options) {
|
|
97
74
|
super();
|
|
98
75
|
this.options = options;
|
|
99
|
-
|
|
76
|
+
/**`
|
|
100
77
|
* @hidden
|
|
101
78
|
*/
|
|
102
79
|
this._sync = InMemory.create({ name: 'port-tmpfs' });
|
|
@@ -110,11 +87,7 @@ export class PortFS extends Async(FileSystem) {
|
|
|
110
87
|
};
|
|
111
88
|
}
|
|
112
89
|
rpc(method, ...args) {
|
|
113
|
-
return RPC.request({
|
|
114
|
-
scope: 'fs',
|
|
115
|
-
method,
|
|
116
|
-
args,
|
|
117
|
-
}, { ...this.options, fs: this });
|
|
90
|
+
return RPC.request({ method, args }, { ...this.options, fs: this });
|
|
118
91
|
}
|
|
119
92
|
async ready() {
|
|
120
93
|
await this.rpc('ready');
|
|
@@ -127,6 +100,7 @@ export class PortFS extends Async(FileSystem) {
|
|
|
127
100
|
return new Stats(await this.rpc('stat', path));
|
|
128
101
|
}
|
|
129
102
|
sync(path, data, stats) {
|
|
103
|
+
stats = 'toJSON' in stats ? stats.toJSON() : stats;
|
|
130
104
|
return this.rpc('sync', path, data, stats);
|
|
131
105
|
}
|
|
132
106
|
openFile(path, flag) {
|
|
@@ -153,51 +127,54 @@ export class PortFS extends Async(FileSystem) {
|
|
|
153
127
|
link(srcpath, dstpath) {
|
|
154
128
|
return this.rpc('link', srcpath, dstpath);
|
|
155
129
|
}
|
|
130
|
+
read(path, offset, length) {
|
|
131
|
+
return this.rpc('read', path, offset, length);
|
|
132
|
+
}
|
|
133
|
+
write(path, buffer, offset) {
|
|
134
|
+
return this.rpc('write', path, buffer, offset);
|
|
135
|
+
}
|
|
156
136
|
}
|
|
157
|
-
let nextFd = 0;
|
|
158
|
-
const descriptors = new Map();
|
|
159
137
|
/** @internal */
|
|
160
138
|
export async function handleRequest(port, fs, request) {
|
|
161
|
-
if (!RPC.isMessage(request))
|
|
139
|
+
if (!RPC.isMessage(request))
|
|
162
140
|
return;
|
|
163
|
-
}
|
|
164
|
-
const { method, args, id, scope, stack } = request;
|
|
141
|
+
const { method, args, id, stack } = request;
|
|
165
142
|
let value, error = false;
|
|
143
|
+
const transfer = [];
|
|
166
144
|
try {
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
145
|
+
// @ts-expect-error 2556
|
|
146
|
+
value = await fs[method](...args);
|
|
147
|
+
if (value instanceof File) {
|
|
148
|
+
const env_1 = { stack: [], error: void 0, hasError: false };
|
|
149
|
+
try {
|
|
150
|
+
const file = __addDisposableResource(env_1, await fs.openFile(args[0], 'r+'), true);
|
|
151
|
+
const stats = await file.stat();
|
|
152
|
+
const data = new Uint8Array(stats.size);
|
|
153
|
+
await file.read(data);
|
|
154
|
+
value = {
|
|
155
|
+
path: value.path,
|
|
156
|
+
flag: args[1],
|
|
157
|
+
stats,
|
|
158
|
+
buffer: data.buffer,
|
|
159
|
+
};
|
|
160
|
+
transfer.push(data.buffer);
|
|
161
|
+
}
|
|
162
|
+
catch (e_1) {
|
|
163
|
+
env_1.error = e_1;
|
|
164
|
+
env_1.hasError = true;
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
const result_1 = __disposeResources(env_1);
|
|
168
|
+
if (result_1)
|
|
169
|
+
await result_1;
|
|
191
170
|
}
|
|
192
|
-
default:
|
|
193
|
-
return;
|
|
194
171
|
}
|
|
195
172
|
}
|
|
196
173
|
catch (e) {
|
|
197
|
-
value = e instanceof ErrnoError ? e.toJSON() : e
|
|
174
|
+
value = e instanceof ErrnoError ? e.toJSON() : pick(e, 'message', 'stack');
|
|
198
175
|
error = true;
|
|
199
176
|
}
|
|
200
|
-
port.postMessage({ _zenfs: true,
|
|
177
|
+
port.postMessage({ _zenfs: true, id, error, method, stack, value }, transfer);
|
|
201
178
|
}
|
|
202
179
|
export function attachFS(port, fs) {
|
|
203
180
|
RPC.attach(port, request => handleRequest(port, fs, request));
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
+
import type { TransferListItem } from 'node:worker_threads';
|
|
2
|
+
import type { WithOptional } from 'utilium';
|
|
1
3
|
import type { ErrnoErrorJSON } from '../../error.js';
|
|
2
4
|
import type { Backend, FilesystemOf } from '../backend.js';
|
|
3
5
|
import type { PortFS } from './fs.js';
|
|
6
|
+
import { type StatsLike } from '../../stats.js';
|
|
4
7
|
type _MessageEvent<T = any> = T | {
|
|
5
8
|
data: T;
|
|
6
9
|
};
|
|
7
10
|
/** @internal */
|
|
8
11
|
export interface Port {
|
|
9
|
-
postMessage(value: unknown): void;
|
|
12
|
+
postMessage(value: unknown, transfer?: TransferListItem[]): void;
|
|
10
13
|
on?(event: 'message', listener: (value: unknown) => void): this;
|
|
11
14
|
off?(event: 'message', listener: (value: unknown) => void): this;
|
|
12
15
|
addEventListener?(type: 'message', listener: (ev: _MessageEvent) => void): void;
|
|
@@ -27,7 +30,6 @@ export interface Options {
|
|
|
27
30
|
*/
|
|
28
31
|
export interface Message {
|
|
29
32
|
_zenfs: true;
|
|
30
|
-
scope: 'fs' | 'file';
|
|
31
33
|
id: string;
|
|
32
34
|
method: string;
|
|
33
35
|
stack: string;
|
|
@@ -37,7 +39,7 @@ export interface Request extends Message {
|
|
|
37
39
|
}
|
|
38
40
|
interface _ResponseWithError extends Message {
|
|
39
41
|
error: true;
|
|
40
|
-
value: ErrnoErrorJSON |
|
|
42
|
+
value: WithOptional<ErrnoErrorJSON, 'code' | 'errno'>;
|
|
41
43
|
}
|
|
42
44
|
interface _ResponseWithValue<T> extends Message {
|
|
43
45
|
error: false;
|
|
@@ -45,9 +47,10 @@ interface _ResponseWithValue<T> extends Message {
|
|
|
45
47
|
}
|
|
46
48
|
export type Response<T = unknown> = _ResponseWithError | _ResponseWithValue<T>;
|
|
47
49
|
export interface FileData {
|
|
48
|
-
fd: number;
|
|
49
50
|
path: string;
|
|
50
|
-
|
|
51
|
+
flag: string;
|
|
52
|
+
stats: StatsLike<number>;
|
|
53
|
+
buffer: ArrayBuffer;
|
|
51
54
|
}
|
|
52
55
|
export { FileData as File };
|
|
53
56
|
export declare function isMessage(arg: unknown): arg is Message;
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Errno, ErrnoError } from '../../error.js';
|
|
2
|
-
import {
|
|
2
|
+
import { PreloadFile } from '../../file.js';
|
|
3
|
+
import { Stats } from '../../stats.js';
|
|
4
|
+
import { handleRequest } from './fs.js';
|
|
3
5
|
function isFileData(value) {
|
|
4
|
-
return typeof value == 'object' && value != null && '
|
|
6
|
+
return typeof value == 'object' && value != null && 'path' in value && 'flag' in value;
|
|
5
7
|
}
|
|
6
8
|
// general types
|
|
7
9
|
export function isMessage(arg) {
|
|
@@ -18,7 +20,7 @@ export function request(request, { port, timeout = 1000, fs } = {}) {
|
|
|
18
20
|
executors.set(id, { resolve, reject, fs });
|
|
19
21
|
port.postMessage({ ...request, _zenfs: true, id, stack });
|
|
20
22
|
const _ = setTimeout(() => {
|
|
21
|
-
const error = new ErrnoError(Errno.EIO, 'RPC Failed');
|
|
23
|
+
const error = new ErrnoError(Errno.EIO, 'RPC Failed', typeof request.args[0] == 'string' ? request.args[0] : '', request.method);
|
|
22
24
|
error.stack += stack;
|
|
23
25
|
reject(error);
|
|
24
26
|
if (typeof _ == 'object')
|
|
@@ -38,15 +40,15 @@ export function handleResponse(response) {
|
|
|
38
40
|
}
|
|
39
41
|
const { resolve, reject, fs } = executors.get(id);
|
|
40
42
|
if (error) {
|
|
41
|
-
const e =
|
|
43
|
+
const e = ErrnoError.fromJSON({ code: 'EIO', errno: Errno.EIO, ...value });
|
|
42
44
|
e.stack += stack;
|
|
43
45
|
reject(e);
|
|
44
46
|
executors.delete(id);
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
49
|
if (isFileData(value)) {
|
|
48
|
-
const {
|
|
49
|
-
const file = new
|
|
50
|
+
const { path, flag, stats, buffer } = value;
|
|
51
|
+
const file = new PreloadFile(fs, path, flag, new Stats(stats), new Uint8Array(buffer));
|
|
50
52
|
resolve(file);
|
|
51
53
|
executors.delete(id);
|
|
52
54
|
return;
|
|
@@ -78,7 +80,7 @@ export function catchMessages(port) {
|
|
|
78
80
|
return function (fs) {
|
|
79
81
|
detach(port, handler);
|
|
80
82
|
for (const event of events) {
|
|
81
|
-
const request =
|
|
83
|
+
const request = 'data' in event ? event.data : event;
|
|
82
84
|
void handleRequest(port, fs, request);
|
|
83
85
|
}
|
|
84
86
|
};
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import type { InodeLike } from './inode.js';
|
|
2
|
+
import { Inode } from './inode.js';
|
|
3
|
+
/**
|
|
4
|
+
* An Index in JSON form
|
|
5
|
+
* @internal
|
|
6
|
+
*/
|
|
7
|
+
export interface IndexData {
|
|
8
|
+
version: number;
|
|
9
|
+
entries: Record<string, InodeLike>;
|
|
10
|
+
}
|
|
11
|
+
export declare const version = 1;
|
|
12
|
+
/**
|
|
13
|
+
* An index of files
|
|
14
|
+
* @internal
|
|
15
|
+
*/
|
|
16
|
+
export declare class Index extends Map<string, Readonly<Inode>> {
|
|
17
|
+
protected _directories?: Map<string, Record<string, number>>;
|
|
18
|
+
/**
|
|
19
|
+
* Converts the index to JSON
|
|
20
|
+
*/
|
|
21
|
+
toJSON(): IndexData;
|
|
22
|
+
/**
|
|
23
|
+
* Converts the index to a string
|
|
24
|
+
*/
|
|
25
|
+
toString(): string;
|
|
26
|
+
/**
|
|
27
|
+
* Gets a list of entries for each directory in the index. Memoized.
|
|
28
|
+
*/
|
|
29
|
+
directories(): Map<string, Record<string, number>>;
|
|
30
|
+
/**
|
|
31
|
+
* Loads the index from JSON data
|
|
32
|
+
*/
|
|
33
|
+
fromJSON(json: IndexData): void;
|
|
34
|
+
/**
|
|
35
|
+
* Parses an index from a string
|
|
36
|
+
*/
|
|
37
|
+
static parse(data: string): Index;
|
|
38
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
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, randomInt } from 'utilium';
|
|
3
|
+
import { Errno, ErrnoError } from '../../error.js';
|
|
4
|
+
import { S_IFDIR, S_IFMT, size_max } from '../../vfs/constants.js';
|
|
5
|
+
import { basename, dirname } from '../../vfs/path.js';
|
|
6
|
+
import { Inode } from './inode.js';
|
|
7
|
+
export const version = 1;
|
|
8
|
+
/**
|
|
9
|
+
* An index of files
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
export class Index extends Map {
|
|
13
|
+
/**
|
|
14
|
+
* Converts the index to JSON
|
|
15
|
+
*/
|
|
16
|
+
toJSON() {
|
|
17
|
+
return {
|
|
18
|
+
version,
|
|
19
|
+
entries: Object.fromEntries([...this].map(([k, v]) => [k, v.toJSON()])),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Converts the index to a string
|
|
24
|
+
*/
|
|
25
|
+
toString() {
|
|
26
|
+
return JSON.stringify(this.toJSON());
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Gets a list of entries for each directory in the index. Memoized.
|
|
30
|
+
*/
|
|
31
|
+
directories() {
|
|
32
|
+
if (this._directories)
|
|
33
|
+
return this._directories;
|
|
34
|
+
const dirs = new Map();
|
|
35
|
+
for (const [path, node] of this) {
|
|
36
|
+
if ((node.mode & S_IFMT) != S_IFDIR)
|
|
37
|
+
continue;
|
|
38
|
+
const entries = {};
|
|
39
|
+
for (const entry of this.keys()) {
|
|
40
|
+
if (dirname(entry) == path && entry != path)
|
|
41
|
+
entries[basename(entry)] = this.get(entry).ino;
|
|
42
|
+
}
|
|
43
|
+
dirs.set(path, entries);
|
|
44
|
+
}
|
|
45
|
+
this._directories = dirs;
|
|
46
|
+
return dirs;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Loads the index from JSON data
|
|
50
|
+
*/
|
|
51
|
+
fromJSON(json) {
|
|
52
|
+
var _a;
|
|
53
|
+
if (json.version != version) {
|
|
54
|
+
throw new ErrnoError(Errno.EINVAL, 'Index version mismatch');
|
|
55
|
+
}
|
|
56
|
+
this.clear();
|
|
57
|
+
for (const [path, node] of Object.entries(json.entries)) {
|
|
58
|
+
(_a = node.data) !== null && _a !== void 0 ? _a : (node.data = randomInt(1, size_max));
|
|
59
|
+
if (path == '/')
|
|
60
|
+
node.ino = 0;
|
|
61
|
+
this.set(path, new Inode(node));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Parses an index from a string
|
|
66
|
+
*/
|
|
67
|
+
static parse(data) {
|
|
68
|
+
if (!isJSON(data)) {
|
|
69
|
+
throw new ErrnoError(Errno.EINVAL, 'Invalid JSON');
|
|
70
|
+
}
|
|
71
|
+
const json = JSON.parse(data);
|
|
72
|
+
const index = new Index();
|
|
73
|
+
index.fromJSON(json);
|
|
74
|
+
return index;
|
|
75
|
+
}
|
|
76
|
+
}
|