@zenfs/core 1.2.7 → 1.2.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/backends/backend.d.ts +2 -1
- package/dist/backends/fetch.d.ts +10 -3
- package/dist/backends/fetch.js +9 -15
- package/dist/backends/memory.d.ts +0 -1
- package/dist/backends/memory.js +1 -5
- package/dist/backends/port/fs.d.ts +0 -2
- package/dist/backends/port/fs.js +0 -2
- package/dist/backends/store/fs.js +10 -10
- package/dist/config.js +32 -9
- package/dist/credentials.d.ts +3 -0
- package/dist/credentials.js +3 -0
- package/dist/devices.d.ts +50 -13
- package/dist/devices.js +32 -6
- package/dist/emulation/promises.js +3 -13
- package/dist/emulation/shared.d.ts +4 -1
- package/dist/emulation/shared.js +5 -3
- package/dist/emulation/sync.js +3 -14
- package/dist/mixins/async.js +2 -2
- package/package.json +2 -1
- package/readme.md +4 -0
- package/src/backends/backend.ts +2 -1
- package/src/backends/fetch.ts +18 -18
- package/src/backends/memory.ts +1 -5
- package/src/backends/port/fs.ts +0 -2
- package/src/backends/store/fs.ts +18 -18
- package/src/config.ts +33 -10
- package/src/credentials.ts +3 -0
- package/src/devices.ts +75 -12
- package/src/emulation/promises.ts +3 -14
- package/src/emulation/shared.ts +5 -3
- package/src/emulation/sync.ts +3 -15
- package/src/mixins/async.ts +2 -2
- package/tests/fs/directory.test.ts +83 -1
- package/tests/fs/permissions.test.ts +45 -2
- package/tests/mounts.test.ts +18 -0
- package/tests/fs/chmod.test.ts +0 -44
- package/tests/fs/readdir.test.ts +0 -87
|
@@ -12,8 +12,9 @@ export type OptionsConfig<T> = {
|
|
|
12
12
|
type: OptionType | readonly OptionType[];
|
|
13
13
|
/**
|
|
14
14
|
* Description of the option. Used in error messages and documentation.
|
|
15
|
+
* @deprecated
|
|
15
16
|
*/
|
|
16
|
-
description
|
|
17
|
+
description?: string;
|
|
17
18
|
/**
|
|
18
19
|
* Whether or not the option is required (optional can be set to null or undefined). Defaults to false.
|
|
19
20
|
*/
|
package/dist/backends/fetch.d.ts
CHANGED
|
@@ -6,6 +6,10 @@ import type { IndexData } from './file_index.js';
|
|
|
6
6
|
* Configuration options for FetchFS.
|
|
7
7
|
*/
|
|
8
8
|
export interface FetchOptions {
|
|
9
|
+
/**
|
|
10
|
+
* Options to pass through to fetch calls
|
|
11
|
+
*/
|
|
12
|
+
requestInit?: RequestInit;
|
|
9
13
|
/**
|
|
10
14
|
* URL to a file index as a JSON file or the file index object itself.
|
|
11
15
|
* Defaults to `index.json`.
|
|
@@ -37,8 +41,9 @@ export interface FetchOptions {
|
|
|
37
41
|
*/
|
|
38
42
|
export declare class FetchFS extends IndexFS {
|
|
39
43
|
readonly baseUrl: string;
|
|
44
|
+
readonly requestInit?: RequestInit;
|
|
40
45
|
ready(): Promise<void>;
|
|
41
|
-
constructor({ index, baseUrl }: FetchOptions);
|
|
46
|
+
constructor({ index, baseUrl, requestInit }: FetchOptions);
|
|
42
47
|
metadata(): FileSystemMetadata;
|
|
43
48
|
/**
|
|
44
49
|
* Preload the `path` into the index.
|
|
@@ -56,12 +61,14 @@ declare const _Fetch: {
|
|
|
56
61
|
readonly index: {
|
|
57
62
|
readonly type: readonly ["string", "object"];
|
|
58
63
|
readonly required: false;
|
|
59
|
-
readonly description: "URL to a file index as a JSON file or the file index object itself, generated with the make-index script. Defaults to `index.json`.";
|
|
60
64
|
};
|
|
61
65
|
readonly baseUrl: {
|
|
62
66
|
readonly type: "string";
|
|
63
67
|
readonly required: false;
|
|
64
|
-
|
|
68
|
+
};
|
|
69
|
+
readonly requestInit: {
|
|
70
|
+
readonly type: "object";
|
|
71
|
+
readonly required: false;
|
|
65
72
|
};
|
|
66
73
|
};
|
|
67
74
|
readonly isAvailable: () => boolean;
|
package/dist/backends/fetch.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Errno, ErrnoError } from '../error.js';
|
|
2
2
|
import { IndexFS } from './file_index.js';
|
|
3
|
-
async function fetchFile(path, type) {
|
|
4
|
-
const response = await fetch(path).catch((e) => {
|
|
3
|
+
async function fetchFile(path, type, init) {
|
|
4
|
+
const response = await fetch(path, init).catch((e) => {
|
|
5
5
|
throw new ErrnoError(Errno.EIO, e.message, path);
|
|
6
6
|
});
|
|
7
7
|
if (!response.ok) {
|
|
@@ -57,13 +57,14 @@ export class FetchFS extends IndexFS {
|
|
|
57
57
|
await this.getData(path, stats);
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
|
-
constructor({ index = 'index.json', baseUrl = '' }) {
|
|
60
|
+
constructor({ index = 'index.json', baseUrl = '', requestInit }) {
|
|
61
61
|
// prefix url must end in a directory separator.
|
|
62
62
|
if (baseUrl.at(-1) != '/') {
|
|
63
63
|
baseUrl += '/';
|
|
64
64
|
}
|
|
65
|
-
super(typeof index != 'string' ? index : fetchFile(baseUrl + index, 'json'));
|
|
65
|
+
super(typeof index != 'string' ? index : fetchFile(baseUrl + index, 'json', requestInit));
|
|
66
66
|
this.baseUrl = baseUrl;
|
|
67
|
+
this.requestInit = requestInit;
|
|
67
68
|
}
|
|
68
69
|
metadata() {
|
|
69
70
|
return {
|
|
@@ -93,7 +94,7 @@ export class FetchFS extends IndexFS {
|
|
|
93
94
|
if (stats.fileData) {
|
|
94
95
|
return stats.fileData;
|
|
95
96
|
}
|
|
96
|
-
const data = await fetchFile(this.baseUrl + (path.startsWith('/') ? path.slice(1) : path), 'buffer');
|
|
97
|
+
const data = await fetchFile(this.baseUrl + (path.startsWith('/') ? path.slice(1) : path), 'buffer', this.requestInit);
|
|
97
98
|
stats.fileData = data;
|
|
98
99
|
return data;
|
|
99
100
|
}
|
|
@@ -107,16 +108,9 @@ export class FetchFS extends IndexFS {
|
|
|
107
108
|
const _Fetch = {
|
|
108
109
|
name: 'Fetch',
|
|
109
110
|
options: {
|
|
110
|
-
index: {
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
description: 'URL to a file index as a JSON file or the file index object itself, generated with the make-index script. Defaults to `index.json`.',
|
|
114
|
-
},
|
|
115
|
-
baseUrl: {
|
|
116
|
-
type: 'string',
|
|
117
|
-
required: false,
|
|
118
|
-
description: 'Used as the URL prefix for fetched files. Default: Fetch files relative to the index.',
|
|
119
|
-
},
|
|
111
|
+
index: { type: ['string', 'object'], required: false },
|
|
112
|
+
baseUrl: { type: 'string', required: false },
|
|
113
|
+
requestInit: { type: 'object', required: false },
|
|
120
114
|
},
|
|
121
115
|
isAvailable() {
|
|
122
116
|
return typeof globalThis.fetch == 'function';
|
package/dist/backends/memory.js
CHANGED
|
@@ -26,11 +26,7 @@ const _InMemory = {
|
|
|
26
26
|
return true;
|
|
27
27
|
},
|
|
28
28
|
options: {
|
|
29
|
-
name: {
|
|
30
|
-
type: 'string',
|
|
31
|
-
required: false,
|
|
32
|
-
description: 'The name of the store',
|
|
33
|
-
},
|
|
29
|
+
name: { type: 'string', required: false },
|
|
34
30
|
},
|
|
35
31
|
create({ name }) {
|
|
36
32
|
const fs = new StoreFS(new InMemoryStore(name));
|
|
@@ -110,13 +110,11 @@ declare const _Port: {
|
|
|
110
110
|
port: {
|
|
111
111
|
type: "object";
|
|
112
112
|
required: true;
|
|
113
|
-
description: string;
|
|
114
113
|
validator(port: RPC.Port): void;
|
|
115
114
|
};
|
|
116
115
|
timeout: {
|
|
117
116
|
type: "number";
|
|
118
117
|
required: false;
|
|
119
|
-
description: string;
|
|
120
118
|
};
|
|
121
119
|
};
|
|
122
120
|
isAvailable(): boolean;
|
package/dist/backends/port/fs.js
CHANGED
|
@@ -214,7 +214,6 @@ const _Port = {
|
|
|
214
214
|
port: {
|
|
215
215
|
type: 'object',
|
|
216
216
|
required: true,
|
|
217
|
-
description: 'The target port that you want to connect to',
|
|
218
217
|
validator(port) {
|
|
219
218
|
// Check for a `postMessage` function.
|
|
220
219
|
if (typeof port?.postMessage != 'function') {
|
|
@@ -225,7 +224,6 @@ const _Port = {
|
|
|
225
224
|
timeout: {
|
|
226
225
|
type: 'number',
|
|
227
226
|
required: false,
|
|
228
|
-
description: 'How long to wait before the request times out',
|
|
229
227
|
},
|
|
230
228
|
},
|
|
231
229
|
isAvailable() {
|
|
@@ -642,17 +642,17 @@ export class StoreFS extends FileSystem {
|
|
|
642
642
|
async commitNew(path, type, mode, data) {
|
|
643
643
|
const env_15 = { stack: [], error: void 0, hasError: false };
|
|
644
644
|
try {
|
|
645
|
-
const tx = __addDisposableResource(env_15, this.store.transaction(), true);
|
|
646
|
-
const parentPath = dirname(path), parent = await this.findINode(tx, parentPath);
|
|
647
|
-
const fname = basename(path), listing = await this.getDirListing(tx, parent, parentPath);
|
|
648
645
|
/*
|
|
649
646
|
The root always exists.
|
|
650
647
|
If we don't check this prior to taking steps below,
|
|
651
|
-
we will create a file with name '' in root
|
|
648
|
+
we will create a file with name '' in root if path is '/'.
|
|
652
649
|
*/
|
|
653
|
-
if (path
|
|
650
|
+
if (path == '/') {
|
|
654
651
|
throw ErrnoError.With('EEXIST', path, 'commitNew');
|
|
655
652
|
}
|
|
653
|
+
const tx = __addDisposableResource(env_15, this.store.transaction(), true);
|
|
654
|
+
const parentPath = dirname(path), parent = await this.findINode(tx, parentPath);
|
|
655
|
+
const fname = basename(path), listing = await this.getDirListing(tx, parent, parentPath);
|
|
656
656
|
// Check if file already exists.
|
|
657
657
|
if (listing[fname]) {
|
|
658
658
|
await tx.abort();
|
|
@@ -693,17 +693,17 @@ export class StoreFS extends FileSystem {
|
|
|
693
693
|
commitNewSync(path, type, mode, data = new Uint8Array()) {
|
|
694
694
|
const env_16 = { stack: [], error: void 0, hasError: false };
|
|
695
695
|
try {
|
|
696
|
-
const tx = __addDisposableResource(env_16, this.store.transaction(), false);
|
|
697
|
-
const parentPath = dirname(path), parent = this.findINodeSync(tx, parentPath);
|
|
698
|
-
const fname = basename(path), listing = this.getDirListingSync(tx, parent, parentPath);
|
|
699
696
|
/*
|
|
700
697
|
The root always exists.
|
|
701
698
|
If we don't check this prior to taking steps below,
|
|
702
|
-
we will create a file with name '' in root
|
|
699
|
+
we will create a file with name '' in root if path is '/'.
|
|
703
700
|
*/
|
|
704
|
-
if (path
|
|
701
|
+
if (path == '/') {
|
|
705
702
|
throw ErrnoError.With('EEXIST', path, 'commitNew');
|
|
706
703
|
}
|
|
704
|
+
const tx = __addDisposableResource(env_16, this.store.transaction(), false);
|
|
705
|
+
const parentPath = dirname(path), parent = this.findINodeSync(tx, parentPath);
|
|
706
|
+
const fname = basename(path), listing = this.getDirListingSync(tx, parent, parentPath);
|
|
707
707
|
// Check if file already exists.
|
|
708
708
|
if (listing[fname]) {
|
|
709
709
|
throw ErrnoError.With('EEXIST', path, 'commitNew');
|
package/dist/config.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { checkOptions, isBackend, isBackendConfig } from './backends/backend.js';
|
|
2
2
|
import { credentials } from './credentials.js';
|
|
3
|
-
import { DeviceFS
|
|
3
|
+
import { DeviceFS } from './devices.js';
|
|
4
4
|
import * as cache from './emulation/cache.js';
|
|
5
|
-
import * as fs from './emulation/index.js';
|
|
6
5
|
import { config } from './emulation/config.js';
|
|
6
|
+
import * as fs from './emulation/index.js';
|
|
7
7
|
import { Errno, ErrnoError } from './error.js';
|
|
8
8
|
import { FileSystem } from './filesystem.js';
|
|
9
9
|
function isMountConfig(arg) {
|
|
@@ -60,6 +60,26 @@ export async function configureSingle(configuration) {
|
|
|
60
60
|
fs.umount('/');
|
|
61
61
|
fs.mount('/', resolved);
|
|
62
62
|
}
|
|
63
|
+
/**
|
|
64
|
+
* Like `fs.mount`, but it also creates missing directories.
|
|
65
|
+
* @privateRemarks
|
|
66
|
+
* This is implemented as a separate function to avoid a circular dependency between emulation/shared.ts and other emulation layer files.
|
|
67
|
+
* @internal
|
|
68
|
+
*/
|
|
69
|
+
async function mount(path, mount) {
|
|
70
|
+
if (path == '/') {
|
|
71
|
+
fs.mount(path, mount);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const stats = await fs.promises.stat(path).catch(() => null);
|
|
75
|
+
if (!stats) {
|
|
76
|
+
await fs.promises.mkdir(path, { recursive: true });
|
|
77
|
+
}
|
|
78
|
+
else if (!stats.isDirectory()) {
|
|
79
|
+
throw ErrnoError.With('ENOTDIR', path, 'configure');
|
|
80
|
+
}
|
|
81
|
+
fs.mount(path, mount);
|
|
82
|
+
}
|
|
63
83
|
/**
|
|
64
84
|
* Configures ZenFS with `configuration`
|
|
65
85
|
* @see Configuration
|
|
@@ -74,16 +94,15 @@ export async function configure(configuration) {
|
|
|
74
94
|
config.syncImmediately = !configuration.onlySyncOnClose;
|
|
75
95
|
if (configuration.addDevices) {
|
|
76
96
|
const devfs = new DeviceFS();
|
|
77
|
-
devfs.
|
|
78
|
-
devfs.createDevice('/zero', zeroDevice);
|
|
79
|
-
devfs.createDevice('/full', fullDevice);
|
|
80
|
-
devfs.createDevice('/random', randomDevice);
|
|
97
|
+
devfs.addDefaults();
|
|
81
98
|
await devfs.ready();
|
|
82
|
-
|
|
99
|
+
await mount('/dev', devfs);
|
|
83
100
|
}
|
|
84
101
|
if (!configuration.mounts) {
|
|
85
102
|
return;
|
|
86
103
|
}
|
|
104
|
+
const toMount = [];
|
|
105
|
+
let unmountRoot = false;
|
|
87
106
|
for (const [point, mountConfig] of Object.entries(configuration.mounts)) {
|
|
88
107
|
if (!point.startsWith('/')) {
|
|
89
108
|
throw new ErrnoError(Errno.EINVAL, 'Mount points must have absolute paths');
|
|
@@ -91,7 +110,11 @@ export async function configure(configuration) {
|
|
|
91
110
|
if (isBackendConfig(mountConfig)) {
|
|
92
111
|
mountConfig.disableAsyncCache ?? (mountConfig.disableAsyncCache = configuration.disableAsyncCache || false);
|
|
93
112
|
}
|
|
94
|
-
|
|
113
|
+
if (point == '/')
|
|
114
|
+
unmountRoot = true;
|
|
115
|
+
toMount.push([point, await resolveMountConfig(mountConfig)]);
|
|
95
116
|
}
|
|
96
|
-
|
|
117
|
+
if (unmountRoot)
|
|
118
|
+
fs.umount('/');
|
|
119
|
+
await Promise.all(toMount.map(([point, fs]) => mount(point, fs)));
|
|
97
120
|
}
|
package/dist/credentials.d.ts
CHANGED
package/dist/credentials.js
CHANGED
package/dist/devices.d.ts
CHANGED
|
@@ -8,9 +8,10 @@ import type { Ino } from './inode.js';
|
|
|
8
8
|
/**
|
|
9
9
|
* A device
|
|
10
10
|
* @todo Maybe add major/minor number or some other device information, like a UUID?
|
|
11
|
-
* @
|
|
11
|
+
* @privateRemarks
|
|
12
|
+
* UUIDs were considered, however they don't make sense without an easy mechanism for persistance
|
|
12
13
|
*/
|
|
13
|
-
export interface Device {
|
|
14
|
+
export interface Device<TData = any> {
|
|
14
15
|
/**
|
|
15
16
|
* The device's driver
|
|
16
17
|
*/
|
|
@@ -19,34 +20,64 @@ export interface Device {
|
|
|
19
20
|
* Which inode the device is assigned
|
|
20
21
|
*/
|
|
21
22
|
ino: Ino;
|
|
23
|
+
/**
|
|
24
|
+
* Data associated with a device.
|
|
25
|
+
* This is meant to be used by device drivers.
|
|
26
|
+
* @experimental
|
|
27
|
+
*/
|
|
28
|
+
data: TData;
|
|
29
|
+
/**
|
|
30
|
+
* Major device number
|
|
31
|
+
* @experimental
|
|
32
|
+
*/
|
|
33
|
+
major: number;
|
|
34
|
+
/**
|
|
35
|
+
* Minor device number
|
|
36
|
+
* @experimental
|
|
37
|
+
*/
|
|
38
|
+
minor: number;
|
|
22
39
|
}
|
|
23
40
|
/**
|
|
24
41
|
* A device driver
|
|
25
|
-
* @experimental
|
|
26
42
|
*/
|
|
27
|
-
export interface DeviceDriver {
|
|
43
|
+
export interface DeviceDriver<TData = any> {
|
|
28
44
|
/**
|
|
29
45
|
* The name of the device driver
|
|
30
46
|
*/
|
|
31
47
|
name: string;
|
|
32
48
|
/**
|
|
33
49
|
* Whether the device is buffered (a "block" device) or unbuffered (a "character" device)
|
|
50
|
+
* @default false
|
|
34
51
|
*/
|
|
35
|
-
isBuffered
|
|
52
|
+
isBuffered?: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* Initializes a new device.
|
|
55
|
+
* @returns `Device.data`
|
|
56
|
+
* @experimental
|
|
57
|
+
*/
|
|
58
|
+
init?(ino: Ino): {
|
|
59
|
+
data?: TData;
|
|
60
|
+
minor?: number;
|
|
61
|
+
major?: number;
|
|
62
|
+
};
|
|
36
63
|
/**
|
|
37
64
|
* Synchronously read from the device
|
|
65
|
+
* @group File operations
|
|
38
66
|
*/
|
|
39
67
|
read(file: DeviceFile, buffer: ArrayBufferView, offset?: number, length?: number, position?: number): number;
|
|
40
68
|
/**
|
|
41
69
|
* Synchronously write to the device
|
|
70
|
+
* @group File operations
|
|
42
71
|
*/
|
|
43
72
|
write(file: DeviceFile, buffer: Uint8Array, offset: number, length: number, position?: number): number;
|
|
44
73
|
/**
|
|
45
74
|
* Sync the device
|
|
75
|
+
* @group File operations
|
|
46
76
|
*/
|
|
47
77
|
sync?(file: DeviceFile): void;
|
|
48
78
|
/**
|
|
49
79
|
* Close the device
|
|
80
|
+
* @group File operations
|
|
50
81
|
*/
|
|
51
82
|
close?(file: DeviceFile): void;
|
|
52
83
|
}
|
|
@@ -55,7 +86,6 @@ export interface DeviceDriver {
|
|
|
55
86
|
* This class only does some simple things:
|
|
56
87
|
* It implements `truncate` using `write` and it has non-device methods throw.
|
|
57
88
|
* It is up to device drivers to implement the rest of the functionality.
|
|
58
|
-
* @experimental
|
|
59
89
|
*/
|
|
60
90
|
export declare class DeviceFile extends File {
|
|
61
91
|
fs: DeviceFS;
|
|
@@ -86,11 +116,18 @@ export declare class DeviceFile extends File {
|
|
|
86
116
|
_setTypeSync(): void;
|
|
87
117
|
}
|
|
88
118
|
/**
|
|
89
|
-
*
|
|
119
|
+
* A temporary file system that manages and interfaces with devices
|
|
90
120
|
*/
|
|
91
121
|
export declare class DeviceFS extends StoreFS<InMemoryStore> {
|
|
92
|
-
protected readonly devices: Map<string, Device
|
|
93
|
-
|
|
122
|
+
protected readonly devices: Map<string, Device<any>>;
|
|
123
|
+
/**
|
|
124
|
+
* Creates a new device at `path` relative to the `DeviceFS` root.
|
|
125
|
+
*/
|
|
126
|
+
createDevice<TData = any>(path: string, driver: DeviceDriver<TData>): Device<TData | Record<string, never>>;
|
|
127
|
+
/**
|
|
128
|
+
* Adds default devices
|
|
129
|
+
*/
|
|
130
|
+
addDefaults(): void;
|
|
94
131
|
constructor();
|
|
95
132
|
rename(oldPath: string, newPath: string): Promise<void>;
|
|
96
133
|
renameSync(oldPath: string, newPath: string): void;
|
|
@@ -150,9 +187,9 @@ export declare const randomDevice: DeviceDriver;
|
|
|
150
187
|
* @experimental
|
|
151
188
|
*/
|
|
152
189
|
declare const _default: {
|
|
153
|
-
null: DeviceDriver
|
|
154
|
-
zero: DeviceDriver
|
|
155
|
-
full: DeviceDriver
|
|
156
|
-
random: DeviceDriver
|
|
190
|
+
null: DeviceDriver<any>;
|
|
191
|
+
zero: DeviceDriver<any>;
|
|
192
|
+
full: DeviceDriver<any>;
|
|
193
|
+
random: DeviceDriver<any>;
|
|
157
194
|
};
|
|
158
195
|
export default _default;
|
package/dist/devices.js
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
/*
|
|
2
|
+
This is a great resource: https://www.kernel.org/doc/html/latest/admin-guide/devices.html
|
|
3
|
+
*/
|
|
1
4
|
var __addDisposableResource = (this && this.__addDisposableResource) || function (env, value, async) {
|
|
2
5
|
if (value !== null && value !== void 0) {
|
|
3
6
|
if (typeof value !== "object" && typeof value !== "function") throw new TypeError("Object expected.");
|
|
@@ -57,7 +60,6 @@ import { basename, dirname } from './emulation/path.js';
|
|
|
57
60
|
* This class only does some simple things:
|
|
58
61
|
* It implements `truncate` using `write` and it has non-device methods throw.
|
|
59
62
|
* It is up to device drivers to implement the rest of the functionality.
|
|
60
|
-
* @experimental
|
|
61
63
|
*/
|
|
62
64
|
export class DeviceFile extends File {
|
|
63
65
|
constructor(fs, path, device) {
|
|
@@ -142,9 +144,12 @@ export class DeviceFile extends File {
|
|
|
142
144
|
}
|
|
143
145
|
}
|
|
144
146
|
/**
|
|
145
|
-
*
|
|
147
|
+
* A temporary file system that manages and interfaces with devices
|
|
146
148
|
*/
|
|
147
149
|
export class DeviceFS extends StoreFS {
|
|
150
|
+
/**
|
|
151
|
+
* Creates a new device at `path` relative to the `DeviceFS` root.
|
|
152
|
+
*/
|
|
148
153
|
createDevice(path, driver) {
|
|
149
154
|
if (this.existsSync(path)) {
|
|
150
155
|
throw ErrnoError.With('EEXIST', path, 'mknod');
|
|
@@ -155,10 +160,23 @@ export class DeviceFS extends StoreFS {
|
|
|
155
160
|
const dev = {
|
|
156
161
|
driver,
|
|
157
162
|
ino,
|
|
163
|
+
data: {},
|
|
164
|
+
minor: 0,
|
|
165
|
+
major: 0,
|
|
166
|
+
...driver.init?.(ino),
|
|
158
167
|
};
|
|
159
168
|
this.devices.set(path, dev);
|
|
160
169
|
return dev;
|
|
161
170
|
}
|
|
171
|
+
/**
|
|
172
|
+
* Adds default devices
|
|
173
|
+
*/
|
|
174
|
+
addDefaults() {
|
|
175
|
+
this.createDevice('/null', nullDevice);
|
|
176
|
+
this.createDevice('/zero', zeroDevice);
|
|
177
|
+
this.createDevice('/full', fullDevice);
|
|
178
|
+
this.createDevice('/random', randomDevice);
|
|
179
|
+
}
|
|
162
180
|
constructor() {
|
|
163
181
|
super(new InMemoryStore('devfs'));
|
|
164
182
|
this.devices = new Map();
|
|
@@ -332,7 +350,9 @@ function defaultWrite(file, buffer, offset, length) {
|
|
|
332
350
|
*/
|
|
333
351
|
export const nullDevice = {
|
|
334
352
|
name: 'null',
|
|
335
|
-
|
|
353
|
+
init() {
|
|
354
|
+
return { major: 1, minor: 3 };
|
|
355
|
+
},
|
|
336
356
|
read() {
|
|
337
357
|
return 0;
|
|
338
358
|
},
|
|
@@ -350,7 +370,9 @@ export const nullDevice = {
|
|
|
350
370
|
*/
|
|
351
371
|
export const zeroDevice = {
|
|
352
372
|
name: 'zero',
|
|
353
|
-
|
|
373
|
+
init() {
|
|
374
|
+
return { major: 1, minor: 5 };
|
|
375
|
+
},
|
|
354
376
|
read(file, buffer, offset = 0, length = buffer.byteLength) {
|
|
355
377
|
const data = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
356
378
|
for (let i = offset; i < offset + length; i++) {
|
|
@@ -369,7 +391,9 @@ export const zeroDevice = {
|
|
|
369
391
|
*/
|
|
370
392
|
export const fullDevice = {
|
|
371
393
|
name: 'full',
|
|
372
|
-
|
|
394
|
+
init() {
|
|
395
|
+
return { major: 1, minor: 7 };
|
|
396
|
+
},
|
|
373
397
|
read(file, buffer, offset = 0, length = buffer.byteLength) {
|
|
374
398
|
const data = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
375
399
|
for (let i = offset; i < offset + length; i++) {
|
|
@@ -390,7 +414,9 @@ export const fullDevice = {
|
|
|
390
414
|
*/
|
|
391
415
|
export const randomDevice = {
|
|
392
416
|
name: 'random',
|
|
393
|
-
|
|
417
|
+
init() {
|
|
418
|
+
return { major: 1, minor: 8 };
|
|
419
|
+
},
|
|
394
420
|
read(file, buffer, offset = 0, length = buffer.byteLength) {
|
|
395
421
|
const data = new Uint8Array(buffer.buffer, buffer.byteOffset, buffer.byteLength);
|
|
396
422
|
for (let i = offset; i < offset + length; i++) {
|
|
@@ -53,11 +53,11 @@ import '../polyfills.js';
|
|
|
53
53
|
import { BigIntStats } from '../stats.js';
|
|
54
54
|
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
55
55
|
import * as cache from './cache.js';
|
|
56
|
+
import { config } from './config.js';
|
|
56
57
|
import * as constants from './constants.js';
|
|
57
58
|
import { Dir, Dirent } from './dir.js';
|
|
58
59
|
import { dirname, join, parse } from './path.js';
|
|
59
|
-
import { _statfs, fd2file, fdMap, file2fd, fixError,
|
|
60
|
-
import { config } from './config.js';
|
|
60
|
+
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
|
|
61
61
|
import { ReadStream, WriteStream } from './streams.js';
|
|
62
62
|
import { FSWatcher, emitChange } from './watchers.js';
|
|
63
63
|
export * as constants from './constants.js';
|
|
@@ -585,7 +585,7 @@ export async function rmdir(path) {
|
|
|
585
585
|
try {
|
|
586
586
|
const stats = await (cache.getStats(path) || fs.stat(resolved));
|
|
587
587
|
if (!stats) {
|
|
588
|
-
throw ErrnoError.With('ENOENT', path, '
|
|
588
|
+
throw ErrnoError.With('ENOENT', path, 'rmdir');
|
|
589
589
|
}
|
|
590
590
|
if (!stats.isDirectory()) {
|
|
591
591
|
throw ErrnoError.With('ENOTDIR', resolved, 'rmdir');
|
|
@@ -655,16 +655,6 @@ export async function readdir(path, options) {
|
|
|
655
655
|
throw ErrnoError.With('ENOTDIR', path, 'readdir');
|
|
656
656
|
}
|
|
657
657
|
const entries = await fs.readdir(resolved).catch(handleError);
|
|
658
|
-
for (const point of mounts.keys()) {
|
|
659
|
-
if (point.startsWith(path)) {
|
|
660
|
-
const entry = point.slice(path.length);
|
|
661
|
-
if (entry.includes('/') || entry.length == 0) {
|
|
662
|
-
// ignore FSs mounted in subdirectories and any FS mounted to `path`.
|
|
663
|
-
continue;
|
|
664
|
-
}
|
|
665
|
-
entries.push(entry);
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
658
|
const values = [];
|
|
669
659
|
const addEntry = async (entry) => {
|
|
670
660
|
let entryStats;
|
|
@@ -21,7 +21,7 @@ export declare function mount(mountPoint: string, fs: FileSystem): void;
|
|
|
21
21
|
*/
|
|
22
22
|
export declare function umount(mountPoint: string): void;
|
|
23
23
|
/**
|
|
24
|
-
* Gets the internal FileSystem for the path, then returns it along with the path relative to the FS' root
|
|
24
|
+
* Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
|
|
25
25
|
*/
|
|
26
26
|
export declare function resolveMount(path: string): {
|
|
27
27
|
fs: FileSystem;
|
|
@@ -38,6 +38,9 @@ export declare function fixPaths(text: string, paths: Record<string, string>): s
|
|
|
38
38
|
* @hidden
|
|
39
39
|
*/
|
|
40
40
|
export declare function fixError<E extends ErrnoError>(e: E, paths: Record<string, string>): E;
|
|
41
|
+
/**
|
|
42
|
+
* @deprecated
|
|
43
|
+
*/
|
|
41
44
|
export declare function mountObject(mounts: MountObject): void;
|
|
42
45
|
/**
|
|
43
46
|
* @hidden
|
package/dist/emulation/shared.js
CHANGED
|
@@ -43,7 +43,7 @@ export function mount(mountPoint, fs) {
|
|
|
43
43
|
*/
|
|
44
44
|
export function umount(mountPoint) {
|
|
45
45
|
if (mountPoint[0] !== '/') {
|
|
46
|
-
mountPoint =
|
|
46
|
+
mountPoint = '/' + mountPoint;
|
|
47
47
|
}
|
|
48
48
|
mountPoint = resolve(mountPoint);
|
|
49
49
|
if (!mounts.has(mountPoint)) {
|
|
@@ -52,11 +52,10 @@ export function umount(mountPoint) {
|
|
|
52
52
|
mounts.delete(mountPoint);
|
|
53
53
|
}
|
|
54
54
|
/**
|
|
55
|
-
* Gets the internal FileSystem for the path, then returns it along with the path relative to the FS' root
|
|
55
|
+
* Gets the internal `FileSystem` for the path, then returns it along with the path relative to the FS' root
|
|
56
56
|
*/
|
|
57
57
|
export function resolveMount(path) {
|
|
58
58
|
path = normalizePath(path);
|
|
59
|
-
// Maybe do something for devices here
|
|
60
59
|
const sortedMounts = [...mounts].sort((a, b) => (a[0].length > b[0].length ? -1 : 1)); // descending order of the string length
|
|
61
60
|
for (const [mountPoint, fs] of sortedMounts) {
|
|
62
61
|
// We know path is normalized, so it would be a substring of the mount point.
|
|
@@ -91,6 +90,9 @@ export function fixError(e, paths) {
|
|
|
91
90
|
e.message = fixPaths(e.message, paths);
|
|
92
91
|
return e;
|
|
93
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* @deprecated
|
|
95
|
+
*/
|
|
94
96
|
export function mountObject(mounts) {
|
|
95
97
|
if ('/' in mounts) {
|
|
96
98
|
umount('/');
|
package/dist/emulation/sync.js
CHANGED
|
@@ -50,13 +50,13 @@ import { Errno, ErrnoError } from '../error.js';
|
|
|
50
50
|
import { flagToMode, isAppendable, isExclusive, isReadable, isTruncating, isWriteable, parseFlag } from '../file.js';
|
|
51
51
|
import { BigIntStats } from '../stats.js';
|
|
52
52
|
import { decodeUTF8, normalizeMode, normalizeOptions, normalizePath, normalizeTime } from '../utils.js';
|
|
53
|
+
import * as cache from './cache.js';
|
|
54
|
+
import { config } from './config.js';
|
|
53
55
|
import * as constants from './constants.js';
|
|
54
56
|
import { Dir, Dirent } from './dir.js';
|
|
55
57
|
import { dirname, join, parse } from './path.js';
|
|
56
|
-
import { _statfs, fd2file, fdMap, file2fd, fixError,
|
|
57
|
-
import { config } from './config.js';
|
|
58
|
+
import { _statfs, fd2file, fdMap, file2fd, fixError, resolveMount } from './shared.js';
|
|
58
59
|
import { emitChange } from './watchers.js';
|
|
59
|
-
import * as cache from './cache.js';
|
|
60
60
|
export function renameSync(oldPath, newPath) {
|
|
61
61
|
oldPath = normalizePath(oldPath);
|
|
62
62
|
newPath = normalizePath(newPath);
|
|
@@ -459,17 +459,6 @@ export function readdirSync(path, options) {
|
|
|
459
459
|
catch (e) {
|
|
460
460
|
throw fixError(e, { [resolved]: path });
|
|
461
461
|
}
|
|
462
|
-
for (const mount of mounts.keys()) {
|
|
463
|
-
if (!mount.startsWith(path)) {
|
|
464
|
-
continue;
|
|
465
|
-
}
|
|
466
|
-
const entry = mount.slice(path.length);
|
|
467
|
-
if (entry.includes('/') || entry.length == 0) {
|
|
468
|
-
// ignore FSs mounted in subdirectories and any FS mounted to `path`.
|
|
469
|
-
continue;
|
|
470
|
-
}
|
|
471
|
-
entries.push(entry);
|
|
472
|
-
}
|
|
473
462
|
// Iterate over entries and handle recursive case if needed
|
|
474
463
|
const values = [];
|
|
475
464
|
for (const entry of entries) {
|
package/dist/mixins/async.js
CHANGED
|
@@ -90,8 +90,8 @@ export function Async(FS) {
|
|
|
90
90
|
const sync = this._sync['store'].transaction();
|
|
91
91
|
const async = this['store'].transaction();
|
|
92
92
|
const promises = [];
|
|
93
|
-
for (const key of
|
|
94
|
-
promises.push(async.
|
|
93
|
+
for (const key of await async.keys()) {
|
|
94
|
+
promises.push(async.get(key).then(data => sync.setSync(key, data)));
|
|
95
95
|
}
|
|
96
96
|
await Promise.all(promises);
|
|
97
97
|
this._isInitialized = true;
|