@bytecodealliance/preview2-shim 0.0.21 → 0.14.1
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/README.md +4 -14
- package/lib/browser/cli.js +2 -4
- package/lib/browser/clocks.js +15 -27
- package/lib/browser/filesystem.js +2 -30
- package/lib/browser/http.js +1 -3
- package/lib/browser/io.js +4 -2
- package/lib/common/assert.js +7 -0
- package/lib/io/calls.js +64 -0
- package/lib/io/worker-http.js +95 -0
- package/lib/io/worker-io.js +322 -0
- package/lib/io/worker-thread.js +569 -0
- package/lib/nodejs/cli.js +45 -59
- package/lib/nodejs/clocks.js +13 -27
- package/lib/nodejs/filesystem.js +539 -459
- package/lib/nodejs/http.js +440 -173
- package/lib/nodejs/index.js +4 -1
- package/lib/nodejs/io.js +1 -0
- package/lib/nodejs/sockets/socket-common.js +116 -0
- package/lib/nodejs/sockets/socketopts-bindings.js +94 -0
- package/lib/nodejs/sockets/tcp-socket-impl.js +794 -0
- package/lib/nodejs/sockets/udp-socket-impl.js +628 -0
- package/lib/nodejs/sockets/wasi-sockets.js +320 -0
- package/lib/nodejs/sockets.js +11 -200
- package/lib/synckit/index.js +4 -2
- package/package.json +1 -5
- package/types/interfaces/wasi-cli-terminal-input.d.ts +4 -0
- package/types/interfaces/wasi-cli-terminal-output.d.ts +4 -0
- package/types/interfaces/wasi-clocks-monotonic-clock.d.ts +19 -6
- package/types/interfaces/wasi-filesystem-types.d.ts +1 -178
- package/types/interfaces/wasi-http-outgoing-handler.d.ts +2 -2
- package/types/interfaces/wasi-http-types.d.ts +412 -82
- package/types/interfaces/wasi-io-error.d.ts +16 -0
- package/types/interfaces/wasi-io-poll.d.ts +19 -8
- package/types/interfaces/wasi-io-streams.d.ts +26 -46
- package/types/interfaces/wasi-sockets-ip-name-lookup.d.ts +9 -21
- package/types/interfaces/wasi-sockets-network.d.ts +4 -0
- package/types/interfaces/wasi-sockets-tcp.d.ts +75 -18
- package/types/interfaces/wasi-sockets-udp-create-socket.d.ts +1 -1
- package/types/interfaces/wasi-sockets-udp.d.ts +282 -193
- package/types/wasi-cli-command.d.ts +28 -28
- package/types/wasi-http-proxy.d.ts +12 -12
- package/lib/common/io.js +0 -183
- package/lib/common/make-request.js +0 -30
- package/types/interfaces/wasi-clocks-timezone.d.ts +0 -56
package/lib/nodejs/filesystem.js
CHANGED
|
@@ -1,510 +1,590 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
1
|
+
import {
|
|
2
|
+
ioCall,
|
|
3
|
+
inputStreamCreate,
|
|
4
|
+
outputStreamCreate,
|
|
5
|
+
} from "../io/worker-io.js";
|
|
6
|
+
import { INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE } from "../io/calls.js";
|
|
7
|
+
import { FILE } from "../io/calls.js";
|
|
8
|
+
// import { environment } from "./cli.js";
|
|
9
|
+
import {
|
|
10
|
+
closeSync,
|
|
11
|
+
constants,
|
|
12
|
+
fdatasyncSync,
|
|
13
|
+
fstatSync,
|
|
14
|
+
fsyncSync,
|
|
15
|
+
ftruncateSync,
|
|
16
|
+
futimesSync,
|
|
17
|
+
linkSync,
|
|
18
|
+
lstatSync,
|
|
19
|
+
lutimesSync,
|
|
20
|
+
mkdirSync,
|
|
21
|
+
opendirSync,
|
|
22
|
+
openSync,
|
|
23
|
+
readlinkSync,
|
|
24
|
+
readSync,
|
|
25
|
+
renameSync,
|
|
26
|
+
rmdirSync,
|
|
27
|
+
statSync,
|
|
28
|
+
symlinkSync,
|
|
29
|
+
unlinkSync,
|
|
30
|
+
utimesSync,
|
|
31
|
+
writeSync,
|
|
32
|
+
} from "node:fs";
|
|
33
|
+
import { platform } from "node:process";
|
|
34
|
+
|
|
35
|
+
const symbolDispose = Symbol.dispose || Symbol.for("dispose");
|
|
36
|
+
|
|
37
|
+
const isWindows = platform === "win32";
|
|
11
38
|
|
|
12
39
|
const nsMagnitude = 1_000_000_000_000n;
|
|
13
|
-
function nsToDateTime
|
|
40
|
+
function nsToDateTime(ns) {
|
|
14
41
|
const seconds = ns / nsMagnitude;
|
|
15
42
|
const nanoseconds = Number(ns % seconds);
|
|
16
43
|
return { seconds, nanoseconds };
|
|
17
44
|
}
|
|
18
45
|
|
|
19
|
-
function lookupType
|
|
20
|
-
if (obj.isFile())
|
|
21
|
-
|
|
22
|
-
else if (obj.
|
|
23
|
-
|
|
24
|
-
else if (obj.
|
|
25
|
-
|
|
26
|
-
else if (obj.
|
|
27
|
-
|
|
28
|
-
else if (obj.isDirectory())
|
|
29
|
-
return 'directory';
|
|
30
|
-
else if (obj.isCharacterDevice())
|
|
31
|
-
return 'character-device';
|
|
32
|
-
else if (obj.isBlockDevice())
|
|
33
|
-
return 'block-device';
|
|
34
|
-
return 'unknown';
|
|
46
|
+
function lookupType(obj) {
|
|
47
|
+
if (obj.isFile()) return "regular-file";
|
|
48
|
+
else if (obj.isSocket()) return "socket";
|
|
49
|
+
else if (obj.isSymbolicLink()) return "symbolic-link";
|
|
50
|
+
else if (obj.isFIFO()) return "fifo";
|
|
51
|
+
else if (obj.isDirectory()) return "directory";
|
|
52
|
+
else if (obj.isCharacterDevice()) return "character-device";
|
|
53
|
+
else if (obj.isBlockDevice()) return "block-device";
|
|
54
|
+
return "unknown";
|
|
35
55
|
}
|
|
36
56
|
|
|
57
|
+
// Note: This should implement per-segment semantics of openAt, but we cannot currently
|
|
58
|
+
// due to the lack of support for openat() in Node.js.
|
|
59
|
+
// Tracking issue: https://github.com/libuv/libuv/issues/4167
|
|
37
60
|
/**
|
|
38
|
-
* @
|
|
39
|
-
* { hostPreopen: string } |
|
|
40
|
-
* { fullPath: string, fd: number }
|
|
41
|
-
* } DescriptorProps
|
|
61
|
+
* @implements {DescriptorProps}
|
|
42
62
|
*/
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const buf = new Uint8Array(Number(len));
|
|
61
|
-
try {
|
|
62
|
-
var bytesRead = readSync(self.#hostFd, buf, 0, buf.byteLength, self.#position);
|
|
63
|
-
} catch (e) {
|
|
64
|
-
throw { tag: 'last-operation-failed', val: new StreamError(e.message) };
|
|
65
|
-
}
|
|
66
|
-
self.#position += bytesRead;
|
|
67
|
-
if (bytesRead < buf.byteLength) {
|
|
68
|
-
if (bytesRead === 0)
|
|
69
|
-
throw { tag: 'closed' };
|
|
70
|
-
return new Uint8Array(buf.buffer, 0, bytesRead);
|
|
71
|
-
}
|
|
72
|
-
return buf;
|
|
73
|
-
},
|
|
74
|
-
subscribe () {
|
|
75
|
-
// TODO
|
|
76
|
-
},
|
|
77
|
-
[symbolDispose] () {
|
|
78
|
-
// TODO
|
|
79
|
-
}
|
|
80
|
-
});
|
|
81
|
-
const self = this;
|
|
82
|
-
this.#hostFd = hostFd;
|
|
83
|
-
this.#position = Number(position);
|
|
84
|
-
}
|
|
63
|
+
let descriptorCnt = 3;
|
|
64
|
+
class Descriptor {
|
|
65
|
+
#hostPreopen;
|
|
66
|
+
#fd;
|
|
67
|
+
#mode;
|
|
68
|
+
#fullPath;
|
|
69
|
+
|
|
70
|
+
static _createPreopen(hostPreopen) {
|
|
71
|
+
const descriptor = new Descriptor();
|
|
72
|
+
descriptor.#hostPreopen = hostPreopen.endsWith("/")
|
|
73
|
+
? hostPreopen.slice(0, -1) || '/'
|
|
74
|
+
: hostPreopen;
|
|
75
|
+
// Windows requires UNC paths at minimum
|
|
76
|
+
if (isWindows) {
|
|
77
|
+
descriptor.#hostPreopen = descriptor.#hostPreopen.replace(/\\/g, '/');
|
|
78
|
+
if (descriptor.#hostPreopen === '/')
|
|
79
|
+
descriptor.#hostPreopen = '//';
|
|
85
80
|
}
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
#hostFd;
|
|
89
|
-
#position;
|
|
90
|
-
constructor (hostFd, position) {
|
|
91
|
-
super({
|
|
92
|
-
write (contents) {
|
|
93
|
-
let totalWritten = 0;
|
|
94
|
-
while (totalWritten !== contents.byteLength) {
|
|
95
|
-
const bytesWritten = writeSync(self.#hostFd, contents, null, null, self.#position);
|
|
96
|
-
totalWritten += bytesWritten;
|
|
97
|
-
contents = new Uint8Array(contents.buffer, bytesWritten);
|
|
98
|
-
}
|
|
99
|
-
self.#position += contents.byteLength;
|
|
100
|
-
},
|
|
101
|
-
blockingFlush () {
|
|
102
|
-
|
|
103
|
-
},
|
|
104
|
-
drop () {
|
|
105
|
-
|
|
106
|
-
}
|
|
107
|
-
});
|
|
108
|
-
const self = this;
|
|
109
|
-
this.#hostFd = hostFd;
|
|
110
|
-
this.#position = Number(position);
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
class DirectoryEntryStream {
|
|
115
|
-
#dir;
|
|
116
|
-
readDirectoryEntry () {
|
|
117
|
-
let entry;
|
|
118
|
-
try {
|
|
119
|
-
entry = this.#dir.readSync();
|
|
120
|
-
} catch (e) {
|
|
121
|
-
throw convertFsError(e);
|
|
122
|
-
}
|
|
123
|
-
if (entry === null) {
|
|
124
|
-
return null;
|
|
125
|
-
}
|
|
126
|
-
const name = entry.name;
|
|
127
|
-
const type = lookupType(entry);
|
|
128
|
-
return { name, type };
|
|
129
|
-
}
|
|
130
|
-
[symbolDispose] () {
|
|
131
|
-
this.#dir.closeSync();
|
|
132
|
-
}
|
|
81
|
+
return descriptor;
|
|
82
|
+
}
|
|
133
83
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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
|
|
146
|
-
/**
|
|
147
|
-
* @implements {DescriptorProps}
|
|
148
|
-
*/
|
|
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
|
-
}
|
|
84
|
+
static _create(fd, mode, fullPath) {
|
|
85
|
+
const descriptor = new Descriptor();
|
|
86
|
+
descriptor.#fd = fd;
|
|
87
|
+
descriptor.#mode = mode;
|
|
88
|
+
if (fullPath.endsWith("/")) throw new Error("bad full path");
|
|
89
|
+
descriptor.#fullPath = fullPath;
|
|
90
|
+
return descriptor;
|
|
91
|
+
}
|
|
165
92
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
if (this.#hostPreopen)
|
|
171
|
-
throw { tag: 'last-operation-failed', val: new StreamError };
|
|
172
|
-
return new FileInputStream(this.#fd, offset);
|
|
173
|
-
}
|
|
174
|
-
writeViaStream(offset) {
|
|
175
|
-
if (this.#hostPreopen)
|
|
176
|
-
throw 'is-directory';
|
|
177
|
-
return new FileOutputStream(this.#fd, offset);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
appendViaStream() {
|
|
181
|
-
console.log(`[filesystem] APPEND STREAM ${this.id}`);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
advise(offset, length, advice) {
|
|
185
|
-
console.log(`[filesystem] ADVISE`, this.id, offset, length, advice);
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
syncData() {
|
|
189
|
-
console.log(`[filesystem] SYNC DATA ${this.id}`);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
getFlags() {
|
|
193
|
-
console.log(`[filesystem] FLAGS FOR ${this.id}`);
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
getType() {
|
|
197
|
-
if (this.#hostPreopen) return 'directory';
|
|
198
|
-
const stats = fstatSync(this.#fd);
|
|
199
|
-
return lookupType(stats);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
setFlags(flags) {
|
|
203
|
-
console.log(`[filesystem] SET FLAGS ${this.id} ${JSON.stringify(flags)}`);
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
setSize(size) {
|
|
207
|
-
console.log(`[filesystem] SET SIZE`, this.id, size);
|
|
208
|
-
}
|
|
209
|
-
|
|
210
|
-
setTimes(dataAccessTimestamp, dataModificationTimestamp) {
|
|
211
|
-
console.log(`[filesystem] SET TIMES`, this.id, dataAccessTimestamp, dataModificationTimestamp);
|
|
212
|
-
}
|
|
93
|
+
constructor() {
|
|
94
|
+
// this id is purely for debugging purposes
|
|
95
|
+
this._id = descriptorCnt++;
|
|
96
|
+
}
|
|
213
97
|
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
98
|
+
readViaStream(offset) {
|
|
99
|
+
if (this.#hostPreopen) throw "is-directory";
|
|
100
|
+
return inputStreamCreate(
|
|
101
|
+
FILE,
|
|
102
|
+
ioCall(INPUT_STREAM_CREATE | FILE, null, {
|
|
103
|
+
fd: this.#fd,
|
|
104
|
+
offset,
|
|
105
|
+
})
|
|
106
|
+
);
|
|
107
|
+
}
|
|
221
108
|
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
109
|
+
writeViaStream(offset) {
|
|
110
|
+
if (this.#hostPreopen) throw "is-directory";
|
|
111
|
+
return outputStreamCreate(
|
|
112
|
+
FILE,
|
|
113
|
+
ioCall(OUTPUT_STREAM_CREATE | FILE, null, { fd: this.#fd, offset })
|
|
114
|
+
);
|
|
115
|
+
}
|
|
226
116
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
const dir = opendirSync(isWindows ? this.#fullPath.slice(1) : this.#fullPath);
|
|
231
|
-
return directoryEntryStreamCreate(dir);
|
|
232
|
-
}
|
|
233
|
-
catch (e) {
|
|
234
|
-
throw convertFsError(e);
|
|
235
|
-
}
|
|
236
|
-
}
|
|
117
|
+
appendViaStream() {
|
|
118
|
+
return this.writeViaStream(this.stat().size);
|
|
119
|
+
}
|
|
237
120
|
|
|
238
|
-
|
|
239
|
-
console.log(`[filesystem] SYNC`, this.id);
|
|
240
|
-
}
|
|
241
|
-
|
|
242
|
-
createDirectoryAt(path) {
|
|
243
|
-
const fullPath = this.#getFullPath(path);
|
|
244
|
-
try {
|
|
245
|
-
mkdirSync(fullPath);
|
|
246
|
-
}
|
|
247
|
-
catch (e) {
|
|
248
|
-
throw convertFsError(e);
|
|
249
|
-
}
|
|
250
|
-
}
|
|
121
|
+
advise(_offset, _length, _advice) { }
|
|
251
122
|
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
}
|
|
261
|
-
const type = lookupType(stats);
|
|
262
|
-
return {
|
|
263
|
-
type,
|
|
264
|
-
linkCount: stats.nlink,
|
|
265
|
-
size: stats.size,
|
|
266
|
-
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
267
|
-
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
268
|
-
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
269
|
-
};
|
|
270
|
-
}
|
|
123
|
+
syncData() {
|
|
124
|
+
if (this.#hostPreopen) throw "invalid";
|
|
125
|
+
try {
|
|
126
|
+
fdatasyncSync(this.#fd);
|
|
127
|
+
} catch (e) {
|
|
128
|
+
throw convertFsError(e);
|
|
129
|
+
}
|
|
130
|
+
}
|
|
271
131
|
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(isWindows ? fullPath.slice(1) : fullPath, { bigint: true });
|
|
277
|
-
}
|
|
278
|
-
catch (e) {
|
|
279
|
-
convertFsError(e);
|
|
280
|
-
}
|
|
281
|
-
const type = lookupType(stats);
|
|
282
|
-
return {
|
|
283
|
-
type,
|
|
284
|
-
linkCount: stats.nlink,
|
|
285
|
-
size: stats.size,
|
|
286
|
-
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
287
|
-
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
288
|
-
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
289
|
-
};
|
|
290
|
-
}
|
|
132
|
+
getFlags() {
|
|
133
|
+
if (this.#hostPreopen) throw "invalid";
|
|
134
|
+
return this.#mode;
|
|
135
|
+
}
|
|
291
136
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
137
|
+
getType() {
|
|
138
|
+
if (this.#hostPreopen) return "directory";
|
|
139
|
+
const stats = fstatSync(this.#fd);
|
|
140
|
+
return lookupType(stats);
|
|
141
|
+
}
|
|
295
142
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
143
|
+
setSize(size) {
|
|
144
|
+
if (this.#hostPreopen) throw "is-directory";
|
|
145
|
+
try {
|
|
146
|
+
ftruncateSync(this.#fd, Number(size));
|
|
147
|
+
} catch (e) {
|
|
148
|
+
throw convertFsError(e);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
299
151
|
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
if (modes.readable)
|
|
324
|
-
fsMode |= 0o444;
|
|
325
|
-
if (modes.writeable)
|
|
326
|
-
fsMode |= 0o222;
|
|
327
|
-
if (modes.executable)
|
|
328
|
-
fsMode |= 0o111;
|
|
329
|
-
|
|
330
|
-
try {
|
|
331
|
-
const fd = openSync(isWindows ? fullPath.slice(1) : fullPath, fsOpenFlags, fsMode);
|
|
332
|
-
return descriptorCreate(fd, fullPath);
|
|
333
|
-
}
|
|
334
|
-
catch (e) {
|
|
335
|
-
throw convertFsError(e);
|
|
336
|
-
}
|
|
337
|
-
}
|
|
152
|
+
setTimes(dataAccessTimestamp, dataModificationTimestamp) {
|
|
153
|
+
if (this.#hostPreopen) throw "invalid";
|
|
154
|
+
let stats;
|
|
155
|
+
if (
|
|
156
|
+
dataAccessTimestamp.tag === "no-change" ||
|
|
157
|
+
dataModificationTimestamp.tag === "no-change"
|
|
158
|
+
)
|
|
159
|
+
stats = this.stat();
|
|
160
|
+
const atime = this.#getNewTimestamp(
|
|
161
|
+
dataAccessTimestamp,
|
|
162
|
+
dataAccessTimestamp.tag === "no-change" && stats.dataAccessTimestamp
|
|
163
|
+
);
|
|
164
|
+
const mtime = this.#getNewTimestamp(
|
|
165
|
+
dataModificationTimestamp,
|
|
166
|
+
dataModificationTimestamp.tag === "no-change" &&
|
|
167
|
+
stats.dataModificationTimestamp
|
|
168
|
+
);
|
|
169
|
+
try {
|
|
170
|
+
futimesSync(this.#fd, atime, mtime);
|
|
171
|
+
} catch (e) {
|
|
172
|
+
throw convertFsError(e);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
338
175
|
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
176
|
+
#getNewTimestamp(newTimestamp, maybeNow) {
|
|
177
|
+
switch (newTimestamp.tag) {
|
|
178
|
+
case "no-change":
|
|
179
|
+
return timestampToMs(maybeNow);
|
|
180
|
+
case "now":
|
|
181
|
+
return Math.floor(Date.now() / 1e3);
|
|
182
|
+
case "timestamp":
|
|
183
|
+
return timestampToMs(newTimestamp.val);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
342
186
|
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
187
|
+
read(length, offset) {
|
|
188
|
+
if (!this.#fullPath) throw "bad-descriptor";
|
|
189
|
+
const buf = new Uint8Array(length);
|
|
190
|
+
const bytesRead = readSync(this.#fd, buf, Number(offset), length, 0);
|
|
191
|
+
const out = new Uint8Array(buf.buffer, 0, bytesRead);
|
|
192
|
+
return [out, bytesRead === 0 ? "ended" : "open"];
|
|
193
|
+
}
|
|
346
194
|
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
195
|
+
write(buffer, offset) {
|
|
196
|
+
if (!this.#fullPath) throw "bad-descriptor";
|
|
197
|
+
return BigInt(
|
|
198
|
+
writeSync(this.#fd, buffer, Number(offset), buffer.byteLength - offset, 0)
|
|
199
|
+
);
|
|
200
|
+
}
|
|
350
201
|
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
202
|
+
readDirectory() {
|
|
203
|
+
if (!this.#fullPath) throw "bad-descriptor";
|
|
204
|
+
try {
|
|
205
|
+
const dir = opendirSync(this.#fullPath);
|
|
206
|
+
return directoryEntryStreamCreate(dir);
|
|
207
|
+
} catch (e) {
|
|
208
|
+
throw convertFsError(e);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
354
211
|
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
212
|
+
sync() {
|
|
213
|
+
if (this.#hostPreopen) throw "invalid";
|
|
214
|
+
try {
|
|
215
|
+
fsyncSync(this.#fd);
|
|
216
|
+
} catch (e) {
|
|
217
|
+
throw convertFsError(e);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
358
220
|
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
221
|
+
createDirectoryAt(path) {
|
|
222
|
+
const fullPath = this.#getFullPath(path);
|
|
223
|
+
try {
|
|
224
|
+
mkdirSync(fullPath);
|
|
225
|
+
} catch (e) {
|
|
226
|
+
throw convertFsError(e);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
362
229
|
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
230
|
+
stat() {
|
|
231
|
+
if (this.#hostPreopen) throw "invalid";
|
|
232
|
+
let stats;
|
|
233
|
+
try {
|
|
234
|
+
stats = fstatSync(this.#fd, { bigint: true });
|
|
235
|
+
} catch (e) {
|
|
236
|
+
throw convertFsError(e);
|
|
237
|
+
}
|
|
238
|
+
const type = lookupType(stats);
|
|
239
|
+
return {
|
|
240
|
+
type,
|
|
241
|
+
linkCount: stats.nlink,
|
|
242
|
+
size: stats.size,
|
|
243
|
+
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
244
|
+
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
245
|
+
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
246
|
+
};
|
|
247
|
+
}
|
|
366
248
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
249
|
+
statAt(pathFlags, path) {
|
|
250
|
+
const fullPath = this.#getFullPath(path, false);
|
|
251
|
+
let stats;
|
|
252
|
+
try {
|
|
253
|
+
stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, { bigint: true });
|
|
254
|
+
} catch (e) {
|
|
255
|
+
throw convertFsError(e);
|
|
256
|
+
}
|
|
257
|
+
const type = lookupType(stats);
|
|
258
|
+
return {
|
|
259
|
+
type,
|
|
260
|
+
linkCount: stats.nlink,
|
|
261
|
+
size: stats.size,
|
|
262
|
+
dataAccessTimestamp: nsToDateTime(stats.atimeNs),
|
|
263
|
+
dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
|
|
264
|
+
statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
|
|
265
|
+
};
|
|
266
|
+
}
|
|
370
267
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
268
|
+
setTimesAt(pathFlags, path, dataAccessTimestamp, dataModificationTimestamp) {
|
|
269
|
+
const fullPath = this.#getFullPath(path, false);
|
|
270
|
+
let stats;
|
|
271
|
+
if (
|
|
272
|
+
dataAccessTimestamp.tag === "no-change" ||
|
|
273
|
+
dataModificationTimestamp.tag === "no-change"
|
|
274
|
+
)
|
|
275
|
+
stats = this.stat();
|
|
276
|
+
const atime = this.#getNewTimestamp(
|
|
277
|
+
dataAccessTimestamp,
|
|
278
|
+
dataAccessTimestamp.tag === "no-change" && stats.dataAccessTimestamp
|
|
279
|
+
);
|
|
280
|
+
const mtime = this.#getNewTimestamp(
|
|
281
|
+
dataModificationTimestamp,
|
|
282
|
+
dataModificationTimestamp.tag === "no-change" &&
|
|
283
|
+
stats.dataModificationTimestamp
|
|
284
|
+
);
|
|
285
|
+
try {
|
|
286
|
+
(pathFlags.symlinkFollow ? utimesSync : lutimesSync)(
|
|
287
|
+
fullPath,
|
|
288
|
+
atime,
|
|
289
|
+
mtime
|
|
290
|
+
);
|
|
291
|
+
} catch (e) {
|
|
292
|
+
throw convertFsError(e);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
374
295
|
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
296
|
+
linkAt(oldPathFlags, oldPath, newDescriptor, newPath) {
|
|
297
|
+
const oldFullPath = this.#getFullPath(oldPath, oldPathFlags.symlinkFollow);
|
|
298
|
+
const newFullPath = newDescriptor.#getFullPath(newPath, false);
|
|
299
|
+
// Windows doesn't automatically fail on trailing slashes
|
|
300
|
+
if (isWindows && newFullPath.endsWith('/'))
|
|
301
|
+
throw 'no-entry';
|
|
302
|
+
try {
|
|
303
|
+
linkSync(oldFullPath, newFullPath);
|
|
304
|
+
} catch (e) {
|
|
305
|
+
throw convertFsError(e);
|
|
306
|
+
}
|
|
307
|
+
}
|
|
378
308
|
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
309
|
+
openAt(pathFlags, path, openFlags, descriptorFlags) {
|
|
310
|
+
const fullPath = this.#getFullPath(path, pathFlags.symlinkFollow);
|
|
311
|
+
let fsOpenFlags = 0x0;
|
|
312
|
+
if (openFlags.create) fsOpenFlags |= constants.O_CREAT;
|
|
313
|
+
if (openFlags.directory) fsOpenFlags |= constants.O_DIRECTORY;
|
|
314
|
+
if (openFlags.exclusive) fsOpenFlags |= constants.O_EXCL;
|
|
315
|
+
if (openFlags.truncate) fsOpenFlags |= constants.O_TRUNC;
|
|
316
|
+
|
|
317
|
+
if (descriptorFlags.read && descriptorFlags.write)
|
|
318
|
+
fsOpenFlags |= constants.O_RDWR;
|
|
319
|
+
else if (descriptorFlags.write) fsOpenFlags |= constants.O_WRONLY;
|
|
320
|
+
else if (descriptorFlags.read) fsOpenFlags |= constants.O_RDONLY;
|
|
321
|
+
if (descriptorFlags.fileIntegritySync) fsOpenFlags |= constants.O_SYNC;
|
|
322
|
+
if (descriptorFlags.dataIntegritySync) fsOpenFlags |= constants.O_DSYNC;
|
|
323
|
+
// Unsupported:
|
|
324
|
+
// if (descriptorFlags.requestedWriteSync)
|
|
325
|
+
// if (descriptorFlags.mutateDirectory)
|
|
326
|
+
|
|
327
|
+
try {
|
|
328
|
+
const fd = openSync(fullPath, fsOpenFlags);
|
|
329
|
+
return descriptorCreate(fd, descriptorFlags, fullPath, preopenEntries);
|
|
330
|
+
} catch (e) {
|
|
331
|
+
throw convertFsError(e);
|
|
332
|
+
}
|
|
333
|
+
}
|
|
382
334
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
335
|
+
readlinkAt(path) {
|
|
336
|
+
const fullPath = this.#getFullPath(path, false);
|
|
337
|
+
try {
|
|
338
|
+
return readlinkSync(fullPath);
|
|
339
|
+
} catch (e) {
|
|
340
|
+
throw convertFsError(e);
|
|
341
|
+
}
|
|
342
|
+
}
|
|
386
343
|
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
344
|
+
removeDirectoryAt(path) {
|
|
345
|
+
const fullPath = this.#getFullPath(path, false);
|
|
346
|
+
try {
|
|
347
|
+
rmdirSync(fullPath);
|
|
348
|
+
} catch (e) {
|
|
349
|
+
if (isWindows && e.code === 'ENOENT')
|
|
350
|
+
throw 'not-directory';
|
|
351
|
+
throw convertFsError(e);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
391
354
|
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
355
|
+
renameAt(oldPath, newDescriptor, newPath) {
|
|
356
|
+
const oldFullPath = this.#getFullPath(oldPath, false);
|
|
357
|
+
const newFullPath = newDescriptor.#getFullPath(newPath, false);
|
|
358
|
+
try {
|
|
359
|
+
renameSync(oldFullPath, newFullPath);
|
|
360
|
+
} catch (e) {
|
|
361
|
+
if (isWindows && e.code === 'EPERM')
|
|
362
|
+
throw 'access';
|
|
363
|
+
throw convertFsError(e);
|
|
364
|
+
}
|
|
365
|
+
}
|
|
403
366
|
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
367
|
+
symlinkAt(target, path) {
|
|
368
|
+
const fullPath = this.#getFullPath(path, false);
|
|
369
|
+
try {
|
|
370
|
+
symlinkSync(target, fullPath);
|
|
371
|
+
} catch (e) {
|
|
372
|
+
if (isWindows && (e.code === 'EPERM' || e.code === 'EEXIST'))
|
|
373
|
+
throw 'no-entry'
|
|
374
|
+
throw convertFsError(e);
|
|
375
|
+
}
|
|
376
|
+
}
|
|
414
377
|
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
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
|
-
}
|
|
378
|
+
unlinkFileAt(path) {
|
|
379
|
+
const fullPath = this.#getFullPath(path, false);
|
|
380
|
+
try {
|
|
381
|
+
unlinkSync(fullPath);
|
|
382
|
+
} catch (e) {
|
|
383
|
+
throw convertFsError(e);
|
|
440
384
|
}
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
isSameObject(other) {
|
|
388
|
+
return other === this;
|
|
389
|
+
}
|
|
441
390
|
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
391
|
+
metadataHash() {
|
|
392
|
+
if (this.#hostPreopen) return { upper: 0n, lower: BigInt(this._id) };
|
|
393
|
+
try {
|
|
394
|
+
const stats = fstatSync(this.#fd, { bigint: true });
|
|
395
|
+
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
396
|
+
} catch (e) {
|
|
397
|
+
throw convertFsError(e);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
446
400
|
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
const
|
|
451
|
-
|
|
401
|
+
metadataHashAt(pathFlags, path) {
|
|
402
|
+
const fullPath = this.#getFullPath(path, false);
|
|
403
|
+
try {
|
|
404
|
+
const stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, { bigint: true });
|
|
405
|
+
return { upper: stats.mtimeNs, lower: stats.ino };
|
|
406
|
+
} catch (e) {
|
|
407
|
+
throw convertFsError(e);
|
|
452
408
|
}
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// TODO: support followSymlinks
|
|
412
|
+
#getFullPath(subpath, _followSymlinks) {
|
|
413
|
+
let descriptor = this;
|
|
414
|
+
if (subpath.indexOf("\\") !== -1) subpath = subpath.replace(/\\/g, "/");
|
|
415
|
+
if (subpath[0] === '/') {
|
|
416
|
+
let bestPreopenMatch = "";
|
|
417
|
+
for (const preopenEntry of preopenEntries) {
|
|
418
|
+
if (
|
|
419
|
+
subpath.startsWith(preopenEntry[1]) &&
|
|
420
|
+
(!bestPreopenMatch ||
|
|
421
|
+
bestPreopenMatch.length < preopenEntry[1].length)
|
|
422
|
+
) {
|
|
423
|
+
bestPreopenMatch = preopenEntry;
|
|
424
|
+
}
|
|
457
425
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
}
|
|
426
|
+
if (!bestPreopenMatch) throw "no-entry";
|
|
427
|
+
descriptor = bestPreopenMatch[0];
|
|
428
|
+
subpath = subpath.slice(bestPreopenMatch[1]);
|
|
429
|
+
if (subpath[0] === "/") subpath = subpath.slice(1);
|
|
430
|
+
}
|
|
431
|
+
if (subpath.startsWith("."))
|
|
432
|
+
subpath = subpath.slice(subpath[1] === "/" ? 2 : 1);
|
|
433
|
+
if (descriptor.#hostPreopen)
|
|
434
|
+
return (
|
|
435
|
+
descriptor.#hostPreopen + (descriptor.#hostPreopen.endsWith('/') ? '' : (subpath.length > 0 ? "/" : "")) + subpath
|
|
436
|
+
);
|
|
437
|
+
return descriptor.#fullPath + (subpath.length > 0 ? "/" : "") + subpath;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
[symbolDispose]() {
|
|
441
|
+
if (this.#fd) closeSync(this.#fd);
|
|
463
442
|
}
|
|
464
443
|
}
|
|
444
|
+
const descriptorCreatePreopen = Descriptor._createPreopen;
|
|
445
|
+
delete Descriptor._createPreopen;
|
|
446
|
+
const descriptorCreate = Descriptor._create;
|
|
447
|
+
delete Descriptor._create;
|
|
448
|
+
|
|
449
|
+
class DirectoryEntryStream {
|
|
450
|
+
#dir;
|
|
451
|
+
readDirectoryEntry() {
|
|
452
|
+
let entry;
|
|
453
|
+
try {
|
|
454
|
+
entry = this.#dir.readSync();
|
|
455
|
+
} catch (e) {
|
|
456
|
+
throw convertFsError(e);
|
|
457
|
+
}
|
|
458
|
+
if (entry === null) {
|
|
459
|
+
return null;
|
|
460
|
+
}
|
|
461
|
+
const name = entry.name;
|
|
462
|
+
const type = lookupType(entry);
|
|
463
|
+
return { name, type };
|
|
464
|
+
}
|
|
465
|
+
[symbolDispose]() {
|
|
466
|
+
this.#dir.closeSync();
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static _create(dir) {
|
|
470
|
+
const dirStream = new DirectoryEntryStream();
|
|
471
|
+
dirStream.#dir = dir;
|
|
472
|
+
return dirStream;
|
|
473
|
+
}
|
|
474
|
+
}
|
|
475
|
+
const directoryEntryStreamCreate = DirectoryEntryStream._create;
|
|
476
|
+
delete DirectoryEntryStream._create;
|
|
477
|
+
|
|
478
|
+
let preopenEntries = [];
|
|
479
|
+
|
|
480
|
+
export const preopens = {
|
|
481
|
+
Descriptor,
|
|
482
|
+
getDirectories() {
|
|
483
|
+
return preopenEntries;
|
|
484
|
+
},
|
|
485
|
+
};
|
|
465
486
|
|
|
466
|
-
|
|
487
|
+
_addPreopen("/", isWindows ? "//" : "/");
|
|
467
488
|
|
|
468
|
-
|
|
489
|
+
export const types = {
|
|
490
|
+
Descriptor,
|
|
491
|
+
DirectoryEntryStream,
|
|
492
|
+
};
|
|
493
|
+
|
|
494
|
+
export function _setPreopens(preopens) {
|
|
495
|
+
preopenEntries = [];
|
|
496
|
+
for (const [virtualPath, hostPreopen] of Object.entries(preopens)) {
|
|
497
|
+
_addPreopen(virtualPath, hostPreopen);
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
export function _addPreopen(virtualPath, hostPreopen) {
|
|
502
|
+
const preopenEntry = [descriptorCreatePreopen(hostPreopen), virtualPath];
|
|
503
|
+
preopenEntries.push(preopenEntry);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
function convertFsError(e) {
|
|
469
507
|
switch (e.code) {
|
|
470
|
-
case
|
|
471
|
-
|
|
472
|
-
case
|
|
473
|
-
case
|
|
474
|
-
|
|
475
|
-
case
|
|
476
|
-
|
|
477
|
-
case
|
|
478
|
-
|
|
479
|
-
case
|
|
480
|
-
|
|
481
|
-
case
|
|
482
|
-
|
|
483
|
-
case
|
|
484
|
-
|
|
485
|
-
case
|
|
486
|
-
|
|
487
|
-
case
|
|
488
|
-
|
|
489
|
-
case
|
|
490
|
-
|
|
491
|
-
case
|
|
492
|
-
|
|
493
|
-
case
|
|
494
|
-
|
|
495
|
-
case
|
|
496
|
-
|
|
497
|
-
case
|
|
498
|
-
|
|
499
|
-
case
|
|
500
|
-
|
|
501
|
-
case
|
|
502
|
-
|
|
503
|
-
case
|
|
504
|
-
|
|
505
|
-
case
|
|
506
|
-
|
|
507
|
-
case
|
|
508
|
-
|
|
508
|
+
case "EACCES":
|
|
509
|
+
return "access";
|
|
510
|
+
case "EAGAIN":
|
|
511
|
+
case "EWOULDBLOCK":
|
|
512
|
+
return "would-block";
|
|
513
|
+
case "EALREADY":
|
|
514
|
+
return "already";
|
|
515
|
+
case "EBADF":
|
|
516
|
+
return "bad-descriptor";
|
|
517
|
+
case "EBUSY":
|
|
518
|
+
return "busy";
|
|
519
|
+
case "EDEADLK":
|
|
520
|
+
return "deadlock";
|
|
521
|
+
case "EDQUOT":
|
|
522
|
+
return "quota";
|
|
523
|
+
case "EEXIST":
|
|
524
|
+
return "exist";
|
|
525
|
+
case "EFBIG":
|
|
526
|
+
return "file-too-large";
|
|
527
|
+
case "EILSEQ":
|
|
528
|
+
return "illegal-byte-sequence";
|
|
529
|
+
case "EINPROGRESS":
|
|
530
|
+
return "in-progress";
|
|
531
|
+
case "EINTR":
|
|
532
|
+
return "interrupted";
|
|
533
|
+
case "EINVAL":
|
|
534
|
+
return "invalid";
|
|
535
|
+
case "EIO":
|
|
536
|
+
return "io";
|
|
537
|
+
case "EISDIR":
|
|
538
|
+
return "is-directory";
|
|
539
|
+
case "ELOOP":
|
|
540
|
+
return "loop";
|
|
541
|
+
case "EMLINK":
|
|
542
|
+
return "too-many-links";
|
|
543
|
+
case "EMSGSIZE":
|
|
544
|
+
return "message-size";
|
|
545
|
+
case "ENAMETOOLONG":
|
|
546
|
+
return "name-too-long";
|
|
547
|
+
case "ENODEV":
|
|
548
|
+
return "no-device";
|
|
549
|
+
case "ENOENT":
|
|
550
|
+
return "no-entry";
|
|
551
|
+
case "ENOLCK":
|
|
552
|
+
return "no-lock";
|
|
553
|
+
case "ENOMEM":
|
|
554
|
+
return "insufficient-memory";
|
|
555
|
+
case "ENOSPC":
|
|
556
|
+
return "insufficient-space";
|
|
557
|
+
case "ENOTDIR":
|
|
558
|
+
return "not-directory";
|
|
559
|
+
case "ENOTEMPTY":
|
|
560
|
+
return "not-empty";
|
|
561
|
+
case "ENOTRECOVERABLE":
|
|
562
|
+
return "not-recoverable";
|
|
563
|
+
case "ENOTSUP":
|
|
564
|
+
return "unsupported";
|
|
565
|
+
case "ENOTTY":
|
|
566
|
+
return "no-tty";
|
|
567
|
+
case "ENXIO":
|
|
568
|
+
return "no-such-device";
|
|
569
|
+
case "EOVERFLOW":
|
|
570
|
+
return "overflow";
|
|
571
|
+
case "EPERM":
|
|
572
|
+
return "not-permitted";
|
|
573
|
+
case "EPIPE":
|
|
574
|
+
return "pipe";
|
|
575
|
+
case "EROFS":
|
|
576
|
+
return "read-only";
|
|
577
|
+
case "ESPIPE":
|
|
578
|
+
return "invalid-seek";
|
|
579
|
+
case "ETXTBSY":
|
|
580
|
+
return "text-file-busy";
|
|
581
|
+
case "EXDEV":
|
|
582
|
+
return "cross-device";
|
|
583
|
+
default:
|
|
584
|
+
throw e;
|
|
509
585
|
}
|
|
510
586
|
}
|
|
587
|
+
|
|
588
|
+
function timestampToMs(timestamp) {
|
|
589
|
+
return Number(timestamp.seconds) * 1000 + timestamp.nanoseconds / 1e9;
|
|
590
|
+
}
|