@zenfs/core 0.16.0 → 0.16.2
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/fetch.js +5 -4
- package/dist/backends/index/fs.js +12 -5
- package/dist/backends/overlay.d.ts +1 -1
- package/dist/backends/overlay.js +9 -9
- package/dist/backends/port/fs.d.ts +22 -0
- package/dist/backends/port/fs.js +12 -1
- package/dist/backends/port/rpc.d.ts +2 -0
- package/dist/backends/port/rpc.js +13 -1
- package/dist/backends/store/fs.js +1 -1
- package/dist/browser.min.js +4 -4
- package/dist/browser.min.js.map +4 -4
- package/dist/config.d.ts +1 -2
- package/dist/config.js +2 -2
- package/dist/emulation/async.d.ts +4 -5
- package/dist/emulation/async.js +9 -9
- package/dist/emulation/dir.js +1 -1
- package/dist/emulation/promises.js +10 -5
- package/dist/emulation/sync.d.ts +1 -0
- package/dist/emulation/sync.js +1 -1
- package/dist/file.js +6 -6
- package/dist/filesystem.js +1 -1
- package/dist/inode.d.ts +1 -1
- package/dist/inode.js +54 -37
- package/dist/polyfills.js +1 -0
- package/dist/utils.d.ts +1 -1
- package/dist/utils.js +1 -1
- package/eslint.shared.js +53 -0
- package/package.json +16 -6
- package/src/backends/fetch.ts +6 -5
- package/src/backends/index/fs.ts +12 -5
- package/src/backends/index/index.ts +1 -1
- package/src/backends/overlay.ts +9 -9
- package/src/backends/port/fs.ts +18 -3
- package/src/backends/port/readme.md +7 -12
- package/src/backends/port/rpc.ts +16 -1
- package/src/backends/store/fs.ts +2 -2
- package/src/config.ts +3 -3
- package/src/emulation/async.ts +31 -18
- package/src/emulation/dir.ts +1 -1
- package/src/emulation/promises.ts +11 -6
- package/src/emulation/sync.ts +5 -4
- package/src/file.ts +6 -6
- package/src/filesystem.ts +2 -1
- package/src/inode.ts +56 -37
- package/src/polyfills.ts +1 -0
- package/src/utils.ts +5 -2
package/src/backends/overlay.ts
CHANGED
|
@@ -126,10 +126,10 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
126
126
|
return this._deleteLog;
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
-
public restoreDeletionLog(log: string, cred: Cred): void {
|
|
129
|
+
public async restoreDeletionLog(log: string, cred: Cred): Promise<void> {
|
|
130
130
|
this._deleteLog = log;
|
|
131
131
|
this._reparseDeletionLog();
|
|
132
|
-
this.updateLog('', cred);
|
|
132
|
+
await this.updateLog('', cred);
|
|
133
133
|
}
|
|
134
134
|
|
|
135
135
|
public async rename(oldPath: string, newPath: string, cred: Cred): Promise<void> {
|
|
@@ -248,7 +248,7 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
248
248
|
|
|
249
249
|
// if it still exists add to the delete log
|
|
250
250
|
if (await this.exists(path, cred)) {
|
|
251
|
-
this.deletePath(path, cred);
|
|
251
|
+
await this.deletePath(path, cred);
|
|
252
252
|
}
|
|
253
253
|
}
|
|
254
254
|
|
|
@@ -265,7 +265,7 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
265
265
|
|
|
266
266
|
// if it still exists add to the delete log
|
|
267
267
|
if (this.existsSync(path, cred)) {
|
|
268
|
-
this.deletePath(path, cred);
|
|
268
|
+
void this.deletePath(path, cred);
|
|
269
269
|
}
|
|
270
270
|
}
|
|
271
271
|
|
|
@@ -282,7 +282,7 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
282
282
|
if ((await this.readdir(path, cred)).length > 0) {
|
|
283
283
|
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
284
284
|
} else {
|
|
285
|
-
this.deletePath(path, cred);
|
|
285
|
+
await this.deletePath(path, cred);
|
|
286
286
|
}
|
|
287
287
|
}
|
|
288
288
|
}
|
|
@@ -300,7 +300,7 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
300
300
|
if (this.readdirSync(path, cred).length > 0) {
|
|
301
301
|
throw ErrnoError.With('ENOTEMPTY', path, 'rmdir');
|
|
302
302
|
} else {
|
|
303
|
-
this.deletePath(path, cred);
|
|
303
|
+
void this.deletePath(path, cred);
|
|
304
304
|
}
|
|
305
305
|
}
|
|
306
306
|
}
|
|
@@ -379,9 +379,9 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
379
379
|
});
|
|
380
380
|
}
|
|
381
381
|
|
|
382
|
-
private deletePath(path: string, cred: Cred): void {
|
|
382
|
+
private async deletePath(path: string, cred: Cred): Promise<void> {
|
|
383
383
|
this._deletedFiles.add(path);
|
|
384
|
-
this.updateLog(`d${path}\n`, cred);
|
|
384
|
+
await this.updateLog(`d${path}\n`, cred);
|
|
385
385
|
}
|
|
386
386
|
|
|
387
387
|
private async updateLog(addition: string, cred: Cred) {
|
|
@@ -396,7 +396,7 @@ export class UnlockedOverlayFS extends FileSystem {
|
|
|
396
396
|
await log.write(encode(this._deleteLog));
|
|
397
397
|
if (this._deleteLogUpdateNeeded) {
|
|
398
398
|
this._deleteLogUpdateNeeded = false;
|
|
399
|
-
this.updateLog('', cred);
|
|
399
|
+
await this.updateLog('', cred);
|
|
400
400
|
}
|
|
401
401
|
} catch (e) {
|
|
402
402
|
this._deleteLogError = e as ErrnoError;
|
package/src/backends/port/fs.ts
CHANGED
|
@@ -7,8 +7,9 @@ import { File } from '../../file.js';
|
|
|
7
7
|
import { Async, FileSystem, type FileSystemMetadata } from '../../filesystem.js';
|
|
8
8
|
import { Stats, type FileType } from '../../stats.js';
|
|
9
9
|
import { InMemory } from '../memory.js';
|
|
10
|
-
import type { Backend } from '../backend.js';
|
|
10
|
+
import type { Backend, FilesystemOf } from '../backend.js';
|
|
11
11
|
import * as RPC from './rpc.js';
|
|
12
|
+
import { type MountConfiguration, resolveMountConfig } from '../../config.js';
|
|
12
13
|
|
|
13
14
|
type FileMethods = Omit<ExtractProperties<File, (...args: any[]) => Promise<any>>, typeof Symbol.asyncDispose>;
|
|
14
15
|
type FileMethod = keyof FileMethods;
|
|
@@ -223,9 +224,15 @@ let nextFd = 0;
|
|
|
223
224
|
|
|
224
225
|
const descriptors: Map<number, File> = new Map();
|
|
225
226
|
|
|
226
|
-
|
|
227
|
+
/**
|
|
228
|
+
* @internal
|
|
229
|
+
*/
|
|
230
|
+
export type FileOrFSRequest = FSRequest | FileRequest;
|
|
227
231
|
|
|
228
|
-
|
|
232
|
+
/**
|
|
233
|
+
* @internal
|
|
234
|
+
*/
|
|
235
|
+
export async function handleRequest(port: RPC.Port, fs: FileSystem, request: FileOrFSRequest): Promise<void> {
|
|
229
236
|
if (!RPC.isMessage(request)) {
|
|
230
237
|
return;
|
|
231
238
|
}
|
|
@@ -308,3 +315,11 @@ export const Port = {
|
|
|
308
315
|
return new PortFS(options);
|
|
309
316
|
},
|
|
310
317
|
} satisfies Backend<PortFS, RPC.Options>;
|
|
318
|
+
|
|
319
|
+
export async function resolveRemoteMount<T extends Backend>(port: RPC.Port, config: MountConfiguration<T>, _depth = 0): Promise<FilesystemOf<T>> {
|
|
320
|
+
const stopAndReplay = RPC.catchMessages(port);
|
|
321
|
+
const fs = await resolveMountConfig(config, _depth);
|
|
322
|
+
attachFS(port, fs);
|
|
323
|
+
stopAndReplay(fs);
|
|
324
|
+
return fs;
|
|
325
|
+
}
|
|
@@ -26,12 +26,10 @@ await configure({
|
|
|
26
26
|
Worker:
|
|
27
27
|
|
|
28
28
|
```ts
|
|
29
|
-
import { InMemory,
|
|
30
|
-
import { attachFS } from '@zenfs/port';
|
|
29
|
+
import { InMemory, resolveRemoteMount, attachFS } from '@zenfs/core';
|
|
31
30
|
import { parentPort } from 'node:worker_threads';
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
attachFS(parentPort, tmpfs);
|
|
32
|
+
await resolveRemoteMount(parentPort, { backend: InMemory, name: 'tmp' });
|
|
35
33
|
```
|
|
36
34
|
|
|
37
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.
|
|
@@ -39,21 +37,18 @@ If you are using using web workers, you would use `self` instead of importing `p
|
|
|
39
37
|
#### Using with multiple ports on the same thread
|
|
40
38
|
|
|
41
39
|
```ts
|
|
42
|
-
import { InMemory, fs, resolveMountConfig } from '@zenfs/core';
|
|
43
|
-
import { Port, attachFS } from '@zenfs/port';
|
|
40
|
+
import { InMemory, fs, resolveMountConfig, resolveRemoteMount, Port } from '@zenfs/core';
|
|
44
41
|
import { MessageChannel } from 'node:worker_threads';
|
|
45
42
|
|
|
46
|
-
const { port1, port2 } = new MessageChannel();
|
|
43
|
+
const { port1: localPort, port2: remotePort } = new MessageChannel();
|
|
47
44
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
fs.mount('/port', await resolveMountConfig({ backend: Port, port: port1 }));
|
|
51
|
-
console.log('/port');
|
|
45
|
+
fs.mount('/remote', await resolveRemoteMount(remotePort, { backend: InMemory, name: 'tmp' }));
|
|
46
|
+
fs.mount('/port', await resolveMountConfig({ backend: Port, port: localPort }));
|
|
52
47
|
|
|
53
48
|
const content = 'FS is in a port';
|
|
54
49
|
|
|
55
50
|
await fs.promises.writeFile('/port/test', content);
|
|
56
51
|
|
|
57
|
-
fs.readFileSync('/
|
|
52
|
+
fs.readFileSync('/remote/test', 'utf8'); // FS is in a port
|
|
58
53
|
await fs.promises.readFile('/port/test', 'utf8'); // FS is in a port
|
|
59
54
|
```
|
package/src/backends/port/rpc.ts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
2
2
|
import { Errno, ErrnoError, type ErrnoErrorJSON } from '../../error.js';
|
|
3
|
-
import {
|
|
3
|
+
import type { Backend, FilesystemOf } from '../backend.js';
|
|
4
|
+
import { handleRequest, PortFile, type PortFS } from './fs.js';
|
|
5
|
+
import type { FileOrFSRequest } from './fs.js';
|
|
4
6
|
|
|
5
7
|
type _MessageEvent<T = any> = T | { data: T };
|
|
6
8
|
|
|
@@ -148,3 +150,16 @@ export function detach<T extends Message>(port: Port, handler: (message: T) => u
|
|
|
148
150
|
handler('data' in message ? message.data : message);
|
|
149
151
|
});
|
|
150
152
|
}
|
|
153
|
+
|
|
154
|
+
export function catchMessages<T extends Backend>(port: Port): (fs: FilesystemOf<T>) => void {
|
|
155
|
+
const events: _MessageEvent[] = [];
|
|
156
|
+
const handler = events.push.bind(events);
|
|
157
|
+
attach(port, handler);
|
|
158
|
+
return function (fs: any) {
|
|
159
|
+
detach(port, handler);
|
|
160
|
+
for (const event of events) {
|
|
161
|
+
const request: FileOrFSRequest = 'data' in event ? event.data : event;
|
|
162
|
+
void handleRequest(port, fs, request);
|
|
163
|
+
}
|
|
164
|
+
};
|
|
165
|
+
}
|
package/src/backends/store/fs.ts
CHANGED
|
@@ -535,7 +535,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
535
535
|
*/
|
|
536
536
|
private async findINode(tx: Transaction, path: string, visited: Set<string> = new Set()): Promise<Inode> {
|
|
537
537
|
const id = await this._findINode(tx, dirname(path), basename(path), visited);
|
|
538
|
-
return this.getINode(tx, id
|
|
538
|
+
return this.getINode(tx, id, path);
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
/**
|
|
@@ -698,7 +698,7 @@ export class StoreFS<T extends Store = Store> extends FileSystem {
|
|
|
698
698
|
await tx.commit();
|
|
699
699
|
return inode;
|
|
700
700
|
} catch (e) {
|
|
701
|
-
tx.abort();
|
|
701
|
+
await tx.abort();
|
|
702
702
|
throw e;
|
|
703
703
|
}
|
|
704
704
|
}
|
package/src/config.ts
CHANGED
|
@@ -55,9 +55,9 @@ export async function resolveMountConfig<T extends Backend>(config: MountConfigu
|
|
|
55
55
|
const { backend } = config;
|
|
56
56
|
|
|
57
57
|
if (!(await backend.isAvailable())) {
|
|
58
|
-
throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend);
|
|
58
|
+
throw new ErrnoError(Errno.EPERM, 'Backend not available: ' + backend.name);
|
|
59
59
|
}
|
|
60
|
-
checkOptions(backend, config);
|
|
60
|
+
await checkOptions(backend, config);
|
|
61
61
|
const mount = (await backend.create(config)) as FilesystemOf<T>;
|
|
62
62
|
if ('_disableSync' in mount) {
|
|
63
63
|
type AsyncFS = InstanceType<ReturnType<typeof Async<new () => FilesystemOf<T>>>>;
|
|
@@ -67,7 +67,7 @@ export async function resolveMountConfig<T extends Backend>(config: MountConfigu
|
|
|
67
67
|
return mount;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
type ConfigMounts = { [K in AbsolutePath]: Backend };
|
|
70
|
+
export type ConfigMounts = { [K in AbsolutePath]: Backend };
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* Configuration
|
package/src/emulation/async.ts
CHANGED
|
@@ -207,12 +207,18 @@ writeFile satisfies Omit<typeof fs.writeFile, '__promisify__'>;
|
|
|
207
207
|
* @param callback
|
|
208
208
|
*/
|
|
209
209
|
export function appendFile(filename: fs.PathLike, data: FileContents, cb?: Callback): void;
|
|
210
|
-
export function appendFile(filename: fs.PathLike, data: FileContents, options?:
|
|
211
|
-
export function appendFile(filename: fs.PathLike, data: FileContents, encoding?:
|
|
212
|
-
export function appendFile(
|
|
210
|
+
export function appendFile(filename: fs.PathLike, data: FileContents, options?: fs.EncodingOption & { mode?: fs.Mode; flag?: fs.OpenMode }, cb?: Callback): void;
|
|
211
|
+
export function appendFile(filename: fs.PathLike, data: FileContents, encoding?: BufferEncoding, cb?: Callback): void;
|
|
212
|
+
export function appendFile(
|
|
213
|
+
filename: fs.PathLike,
|
|
214
|
+
data: FileContents,
|
|
215
|
+
cbEncOpts?: (fs.EncodingOption & { mode?: fs.Mode; flag?: fs.OpenMode }) | Callback,
|
|
216
|
+
cb: Callback = nop
|
|
217
|
+
): void {
|
|
218
|
+
const optionsOrEncoding = typeof cbEncOpts != 'function' ? cbEncOpts : undefined;
|
|
213
219
|
cb = typeof cbEncOpts === 'function' ? cbEncOpts : cb;
|
|
214
220
|
promises
|
|
215
|
-
.appendFile(filename, data,
|
|
221
|
+
.appendFile(filename, data, optionsOrEncoding)
|
|
216
222
|
.then(() => cb())
|
|
217
223
|
.catch(cb);
|
|
218
224
|
}
|
|
@@ -259,7 +265,7 @@ close satisfies Omit<typeof fs.close, '__promisify__'>;
|
|
|
259
265
|
*/
|
|
260
266
|
export function ftruncate(fd: number, cb?: Callback): void;
|
|
261
267
|
export function ftruncate(fd: number, len?: number, cb?: Callback): void;
|
|
262
|
-
export function ftruncate(fd: number, lenOrCB?:
|
|
268
|
+
export function ftruncate(fd: number, lenOrCB?: number | Callback, cb: Callback = nop): void {
|
|
263
269
|
const length = typeof lenOrCB === 'number' ? lenOrCB : 0;
|
|
264
270
|
cb = typeof lenOrCB === 'function' ? lenOrCB : cb;
|
|
265
271
|
const file = fd2file(fd);
|
|
@@ -317,8 +323,15 @@ export function write(fd: number, buffer: Uint8Array, offset: number, length: nu
|
|
|
317
323
|
export function write(fd: number, data: FileContents, cb?: Callback<[number, string]>): void;
|
|
318
324
|
export function write(fd: number, data: FileContents, position?: number, cb?: Callback<[number, string]>): void;
|
|
319
325
|
export function write(fd: number, data: FileContents, position: number | null, encoding: BufferEncoding, cb?: Callback<[number, string]>): void;
|
|
320
|
-
export function write(
|
|
321
|
-
|
|
326
|
+
export function write(
|
|
327
|
+
fd: number,
|
|
328
|
+
data: FileContents,
|
|
329
|
+
cbPosOff?: number | Callback<[number, string]> | null,
|
|
330
|
+
cbLenEnc?: number | BufferEncoding | Callback<[number, string]>,
|
|
331
|
+
cbPosEnc?: number | BufferEncoding | Callback<[number, Uint8Array]> | Callback<[number, string]>,
|
|
332
|
+
cb: Callback<[number, Uint8Array]> | Callback<[number, string]> = nop
|
|
333
|
+
): void {
|
|
334
|
+
let buffer: Buffer, offset: number | undefined, length: number | undefined, position: number | undefined | null, encoding: BufferEncoding;
|
|
322
335
|
const handle = new promises.FileHandle(fd);
|
|
323
336
|
if (typeof data === 'string') {
|
|
324
337
|
// Signature 1: (fd, string, [position?, [encoding?]], cb?)
|
|
@@ -331,12 +344,12 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
|
|
|
331
344
|
case 'number':
|
|
332
345
|
// (fd, string, position, encoding?, cb?)
|
|
333
346
|
position = cbPosOff;
|
|
334
|
-
encoding = typeof cbLenEnc === 'string' ?
|
|
335
|
-
cb = typeof
|
|
347
|
+
encoding = typeof cbLenEnc === 'string' ? cbLenEnc : 'utf8';
|
|
348
|
+
cb = typeof cbPosEnc === 'function' ? cbPosEnc : cb;
|
|
336
349
|
break;
|
|
337
350
|
default:
|
|
338
351
|
// ...try to find the callback and get out of here!
|
|
339
|
-
cb = typeof cbLenEnc === 'function' ? cbLenEnc : typeof
|
|
352
|
+
cb = (typeof cbLenEnc === 'function' ? cbLenEnc : typeof cbPosEnc === 'function' ? cbPosEnc : cb) as Callback<[number, Uint8Array | string]>;
|
|
340
353
|
(cb as Callback<[number, Uint8Array | string]>)(new ErrnoError(Errno.EINVAL, 'Invalid arguments.'));
|
|
341
354
|
return;
|
|
342
355
|
}
|
|
@@ -353,11 +366,11 @@ export function write(fd: number, data: FileContents, cbPosOff?: any, cbLenEnc?:
|
|
|
353
366
|
} else {
|
|
354
367
|
// Signature 2: (fd, buffer, offset, length, position?, cb?)
|
|
355
368
|
buffer = Buffer.from(data.buffer);
|
|
356
|
-
offset = cbPosOff;
|
|
357
|
-
length = cbLenEnc;
|
|
358
|
-
position = typeof
|
|
359
|
-
const _cb = typeof
|
|
360
|
-
handle
|
|
369
|
+
offset = cbPosOff as number;
|
|
370
|
+
length = cbLenEnc as number;
|
|
371
|
+
position = typeof cbPosEnc === 'number' ? cbPosEnc : null;
|
|
372
|
+
const _cb = (typeof cbPosEnc === 'function' ? cbPosEnc : cb) as Callback<[number, Uint8Array]>;
|
|
373
|
+
void handle
|
|
361
374
|
.write(buffer, offset, length, position)
|
|
362
375
|
.then(({ bytesWritten }) => _cb(undefined, bytesWritten, buffer))
|
|
363
376
|
.catch(_cb);
|
|
@@ -472,7 +485,7 @@ export function readdir(path: fs.PathLike, _options: { withFileTypes?: boolean }
|
|
|
472
485
|
const options = typeof _options != 'function' ? _options : {};
|
|
473
486
|
promises
|
|
474
487
|
.readdir(path, options as object)
|
|
475
|
-
|
|
488
|
+
|
|
476
489
|
.then(entries => cb(undefined, entries as any))
|
|
477
490
|
.catch(cb);
|
|
478
491
|
}
|
|
@@ -647,11 +660,11 @@ realpath satisfies Omit<typeof fs.realpath, '__promisify__' | 'native'>;
|
|
|
647
660
|
*/
|
|
648
661
|
export function access(path: fs.PathLike, cb: Callback): void;
|
|
649
662
|
export function access(path: fs.PathLike, mode: number, cb: Callback): void;
|
|
650
|
-
export function access(path: fs.PathLike, cbMode:
|
|
663
|
+
export function access(path: fs.PathLike, cbMode: number | Callback, cb: Callback = nop): void {
|
|
651
664
|
const mode = typeof cbMode === 'number' ? cbMode : R_OK;
|
|
652
665
|
cb = typeof cbMode === 'function' ? cbMode : cb;
|
|
653
666
|
promises
|
|
654
|
-
.access(path,
|
|
667
|
+
.access(path, mode)
|
|
655
668
|
.then(() => cb())
|
|
656
669
|
.catch(cb);
|
|
657
670
|
}
|
package/src/emulation/dir.ts
CHANGED
|
@@ -164,7 +164,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
164
164
|
*/
|
|
165
165
|
public readableWebStream(options: promises.ReadableWebStreamOptions = {}): TReadableStream<Uint8Array> {
|
|
166
166
|
// Note: using an arrow function to preserve `this`
|
|
167
|
-
const start = async (
|
|
167
|
+
const start = async (controller: ReadableStreamController<Uint8Array>) => {
|
|
168
168
|
try {
|
|
169
169
|
const chunkSize = 64 * 1024,
|
|
170
170
|
maxChunks = 1e7;
|
|
@@ -175,10 +175,10 @@ export class FileHandle implements promises.FileHandle {
|
|
|
175
175
|
while (bytesRead > 0) {
|
|
176
176
|
const result = await this.read(new Uint8Array(chunkSize), 0, chunkSize, position);
|
|
177
177
|
if (!result.bytesRead) {
|
|
178
|
-
close();
|
|
178
|
+
controller.close();
|
|
179
179
|
return;
|
|
180
180
|
}
|
|
181
|
-
enqueue(result.buffer.slice(0, result.bytesRead));
|
|
181
|
+
controller.enqueue(result.buffer.slice(0, result.bytesRead));
|
|
182
182
|
position += result.bytesRead;
|
|
183
183
|
if (++i >= maxChunks) {
|
|
184
184
|
throw new ErrnoError(Errno.EFBIG, 'Too many iterations on readable stream', this.file.path, 'FileHandle.readableWebStream');
|
|
@@ -186,11 +186,15 @@ export class FileHandle implements promises.FileHandle {
|
|
|
186
186
|
bytesRead = result.bytesRead;
|
|
187
187
|
}
|
|
188
188
|
} catch (e) {
|
|
189
|
-
error(e);
|
|
189
|
+
controller.error(e);
|
|
190
190
|
}
|
|
191
191
|
};
|
|
192
192
|
|
|
193
|
-
|
|
193
|
+
const _gt = globalThis;
|
|
194
|
+
if (!('ReadableStream' in _gt)) {
|
|
195
|
+
throw new ErrnoError(Errno.ENOSYS, 'ReadableStream is missing on globalThis');
|
|
196
|
+
}
|
|
197
|
+
return new (_gt as { ReadableStream: new (...args: unknown[]) => TReadableStream<Uint8Array> }).ReadableStream({ start, type: options.type });
|
|
194
198
|
}
|
|
195
199
|
|
|
196
200
|
public readLines(options?: promises.CreateReadStreamOptions): ReadlineInterface {
|
|
@@ -326,6 +330,7 @@ export class FileHandle implements promises.FileHandle {
|
|
|
326
330
|
highWaterMark: options?.highWaterMark || 64 * 1024,
|
|
327
331
|
encoding: options!.encoding!,
|
|
328
332
|
|
|
333
|
+
// eslint-disable-next-line @typescript-eslint/no-misused-promises
|
|
329
334
|
read: async (size: number) => {
|
|
330
335
|
try {
|
|
331
336
|
const result = await this.read(new Uint8Array(size), 0, size, this.file.position);
|
|
@@ -573,7 +578,7 @@ export async function writeFile(
|
|
|
573
578
|
_options?: (fs.ObjectEncodingOptions & { mode?: fs.Mode; flag?: fs.OpenMode; flush?: boolean }) | BufferEncoding | null
|
|
574
579
|
): Promise<void> {
|
|
575
580
|
const options = normalizeOptions(_options, 'utf8', 'w+', 0o644);
|
|
576
|
-
await using handle = path instanceof FileHandle ? path : await open(path.toString(), options.flag, options.mode);
|
|
581
|
+
await using handle = path instanceof FileHandle ? path : await open((path as fs.PathLike).toString(), options.flag, options.mode);
|
|
577
582
|
|
|
578
583
|
const _data = typeof data == 'string' ? data : data;
|
|
579
584
|
if (typeof _data != 'string' && !(_data instanceof Uint8Array)) {
|
package/src/emulation/sync.ts
CHANGED
|
@@ -219,7 +219,7 @@ export function readFileSync(path: fs.PathOrFileDescriptor, _options: fs.WriteFi
|
|
|
219
219
|
if (!isReadable(flag)) {
|
|
220
220
|
throw new ErrnoError(Errno.EINVAL, 'Flag passed to readFile must allow for reading.');
|
|
221
221
|
}
|
|
222
|
-
const data: Buffer = Buffer.from(_readFileSync(typeof path == 'number' ? fd2file(path).path
|
|
222
|
+
const data: Buffer = Buffer.from(_readFileSync(typeof path == 'number' ? fd2file(path).path : path.toString(), options.flag, true));
|
|
223
223
|
return options.encoding ? data.toString(options.encoding) : data;
|
|
224
224
|
}
|
|
225
225
|
readFileSync satisfies typeof fs.readFileSync;
|
|
@@ -251,7 +251,7 @@ export function writeFileSync(path: fs.PathOrFileDescriptor, data: FileContents,
|
|
|
251
251
|
if (!encodedData) {
|
|
252
252
|
throw new ErrnoError(Errno.EINVAL, 'Data not specified');
|
|
253
253
|
}
|
|
254
|
-
using file = _openSync(typeof path == 'number' ? fd2file(path).path
|
|
254
|
+
using file = _openSync(typeof path == 'number' ? fd2file(path).path : path.toString(), flag, options.mode, true);
|
|
255
255
|
file.writeSync(encodedData, 0, encodedData.byteLength, 0);
|
|
256
256
|
}
|
|
257
257
|
writeFileSync satisfies typeof fs.writeFileSync;
|
|
@@ -277,7 +277,7 @@ export function appendFileSync(filename: fs.PathOrFileDescriptor, data: FileCont
|
|
|
277
277
|
throw new ErrnoError(Errno.EINVAL, 'Encoding not specified');
|
|
278
278
|
}
|
|
279
279
|
const encodedData = typeof data == 'string' ? Buffer.from(data, options.encoding!) : new Uint8Array(data.buffer, data.byteOffset, data.byteLength);
|
|
280
|
-
using file = _openSync(typeof filename == 'number' ? fd2file(filename).path
|
|
280
|
+
using file = _openSync(typeof filename == 'number' ? fd2file(filename).path : filename.toString(), flag, options.mode, true);
|
|
281
281
|
file.writeSync(encodedData, 0, encodedData.byteLength);
|
|
282
282
|
}
|
|
283
283
|
appendFileSync satisfies typeof fs.appendFileSync;
|
|
@@ -587,6 +587,7 @@ symlinkSync satisfies typeof fs.symlinkSync;
|
|
|
587
587
|
*/
|
|
588
588
|
export function readlinkSync(path: fs.PathLike, options?: fs.BufferEncodingOption): Buffer;
|
|
589
589
|
export function readlinkSync(path: fs.PathLike, options: fs.EncodingOption | BufferEncoding): string;
|
|
590
|
+
export function readlinkSync(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Buffer | string;
|
|
590
591
|
export function readlinkSync(path: fs.PathLike, options?: fs.EncodingOption | BufferEncoding | fs.BufferEncodingOption): Buffer | string {
|
|
591
592
|
const value: Buffer = Buffer.from(_readFileSync(path.toString(), 'r', false));
|
|
592
593
|
const encoding = typeof options == 'object' ? options?.encoding : options;
|
|
@@ -697,7 +698,7 @@ export function realpathSync(path: fs.PathLike, options?: fs.EncodingOption | fs
|
|
|
697
698
|
return lpath;
|
|
698
699
|
}
|
|
699
700
|
|
|
700
|
-
return realpathSync(mountPoint + readlinkSync(lpath));
|
|
701
|
+
return realpathSync(mountPoint + readlinkSync(lpath, options).toString());
|
|
701
702
|
} catch (e) {
|
|
702
703
|
throw fixError(e as Error, { [resolvedPath]: lpath });
|
|
703
704
|
}
|
package/src/file.ts
CHANGED
|
@@ -429,8 +429,8 @@ export class PreloadFile<FS extends FileSystem> extends File {
|
|
|
429
429
|
/**
|
|
430
430
|
* Asynchronous `stat`.
|
|
431
431
|
*/
|
|
432
|
-
public
|
|
433
|
-
return new Stats(this.stats);
|
|
432
|
+
public stat(): Promise<Stats> {
|
|
433
|
+
return Promise.resolve(new Stats(this.stats));
|
|
434
434
|
}
|
|
435
435
|
|
|
436
436
|
/**
|
|
@@ -673,8 +673,8 @@ export class NoSyncFile<T extends FileSystem> extends PreloadFile<T> {
|
|
|
673
673
|
/**
|
|
674
674
|
* Asynchronous sync. Doesn't do anything, simply calls the cb.
|
|
675
675
|
*/
|
|
676
|
-
public
|
|
677
|
-
return;
|
|
676
|
+
public sync(): Promise<void> {
|
|
677
|
+
return Promise.resolve();
|
|
678
678
|
}
|
|
679
679
|
/**
|
|
680
680
|
* Synchronous sync. Doesn't do anything.
|
|
@@ -685,8 +685,8 @@ export class NoSyncFile<T extends FileSystem> extends PreloadFile<T> {
|
|
|
685
685
|
/**
|
|
686
686
|
* Asynchronous close. Doesn't do anything, simply calls the cb.
|
|
687
687
|
*/
|
|
688
|
-
public
|
|
689
|
-
return;
|
|
688
|
+
public close(): Promise<void> {
|
|
689
|
+
return Promise.resolve();
|
|
690
690
|
}
|
|
691
691
|
/**
|
|
692
692
|
* Synchronous close. Doesn't do anything.
|
package/src/filesystem.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
/* eslint-disable @typescript-eslint/require-await */
|
|
1
2
|
import type { ExtractProperties } from 'utilium';
|
|
2
3
|
import { rootCred, type Cred } from './cred.js';
|
|
3
4
|
import { join } from './emulation/path.js';
|
|
@@ -490,7 +491,7 @@ export function Async<T extends typeof FileSystem>(
|
|
|
490
491
|
*/
|
|
491
492
|
private queue(...op: AsyncOperation) {
|
|
492
493
|
this._queue.push(op);
|
|
493
|
-
this._next();
|
|
494
|
+
void this._next();
|
|
494
495
|
}
|
|
495
496
|
}
|
|
496
497
|
|
package/src/inode.ts
CHANGED
|
@@ -16,7 +16,7 @@ export const size_max = 2 ** 32 - 1;
|
|
|
16
16
|
* Room inode
|
|
17
17
|
* @hidden
|
|
18
18
|
*/
|
|
19
|
-
export const rootIno = 0n
|
|
19
|
+
export const rootIno = 0n;
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
22
|
* Generates a random 32 bit integer, then converts to a hex string
|
|
@@ -36,19 +36,38 @@ export function randomIno(): Ino {
|
|
|
36
36
|
/**
|
|
37
37
|
* Offsets for inode members
|
|
38
38
|
*/
|
|
39
|
-
|
|
40
|
-
ino
|
|
41
|
-
size
|
|
42
|
-
mode
|
|
43
|
-
nlink
|
|
44
|
-
uid
|
|
45
|
-
gid
|
|
46
|
-
atime
|
|
47
|
-
birthtime
|
|
48
|
-
mtime
|
|
49
|
-
ctime
|
|
50
|
-
end
|
|
51
|
-
}
|
|
39
|
+
const offsets = {
|
|
40
|
+
ino: 0,
|
|
41
|
+
size: 8,
|
|
42
|
+
mode: 12,
|
|
43
|
+
nlink: 14,
|
|
44
|
+
uid: 18,
|
|
45
|
+
gid: 22,
|
|
46
|
+
atime: 26,
|
|
47
|
+
birthtime: 34,
|
|
48
|
+
mtime: 42,
|
|
49
|
+
ctime: 50,
|
|
50
|
+
end: 58,
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Offsets for a 64-bit inode's members
|
|
55
|
+
* Currently unused
|
|
56
|
+
*/
|
|
57
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
58
|
+
const offsets_64 = {
|
|
59
|
+
ino: 0,
|
|
60
|
+
size: 8,
|
|
61
|
+
mode: 16,
|
|
62
|
+
nlink: 18,
|
|
63
|
+
uid: 22,
|
|
64
|
+
gid: 26,
|
|
65
|
+
atime: 30,
|
|
66
|
+
birthtime: 38,
|
|
67
|
+
mtime: 46,
|
|
68
|
+
ctime: 54,
|
|
69
|
+
end: 62,
|
|
70
|
+
};
|
|
52
71
|
|
|
53
72
|
/**
|
|
54
73
|
* Generic inode definition that can easily be serialized.
|
|
@@ -64,9 +83,9 @@ export class Inode implements StatsLike {
|
|
|
64
83
|
|
|
65
84
|
constructor(buffer?: ArrayBufferLike) {
|
|
66
85
|
const setDefaults = !buffer;
|
|
67
|
-
buffer ??= new ArrayBuffer(
|
|
68
|
-
if (buffer?.byteLength <
|
|
69
|
-
throw new RangeError(`Can not create an inode from a buffer less than ${
|
|
86
|
+
buffer ??= new ArrayBuffer(offsets.end);
|
|
87
|
+
if (buffer?.byteLength < offsets.end) {
|
|
88
|
+
throw new RangeError(`Can not create an inode from a buffer less than ${offsets.end} bytes`);
|
|
70
89
|
}
|
|
71
90
|
this.view = new DataView(buffer);
|
|
72
91
|
this.buffer = buffer;
|
|
@@ -87,83 +106,83 @@ export class Inode implements StatsLike {
|
|
|
87
106
|
}
|
|
88
107
|
|
|
89
108
|
public get ino(): Ino {
|
|
90
|
-
return this.view.getBigUint64(
|
|
109
|
+
return this.view.getBigUint64(offsets.ino, true);
|
|
91
110
|
}
|
|
92
111
|
|
|
93
112
|
public set ino(value: Ino) {
|
|
94
|
-
this.view.setBigUint64(
|
|
113
|
+
this.view.setBigUint64(offsets.ino, value, true);
|
|
95
114
|
}
|
|
96
115
|
|
|
97
116
|
public get size(): number {
|
|
98
|
-
return this.view.getUint32(
|
|
117
|
+
return this.view.getUint32(offsets.size, true);
|
|
99
118
|
}
|
|
100
119
|
|
|
101
120
|
public set size(value: number) {
|
|
102
|
-
this.view.setUint32(
|
|
121
|
+
this.view.setUint32(offsets.size, value, true);
|
|
103
122
|
}
|
|
104
123
|
|
|
105
124
|
public get mode(): number {
|
|
106
|
-
return this.view.getUint16(
|
|
125
|
+
return this.view.getUint16(offsets.mode, true);
|
|
107
126
|
}
|
|
108
127
|
|
|
109
128
|
public set mode(value: number) {
|
|
110
|
-
this.view.setUint16(
|
|
129
|
+
this.view.setUint16(offsets.mode, value, true);
|
|
111
130
|
}
|
|
112
131
|
|
|
113
132
|
public get nlink(): number {
|
|
114
|
-
return this.view.getUint32(
|
|
133
|
+
return this.view.getUint32(offsets.nlink, true);
|
|
115
134
|
}
|
|
116
135
|
|
|
117
136
|
public set nlink(value: number) {
|
|
118
|
-
this.view.setUint32(
|
|
137
|
+
this.view.setUint32(offsets.nlink, value, true);
|
|
119
138
|
}
|
|
120
139
|
|
|
121
140
|
public get uid(): number {
|
|
122
|
-
return this.view.getUint32(
|
|
141
|
+
return this.view.getUint32(offsets.uid, true);
|
|
123
142
|
}
|
|
124
143
|
|
|
125
144
|
public set uid(value: number) {
|
|
126
|
-
this.view.setUint32(
|
|
145
|
+
this.view.setUint32(offsets.uid, value, true);
|
|
127
146
|
}
|
|
128
147
|
|
|
129
148
|
public get gid(): number {
|
|
130
|
-
return this.view.getUint32(
|
|
149
|
+
return this.view.getUint32(offsets.gid, true);
|
|
131
150
|
}
|
|
132
151
|
|
|
133
152
|
public set gid(value: number) {
|
|
134
|
-
this.view.setUint32(
|
|
153
|
+
this.view.setUint32(offsets.gid, value, true);
|
|
135
154
|
}
|
|
136
155
|
|
|
137
156
|
public get atimeMs(): number {
|
|
138
|
-
return this.view.getFloat64(
|
|
157
|
+
return this.view.getFloat64(offsets.atime, true);
|
|
139
158
|
}
|
|
140
159
|
|
|
141
160
|
public set atimeMs(value: number) {
|
|
142
|
-
this.view.setFloat64(
|
|
161
|
+
this.view.setFloat64(offsets.atime, value, true);
|
|
143
162
|
}
|
|
144
163
|
|
|
145
164
|
public get birthtimeMs(): number {
|
|
146
|
-
return this.view.getFloat64(
|
|
165
|
+
return this.view.getFloat64(offsets.birthtime, true);
|
|
147
166
|
}
|
|
148
167
|
|
|
149
168
|
public set birthtimeMs(value: number) {
|
|
150
|
-
this.view.setFloat64(
|
|
169
|
+
this.view.setFloat64(offsets.birthtime, value, true);
|
|
151
170
|
}
|
|
152
171
|
|
|
153
172
|
public get mtimeMs(): number {
|
|
154
|
-
return this.view.getFloat64(
|
|
173
|
+
return this.view.getFloat64(offsets.mtime, true);
|
|
155
174
|
}
|
|
156
175
|
|
|
157
176
|
public set mtimeMs(value: number) {
|
|
158
|
-
this.view.setFloat64(
|
|
177
|
+
this.view.setFloat64(offsets.mtime, value, true);
|
|
159
178
|
}
|
|
160
179
|
|
|
161
180
|
public get ctimeMs(): number {
|
|
162
|
-
return this.view.getFloat64(
|
|
181
|
+
return this.view.getFloat64(offsets.ctime, true);
|
|
163
182
|
}
|
|
164
183
|
|
|
165
184
|
public set ctimeMs(value: number) {
|
|
166
|
-
this.view.setFloat64(
|
|
185
|
+
this.view.setFloat64(offsets.ctime, value, true);
|
|
167
186
|
}
|
|
168
187
|
|
|
169
188
|
/**
|
package/src/polyfills.ts
CHANGED