@zenfs/core 1.3.6 → 1.4.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/memory.d.ts +4 -4
- package/dist/backends/memory.js +4 -4
- package/dist/backends/overlay.d.ts +5 -2
- package/dist/backends/overlay.js +7 -10
- package/dist/backends/port/fs.js +1 -4
- package/dist/config.js +4 -8
- package/dist/context.d.ts +32 -0
- package/dist/context.js +23 -0
- package/dist/credentials.d.ts +5 -5
- package/dist/credentials.js +10 -6
- package/dist/emulation/async.d.ts +90 -89
- package/dist/emulation/async.js +76 -75
- package/dist/emulation/dir.d.ts +3 -1
- package/dist/emulation/dir.js +6 -7
- package/dist/emulation/index.d.ts +1 -1
- package/dist/emulation/index.js +1 -1
- package/dist/emulation/promises.d.ts +50 -48
- package/dist/emulation/promises.js +78 -77
- package/dist/emulation/shared.d.ts +35 -8
- package/dist/emulation/shared.js +37 -11
- package/dist/emulation/sync.d.ts +63 -62
- package/dist/emulation/sync.js +72 -73
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/stats.d.ts +2 -1
- package/dist/stats.js +5 -4
- package/package.json +3 -5
- package/scripts/test.js +78 -17
- package/tests/assignment.ts +1 -1
- package/tests/common/context.test.ts +19 -0
- package/tests/{devices.test.ts → common/devices.test.ts} +3 -3
- package/tests/{handle.test.ts → common/handle.test.ts} +1 -1
- package/tests/common/mounts.test.ts +36 -0
- package/tests/{mutex.test.ts → common/mutex.test.ts} +3 -3
- package/tests/common/path.test.ts +34 -0
- package/tests/common.ts +4 -3
- package/tests/fs/dir.test.ts +11 -11
- package/tests/fs/directory.test.ts +17 -17
- package/tests/fs/errors.test.ts +29 -39
- package/tests/fs/watch.test.ts +2 -2
- package/tests/setup/context.ts +9 -0
- package/tests/setup/cow+fetch.ts +1 -1
- package/tests/setup/memory.ts +1 -1
- package/tests/{setup/common.ts → setup.ts} +6 -5
- package/src/backends/backend.ts +0 -161
- package/src/backends/fetch.ts +0 -180
- package/src/backends/file_index.ts +0 -206
- package/src/backends/memory.ts +0 -45
- package/src/backends/overlay.ts +0 -560
- package/src/backends/port/fs.ts +0 -329
- package/src/backends/port/readme.md +0 -54
- package/src/backends/port/rpc.ts +0 -167
- package/src/backends/readme.md +0 -3
- package/src/backends/store/fs.ts +0 -667
- package/src/backends/store/readme.md +0 -9
- package/src/backends/store/simple.ts +0 -154
- package/src/backends/store/store.ts +0 -189
- package/src/config.ts +0 -227
- package/src/credentials.ts +0 -49
- package/src/devices.ts +0 -521
- package/src/emulation/async.ts +0 -834
- package/src/emulation/cache.ts +0 -86
- package/src/emulation/config.ts +0 -21
- package/src/emulation/constants.ts +0 -182
- package/src/emulation/dir.ts +0 -138
- package/src/emulation/index.ts +0 -8
- package/src/emulation/path.ts +0 -440
- package/src/emulation/promises.ts +0 -1140
- package/src/emulation/shared.ts +0 -172
- package/src/emulation/streams.ts +0 -34
- package/src/emulation/sync.ts +0 -863
- package/src/emulation/watchers.ts +0 -194
- package/src/error.ts +0 -307
- package/src/file.ts +0 -631
- package/src/filesystem.ts +0 -174
- package/src/index.ts +0 -35
- package/src/inode.ts +0 -128
- package/src/mixins/async.ts +0 -230
- package/src/mixins/index.ts +0 -5
- package/src/mixins/mutexed.ts +0 -257
- package/src/mixins/readonly.ts +0 -96
- package/src/mixins/shared.ts +0 -25
- package/src/mixins/sync.ts +0 -58
- package/src/polyfills.ts +0 -21
- package/src/stats.ts +0 -405
- package/src/utils.ts +0 -276
- package/tests/mounts.test.ts +0 -18
- package/tests/path.test.ts +0 -34
package/src/backends/port/fs.ts
DELETED
|
@@ -1,329 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import type { FileReadResult } from 'node:fs/promises';
|
|
3
|
-
import type { ExtractProperties } from 'utilium';
|
|
4
|
-
import { resolveMountConfig, type MountConfiguration } from '../../config.js';
|
|
5
|
-
import { Errno, ErrnoError } from '../../error.js';
|
|
6
|
-
import { File } from '../../file.js';
|
|
7
|
-
import { FileSystem, type FileSystemMetadata } from '../../filesystem.js';
|
|
8
|
-
import { Async } from '../../mixins/async.js';
|
|
9
|
-
import { Stats } from '../../stats.js';
|
|
10
|
-
import type { Backend, FilesystemOf } from '../backend.js';
|
|
11
|
-
import { InMemory } from '../memory.js';
|
|
12
|
-
import * as RPC from './rpc.js';
|
|
13
|
-
|
|
14
|
-
type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;
|
|
15
|
-
type FileMethod = keyof FileMethods;
|
|
16
|
-
/** @internal */
|
|
17
|
-
export interface FileRequest<TMethod extends FileMethod = FileMethod> extends RPC.Request {
|
|
18
|
-
fd: number;
|
|
19
|
-
scope: 'file';
|
|
20
|
-
method: TMethod;
|
|
21
|
-
args: Parameters<FileMethods[TMethod]>;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
export class PortFile extends File {
|
|
25
|
-
public constructor(
|
|
26
|
-
public fs: PortFS,
|
|
27
|
-
public readonly fd: number,
|
|
28
|
-
path: string,
|
|
29
|
-
public position: number
|
|
30
|
-
) {
|
|
31
|
-
super(fs, path);
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
public rpc<const T extends FileMethod>(method: T, ...args: Parameters<FileMethods[T]>): Promise<Awaited<ReturnType<FileMethods[T]>>> {
|
|
35
|
-
return RPC.request<FileRequest<T>, Awaited<ReturnType<FileMethods[T]>>>(
|
|
36
|
-
{
|
|
37
|
-
scope: 'file',
|
|
38
|
-
fd: this.fd,
|
|
39
|
-
method,
|
|
40
|
-
args,
|
|
41
|
-
},
|
|
42
|
-
this.fs.options
|
|
43
|
-
);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
protected _throwNoSync(syscall: string): never {
|
|
47
|
-
throw new ErrnoError(Errno.ENOTSUP, 'Synchronous operations not supported on PortFile', this.path, syscall);
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
public async stat(): Promise<Stats> {
|
|
51
|
-
return new Stats(await this.rpc('stat'));
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
public statSync(): Stats {
|
|
55
|
-
this._throwNoSync('stat');
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
public truncate(len: number): Promise<void> {
|
|
59
|
-
return this.rpc('truncate', len);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
public truncateSync(): void {
|
|
63
|
-
this._throwNoSync('truncate');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
public write(buffer: Uint8Array, offset?: number, length?: number, position?: number): Promise<number> {
|
|
67
|
-
return this.rpc('write', buffer, offset, length, position);
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
public writeSync(): number {
|
|
71
|
-
this._throwNoSync('write');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
public async read<TBuffer extends NodeJS.ArrayBufferView>(buffer: TBuffer, offset?: number, length?: number, position?: number): Promise<FileReadResult<TBuffer>> {
|
|
75
|
-
const result = await this.rpc('read', buffer, offset, length, position);
|
|
76
|
-
return result as FileReadResult<TBuffer>;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
public readSync(): number {
|
|
80
|
-
this._throwNoSync('read');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
public chown(uid: number, gid: number): Promise<void> {
|
|
84
|
-
return this.rpc('chown', uid, gid);
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
public chownSync(): void {
|
|
88
|
-
this._throwNoSync('chown');
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
public chmod(mode: number): Promise<void> {
|
|
92
|
-
return this.rpc('chmod', mode);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
public chmodSync(): void {
|
|
96
|
-
this._throwNoSync('chmod');
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
public utimes(atime: Date, mtime: Date): Promise<void> {
|
|
100
|
-
return this.rpc('utimes', atime, mtime);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
public utimesSync(): void {
|
|
104
|
-
this._throwNoSync('utimes');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
public _setTypeSync(): void {
|
|
108
|
-
this._throwNoSync('_setType');
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
public close(): Promise<void> {
|
|
112
|
-
return this.rpc('close');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
public closeSync(): void {
|
|
116
|
-
this._throwNoSync('close');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
public sync(): Promise<void> {
|
|
120
|
-
return this.rpc('sync');
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
public syncSync(): void {
|
|
124
|
-
this._throwNoSync('sync');
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
type FSMethods = ExtractProperties<FileSystem, (...args: any[]) => Promise<any> | FileSystemMetadata>;
|
|
129
|
-
type FSMethod = keyof FSMethods;
|
|
130
|
-
/** @internal */
|
|
131
|
-
export interface FSRequest<TMethod extends FSMethod = FSMethod> extends RPC.Request {
|
|
132
|
-
scope: 'fs';
|
|
133
|
-
method: TMethod;
|
|
134
|
-
args: Parameters<FSMethods[TMethod]>;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
/**
|
|
138
|
-
* PortFS lets you access an FS instance that is running in a port, or the other way around.
|
|
139
|
-
*
|
|
140
|
-
* Note that *direct* synchronous operations are not permitted on the PortFS,
|
|
141
|
-
* regardless of the configuration option of the remote FS.
|
|
142
|
-
*/
|
|
143
|
-
export class PortFS extends Async(FileSystem) {
|
|
144
|
-
public readonly port: RPC.Port;
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* @hidden
|
|
148
|
-
*/
|
|
149
|
-
_sync = InMemory.create({ name: 'port-tmpfs' });
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Constructs a new PortFS instance that connects with the FS running on `options.port`.
|
|
153
|
-
*/
|
|
154
|
-
public constructor(public readonly options: RPC.Options) {
|
|
155
|
-
super();
|
|
156
|
-
this.port = options.port;
|
|
157
|
-
RPC.attach<RPC.Response>(this.port, RPC.handleResponse);
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
public metadata(): FileSystemMetadata {
|
|
161
|
-
return {
|
|
162
|
-
...super.metadata(),
|
|
163
|
-
name: 'PortFS',
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
protected rpc<const T extends FSMethod>(method: T, ...args: Parameters<FSMethods[T]>): Promise<Awaited<ReturnType<FSMethods[T]>>> {
|
|
168
|
-
return RPC.request<FSRequest<T>, Awaited<ReturnType<FSMethods[T]>>>(
|
|
169
|
-
{
|
|
170
|
-
scope: 'fs',
|
|
171
|
-
method,
|
|
172
|
-
args,
|
|
173
|
-
},
|
|
174
|
-
{ ...this.options, fs: this }
|
|
175
|
-
);
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
public async ready(): Promise<void> {
|
|
179
|
-
await this.rpc('ready');
|
|
180
|
-
await super.ready();
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
public rename(oldPath: string, newPath: string): Promise<void> {
|
|
184
|
-
return this.rpc('rename', oldPath, newPath);
|
|
185
|
-
}
|
|
186
|
-
|
|
187
|
-
public async stat(path: string): Promise<Stats> {
|
|
188
|
-
return new Stats(await this.rpc('stat', path));
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
public sync(path: string, data: Uint8Array, stats: Readonly<Stats>): Promise<void> {
|
|
192
|
-
return this.rpc('sync', path, data, stats);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
public openFile(path: string, flag: string): Promise<File> {
|
|
196
|
-
return this.rpc('openFile', path, flag);
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
public createFile(path: string, flag: string, mode: number): Promise<File> {
|
|
200
|
-
return this.rpc('createFile', path, flag, mode);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
public unlink(path: string): Promise<void> {
|
|
204
|
-
return this.rpc('unlink', path);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
public rmdir(path: string): Promise<void> {
|
|
208
|
-
return this.rpc('rmdir', path);
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
public mkdir(path: string, mode: number): Promise<void> {
|
|
212
|
-
return this.rpc('mkdir', path, mode);
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
public readdir(path: string): Promise<string[]> {
|
|
216
|
-
return this.rpc('readdir', path);
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
public exists(path: string): Promise<boolean> {
|
|
220
|
-
return this.rpc('exists', path);
|
|
221
|
-
}
|
|
222
|
-
|
|
223
|
-
public link(srcpath: string, dstpath: string): Promise<void> {
|
|
224
|
-
return this.rpc('link', srcpath, dstpath);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
let nextFd = 0;
|
|
229
|
-
|
|
230
|
-
const descriptors: Map<number, File> = new Map();
|
|
231
|
-
|
|
232
|
-
/** @internal */
|
|
233
|
-
export type FileOrFSRequest = FSRequest | FileRequest;
|
|
234
|
-
|
|
235
|
-
/** @internal */
|
|
236
|
-
export async function handleRequest(port: RPC.Port, fs: FileSystem, request: FileOrFSRequest): Promise<void> {
|
|
237
|
-
if (!RPC.isMessage(request)) {
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
const { method, args, id, scope, stack } = request;
|
|
241
|
-
|
|
242
|
-
let value,
|
|
243
|
-
error: boolean = false;
|
|
244
|
-
|
|
245
|
-
try {
|
|
246
|
-
switch (scope) {
|
|
247
|
-
case 'fs':
|
|
248
|
-
// @ts-expect-error 2556
|
|
249
|
-
value = await fs[method](...args);
|
|
250
|
-
if (value instanceof File) {
|
|
251
|
-
descriptors.set(++nextFd, value);
|
|
252
|
-
value = {
|
|
253
|
-
fd: nextFd,
|
|
254
|
-
path: value.path,
|
|
255
|
-
position: value.position,
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
break;
|
|
259
|
-
case 'file': {
|
|
260
|
-
const { fd } = request;
|
|
261
|
-
if (!descriptors.has(fd)) {
|
|
262
|
-
throw new ErrnoError(Errno.EBADF);
|
|
263
|
-
}
|
|
264
|
-
// @ts-expect-error 2556
|
|
265
|
-
value = await descriptors.get(fd);
|
|
266
|
-
if (method == 'close') {
|
|
267
|
-
descriptors.delete(fd);
|
|
268
|
-
}
|
|
269
|
-
break;
|
|
270
|
-
}
|
|
271
|
-
default:
|
|
272
|
-
return;
|
|
273
|
-
}
|
|
274
|
-
} catch (e) {
|
|
275
|
-
value = e instanceof ErrnoError ? e.toJSON() : (e as object).toString();
|
|
276
|
-
error = true;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
port.postMessage({ _zenfs: true, scope, id, error, method, stack, value });
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
export function attachFS(port: RPC.Port, fs: FileSystem): void {
|
|
283
|
-
RPC.attach<FileOrFSRequest>(port, request => handleRequest(port, fs, request));
|
|
284
|
-
}
|
|
285
|
-
|
|
286
|
-
export function detachFS(port: RPC.Port, fs: FileSystem): void {
|
|
287
|
-
RPC.detach<FileOrFSRequest>(port, request => handleRequest(port, fs, request));
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
const _Port = {
|
|
291
|
-
name: 'Port',
|
|
292
|
-
|
|
293
|
-
options: {
|
|
294
|
-
port: {
|
|
295
|
-
type: 'object',
|
|
296
|
-
required: true,
|
|
297
|
-
validator(port: RPC.Port) {
|
|
298
|
-
// Check for a `postMessage` function.
|
|
299
|
-
if (typeof port?.postMessage != 'function') {
|
|
300
|
-
throw new ErrnoError(Errno.EINVAL, 'option must be a port.');
|
|
301
|
-
}
|
|
302
|
-
},
|
|
303
|
-
},
|
|
304
|
-
timeout: {
|
|
305
|
-
type: 'number',
|
|
306
|
-
required: false,
|
|
307
|
-
},
|
|
308
|
-
},
|
|
309
|
-
|
|
310
|
-
isAvailable(): boolean {
|
|
311
|
-
return true;
|
|
312
|
-
},
|
|
313
|
-
|
|
314
|
-
create(options: RPC.Options) {
|
|
315
|
-
return new PortFS(options);
|
|
316
|
-
},
|
|
317
|
-
} satisfies Backend<PortFS, RPC.Options>;
|
|
318
|
-
type _Port = typeof _Port;
|
|
319
|
-
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
|
|
320
|
-
export interface Port extends _Port {}
|
|
321
|
-
export const Port: Port = _Port;
|
|
322
|
-
|
|
323
|
-
export async function resolveRemoteMount<T extends Backend>(port: RPC.Port, config: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
|
|
324
|
-
const stopAndReplay = RPC.catchMessages(port);
|
|
325
|
-
const fs = await resolveMountConfig(config, _depth);
|
|
326
|
-
attachFS(port, fs);
|
|
327
|
-
stopAndReplay(fs);
|
|
328
|
-
return fs;
|
|
329
|
-
}
|
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
# Port Backend
|
|
2
|
-
|
|
3
|
-
A backend for usage with ports and workers. See the examples below.
|
|
4
|
-
|
|
5
|
-
#### Accessing an FS on a remote Worker from the main thread
|
|
6
|
-
|
|
7
|
-
Main:
|
|
8
|
-
|
|
9
|
-
```ts
|
|
10
|
-
import { configure } from '@zenfs/core';
|
|
11
|
-
import { Port } from '@zenfs/port';
|
|
12
|
-
import { Worker } from 'node:worker_threads';
|
|
13
|
-
|
|
14
|
-
const worker = new Worker('worker.js');
|
|
15
|
-
|
|
16
|
-
await configure({
|
|
17
|
-
mounts: {
|
|
18
|
-
'/worker': {
|
|
19
|
-
backend: Port,
|
|
20
|
-
port: worker,
|
|
21
|
-
},
|
|
22
|
-
},
|
|
23
|
-
});
|
|
24
|
-
```
|
|
25
|
-
|
|
26
|
-
Worker:
|
|
27
|
-
|
|
28
|
-
```ts
|
|
29
|
-
import { InMemory, resolveRemoteMount, attachFS } from '@zenfs/core';
|
|
30
|
-
import { parentPort } from 'node:worker_threads';
|
|
31
|
-
|
|
32
|
-
await resolveRemoteMount(parentPort, { backend: InMemory, name: 'tmp' });
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
If you are using using web workers, you would use `self` instead of importing `parentPort` in the worker, and would not need to import `Worker` in the main thread.
|
|
36
|
-
|
|
37
|
-
#### Using with multiple ports on the same thread
|
|
38
|
-
|
|
39
|
-
```ts
|
|
40
|
-
import { InMemory, fs, resolveMountConfig, resolveRemoteMount, Port } from '@zenfs/core';
|
|
41
|
-
import { MessageChannel } from 'node:worker_threads';
|
|
42
|
-
|
|
43
|
-
const { port1: localPort, port2: remotePort } = new MessageChannel();
|
|
44
|
-
|
|
45
|
-
fs.mount('/remote', await resolveRemoteMount(remotePort, { backend: InMemory, name: 'tmp' }));
|
|
46
|
-
fs.mount('/port', await resolveMountConfig({ backend: Port, port: localPort }));
|
|
47
|
-
|
|
48
|
-
const content = 'FS is in a port';
|
|
49
|
-
|
|
50
|
-
await fs.promises.writeFile('/port/test', content);
|
|
51
|
-
|
|
52
|
-
fs.readFileSync('/remote/test', 'utf8'); // FS is in a port
|
|
53
|
-
await fs.promises.readFile('/port/test', 'utf8'); // FS is in a port
|
|
54
|
-
```
|
package/src/backends/port/rpc.ts
DELETED
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
|
-
import { Errno, ErrnoError, type ErrnoErrorJSON } from '../../error.js';
|
|
3
|
-
import type { FileSystem } from '../../filesystem.js';
|
|
4
|
-
import type { Backend, FilesystemOf } from '../backend.js';
|
|
5
|
-
import { handleRequest, PortFile, type PortFS } from './fs.js';
|
|
6
|
-
import type { FileOrFSRequest } from './fs.js';
|
|
7
|
-
|
|
8
|
-
type _MessageEvent<T = any> = T | { data: T };
|
|
9
|
-
|
|
10
|
-
/** @internal */
|
|
11
|
-
export interface Port {
|
|
12
|
-
postMessage(value: unknown): void;
|
|
13
|
-
on?(event: 'message', listener: (value: unknown) => void): this;
|
|
14
|
-
off?(event: 'message', listener: (value: unknown) => void): this;
|
|
15
|
-
addEventListener?(type: 'message', listener: (ev: _MessageEvent) => void): void;
|
|
16
|
-
removeEventListener?(type: 'message', listener: (ev: _MessageEvent) => void): void;
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
export interface Options {
|
|
20
|
-
/**
|
|
21
|
-
* The target port that you want to connect to, or the current port if in a port context.
|
|
22
|
-
*/
|
|
23
|
-
port: Port;
|
|
24
|
-
/**
|
|
25
|
-
* How long to wait for a request to complete
|
|
26
|
-
*/
|
|
27
|
-
timeout?: number;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* An RPC message
|
|
32
|
-
*/
|
|
33
|
-
export interface Message {
|
|
34
|
-
_zenfs: true;
|
|
35
|
-
scope: 'fs' | 'file';
|
|
36
|
-
id: string;
|
|
37
|
-
method: string;
|
|
38
|
-
stack: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export interface Request extends Message {
|
|
42
|
-
args: unknown[];
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
interface _ResponseWithError extends Message {
|
|
46
|
-
error: true;
|
|
47
|
-
value: ErrnoErrorJSON | string;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
interface _ResponseWithValue<T> extends Message {
|
|
51
|
-
error: false;
|
|
52
|
-
value: Awaited<T> extends File ? FileData : Awaited<T>;
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
export type Response<T = unknown> = _ResponseWithError | _ResponseWithValue<T>;
|
|
56
|
-
|
|
57
|
-
export interface FileData {
|
|
58
|
-
fd: number;
|
|
59
|
-
path: string;
|
|
60
|
-
position: number;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function isFileData(value: unknown): value is FileData {
|
|
64
|
-
return typeof value == 'object' && value != null && 'fd' in value && 'path' in value && 'position' in value;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
export { FileData as File };
|
|
68
|
-
|
|
69
|
-
// general types
|
|
70
|
-
|
|
71
|
-
export function isMessage(arg: unknown): arg is Message {
|
|
72
|
-
return typeof arg == 'object' && arg != null && '_zenfs' in arg && !!arg._zenfs;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
type _Executor = Parameters<ConstructorParameters<typeof Promise<any>>[0]>;
|
|
76
|
-
|
|
77
|
-
export interface Executor {
|
|
78
|
-
resolve: _Executor[0];
|
|
79
|
-
reject: _Executor[1];
|
|
80
|
-
fs?: PortFS;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
const executors: Map<string, Executor> = new Map();
|
|
84
|
-
|
|
85
|
-
export function request<const TRequest extends Request, TValue>(
|
|
86
|
-
request: Omit<TRequest, 'id' | 'stack' | '_zenfs'>,
|
|
87
|
-
{ port, timeout = 1000, fs }: Partial<Options> & { fs?: PortFS } = {}
|
|
88
|
-
): Promise<TValue> {
|
|
89
|
-
const stack = '\n' + new Error().stack!.slice('Error:'.length);
|
|
90
|
-
if (!port) {
|
|
91
|
-
throw ErrnoError.With('EINVAL');
|
|
92
|
-
}
|
|
93
|
-
return new Promise<TValue>((resolve, reject) => {
|
|
94
|
-
const id = Math.random().toString(16).slice(10);
|
|
95
|
-
executors.set(id, { resolve, reject, fs });
|
|
96
|
-
port.postMessage({ ...request, _zenfs: true, id, stack });
|
|
97
|
-
const _ = setTimeout(() => {
|
|
98
|
-
const error = new ErrnoError(Errno.EIO, 'RPC Failed');
|
|
99
|
-
error.stack += stack;
|
|
100
|
-
reject(error);
|
|
101
|
-
if (typeof _ == 'object') _.unref();
|
|
102
|
-
}, timeout);
|
|
103
|
-
});
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function handleResponse<const TResponse extends Response>(response: TResponse): void {
|
|
107
|
-
if (!isMessage(response)) {
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
const { id, value, error, stack } = response;
|
|
111
|
-
if (!executors.has(id)) {
|
|
112
|
-
const error = new ErrnoError(Errno.EIO, 'Invalid RPC id:' + id);
|
|
113
|
-
error.stack += stack;
|
|
114
|
-
throw error;
|
|
115
|
-
}
|
|
116
|
-
const { resolve, reject, fs } = executors.get(id)!;
|
|
117
|
-
if (error) {
|
|
118
|
-
const e = typeof value == 'string' ? new Error(value) : ErrnoError.fromJSON(value);
|
|
119
|
-
e.stack += stack;
|
|
120
|
-
reject(e);
|
|
121
|
-
executors.delete(id);
|
|
122
|
-
return;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (isFileData(value)) {
|
|
126
|
-
const { fd, path, position } = value;
|
|
127
|
-
const file = new PortFile(fs!, fd, path, position);
|
|
128
|
-
resolve(file);
|
|
129
|
-
executors.delete(id);
|
|
130
|
-
return;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
resolve(value);
|
|
134
|
-
executors.delete(id);
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
export function attach<T extends Message>(port: Port, handler: (message: T) => unknown) {
|
|
139
|
-
if (!port) {
|
|
140
|
-
throw ErrnoError.With('EINVAL');
|
|
141
|
-
}
|
|
142
|
-
port['on' in port ? 'on' : 'addEventListener']!('message', (message: T | _MessageEvent<T>) => {
|
|
143
|
-
handler('data' in message ? message.data : message);
|
|
144
|
-
});
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
export function detach<T extends Message>(port: Port, handler: (message: T) => unknown) {
|
|
148
|
-
if (!port) {
|
|
149
|
-
throw ErrnoError.With('EINVAL');
|
|
150
|
-
}
|
|
151
|
-
port['off' in port ? 'off' : 'removeEventListener']!('message', (message: T | _MessageEvent<T>) => {
|
|
152
|
-
handler('data' in message ? message.data : message);
|
|
153
|
-
});
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
export function catchMessages<T extends Backend>(port: Port): (fs: FilesystemOf<T>) => void {
|
|
157
|
-
const events: _MessageEvent[] = [];
|
|
158
|
-
const handler = events.push.bind(events);
|
|
159
|
-
attach(port, handler);
|
|
160
|
-
return function (fs: FileSystem) {
|
|
161
|
-
detach(port, handler);
|
|
162
|
-
for (const event of events) {
|
|
163
|
-
const request = ('data' in event ? event.data : event) as FileOrFSRequest;
|
|
164
|
-
void handleRequest(port, fs, request);
|
|
165
|
-
}
|
|
166
|
-
};
|
|
167
|
-
}
|
package/src/backends/readme.md
DELETED