@bytecodealliance/preview2-shim 0.17.2 → 0.17.3

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.
@@ -1,60 +1,68 @@
1
1
  import {
2
- earlyDispose,
3
- inputStreamCreate,
4
- ioCall,
5
- outputStreamCreate,
6
- registerDispose,
7
- } from "../io/worker-io.js";
8
- import { INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE } from "../io/calls.js";
9
- import { FILE } from "../io/calls.js";
2
+ earlyDispose,
3
+ inputStreamCreate,
4
+ ioCall,
5
+ outputStreamCreate,
6
+ registerDispose,
7
+ } from '../io/worker-io.js';
8
+ import { INPUT_STREAM_CREATE, OUTPUT_STREAM_CREATE } from '../io/calls.js';
9
+ import { FILE } from '../io/calls.js';
10
10
  import nodeFs, {
11
- closeSync,
12
- constants,
13
- fdatasyncSync,
14
- fstatSync,
15
- fsyncSync,
16
- ftruncateSync,
17
- futimesSync,
18
- linkSync,
19
- lstatSync,
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";
11
+ closeSync,
12
+ constants,
13
+ fdatasyncSync,
14
+ fstatSync,
15
+ fsyncSync,
16
+ ftruncateSync,
17
+ futimesSync,
18
+ linkSync,
19
+ lstatSync,
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
34
 
35
35
  const lutimesSync = nodeFs.lutimesSync;
36
36
 
37
- const symbolDispose = Symbol.dispose || Symbol.for("dispose");
37
+ const symbolDispose = Symbol.dispose || Symbol.for('dispose');
38
38
 
39
- const isWindows = platform === "win32";
40
- const isMac = platform === "darwin";
39
+ const isWindows = platform === 'win32';
40
+ const isMac = platform === 'darwin';
41
41
 
42
42
  const nsMagnitude = 1_000_000_000_000n;
43
43
  function nsToDateTime(ns) {
44
- const seconds = ns / nsMagnitude;
45
- const nanoseconds = Number(ns % seconds);
46
- return { seconds, nanoseconds };
44
+ const seconds = ns / nsMagnitude;
45
+ const nanoseconds = Number(ns % nsMagnitude);
46
+ return { seconds, nanoseconds };
47
47
  }
48
48
 
49
49
  function lookupType(obj) {
50
- if (obj.isFile()) return "regular-file";
51
- else if (obj.isSocket()) return "socket";
52
- else if (obj.isSymbolicLink()) return "symbolic-link";
53
- else if (obj.isFIFO()) return "fifo";
54
- else if (obj.isDirectory()) return "directory";
55
- else if (obj.isCharacterDevice()) return "character-device";
56
- else if (obj.isBlockDevice()) return "block-device";
57
- return "unknown";
50
+ if (obj.isFile()) {
51
+ return 'regular-file';
52
+ } else if (obj.isSocket()) {
53
+ return 'socket';
54
+ } else if (obj.isSymbolicLink()) {
55
+ return 'symbolic-link';
56
+ } else if (obj.isFIFO()) {
57
+ return 'fifo';
58
+ } else if (obj.isDirectory()) {
59
+ return 'directory';
60
+ } else if (obj.isCharacterDevice()) {
61
+ return 'character-device';
62
+ } else if (obj.isBlockDevice()) {
63
+ return 'block-device';
64
+ }
65
+ return 'unknown';
58
66
  }
59
67
 
60
68
  // Note: This should implement per-segment semantics of openAt, but we cannot
@@ -65,482 +73,608 @@ function lookupType(obj) {
65
73
  * @implements {DescriptorProps}
66
74
  */
67
75
  class Descriptor {
68
- #hostPreopen;
69
- #fd;
70
- #finalizer;
71
- #mode;
72
- #fullPath;
73
-
74
- static _createPreopen(hostPreopen) {
75
- const descriptor = new Descriptor();
76
- descriptor.#hostPreopen = hostPreopen.endsWith("/")
77
- ? hostPreopen.slice(0, -1) || "/"
78
- : hostPreopen;
79
- // Windows requires UNC paths at minimum
80
- if (isWindows) {
81
- descriptor.#hostPreopen = descriptor.#hostPreopen.replace(/\\/g, "/");
82
- if (descriptor.#hostPreopen === "/") descriptor.#hostPreopen = "//";
83
- }
84
- return descriptor;
85
- }
86
-
87
- static _create(fd, mode, fullPath) {
88
- const descriptor = new Descriptor();
89
- descriptor.#fd = fd;
90
- descriptor.#finalizer = registerDispose(descriptor, null, fd, closeSync);
91
- descriptor.#mode = mode;
92
- descriptor.#fullPath = fullPath;
93
- return descriptor;
94
- }
95
-
96
- [symbolDispose]() {
97
- if (this.#finalizer) {
98
- earlyDispose(this.#finalizer);
99
- this.#finalizer = null;
100
- }
101
- }
102
-
103
- readViaStream(offset) {
104
- if (this.#hostPreopen) throw "is-directory";
105
- return inputStreamCreate(
106
- FILE,
107
- ioCall(INPUT_STREAM_CREATE | FILE, null, {
108
- fd: this.#fd,
109
- offset,
110
- })
111
- );
112
- }
113
-
114
- writeViaStream(offset) {
115
- if (this.#hostPreopen) throw "is-directory";
116
- return outputStreamCreate(
117
- FILE,
118
- ioCall(OUTPUT_STREAM_CREATE | FILE, null, { fd: this.#fd, offset })
119
- );
120
- }
121
-
122
- appendViaStream() {
123
- return this.writeViaStream(this.stat().size);
124
- }
125
-
126
- advise(_offset, _length, _advice) {
127
- if (this.getType() === "directory") throw "bad-descriptor";
128
- }
129
-
130
- syncData() {
131
- if (this.#hostPreopen) throw "invalid";
132
- try {
133
- fdatasyncSync(this.#fd);
134
- } catch (e) {
135
- if (e.code === "EPERM") return;
136
- throw convertFsError(e);
137
- }
138
- }
139
-
140
- getFlags() {
141
- return this.#mode;
142
- }
143
-
144
- getType() {
145
- if (this.#hostPreopen) return "directory";
146
- const stats = fstatSync(this.#fd);
147
- return lookupType(stats);
148
- }
149
-
150
- setSize(size) {
151
- if (this.#hostPreopen) throw "is-directory";
152
- try {
153
- ftruncateSync(this.#fd, Number(size));
154
- } catch (e) {
155
- if (isWindows && e.code === 'EPERM')
156
- throw 'access';
157
- throw convertFsError(e);
158
- }
159
- }
160
-
161
- setTimes(dataAccessTimestamp, dataModificationTimestamp) {
162
- if (this.#hostPreopen) throw "invalid";
163
- let stats;
164
- if (
165
- dataAccessTimestamp.tag === "no-change" ||
166
- dataModificationTimestamp.tag === "no-change"
167
- )
168
- stats = this.stat();
169
- const atime = this.#getNewTimestamp(
170
- dataAccessTimestamp,
171
- dataAccessTimestamp.tag === "no-change" && stats.dataAccessTimestamp
172
- );
173
- const mtime = this.#getNewTimestamp(
174
- dataModificationTimestamp,
175
- dataModificationTimestamp.tag === "no-change" &&
176
- stats.dataModificationTimestamp
177
- );
178
- try {
179
- futimesSync(this.#fd, atime, mtime);
180
- } catch (e) {
181
- throw convertFsError(e);
182
- }
183
- }
184
-
185
- #getNewTimestamp(newTimestamp, maybeNow) {
186
- switch (newTimestamp.tag) {
187
- case "no-change":
188
- return timestampToMs(maybeNow);
189
- case "now":
190
- return Math.floor(Date.now() / 1e3);
191
- case "timestamp":
192
- return timestampToMs(newTimestamp.val);
193
- }
194
- }
195
-
196
- read(length, offset) {
197
- if (!this.#fullPath) throw "bad-descriptor";
198
- const buf = new Uint8Array(Number(length));
199
- const bytesRead = readSync(
200
- this.#fd,
201
- buf,
202
- 0,
203
- Number(length),
204
- Number(offset)
205
- );
206
- const out = new Uint8Array(buf.buffer, 0, bytesRead);
207
- return [out, bytesRead === 0 ? "ended" : "open"];
208
- }
209
-
210
- write(buffer, offset) {
211
- if (!this.#fullPath) throw "bad-descriptor";
212
- return BigInt(
213
- writeSync(this.#fd, buffer, 0, buffer.byteLength, Number(offset))
214
- );
215
- }
216
-
217
- readDirectory() {
218
- if (!this.#fullPath) throw "bad-descriptor";
219
- try {
220
- const dir = opendirSync(this.#fullPath);
221
- return directoryEntryStreamCreate(dir);
222
- } catch (e) {
223
- throw convertFsError(e);
224
- }
225
- }
226
-
227
- sync() {
228
- if (this.#hostPreopen) throw "invalid";
229
- try {
230
- fsyncSync(this.#fd);
231
- } catch (e) {
232
- if (e.code === "EPERM") return;
233
- throw convertFsError(e);
234
- }
235
- }
236
-
237
- createDirectoryAt(path) {
238
- const fullPath = this.#getFullPath(path);
239
- try {
240
- mkdirSync(fullPath);
241
- } catch (e) {
242
- throw convertFsError(e);
243
- }
244
- }
245
-
246
- stat() {
247
- if (this.#hostPreopen) throw "invalid";
248
- let stats;
249
- try {
250
- stats = fstatSync(this.#fd, { bigint: true });
251
- } catch (e) {
252
- throw convertFsError(e);
253
- }
254
- const type = lookupType(stats);
255
- return {
256
- type,
257
- linkCount: stats.nlink,
258
- size: stats.size,
259
- dataAccessTimestamp: nsToDateTime(stats.atimeNs),
260
- dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
261
- statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
262
- };
263
- }
264
-
265
- statAt(pathFlags, path) {
266
- const fullPath = this.#getFullPath(path, false);
267
- let stats;
268
- try {
269
- stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, {
270
- bigint: true,
271
- });
272
- } catch (e) {
273
- throw convertFsError(e);
274
- }
275
- const type = lookupType(stats);
276
- return {
277
- type,
278
- linkCount: stats.nlink,
279
- size: stats.size,
280
- dataAccessTimestamp: nsToDateTime(stats.atimeNs),
281
- dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
282
- statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
283
- };
284
- }
285
-
286
- setTimesAt(pathFlags, path, dataAccessTimestamp, dataModificationTimestamp) {
287
- const fullPath = this.#getFullPath(path, false);
288
- let stats;
289
- if (
290
- dataAccessTimestamp.tag === "no-change" ||
291
- dataModificationTimestamp.tag === "no-change"
292
- )
293
- stats = this.stat();
294
- const atime = this.#getNewTimestamp(
295
- dataAccessTimestamp,
296
- dataAccessTimestamp.tag === "no-change" && stats.dataAccessTimestamp
297
- );
298
- const mtime = this.#getNewTimestamp(
299
- dataModificationTimestamp,
300
- dataModificationTimestamp.tag === "no-change" &&
301
- stats.dataModificationTimestamp
302
- );
303
- if (!pathFlags.symlinkFollow && !lutimesSync){
304
- throw new Error("Changing the timestamps of symlinks isn't supported");
305
- }
306
- try {
307
- (pathFlags.symlinkFollow ? utimesSync : lutimesSync)(
308
- fullPath,
309
- atime,
310
- mtime
311
- );
312
- } catch (e) {
313
- throw convertFsError(e);
314
- }
315
- }
316
-
317
- linkAt(oldPathFlags, oldPath, newDescriptor, newPath) {
318
- const oldFullPath = this.#getFullPath(oldPath, oldPathFlags.symlinkFollow);
319
- const newFullPath = newDescriptor.#getFullPath(newPath, false);
320
- // Windows doesn't automatically fail on trailing slashes
321
- if (isWindows && newFullPath.endsWith("/")) throw "no-entry";
322
- try {
323
- linkSync(oldFullPath, newFullPath);
324
- } catch (e) {
325
- throw convertFsError(e);
326
- }
327
- }
328
-
329
- openAt(pathFlags, path, openFlags, descriptorFlags) {
330
- if (preopenEntries.length === 0)
331
- throw "access";
332
- const fullPath = this.#getFullPath(path, pathFlags.symlinkFollow);
333
- let fsOpenFlags = 0x0;
334
- if (openFlags.create) fsOpenFlags |= constants.O_CREAT;
335
- if (openFlags.directory) fsOpenFlags |= constants.O_DIRECTORY;
336
- if (openFlags.exclusive) fsOpenFlags |= constants.O_EXCL;
337
- if (openFlags.truncate) fsOpenFlags |= constants.O_TRUNC;
338
- if (descriptorFlags.read && descriptorFlags.write)
339
- fsOpenFlags |= constants.O_RDWR;
340
- else if (descriptorFlags.write) fsOpenFlags |= constants.O_WRONLY;
341
- else if (descriptorFlags.read) fsOpenFlags |= constants.O_RDONLY;
342
- if (descriptorFlags.fileIntegritySync) fsOpenFlags |= constants.O_SYNC;
343
- if (descriptorFlags.dataIntegritySync) fsOpenFlags |= constants.O_DSYNC;
344
- if (!pathFlags.symlinkFollow) fsOpenFlags |= constants.O_NOFOLLOW;
345
- if (descriptorFlags.requestedWriteSync || descriptorFlags.mutateDirectory)
346
- throw "unsupported";
347
- // Currently throw to match Wasmtime
348
- if (descriptorFlags.fileIntegritySync || descriptorFlags.dataIntegritySync)
349
- throw "unsupported";
350
- if (isWindows) {
351
- if (!pathFlags.symlinkFollow && !openFlags.create) {
352
- let isSymlink = false;
76
+ #hostPreopen;
77
+ #fd;
78
+ #finalizer;
79
+ #mode;
80
+ #fullPath;
81
+
82
+ static _createPreopen(hostPreopen) {
83
+ const descriptor = new Descriptor();
84
+ descriptor.#hostPreopen = hostPreopen.endsWith('/')
85
+ ? hostPreopen.slice(0, -1) || '/'
86
+ : hostPreopen;
87
+ // Windows requires UNC paths at minimum
88
+ if (isWindows) {
89
+ descriptor.#hostPreopen = descriptor.#hostPreopen.replace(
90
+ /\\/g,
91
+ '/'
92
+ );
93
+ if (descriptor.#hostPreopen === '/') {
94
+ descriptor.#hostPreopen = '//';
95
+ }
96
+ }
97
+ return descriptor;
98
+ }
99
+
100
+ static _create(fd, mode, fullPath) {
101
+ const descriptor = new Descriptor();
102
+ descriptor.#fd = fd;
103
+ descriptor.#finalizer = registerDispose(
104
+ descriptor,
105
+ null,
106
+ fd,
107
+ closeSync
108
+ );
109
+ descriptor.#mode = mode;
110
+ descriptor.#fullPath = fullPath;
111
+ return descriptor;
112
+ }
113
+
114
+ [symbolDispose]() {
115
+ if (this.#finalizer) {
116
+ earlyDispose(this.#finalizer);
117
+ this.#finalizer = null;
118
+ }
119
+ }
120
+
121
+ readViaStream(offset) {
122
+ if (this.#hostPreopen) {
123
+ throw 'is-directory';
124
+ }
125
+ return inputStreamCreate(
126
+ FILE,
127
+ ioCall(INPUT_STREAM_CREATE | FILE, null, {
128
+ fd: this.#fd,
129
+ offset,
130
+ })
131
+ );
132
+ }
133
+
134
+ writeViaStream(offset) {
135
+ if (this.#hostPreopen) {
136
+ throw 'is-directory';
137
+ }
138
+ return outputStreamCreate(
139
+ FILE,
140
+ ioCall(OUTPUT_STREAM_CREATE | FILE, null, { fd: this.#fd, offset })
141
+ );
142
+ }
143
+
144
+ appendViaStream() {
145
+ return this.writeViaStream(this.stat().size);
146
+ }
147
+
148
+ advise(_offset, _length, _advice) {
149
+ if (this.getType() === 'directory') {
150
+ throw 'bad-descriptor';
151
+ }
152
+ }
153
+
154
+ syncData() {
155
+ if (this.#hostPreopen) {
156
+ throw 'invalid';
157
+ }
158
+ try {
159
+ fdatasyncSync(this.#fd);
160
+ } catch (e) {
161
+ if (e.code === 'EPERM') {
162
+ return;
163
+ }
164
+ throw convertFsError(e);
165
+ }
166
+ }
167
+
168
+ getFlags() {
169
+ return this.#mode;
170
+ }
171
+
172
+ getType() {
173
+ if (this.#hostPreopen) {
174
+ return 'directory';
175
+ }
176
+ const stats = fstatSync(this.#fd);
177
+ return lookupType(stats);
178
+ }
179
+
180
+ setSize(size) {
181
+ if (this.#hostPreopen) {
182
+ throw 'is-directory';
183
+ }
184
+ try {
185
+ ftruncateSync(this.#fd, Number(size));
186
+ } catch (e) {
187
+ if (isWindows && e.code === 'EPERM') {
188
+ throw 'access';
189
+ }
190
+ throw convertFsError(e);
191
+ }
192
+ }
193
+
194
+ setTimes(dataAccessTimestamp, dataModificationTimestamp) {
195
+ if (this.#hostPreopen) {
196
+ throw 'invalid';
197
+ }
198
+ let stats;
199
+ if (
200
+ dataAccessTimestamp.tag === 'no-change' ||
201
+ dataModificationTimestamp.tag === 'no-change'
202
+ ) {
203
+ stats = this.stat();
204
+ }
205
+ const atime = this.#getNewTimestamp(
206
+ dataAccessTimestamp,
207
+ dataAccessTimestamp.tag === 'no-change' && stats.dataAccessTimestamp
208
+ );
209
+ const mtime = this.#getNewTimestamp(
210
+ dataModificationTimestamp,
211
+ dataModificationTimestamp.tag === 'no-change' &&
212
+ stats.dataModificationTimestamp
213
+ );
214
+ try {
215
+ futimesSync(this.#fd, atime, mtime);
216
+ } catch (e) {
217
+ throw convertFsError(e);
218
+ }
219
+ }
220
+
221
+ #getNewTimestamp(newTimestamp, maybeNow) {
222
+ switch (newTimestamp.tag) {
223
+ case 'no-change':
224
+ return timestampToMs(maybeNow);
225
+ case 'now':
226
+ return Math.floor(Date.now() / 1e3);
227
+ case 'timestamp':
228
+ return timestampToMs(newTimestamp.val);
229
+ }
230
+ }
231
+
232
+ read(length, offset) {
233
+ if (!this.#fullPath) {
234
+ throw 'bad-descriptor';
235
+ }
236
+ const buf = new Uint8Array(Number(length));
237
+ const bytesRead = readSync(
238
+ this.#fd,
239
+ buf,
240
+ 0,
241
+ Number(length),
242
+ Number(offset)
243
+ );
244
+ const out = new Uint8Array(buf.buffer, 0, bytesRead);
245
+ return [out, bytesRead === 0 ? 'ended' : 'open'];
246
+ }
247
+
248
+ write(buffer, offset) {
249
+ if (!this.#fullPath) {
250
+ throw 'bad-descriptor';
251
+ }
252
+ return BigInt(
253
+ writeSync(this.#fd, buffer, 0, buffer.byteLength, Number(offset))
254
+ );
255
+ }
256
+
257
+ readDirectory() {
258
+ if (!this.#fullPath) {
259
+ throw 'bad-descriptor';
260
+ }
261
+ try {
262
+ const dir = opendirSync(this.#fullPath);
263
+ return directoryEntryStreamCreate(dir);
264
+ } catch (e) {
265
+ throw convertFsError(e);
266
+ }
267
+ }
268
+
269
+ sync() {
270
+ if (this.#hostPreopen) {
271
+ throw 'invalid';
272
+ }
273
+ try {
274
+ fsyncSync(this.#fd);
275
+ } catch (e) {
276
+ if (e.code === 'EPERM') {
277
+ return;
278
+ }
279
+ throw convertFsError(e);
280
+ }
281
+ }
282
+
283
+ createDirectoryAt(path) {
284
+ const fullPath = this.#getFullPath(path);
353
285
  try {
354
- isSymlink = lstatSync(fullPath).isSymbolicLink();
355
- } catch {
356
- //
357
- }
358
- if (isSymlink) throw openFlags.directory ? "not-directory" : "loop";
359
- }
360
- if (pathFlags.symlinkFollow && openFlags.directory) {
361
- let isFile = false;
286
+ mkdirSync(fullPath);
287
+ } catch (e) {
288
+ throw convertFsError(e);
289
+ }
290
+ }
291
+
292
+ stat() {
293
+ if (this.#hostPreopen) {
294
+ throw 'invalid';
295
+ }
296
+ let stats;
362
297
  try {
363
- isFile = !statSync(fullPath).isDirectory();
364
- } catch {
365
- //
366
- }
367
- if (isFile) throw "not-directory";
368
- }
369
- }
370
- try {
371
- const fd = openSync(fullPath.endsWith('/') ? fullPath.slice(0, -1) : fullPath, fsOpenFlags);
372
- const descriptor = descriptorCreate(
373
- fd,
374
- descriptorFlags,
375
- fullPath,
376
- preopenEntries
377
- );
378
- if (fullPath.endsWith('/') && descriptor.getType() !== 'directory') {
379
- descriptor[symbolDispose]();
380
- throw "not-directory";
381
- }
382
- return descriptor;
383
- } catch (e) {
384
- if (e.code === "ERR_INVALID_ARG_VALUE")
385
- throw isWindows ? "no-entry" : "invalid";
386
- throw convertFsError(e);
387
- }
388
- }
389
-
390
- readlinkAt(path) {
391
- const fullPath = this.#getFullPath(path, false);
392
- try {
393
- return readlinkSync(fullPath);
394
- } catch (e) {
395
- throw convertFsError(e);
396
- }
397
- }
398
-
399
- removeDirectoryAt(path) {
400
- const fullPath = this.#getFullPath(path, false);
401
- try {
402
- rmdirSync(fullPath);
403
- } catch (e) {
404
- if (isWindows && e.code === "ENOENT") throw "not-directory";
405
- throw convertFsError(e);
406
- }
407
- }
408
-
409
- renameAt(oldPath, newDescriptor, newPath) {
410
- const oldFullPath = this.#getFullPath(oldPath, false);
411
- const newFullPath = newDescriptor.#getFullPath(newPath, false);
412
- try {
413
- renameSync(oldFullPath, newFullPath);
414
- } catch (e) {
415
- if (isWindows && e.code === "EPERM") throw "access";
416
- throw convertFsError(e);
417
- }
418
- }
419
-
420
- symlinkAt(target, path) {
421
- const fullPath = this.#getFullPath(path, false);
422
- if (target.startsWith("/")) throw "not-permitted";
423
- try {
424
- symlinkSync(target, fullPath);
425
- } catch (e) {
426
- if (fullPath.endsWith("/") && e.code === "EEXIST") {
427
- let isDir = false;
298
+ stats = fstatSync(this.#fd, { bigint: true });
299
+ } catch (e) {
300
+ throw convertFsError(e);
301
+ }
302
+ const type = lookupType(stats);
303
+ return {
304
+ type,
305
+ linkCount: stats.nlink,
306
+ size: stats.size,
307
+ dataAccessTimestamp: nsToDateTime(stats.atimeNs),
308
+ dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
309
+ statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
310
+ };
311
+ }
312
+
313
+ statAt(pathFlags, path) {
314
+ const fullPath = this.#getFullPath(path, false);
315
+ let stats;
316
+ try {
317
+ stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, {
318
+ bigint: true,
319
+ });
320
+ } catch (e) {
321
+ throw convertFsError(e);
322
+ }
323
+ const type = lookupType(stats);
324
+ return {
325
+ type,
326
+ linkCount: stats.nlink,
327
+ size: stats.size,
328
+ dataAccessTimestamp: nsToDateTime(stats.atimeNs),
329
+ dataModificationTimestamp: nsToDateTime(stats.mtimeNs),
330
+ statusChangeTimestamp: nsToDateTime(stats.ctimeNs),
331
+ };
332
+ }
333
+
334
+ setTimesAt(
335
+ pathFlags,
336
+ path,
337
+ dataAccessTimestamp,
338
+ dataModificationTimestamp
339
+ ) {
340
+ const fullPath = this.#getFullPath(path, false);
341
+ let stats;
342
+ if (
343
+ dataAccessTimestamp.tag === 'no-change' ||
344
+ dataModificationTimestamp.tag === 'no-change'
345
+ ) {
346
+ stats = this.stat();
347
+ }
348
+ const atime = this.#getNewTimestamp(
349
+ dataAccessTimestamp,
350
+ dataAccessTimestamp.tag === 'no-change' && stats.dataAccessTimestamp
351
+ );
352
+ const mtime = this.#getNewTimestamp(
353
+ dataModificationTimestamp,
354
+ dataModificationTimestamp.tag === 'no-change' &&
355
+ stats.dataModificationTimestamp
356
+ );
357
+ if (!pathFlags.symlinkFollow && !lutimesSync) {
358
+ throw new Error(
359
+ "Changing the timestamps of symlinks isn't supported"
360
+ );
361
+ }
428
362
  try {
429
- isDir = statSync(fullPath).isDirectory();
430
- } catch {
431
- //
432
- }
433
- if (!isDir) throw isWindows ? "no-entry" : "not-directory";
434
- }
435
- if (isWindows) {
436
- if (e.code === "EPERM" || e.code === "EEXIST") throw "no-entry";
437
- }
438
- throw convertFsError(e);
439
- }
440
- }
441
-
442
- unlinkFileAt(path) {
443
- const fullPath = this.#getFullPath(path, false);
444
- try {
445
- if (fullPath.endsWith("/")) {
446
- let isDir = false;
363
+ (pathFlags.symlinkFollow ? utimesSync : lutimesSync)(
364
+ fullPath,
365
+ atime,
366
+ mtime
367
+ );
368
+ } catch (e) {
369
+ throw convertFsError(e);
370
+ }
371
+ }
372
+
373
+ linkAt(oldPathFlags, oldPath, newDescriptor, newPath) {
374
+ const oldFullPath = this.#getFullPath(
375
+ oldPath,
376
+ oldPathFlags.symlinkFollow
377
+ );
378
+ const newFullPath = newDescriptor.#getFullPath(newPath, false);
379
+ // Windows doesn't automatically fail on trailing slashes
380
+ if (isWindows && newFullPath.endsWith('/')) {
381
+ throw 'no-entry';
382
+ }
447
383
  try {
448
- isDir = statSync(fullPath).isDirectory();
449
- } catch {
450
- //
451
- }
452
- throw isDir ? (isWindows ? "access" : (isMac ? "not-permitted" : "is-directory")) : "not-directory";
453
- }
454
- unlinkSync(fullPath);
455
- } catch (e) {
456
- if (isWindows && e.code === "EPERM") throw "access";
457
- throw convertFsError(e);
458
- }
459
- }
460
-
461
- isSameObject(other) {
462
- return other === this;
463
- }
464
-
465
- metadataHash() {
466
- if (this.#hostPreopen) return { upper: 0n, lower: BigInt(this._id) };
467
- try {
468
- const stats = fstatSync(this.#fd, { bigint: true });
469
- return { upper: stats.mtimeNs, lower: stats.ino };
470
- } catch (e) {
471
- throw convertFsError(e);
472
- }
473
- }
474
-
475
- metadataHashAt(pathFlags, path) {
476
- const fullPath = this.#getFullPath(path, false);
477
- try {
478
- const stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(fullPath, {
479
- bigint: true,
480
- });
481
- return { upper: stats.mtimeNs, lower: stats.ino };
482
- } catch (e) {
483
- throw convertFsError(e);
484
- }
485
- }
486
-
487
- // TODO: support followSymlinks
488
- #getFullPath(subpath, _followSymlinks) {
489
- let descriptor = this;
490
- if (subpath.indexOf("\\") !== -1) subpath = subpath.replace(/\\/g, "/");
491
- if (subpath.indexOf("//") !== -1) subpath = subpath.replace(/\/\/+/g, "/");
492
- if (subpath[0] === "/") throw "not-permitted";
493
-
494
- // segment resolution
495
- const segments = [];
496
- let segmentIndex = -1;
497
- for (let i = 0; i < subpath.length; i++) {
498
- // busy reading a segment - only terminate on '/'
499
- if (segmentIndex !== -1) {
500
- if (subpath[i] === "/") {
501
- segments.push(subpath.slice(segmentIndex, i + 1));
502
- segmentIndex = -1;
503
- }
504
- continue;
505
- }
506
- // new segment - check if it is relative
507
- else if (subpath[i] === ".") {
508
- // ../ segment
384
+ linkSync(oldFullPath, newFullPath);
385
+ } catch (e) {
386
+ throw convertFsError(e);
387
+ }
388
+ }
389
+
390
+ openAt(pathFlags, path, openFlags, descriptorFlags) {
391
+ if (preopenEntries.length === 0) {
392
+ throw 'access';
393
+ }
394
+ const fullPath = this.#getFullPath(path, pathFlags.symlinkFollow);
395
+ let fsOpenFlags = 0x0;
396
+ if (openFlags.create) {
397
+ fsOpenFlags |= constants.O_CREAT;
398
+ }
399
+ if (openFlags.directory) {
400
+ fsOpenFlags |= constants.O_DIRECTORY;
401
+ }
402
+ if (openFlags.exclusive) {
403
+ fsOpenFlags |= constants.O_EXCL;
404
+ }
405
+ if (openFlags.truncate) {
406
+ fsOpenFlags |= constants.O_TRUNC;
407
+ }
408
+ if (descriptorFlags.read && descriptorFlags.write) {
409
+ fsOpenFlags |= constants.O_RDWR;
410
+ } else if (descriptorFlags.write) {
411
+ fsOpenFlags |= constants.O_WRONLY;
412
+ } else if (descriptorFlags.read) {
413
+ fsOpenFlags |= constants.O_RDONLY;
414
+ }
415
+ if (descriptorFlags.fileIntegritySync) {
416
+ fsOpenFlags |= constants.O_SYNC;
417
+ }
418
+ if (descriptorFlags.dataIntegritySync) {
419
+ fsOpenFlags |= constants.O_DSYNC;
420
+ }
421
+ if (!pathFlags.symlinkFollow) {
422
+ fsOpenFlags |= constants.O_NOFOLLOW;
423
+ }
424
+ if (
425
+ descriptorFlags.requestedWriteSync ||
426
+ descriptorFlags.mutateDirectory
427
+ ) {
428
+ throw 'unsupported';
429
+ }
430
+ // Currently throw to match Wasmtime
509
431
  if (
510
- subpath[i + 1] === "." &&
511
- (subpath[i + 2] === "/" || i + 2 === subpath.length)
432
+ descriptorFlags.fileIntegritySync ||
433
+ descriptorFlags.dataIntegritySync
512
434
  ) {
513
- if (segments.pop() === undefined) throw "not-permitted";
514
- i += 2;
515
- continue;
516
- }
517
- // ./ segment
518
- else if (subpath[i + 1] === "/" || i + 1 === subpath.length) {
519
- i += 1;
520
- continue;
521
- }
522
- }
523
- // it is the start of a new segment
524
- while (subpath[i] === "/") i++;
525
- segmentIndex = i;
526
- }
527
- // finish reading out the last segment
528
- if (segmentIndex !== -1) segments.push(subpath.slice(segmentIndex));
529
-
530
- subpath = segments.join("");
531
-
532
- if (descriptor.#hostPreopen)
533
- return (
534
- descriptor.#hostPreopen +
535
- (descriptor.#hostPreopen.endsWith("/")
536
- ? ""
537
- : subpath.length > 0
538
- ? "/"
539
- : "") +
540
- subpath
541
- );
542
- return descriptor.#fullPath + (subpath.length > 0 ? "/" : "") + subpath;
543
- }
435
+ throw 'unsupported';
436
+ }
437
+ if (isWindows) {
438
+ if (!pathFlags.symlinkFollow && !openFlags.create) {
439
+ let isSymlink = false;
440
+ try {
441
+ isSymlink = lstatSync(fullPath).isSymbolicLink();
442
+ } catch {
443
+ //
444
+ }
445
+ if (isSymlink) {
446
+ throw openFlags.directory ? 'not-directory' : 'loop';
447
+ }
448
+ }
449
+ if (pathFlags.symlinkFollow && openFlags.directory) {
450
+ let isFile = false;
451
+ try {
452
+ isFile = !statSync(fullPath).isDirectory();
453
+ } catch {
454
+ //
455
+ }
456
+ if (isFile) {
457
+ throw 'not-directory';
458
+ }
459
+ }
460
+ }
461
+ try {
462
+ const fd = openSync(
463
+ fullPath.endsWith('/') ? fullPath.slice(0, -1) : fullPath,
464
+ fsOpenFlags
465
+ );
466
+ const descriptor = descriptorCreate(
467
+ fd,
468
+ descriptorFlags,
469
+ fullPath,
470
+ preopenEntries
471
+ );
472
+ if (
473
+ fullPath.endsWith('/') &&
474
+ descriptor.getType() !== 'directory'
475
+ ) {
476
+ descriptor[symbolDispose]();
477
+ throw 'not-directory';
478
+ }
479
+ return descriptor;
480
+ } catch (e) {
481
+ if (e.code === 'ERR_INVALID_ARG_VALUE') {
482
+ throw isWindows ? 'no-entry' : 'invalid';
483
+ }
484
+ throw convertFsError(e);
485
+ }
486
+ }
487
+
488
+ readlinkAt(path) {
489
+ const fullPath = this.#getFullPath(path, false);
490
+ try {
491
+ return readlinkSync(fullPath);
492
+ } catch (e) {
493
+ throw convertFsError(e);
494
+ }
495
+ }
496
+
497
+ removeDirectoryAt(path) {
498
+ const fullPath = this.#getFullPath(path, false);
499
+ try {
500
+ rmdirSync(fullPath);
501
+ } catch (e) {
502
+ if (isWindows && e.code === 'ENOENT') {
503
+ throw 'not-directory';
504
+ }
505
+ throw convertFsError(e);
506
+ }
507
+ }
508
+
509
+ renameAt(oldPath, newDescriptor, newPath) {
510
+ const oldFullPath = this.#getFullPath(oldPath, false);
511
+ const newFullPath = newDescriptor.#getFullPath(newPath, false);
512
+ try {
513
+ renameSync(oldFullPath, newFullPath);
514
+ } catch (e) {
515
+ if (isWindows && e.code === 'EPERM') {
516
+ throw 'access';
517
+ }
518
+ throw convertFsError(e);
519
+ }
520
+ }
521
+
522
+ symlinkAt(target, path) {
523
+ const fullPath = this.#getFullPath(path, false);
524
+ if (target.startsWith('/')) {
525
+ throw 'not-permitted';
526
+ }
527
+ try {
528
+ symlinkSync(target, fullPath);
529
+ } catch (e) {
530
+ if (fullPath.endsWith('/') && e.code === 'EEXIST') {
531
+ let isDir = false;
532
+ try {
533
+ isDir = statSync(fullPath).isDirectory();
534
+ } catch {
535
+ //
536
+ }
537
+ if (!isDir) {
538
+ throw isWindows ? 'no-entry' : 'not-directory';
539
+ }
540
+ }
541
+ if (isWindows) {
542
+ if (e.code === 'EPERM' || e.code === 'EEXIST') {
543
+ throw 'no-entry';
544
+ }
545
+ }
546
+ throw convertFsError(e);
547
+ }
548
+ }
549
+
550
+ unlinkFileAt(path) {
551
+ const fullPath = this.#getFullPath(path, false);
552
+ try {
553
+ if (fullPath.endsWith('/')) {
554
+ let isDir = false;
555
+ try {
556
+ isDir = statSync(fullPath).isDirectory();
557
+ } catch {
558
+ //
559
+ }
560
+ throw isDir
561
+ ? isWindows
562
+ ? 'access'
563
+ : isMac
564
+ ? 'not-permitted'
565
+ : 'is-directory'
566
+ : 'not-directory';
567
+ }
568
+ unlinkSync(fullPath);
569
+ } catch (e) {
570
+ if (isWindows && e.code === 'EPERM') {
571
+ throw 'access';
572
+ }
573
+ throw convertFsError(e);
574
+ }
575
+ }
576
+
577
+ isSameObject(other) {
578
+ return other === this;
579
+ }
580
+
581
+ metadataHash() {
582
+ if (this.#hostPreopen) {
583
+ return { upper: 0n, lower: BigInt(this._id) };
584
+ }
585
+ try {
586
+ const stats = fstatSync(this.#fd, { bigint: true });
587
+ return { upper: stats.mtimeNs, lower: stats.ino };
588
+ } catch (e) {
589
+ throw convertFsError(e);
590
+ }
591
+ }
592
+
593
+ metadataHashAt(pathFlags, path) {
594
+ const fullPath = this.#getFullPath(path, false);
595
+ try {
596
+ const stats = (pathFlags.symlinkFollow ? statSync : lstatSync)(
597
+ fullPath,
598
+ {
599
+ bigint: true,
600
+ }
601
+ );
602
+ return { upper: stats.mtimeNs, lower: stats.ino };
603
+ } catch (e) {
604
+ throw convertFsError(e);
605
+ }
606
+ }
607
+
608
+ // TODO: support followSymlinks
609
+ #getFullPath(subpath, _followSymlinks) {
610
+ let descriptor = this;
611
+ if (subpath.indexOf('\\') !== -1) {
612
+ subpath = subpath.replace(/\\/g, '/');
613
+ }
614
+ if (subpath.indexOf('//') !== -1) {
615
+ subpath = subpath.replace(/\/\/+/g, '/');
616
+ }
617
+ if (subpath[0] === '/') {
618
+ throw 'not-permitted';
619
+ }
620
+
621
+ // segment resolution
622
+ const segments = [];
623
+ let segmentIndex = -1;
624
+ for (let i = 0; i < subpath.length; i++) {
625
+ // busy reading a segment - only terminate on '/'
626
+ if (segmentIndex !== -1) {
627
+ if (subpath[i] === '/') {
628
+ segments.push(subpath.slice(segmentIndex, i + 1));
629
+ segmentIndex = -1;
630
+ }
631
+ continue;
632
+ }
633
+ // new segment - check if it is relative
634
+ else if (subpath[i] === '.') {
635
+ // ../ segment
636
+ if (
637
+ subpath[i + 1] === '.' &&
638
+ (subpath[i + 2] === '/' || i + 2 === subpath.length)
639
+ ) {
640
+ if (segments.pop() === undefined) {
641
+ throw 'not-permitted';
642
+ }
643
+ i += 2;
644
+ continue;
645
+ }
646
+ // ./ segment
647
+ else if (subpath[i + 1] === '/' || i + 1 === subpath.length) {
648
+ i += 1;
649
+ continue;
650
+ }
651
+ }
652
+ // it is the start of a new segment
653
+ while (subpath[i] === '/') {
654
+ i++;
655
+ }
656
+ segmentIndex = i;
657
+ }
658
+ // finish reading out the last segment
659
+ if (segmentIndex !== -1) {
660
+ segments.push(subpath.slice(segmentIndex));
661
+ }
662
+
663
+ subpath = segments.join('');
664
+
665
+ if (descriptor.#hostPreopen) {
666
+ return (
667
+ descriptor.#hostPreopen +
668
+ (descriptor.#hostPreopen.endsWith('/')
669
+ ? ''
670
+ : subpath.length > 0
671
+ ? '/'
672
+ : '') +
673
+ subpath
674
+ );
675
+ }
676
+ return descriptor.#fullPath + (subpath.length > 0 ? '/' : '') + subpath;
677
+ }
544
678
  }
545
679
  const descriptorCreatePreopen = Descriptor._createPreopen;
546
680
  delete Descriptor._createPreopen;
@@ -548,39 +682,39 @@ const descriptorCreate = Descriptor._create;
548
682
  delete Descriptor._create;
549
683
 
550
684
  class DirectoryEntryStream {
551
- #dir;
552
- #finalizer;
553
- readDirectoryEntry() {
554
- let entry;
555
- try {
556
- entry = this.#dir.readSync();
557
- } catch (e) {
558
- throw convertFsError(e);
559
- }
560
- if (entry === null) {
561
- return null;
562
- }
563
- const name = entry.name;
564
- const type = lookupType(entry);
565
- return { name, type };
566
- }
567
- static _create(dir) {
568
- const dirStream = new DirectoryEntryStream();
569
- dirStream.#finalizer = registerDispose(
570
- dirStream,
571
- null,
572
- null,
573
- dir.closeSync.bind(dir)
574
- );
575
- dirStream.#dir = dir;
576
- return dirStream;
577
- }
578
- [symbolDispose]() {
579
- if (this.#finalizer) {
580
- earlyDispose(this.#finalizer);
581
- this.#finalizer = null;
582
- }
583
- }
685
+ #dir;
686
+ #finalizer;
687
+ readDirectoryEntry() {
688
+ let entry;
689
+ try {
690
+ entry = this.#dir.readSync();
691
+ } catch (e) {
692
+ throw convertFsError(e);
693
+ }
694
+ if (entry === null) {
695
+ return null;
696
+ }
697
+ const name = entry.name;
698
+ const type = lookupType(entry);
699
+ return { name, type };
700
+ }
701
+ static _create(dir) {
702
+ const dirStream = new DirectoryEntryStream();
703
+ dirStream.#finalizer = registerDispose(
704
+ dirStream,
705
+ null,
706
+ null,
707
+ dir.closeSync.bind(dir)
708
+ );
709
+ dirStream.#dir = dir;
710
+ return dirStream;
711
+ }
712
+ [symbolDispose]() {
713
+ if (this.#finalizer) {
714
+ earlyDispose(this.#finalizer);
715
+ this.#finalizer = null;
716
+ }
717
+ }
584
718
  }
585
719
  const directoryEntryStreamCreate = DirectoryEntryStream._create;
586
720
  delete DirectoryEntryStream._create;
@@ -588,128 +722,128 @@ delete DirectoryEntryStream._create;
588
722
  let preopenEntries = [];
589
723
 
590
724
  export const preopens = {
591
- Descriptor,
592
- getDirectories() {
593
- return preopenEntries;
594
- },
725
+ Descriptor,
726
+ getDirectories() {
727
+ return preopenEntries;
728
+ },
595
729
  };
596
730
 
597
- _addPreopen("/", isWindows ? "//" : "/");
731
+ _addPreopen('/', isWindows ? '//' : '/');
598
732
 
599
733
  export const types = {
600
- Descriptor,
601
- DirectoryEntryStream,
602
- filesystemErrorCode(err) {
603
- return convertFsError(err.payload);
604
- },
734
+ Descriptor,
735
+ DirectoryEntryStream,
736
+ filesystemErrorCode(err) {
737
+ return convertFsError(err.payload);
738
+ },
605
739
  };
606
740
 
607
741
  export function _setPreopens(preopens) {
608
- preopenEntries = [];
609
- for (const [virtualPath, hostPreopen] of Object.entries(preopens)) {
610
- _addPreopen(virtualPath, hostPreopen);
611
- }
742
+ preopenEntries = [];
743
+ for (const [virtualPath, hostPreopen] of Object.entries(preopens)) {
744
+ _addPreopen(virtualPath, hostPreopen);
745
+ }
612
746
  }
613
747
 
614
748
  export function _addPreopen(virtualPath, hostPreopen) {
615
- const preopenEntry = [descriptorCreatePreopen(hostPreopen), virtualPath];
616
- preopenEntries.push(preopenEntry);
749
+ const preopenEntry = [descriptorCreatePreopen(hostPreopen), virtualPath];
750
+ preopenEntries.push(preopenEntry);
617
751
  }
618
752
 
619
753
  function convertFsError(e) {
620
- switch (e.code) {
621
- case "EACCES":
622
- return "access";
623
- case "EAGAIN":
624
- case "EWOULDBLOCK":
625
- return "would-block";
626
- case "EALREADY":
627
- return "already";
628
- case "EBADF":
629
- return "bad-descriptor";
630
- case "EBUSY":
631
- return "busy";
632
- case "EDEADLK":
633
- return "deadlock";
634
- case "EDQUOT":
635
- return "quota";
636
- case "EEXIST":
637
- return "exist";
638
- case "EFBIG":
639
- return "file-too-large";
640
- case "EILSEQ":
641
- return "illegal-byte-sequence";
642
- case "EINPROGRESS":
643
- return "in-progress";
644
- case "EINTR":
645
- return "interrupted";
646
- case "EINVAL":
647
- return "invalid";
648
- case "EIO":
649
- return "io";
650
- case "EISDIR":
651
- return "is-directory";
652
- case "ELOOP":
653
- return "loop";
654
- case "EMLINK":
655
- return "too-many-links";
656
- case "EMSGSIZE":
657
- return "message-size";
658
- case "ENAMETOOLONG":
659
- return "name-too-long";
660
- case "ENODEV":
661
- return "no-device";
662
- case "ENOENT":
663
- return "no-entry";
664
- case "ENOLCK":
665
- return "no-lock";
666
- case "ENOMEM":
667
- return "insufficient-memory";
668
- case "ENOSPC":
669
- return "insufficient-space";
670
- case "ENOTDIR":
671
- case 'ERR_FS_EISDIR':
672
- return "not-directory";
673
- case "ENOTEMPTY":
674
- return "not-empty";
675
- case "ENOTRECOVERABLE":
676
- return "not-recoverable";
677
- case "ENOTSUP":
678
- return "unsupported";
679
- case "ENOTTY":
680
- return "no-tty";
681
- // windows gives this error for badly structured `//` reads
682
- // this seems like a slightly better error than unknown given
683
- // that it's a common footgun
684
- case -4094:
685
- case "ENXIO":
686
- return "no-such-device";
687
- case "EOVERFLOW":
688
- return "overflow";
689
- case "EPERM":
690
- return "not-permitted";
691
- case "EPIPE":
692
- return "pipe";
693
- case "EROFS":
694
- return "read-only";
695
- case "ESPIPE":
696
- return "invalid-seek";
697
- case "ETXTBSY":
698
- return "text-file-busy";
699
- case "EXDEV":
700
- return "cross-device";
701
- case "UNKNOWN":
702
- switch (e.errno) {
754
+ switch (e.code) {
755
+ case 'EACCES':
756
+ return 'access';
757
+ case 'EAGAIN':
758
+ case 'EWOULDBLOCK':
759
+ return 'would-block';
760
+ case 'EALREADY':
761
+ return 'already';
762
+ case 'EBADF':
763
+ return 'bad-descriptor';
764
+ case 'EBUSY':
765
+ return 'busy';
766
+ case 'EDEADLK':
767
+ return 'deadlock';
768
+ case 'EDQUOT':
769
+ return 'quota';
770
+ case 'EEXIST':
771
+ return 'exist';
772
+ case 'EFBIG':
773
+ return 'file-too-large';
774
+ case 'EILSEQ':
775
+ return 'illegal-byte-sequence';
776
+ case 'EINPROGRESS':
777
+ return 'in-progress';
778
+ case 'EINTR':
779
+ return 'interrupted';
780
+ case 'EINVAL':
781
+ return 'invalid';
782
+ case 'EIO':
783
+ return 'io';
784
+ case 'EISDIR':
785
+ return 'is-directory';
786
+ case 'ELOOP':
787
+ return 'loop';
788
+ case 'EMLINK':
789
+ return 'too-many-links';
790
+ case 'EMSGSIZE':
791
+ return 'message-size';
792
+ case 'ENAMETOOLONG':
793
+ return 'name-too-long';
794
+ case 'ENODEV':
795
+ return 'no-device';
796
+ case 'ENOENT':
797
+ return 'no-entry';
798
+ case 'ENOLCK':
799
+ return 'no-lock';
800
+ case 'ENOMEM':
801
+ return 'insufficient-memory';
802
+ case 'ENOSPC':
803
+ return 'insufficient-space';
804
+ case 'ENOTDIR':
805
+ case 'ERR_FS_EISDIR':
806
+ return 'not-directory';
807
+ case 'ENOTEMPTY':
808
+ return 'not-empty';
809
+ case 'ENOTRECOVERABLE':
810
+ return 'not-recoverable';
811
+ case 'ENOTSUP':
812
+ return 'unsupported';
813
+ case 'ENOTTY':
814
+ return 'no-tty';
815
+ // windows gives this error for badly structured `//` reads
816
+ // this seems like a slightly better error than unknown given
817
+ // that it's a common footgun
703
818
  case -4094:
704
- return "no-such-device";
819
+ case 'ENXIO':
820
+ return 'no-such-device';
821
+ case 'EOVERFLOW':
822
+ return 'overflow';
823
+ case 'EPERM':
824
+ return 'not-permitted';
825
+ case 'EPIPE':
826
+ return 'pipe';
827
+ case 'EROFS':
828
+ return 'read-only';
829
+ case 'ESPIPE':
830
+ return 'invalid-seek';
831
+ case 'ETXTBSY':
832
+ return 'text-file-busy';
833
+ case 'EXDEV':
834
+ return 'cross-device';
835
+ case 'UNKNOWN':
836
+ switch (e.errno) {
837
+ case -4094:
838
+ return 'no-such-device';
839
+ default:
840
+ throw e;
841
+ }
705
842
  default:
706
- throw e;
707
- }
708
- default:
709
- throw e;
710
- }
843
+ throw e;
844
+ }
711
845
  }
712
846
 
713
847
  function timestampToMs(timestamp) {
714
- return Number(timestamp.seconds) * 1000 + timestamp.nanoseconds / 1e9;
848
+ return Number(timestamp.seconds) * 1000 + timestamp.nanoseconds / 1e9;
715
849
  }