@bytecodealliance/preview2-shim 0.0.19 → 0.0.21
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/LICENSE +220 -0
- package/README.md +4 -6
- package/lib/browser/cli.js +79 -12
- package/lib/browser/filesystem.js +168 -198
- package/lib/browser/index.js +1 -5
- package/lib/browser/io.js +173 -29
- package/lib/common/io.js +6 -4
- package/lib/{http → common}/make-request.js +1 -1
- package/lib/nodejs/cli.js +5 -4
- package/lib/nodejs/filesystem.js +106 -73
- package/lib/nodejs/http.js +309 -3
- package/lib/nodejs/index.js +0 -5
- package/package.json +1 -1
- package/types/interfaces/wasi-cli-stderr.d.ts +1 -1
- package/types/interfaces/wasi-cli-stdin.d.ts +1 -1
- package/types/interfaces/wasi-cli-stdout.d.ts +1 -1
- package/types/interfaces/wasi-cli-terminal-stderr.d.ts +1 -1
- package/types/interfaces/wasi-cli-terminal-stdin.d.ts +1 -1
- package/types/interfaces/wasi-cli-terminal-stdout.d.ts +1 -1
- package/types/interfaces/wasi-clocks-monotonic-clock.d.ts +1 -1
- package/types/interfaces/wasi-clocks-timezone.d.ts +1 -1
- package/types/interfaces/wasi-filesystem-preopens.d.ts +1 -1
- package/types/interfaces/wasi-filesystem-types.d.ts +8 -8
- package/types/interfaces/wasi-http-incoming-handler.d.ts +19 -0
- package/types/interfaces/wasi-http-outgoing-handler.d.ts +23 -0
- package/types/interfaces/wasi-http-types.d.ts +371 -0
- package/types/interfaces/wasi-io-streams.d.ts +13 -21
- package/types/interfaces/wasi-sockets-instance-network.d.ts +1 -1
- package/types/interfaces/wasi-sockets-ip-name-lookup.d.ts +5 -5
- package/types/interfaces/wasi-sockets-tcp-create-socket.d.ts +4 -4
- package/types/interfaces/wasi-sockets-tcp.d.ts +7 -7
- package/types/interfaces/wasi-sockets-udp-create-socket.d.ts +4 -4
- package/types/interfaces/wasi-sockets-udp.d.ts +5 -5
- package/types/wasi-cli-command.d.ts +3 -3
- package/types/wasi-http-proxy.d.ts +13 -0
- package/lib/browser/poll.js +0 -53
- package/lib/http/wasi-http.js +0 -382
- package/lib/nodejs/poll.js +0 -9
- /package/lib/{http/synckit → synckit}/index.d.ts +0 -0
- /package/lib/{http/synckit → synckit}/index.js +0 -0
package/lib/browser/io.js
CHANGED
|
@@ -1,39 +1,183 @@
|
|
|
1
|
-
|
|
1
|
+
let id = 0;
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
const symbolDispose = Symbol.dispose || Symbol.for('dispose');
|
|
4
|
+
|
|
5
|
+
class Error {
|
|
6
|
+
constructor (msg) {
|
|
7
|
+
this.msg = msg;
|
|
8
|
+
}
|
|
9
|
+
toDebugString () {
|
|
10
|
+
return this.msg;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @typedef {{
|
|
16
|
+
* read?: (len: BigInt) => Uint8Array,
|
|
17
|
+
* blockingRead: (len: BigInt) => Uint8Array,
|
|
18
|
+
* skip?: (len: BigInt) => BigInt,
|
|
19
|
+
* blockingSkip?: (len: BigInt) => BigInt,
|
|
20
|
+
* subscribe: () => void,
|
|
21
|
+
* drop?: () => void,
|
|
22
|
+
* }} InputStreamHandler
|
|
23
|
+
*
|
|
24
|
+
* @typedef {{
|
|
25
|
+
* checkWrite?: () -> BigInt,
|
|
26
|
+
* write: (buf: Uint8Array) => BigInt,
|
|
27
|
+
* blockingWriteAndFlush?: (buf: Uint8Array) => void,
|
|
28
|
+
* flush?: () => void,
|
|
29
|
+
* blockingFlush: () => void,
|
|
30
|
+
* writeZeroes?: (len: BigInt) => void,
|
|
31
|
+
* blockingWriteZeroes?: (len: BigInt) => void,
|
|
32
|
+
* blockingWriteZeroesAndFlush?: (len: BigInt) => void,
|
|
33
|
+
* splice?: (src: InputStream, len: BigInt) => BigInt,
|
|
34
|
+
* blockingSplice?: (src: InputStream, len: BigInt) => BigInt,
|
|
35
|
+
* forward?: (src: InputStream) => void,
|
|
36
|
+
* subscribe?: () => void,
|
|
37
|
+
* drop?: () => void,
|
|
38
|
+
* }} OutputStreamHandler
|
|
39
|
+
*
|
|
40
|
+
**/
|
|
41
|
+
|
|
42
|
+
class InputStream {
|
|
43
|
+
/**
|
|
44
|
+
* @param {InputStreamHandler} handler
|
|
45
|
+
*/
|
|
5
46
|
constructor (handler) {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
this.
|
|
47
|
+
if (!handler)
|
|
48
|
+
console.trace('no handler');
|
|
49
|
+
this.id = ++id;
|
|
9
50
|
this.handler = handler;
|
|
10
51
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
} else {
|
|
16
|
-
this.#addToBuffer(bytes.slice(0, newlineIdx + 1));
|
|
17
|
-
this.handler(new TextDecoder().decode(this.buffer.slice(0, this.bufferLen)));
|
|
18
|
-
this.bufferLen = 0;
|
|
19
|
-
this.#addToBuffer(bytes.slice(newlineIdx + 1));
|
|
20
|
-
}
|
|
52
|
+
read(len) {
|
|
53
|
+
if (this.handler.read)
|
|
54
|
+
return this.handler.read(len);
|
|
55
|
+
return this.handler.blockingRead.call(this, len);
|
|
21
56
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
this.
|
|
57
|
+
blockingRead(len) {
|
|
58
|
+
return this.handler.blockingRead.call(this, len);
|
|
59
|
+
}
|
|
60
|
+
skip(len) {
|
|
61
|
+
if (this.handler.skip)
|
|
62
|
+
return this.handler.skip.call(this, len);
|
|
63
|
+
if (this.handler.read) {
|
|
64
|
+
const bytes = this.handler.read.call(this, len);
|
|
65
|
+
return BigInt(bytes.byteLength);
|
|
28
66
|
}
|
|
29
|
-
this.
|
|
30
|
-
|
|
67
|
+
return this.blockingSkip.call(this, len);
|
|
68
|
+
}
|
|
69
|
+
blockingSkip(len) {
|
|
70
|
+
if (this.handler.blockingSkip)
|
|
71
|
+
return this.handler.blockingSkip.call(this, len);
|
|
72
|
+
const bytes = this.handler.blockingRead.call(this, len);
|
|
73
|
+
return BigInt(bytes.byteLength);
|
|
74
|
+
}
|
|
75
|
+
subscribe() {
|
|
76
|
+
console.log(`[streams] Subscribe to input stream ${this.id}`);
|
|
77
|
+
}
|
|
78
|
+
[symbolDispose] () {
|
|
79
|
+
if (this.handler.drop)
|
|
80
|
+
this.handler.drop.call(this);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
class OutputStream {
|
|
85
|
+
/**
|
|
86
|
+
* @param {OutputStreamHandler} handler
|
|
87
|
+
*/
|
|
88
|
+
constructor (handler) {
|
|
89
|
+
if (!handler)
|
|
90
|
+
console.trace('no handler');
|
|
91
|
+
this.id = ++id;
|
|
92
|
+
this.open = true;
|
|
93
|
+
this.handler = handler;
|
|
94
|
+
}
|
|
95
|
+
checkWrite(len) {
|
|
96
|
+
if (!this.open)
|
|
97
|
+
return 0n;
|
|
98
|
+
if (this.handler.checkWrite)
|
|
99
|
+
return this.handler.checkWrite.call(this, len);
|
|
100
|
+
return 1_000_000n;
|
|
31
101
|
}
|
|
102
|
+
write(buf) {
|
|
103
|
+
this.handler.write.call(this, buf);
|
|
104
|
+
}
|
|
105
|
+
blockingWriteAndFlush(buf) {
|
|
106
|
+
/// Perform a write of up to 4096 bytes, and then flush the stream. Block
|
|
107
|
+
/// until all of these operations are complete, or an error occurs.
|
|
108
|
+
///
|
|
109
|
+
/// This is a convenience wrapper around the use of `check-write`,
|
|
110
|
+
/// `subscribe`, `write`, and `flush`, and is implemented with the
|
|
111
|
+
/// following pseudo-code:
|
|
112
|
+
///
|
|
113
|
+
/// ```text
|
|
114
|
+
/// let pollable = this.subscribe();
|
|
115
|
+
/// while !contents.is_empty() {
|
|
116
|
+
/// // Wait for the stream to become writable
|
|
117
|
+
/// poll-one(pollable);
|
|
118
|
+
/// let Ok(n) = this.check-write(); // eliding error handling
|
|
119
|
+
/// let len = min(n, contents.len());
|
|
120
|
+
/// let (chunk, rest) = contents.split_at(len);
|
|
121
|
+
/// this.write(chunk ); // eliding error handling
|
|
122
|
+
/// contents = rest;
|
|
123
|
+
/// }
|
|
124
|
+
/// this.flush();
|
|
125
|
+
/// // Wait for completion of `flush`
|
|
126
|
+
/// poll-one(pollable);
|
|
127
|
+
/// // Check for any errors that arose during `flush`
|
|
128
|
+
/// let _ = this.check-write(); // eliding error handling
|
|
129
|
+
/// ```
|
|
130
|
+
this.handler.write.call(this, buf);
|
|
131
|
+
}
|
|
132
|
+
flush() {
|
|
133
|
+
if (this.handler.flush)
|
|
134
|
+
this.handler.flush.call(this);
|
|
135
|
+
}
|
|
136
|
+
blockingFlush() {
|
|
137
|
+
this.open = true;
|
|
138
|
+
}
|
|
139
|
+
writeZeroes(len) {
|
|
140
|
+
this.write.call(this, new Uint8Array(Number(len)));
|
|
141
|
+
}
|
|
142
|
+
blockingWriteZeroes(len) {
|
|
143
|
+
this.blockingWrite.call(this, new Uint8Array(Number(len)));
|
|
144
|
+
}
|
|
145
|
+
blockingWriteZeroesAndFlush(len) {
|
|
146
|
+
this.blockingWriteAndFlush.call(this, new Uint8Array(Number(len)));
|
|
147
|
+
}
|
|
148
|
+
splice(src, len) {
|
|
149
|
+
const spliceLen = Math.min(len, this.checkWrite.call(this));
|
|
150
|
+
const bytes = src.read(spliceLen);
|
|
151
|
+
this.write.call(this, bytes);
|
|
152
|
+
return bytes.byteLength;
|
|
153
|
+
}
|
|
154
|
+
blockingSplice(_src, _len) {
|
|
155
|
+
console.log(`[streams] Blocking splice ${this.id}`);
|
|
156
|
+
}
|
|
157
|
+
forward(_src) {
|
|
158
|
+
console.log(`[streams] Forward ${this.id}`);
|
|
159
|
+
}
|
|
160
|
+
subscribe() {
|
|
161
|
+
console.log(`[streams] Subscribe to output stream ${this.id}`);
|
|
162
|
+
}
|
|
163
|
+
[symbolDispose]() {
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export const streams = { Error, InputStream, OutputStream };
|
|
168
|
+
|
|
169
|
+
class Pollable {}
|
|
170
|
+
|
|
171
|
+
function pollList (_list) {
|
|
172
|
+
// TODO
|
|
32
173
|
}
|
|
33
174
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
);
|
|
175
|
+
function pollOne (_poll) {
|
|
176
|
+
// TODO
|
|
177
|
+
}
|
|
38
178
|
|
|
39
|
-
export const
|
|
179
|
+
export const poll = {
|
|
180
|
+
Pollable,
|
|
181
|
+
pollList,
|
|
182
|
+
pollOne
|
|
183
|
+
};
|
package/lib/common/io.js
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
let id = 0;
|
|
2
2
|
|
|
3
|
+
const symbolDispose = Symbol.dispose || Symbol.for('dispose');
|
|
4
|
+
|
|
3
5
|
class Error {
|
|
4
6
|
constructor (msg) {
|
|
5
7
|
this.msg = msg;
|
|
@@ -16,7 +18,7 @@ class Error {
|
|
|
16
18
|
* skip?: (len: BigInt) => BigInt,
|
|
17
19
|
* blockingSkip?: (len: BigInt) => BigInt,
|
|
18
20
|
* subscribe: () => void,
|
|
19
|
-
* drop
|
|
21
|
+
* drop?: () => void,
|
|
20
22
|
* }} InputStreamHandler
|
|
21
23
|
*
|
|
22
24
|
* @typedef {{
|
|
@@ -32,7 +34,7 @@ class Error {
|
|
|
32
34
|
* blockingSplice?: (src: InputStream, len: BigInt) => BigInt,
|
|
33
35
|
* forward?: (src: InputStream) => void,
|
|
34
36
|
* subscribe?: () => void,
|
|
35
|
-
* drop
|
|
37
|
+
* drop?: () => void,
|
|
36
38
|
* }} OutputStreamHandler
|
|
37
39
|
*
|
|
38
40
|
**/
|
|
@@ -73,7 +75,7 @@ class InputStream {
|
|
|
73
75
|
subscribe() {
|
|
74
76
|
console.log(`[streams] Subscribe to input stream ${this.id}`);
|
|
75
77
|
}
|
|
76
|
-
|
|
78
|
+
[symbolDispose] () {
|
|
77
79
|
if (this.handler.drop)
|
|
78
80
|
this.handler.drop.call(this);
|
|
79
81
|
}
|
|
@@ -158,7 +160,7 @@ class OutputStream {
|
|
|
158
160
|
subscribe() {
|
|
159
161
|
console.log(`[streams] Subscribe to output stream ${this.id}`);
|
|
160
162
|
}
|
|
161
|
-
|
|
163
|
+
[symbolDispose]() {
|
|
162
164
|
}
|
|
163
165
|
}
|
|
164
166
|
|
package/lib/nodejs/cli.js
CHANGED
|
@@ -2,7 +2,8 @@ import { argv, env, cwd } from 'node:process';
|
|
|
2
2
|
import { streams } from '../common/io.js';
|
|
3
3
|
const { InputStream, OutputStream } = streams;
|
|
4
4
|
|
|
5
|
-
let _env = Object.entries(env), _args = argv, _cwd = cwd();
|
|
5
|
+
let _env = Object.entries(env), _args = argv.slice(1), _cwd = cwd();
|
|
6
|
+
const symbolDispose = Symbol.dispose || Symbol.for('dispose');
|
|
6
7
|
|
|
7
8
|
export const environment = {
|
|
8
9
|
getEnvironment () {
|
|
@@ -29,7 +30,7 @@ const stdinStream = new InputStream({
|
|
|
29
30
|
subscribe () {
|
|
30
31
|
// TODO
|
|
31
32
|
},
|
|
32
|
-
|
|
33
|
+
[symbolDispose] () {
|
|
33
34
|
// TODO
|
|
34
35
|
}
|
|
35
36
|
});
|
|
@@ -39,7 +40,7 @@ const stdoutStream = new OutputStream({
|
|
|
39
40
|
},
|
|
40
41
|
blockingFlush () {
|
|
41
42
|
},
|
|
42
|
-
|
|
43
|
+
[symbolDispose] () {
|
|
43
44
|
}
|
|
44
45
|
});
|
|
45
46
|
const stderrStream = new OutputStream({
|
|
@@ -49,7 +50,7 @@ const stderrStream = new OutputStream({
|
|
|
49
50
|
blockingFlush () {
|
|
50
51
|
|
|
51
52
|
},
|
|
52
|
-
|
|
53
|
+
[symbolDispose] () {
|
|
53
54
|
|
|
54
55
|
}
|
|
55
56
|
});
|
package/lib/nodejs/filesystem.js
CHANGED
|
@@ -5,6 +5,8 @@ import { platform } from 'node:process';
|
|
|
5
5
|
|
|
6
6
|
const { InputStream, OutputStream, Error: StreamError } = streams;
|
|
7
7
|
|
|
8
|
+
const symbolDispose = Symbol.dispose || Symbol.for('dispose');
|
|
9
|
+
|
|
8
10
|
const isWindows = platform === 'win32';
|
|
9
11
|
|
|
10
12
|
const nsMagnitude = 1_000_000_000_000n;
|
|
@@ -39,35 +41,6 @@ function lookupType (obj) {
|
|
|
39
41
|
* } DescriptorProps
|
|
40
42
|
*/
|
|
41
43
|
export class FileSystem {
|
|
42
|
-
// Note: This should implement per-segment semantics of openAt, but we cannot currently
|
|
43
|
-
// due to the lack of support for openat() in Node.js.
|
|
44
|
-
// Tracking issue: https://github.com/libuv/libuv/issues/4167
|
|
45
|
-
|
|
46
|
-
// TODO: support followSymlinks
|
|
47
|
-
getFullPath (descriptor, subpath, _followSymlinks) {
|
|
48
|
-
if (subpath.indexOf('\\') !== -1)
|
|
49
|
-
subpath = subpath.replace(/\\/g, '/');
|
|
50
|
-
if (subpath[0] === '/') {
|
|
51
|
-
let bestPreopenMatch = '';
|
|
52
|
-
for (const preopenEntry of this.preopenEntries) {
|
|
53
|
-
if (subpath.startsWith(preopenEntry[1]) && (!bestPreopenMatch || bestPreopenMatch.length < preopenEntry[1].length)) {
|
|
54
|
-
bestPreopenMatch = preopenEntry;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (!bestPreopenMatch)
|
|
58
|
-
throw 'no-entry';
|
|
59
|
-
descriptor = bestPreopenMatch[0];
|
|
60
|
-
subpath = subpath.slice(bestPreopenMatch[1]);
|
|
61
|
-
if (subpath[0] === '/')
|
|
62
|
-
subpath = subpath.slice(1);
|
|
63
|
-
}
|
|
64
|
-
if (subpath.startsWith('.'))
|
|
65
|
-
subpath = subpath.slice(subpath[1] === '/' ? 2 : 1);
|
|
66
|
-
if (descriptor.hostPreopen)
|
|
67
|
-
return descriptor.hostPreopen + (descriptor.hostPreopen.endsWith('/') ? '' : '/') + subpath;
|
|
68
|
-
return descriptor.fullPath + '/' + subpath;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
44
|
/**
|
|
72
45
|
*
|
|
73
46
|
* @param {[string, string][]} preopens
|
|
@@ -79,16 +52,18 @@ export class FileSystem {
|
|
|
79
52
|
this.cwd = environment.initialCwd();
|
|
80
53
|
|
|
81
54
|
class FileInputStream extends InputStream {
|
|
55
|
+
#hostFd;
|
|
56
|
+
#position;
|
|
82
57
|
constructor (hostFd, position) {
|
|
83
58
|
super({
|
|
84
59
|
blockingRead (len) {
|
|
85
60
|
const buf = new Uint8Array(Number(len));
|
|
86
61
|
try {
|
|
87
|
-
var bytesRead = readSync(
|
|
62
|
+
var bytesRead = readSync(self.#hostFd, buf, 0, buf.byteLength, self.#position);
|
|
88
63
|
} catch (e) {
|
|
89
64
|
throw { tag: 'last-operation-failed', val: new StreamError(e.message) };
|
|
90
65
|
}
|
|
91
|
-
|
|
66
|
+
self.#position += bytesRead;
|
|
92
67
|
if (bytesRead < buf.byteLength) {
|
|
93
68
|
if (bytesRead === 0)
|
|
94
69
|
throw { tag: 'closed' };
|
|
@@ -99,26 +74,29 @@ export class FileSystem {
|
|
|
99
74
|
subscribe () {
|
|
100
75
|
// TODO
|
|
101
76
|
},
|
|
102
|
-
|
|
77
|
+
[symbolDispose] () {
|
|
103
78
|
// TODO
|
|
104
79
|
}
|
|
105
80
|
});
|
|
106
|
-
|
|
107
|
-
this
|
|
81
|
+
const self = this;
|
|
82
|
+
this.#hostFd = hostFd;
|
|
83
|
+
this.#position = Number(position);
|
|
108
84
|
}
|
|
109
85
|
}
|
|
110
86
|
|
|
111
87
|
class FileOutputStream extends OutputStream {
|
|
88
|
+
#hostFd;
|
|
89
|
+
#position;
|
|
112
90
|
constructor (hostFd, position) {
|
|
113
91
|
super({
|
|
114
92
|
write (contents) {
|
|
115
93
|
let totalWritten = 0;
|
|
116
94
|
while (totalWritten !== contents.byteLength) {
|
|
117
|
-
const bytesWritten = writeSync(
|
|
95
|
+
const bytesWritten = writeSync(self.#hostFd, contents, null, null, self.#position);
|
|
118
96
|
totalWritten += bytesWritten;
|
|
119
97
|
contents = new Uint8Array(contents.buffer, bytesWritten);
|
|
120
98
|
}
|
|
121
|
-
|
|
99
|
+
self.#position += contents.byteLength;
|
|
122
100
|
},
|
|
123
101
|
blockingFlush () {
|
|
124
102
|
|
|
@@ -127,19 +105,18 @@ export class FileSystem {
|
|
|
127
105
|
|
|
128
106
|
}
|
|
129
107
|
});
|
|
130
|
-
|
|
131
|
-
this
|
|
108
|
+
const self = this;
|
|
109
|
+
this.#hostFd = hostFd;
|
|
110
|
+
this.#position = Number(position);
|
|
132
111
|
}
|
|
133
112
|
}
|
|
134
113
|
|
|
135
114
|
class DirectoryEntryStream {
|
|
136
|
-
|
|
137
|
-
this.dir = dir;
|
|
138
|
-
}
|
|
115
|
+
#dir;
|
|
139
116
|
readDirectoryEntry () {
|
|
140
117
|
let entry;
|
|
141
118
|
try {
|
|
142
|
-
entry = this
|
|
119
|
+
entry = this.#dir.readSync();
|
|
143
120
|
} catch (e) {
|
|
144
121
|
throw convertFsError(e);
|
|
145
122
|
}
|
|
@@ -150,27 +127,54 @@ export class FileSystem {
|
|
|
150
127
|
const type = lookupType(entry);
|
|
151
128
|
return { name, type };
|
|
152
129
|
}
|
|
153
|
-
|
|
154
|
-
this
|
|
130
|
+
[symbolDispose] () {
|
|
131
|
+
this.#dir.closeSync();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
static _create (dir) {
|
|
135
|
+
const dirStream = new DirectoryEntryStream();
|
|
136
|
+
dirStream.#dir = dir;
|
|
137
|
+
return dirStream;
|
|
155
138
|
}
|
|
156
139
|
}
|
|
140
|
+
const directoryEntryStreamCreate = DirectoryEntryStream._create;
|
|
141
|
+
delete DirectoryEntryStream._create;
|
|
157
142
|
|
|
143
|
+
// Note: This should implement per-segment semantics of openAt, but we cannot currently
|
|
144
|
+
// due to the lack of support for openat() in Node.js.
|
|
145
|
+
// Tracking issue: https://github.com/libuv/libuv/issues/4167
|
|
158
146
|
/**
|
|
159
147
|
* @implements {DescriptorProps}
|
|
160
148
|
*/
|
|
161
149
|
class Descriptor {
|
|
150
|
+
#hostPreopen;
|
|
151
|
+
#fd;
|
|
152
|
+
#fullPath;
|
|
153
|
+
|
|
154
|
+
static _createPreopen (hostPreopen) {
|
|
155
|
+
const descriptor = new Descriptor();
|
|
156
|
+
descriptor.#hostPreopen = hostPreopen;
|
|
157
|
+
return descriptor;
|
|
158
|
+
}
|
|
159
|
+
static _create (fd, fullPath) {
|
|
160
|
+
const descriptor = new Descriptor();
|
|
161
|
+
descriptor.#fd = fd;
|
|
162
|
+
descriptor.#fullPath = fullPath;
|
|
163
|
+
return descriptor;
|
|
164
|
+
}
|
|
165
|
+
|
|
162
166
|
constructor () {
|
|
163
167
|
this.id = fs.descriptorCnt++;
|
|
164
168
|
}
|
|
165
169
|
readViaStream(offset) {
|
|
166
|
-
if (this
|
|
170
|
+
if (this.#hostPreopen)
|
|
167
171
|
throw { tag: 'last-operation-failed', val: new StreamError };
|
|
168
|
-
return new FileInputStream(this
|
|
172
|
+
return new FileInputStream(this.#fd, offset);
|
|
169
173
|
}
|
|
170
174
|
writeViaStream(offset) {
|
|
171
|
-
if (this
|
|
175
|
+
if (this.#hostPreopen)
|
|
172
176
|
throw 'is-directory';
|
|
173
|
-
return new FileOutputStream(this
|
|
177
|
+
return new FileOutputStream(this.#fd, offset);
|
|
174
178
|
}
|
|
175
179
|
|
|
176
180
|
appendViaStream() {
|
|
@@ -190,8 +194,8 @@ export class FileSystem {
|
|
|
190
194
|
}
|
|
191
195
|
|
|
192
196
|
getType() {
|
|
193
|
-
if (this
|
|
194
|
-
const stats = fstatSync(this
|
|
197
|
+
if (this.#hostPreopen) return 'directory';
|
|
198
|
+
const stats = fstatSync(this.#fd);
|
|
195
199
|
return lookupType(stats);
|
|
196
200
|
}
|
|
197
201
|
|
|
@@ -208,23 +212,23 @@ export class FileSystem {
|
|
|
208
212
|
}
|
|
209
213
|
|
|
210
214
|
read(length, offset) {
|
|
211
|
-
if (!this
|
|
215
|
+
if (!this.#fullPath) throw 'bad-descriptor';
|
|
212
216
|
const buf = new Uint8Array(length);
|
|
213
|
-
const bytesRead = readSync(this
|
|
217
|
+
const bytesRead = readSync(this.#fd, buf, Number(offset), length, 0);
|
|
214
218
|
const out = new Uint8Array(buf.buffer, 0, bytesRead);
|
|
215
219
|
return [out, bytesRead === 0 ? 'ended' : 'open'];
|
|
216
220
|
}
|
|
217
221
|
|
|
218
222
|
write(buffer, offset) {
|
|
219
|
-
if (!this
|
|
220
|
-
return BigInt(writeSync(this
|
|
223
|
+
if (!this.#fullPath) throw 'bad-descriptor';
|
|
224
|
+
return BigInt(writeSync(this.#fd, buffer, Number(offset), buffer.byteLength - offset, 0));
|
|
221
225
|
}
|
|
222
226
|
|
|
223
227
|
readDirectory() {
|
|
224
|
-
if (!this
|
|
228
|
+
if (!this.#fullPath) throw 'bad-descriptor';
|
|
225
229
|
try {
|
|
226
|
-
const dir = opendirSync(isWindows ? this
|
|
227
|
-
return
|
|
230
|
+
const dir = opendirSync(isWindows ? this.#fullPath.slice(1) : this.#fullPath);
|
|
231
|
+
return directoryEntryStreamCreate(dir);
|
|
228
232
|
}
|
|
229
233
|
catch (e) {
|
|
230
234
|
throw convertFsError(e);
|
|
@@ -236,7 +240,7 @@ export class FileSystem {
|
|
|
236
240
|
}
|
|
237
241
|
|
|
238
242
|
createDirectoryAt(path) {
|
|
239
|
-
const fullPath =
|
|
243
|
+
const fullPath = this.#getFullPath(path);
|
|
240
244
|
try {
|
|
241
245
|
mkdirSync(fullPath);
|
|
242
246
|
}
|
|
@@ -246,10 +250,10 @@ export class FileSystem {
|
|
|
246
250
|
}
|
|
247
251
|
|
|
248
252
|
stat() {
|
|
249
|
-
if (this
|
|
253
|
+
if (this.#hostPreopen) throw 'invalid';
|
|
250
254
|
let stats;
|
|
251
255
|
try {
|
|
252
|
-
stats = fstatSync(this
|
|
256
|
+
stats = fstatSync(this.#fd, { bigint: true });
|
|
253
257
|
}
|
|
254
258
|
catch (e) {
|
|
255
259
|
convertFsError(e);
|
|
@@ -266,7 +270,7 @@ export class FileSystem {
|
|
|
266
270
|
}
|
|
267
271
|
|
|
268
272
|
statAt(pathFlags, path) {
|
|
269
|
-
const fullPath =
|
|
273
|
+
const fullPath = this.#getFullPath(path, false);
|
|
270
274
|
let stats;
|
|
271
275
|
try {
|
|
272
276
|
stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(isWindows ? fullPath.slice(1) : fullPath, { bigint: true });
|
|
@@ -294,7 +298,7 @@ export class FileSystem {
|
|
|
294
298
|
}
|
|
295
299
|
|
|
296
300
|
openAt(pathFlags, path, openFlags, descriptorFlags, modes) {
|
|
297
|
-
const fullPath =
|
|
301
|
+
const fullPath = this.#getFullPath(path, pathFlags.symlinkFollow);
|
|
298
302
|
let fsOpenFlags = 0x0;
|
|
299
303
|
if (openFlags.create)
|
|
300
304
|
fsOpenFlags |= constants.O_CREAT;
|
|
@@ -325,7 +329,7 @@ export class FileSystem {
|
|
|
325
329
|
|
|
326
330
|
try {
|
|
327
331
|
const fd = openSync(isWindows ? fullPath.slice(1) : fullPath, fsOpenFlags, fsMode);
|
|
328
|
-
return
|
|
332
|
+
return descriptorCreate(fd, fullPath);
|
|
329
333
|
}
|
|
330
334
|
catch (e) {
|
|
331
335
|
throw convertFsError(e);
|
|
@@ -380,16 +384,16 @@ export class FileSystem {
|
|
|
380
384
|
console.log(`[filesystem] UNLOCK`, this.id);
|
|
381
385
|
}
|
|
382
386
|
|
|
383
|
-
|
|
384
|
-
if (this
|
|
385
|
-
closeSync(this
|
|
387
|
+
[symbolDispose]() {
|
|
388
|
+
if (this.#fd)
|
|
389
|
+
closeSync(this.#fd);
|
|
386
390
|
}
|
|
387
391
|
|
|
388
392
|
metadataHash() {
|
|
389
|
-
if (this
|
|
393
|
+
if (this.#hostPreopen)
|
|
390
394
|
return { upper: 0n, lower: BigInt(this.id) };
|
|
391
395
|
try {
|
|
392
|
-
const stats = fstatSync(this
|
|
396
|
+
const stats = fstatSync(this.#fd, { bigint: true });
|
|
393
397
|
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
394
398
|
}
|
|
395
399
|
catch (e) {
|
|
@@ -398,7 +402,7 @@ export class FileSystem {
|
|
|
398
402
|
}
|
|
399
403
|
|
|
400
404
|
metadataHashAt(pathFlags, path) {
|
|
401
|
-
const fullPath =
|
|
405
|
+
const fullPath = this.#getFullPath(path, false);
|
|
402
406
|
try {
|
|
403
407
|
const stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(isWindows ? fullPath.slice(1) : fullPath, { bigint: true });
|
|
404
408
|
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
@@ -407,12 +411,43 @@ export class FileSystem {
|
|
|
407
411
|
convertFsError(e);
|
|
408
412
|
}
|
|
409
413
|
}
|
|
414
|
+
|
|
415
|
+
// TODO: support followSymlinks
|
|
416
|
+
#getFullPath (subpath, _followSymlinks) {
|
|
417
|
+
let descriptor = this;
|
|
418
|
+
if (subpath.indexOf('\\') !== -1)
|
|
419
|
+
subpath = subpath.replace(/\\/g, '/');
|
|
420
|
+
if (subpath[0] === '/') {
|
|
421
|
+
let bestPreopenMatch = '';
|
|
422
|
+
for (const preopenEntry of fs.preopenEntries) {
|
|
423
|
+
if (subpath.startsWith(preopenEntry[1]) && (!bestPreopenMatch || bestPreopenMatch.length < preopenEntry[1].length)) {
|
|
424
|
+
bestPreopenMatch = preopenEntry;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
if (!bestPreopenMatch)
|
|
428
|
+
throw 'no-entry';
|
|
429
|
+
descriptor = bestPreopenMatch[0];
|
|
430
|
+
subpath = subpath.slice(bestPreopenMatch[1]);
|
|
431
|
+
if (subpath[0] === '/')
|
|
432
|
+
subpath = subpath.slice(1);
|
|
433
|
+
}
|
|
434
|
+
if (subpath.startsWith('.'))
|
|
435
|
+
subpath = subpath.slice(subpath[1] === '/' ? 2 : 1);
|
|
436
|
+
if (descriptor.#hostPreopen)
|
|
437
|
+
return descriptor.#hostPreopen + (descriptor.#hostPreopen.endsWith('/') ? '' : '/') + subpath;
|
|
438
|
+
return descriptor.#fullPath + '/' + subpath;
|
|
439
|
+
}
|
|
410
440
|
}
|
|
411
441
|
|
|
442
|
+
const descriptorCreatePreopen = Descriptor._createPreopen;
|
|
443
|
+
delete Descriptor._createPreopen;
|
|
444
|
+
const descriptorCreate = Descriptor._create;
|
|
445
|
+
delete Descriptor._create;
|
|
446
|
+
|
|
412
447
|
this.descriptorCnt = 3;
|
|
413
448
|
this.preopenEntries = [];
|
|
414
449
|
for (const [virtualPath, hostPreopen] of Object.entries(preopens)) {
|
|
415
|
-
const preopenEntry = [
|
|
450
|
+
const preopenEntry = [descriptorCreatePreopen(hostPreopen), virtualPath];
|
|
416
451
|
this.preopenEntries.push(preopenEntry);
|
|
417
452
|
}
|
|
418
453
|
this.preopens = {
|
|
@@ -428,9 +463,7 @@ export class FileSystem {
|
|
|
428
463
|
}
|
|
429
464
|
}
|
|
430
465
|
|
|
431
|
-
const
|
|
432
|
-
|
|
433
|
-
export const { preopens, types } = _fs;
|
|
466
|
+
export const { preopens, types } = new FileSystem({ '/': '/' }, environment);
|
|
434
467
|
|
|
435
468
|
function convertFsError (e) {
|
|
436
469
|
switch (e.code) {
|