@bytecodealliance/preview2-shim 0.17.7 → 0.17.9

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