@bytecodealliance/preview2-shim 0.27.0 → 0.28.0

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.
@@ -1708,7 +1708,6 @@ function handle(call, id, payload) {
1708
1708
  case OUTPUT_STREAM_BLOCKING_WRITE_AND_FLUSH: {
1709
1709
  const stream = getStreamOrThrow(id);
1710
1710
  if (stream.flushPromise) return stream.flushPromise.then(() => handle(call, id, payload));
1711
- if (payload.byteLength > stream.stream.writableHighWaterMark - stream.stream.writableLength) throw new Error("wasi-io trap: Cannot write more than permitted writable length");
1712
1711
  stream.pollState.ready = false;
1713
1712
  return stream.flushPromise = new Promise((resolve, reject) => {
1714
1713
  if (stream.stream === stdout || stream.stream === stderr) {
@@ -504,10 +504,9 @@ function handle(call, id, payload) {
504
504
  if (stream.flushPromise) {
505
505
  return stream.flushPromise.then(() => handle(call, id, payload));
506
506
  }
507
- if (payload.byteLength >
508
- stream.stream.writableHighWaterMark - stream.stream.writableLength) {
509
- throw new Error("wasi-io trap: Cannot write more than permitted writable length");
510
- }
507
+ // Unlike write(), blocking-write-and-flush does not need a check-write
508
+ // permit. Node may buffer the write above its high-water mark while
509
+ // we wait for its completion callback below.
511
510
  stream.pollState.ready = false;
512
511
  return (stream.flushPromise = new Promise((resolve, reject) => {
513
512
  if (stream.stream === stdout || stream.stream === stderr) {
@@ -10,6 +10,7 @@ declare class Descriptor implements IDescriptor {
10
10
  private _id;
11
11
  static _createPreopen(hostPreopen: any): Descriptor;
12
12
  static _create(fileResourceId: any, mode: any, fullPath: any): Descriptor;
13
+ static _createVirtualDirectory(mode: any, fullPath: any): Descriptor;
13
14
  readViaStream(offset: any): any;
14
15
  writeViaStream(offset: any): any;
15
16
  appendViaStream(): any;
@@ -11,6 +11,23 @@ const MAX_SYMLINKS = 40;
11
11
  function normalizeHostPath(path) {
12
12
  return isWindows ? path.replace(/\\/g, "/") : path;
13
13
  }
14
+ /**
15
+ * Resolve symlinks in a host path.
16
+ *
17
+ * Node's realpathSync() cannot walk namespaced Windows paths (`\\?\C:\...`): it
18
+ * stats the namespace root (`\\?\C:`), which is a volume rather than a directory
19
+ * and fails with EISDIR. Drive and UNC paths resolve the same without the prefix.
20
+ */
21
+ function realHostPath(path) {
22
+ if (isWindows) {
23
+ path = path
24
+ .replace(/^[\\/]{2}\?[\\/]UNC[\\/]/i, "//")
25
+ .replace(/^[\\/]{2}\?[\\/]([a-zA-Z]:)(?=[\\/]|$)/, "$1")
26
+ // A bare drive would otherwise resolve against that drive's cwd
27
+ .replace(/^([a-zA-Z]:)$/, "$1/");
28
+ }
29
+ return normalizeHostPath(realpathSync(path));
30
+ }
14
31
  const nsMagnitude = 1000000000n;
15
32
  function nsToDateTime(ns) {
16
33
  const seconds = ns / nsMagnitude;
@@ -56,6 +73,7 @@ class Descriptor {
56
73
  #finalizer;
57
74
  #mode;
58
75
  #fullPath;
76
+ #virtualDirectory = false;
59
77
  _id = 0;
60
78
  static _createPreopen(hostPreopen) {
61
79
  const descriptor = new Descriptor();
@@ -79,6 +97,13 @@ class Descriptor {
79
97
  descriptor.#fullPath = fullPath;
80
98
  return descriptor;
81
99
  }
100
+ static _createVirtualDirectory(mode, fullPath) {
101
+ const descriptor = new Descriptor();
102
+ descriptor.#mode = mode;
103
+ descriptor.#fullPath = fullPath;
104
+ descriptor.#virtualDirectory = true;
105
+ return descriptor;
106
+ }
82
107
  [symbolDispose]() {
83
108
  if (this.#finalizer) {
84
109
  earlyDispose(this.#finalizer);
@@ -86,7 +111,7 @@ class Descriptor {
86
111
  }
87
112
  }
88
113
  readViaStream(offset) {
89
- if (this.#hostPreopen) {
114
+ if (this.#hostPreopen || this.#virtualDirectory) {
90
115
  throw "is-directory";
91
116
  }
92
117
  return inputStreamCreate(FILE, ioCall(INPUT_STREAM_CREATE | FILE, null, {
@@ -95,7 +120,7 @@ class Descriptor {
95
120
  }));
96
121
  }
97
122
  writeViaStream(offset) {
98
- if (this.#hostPreopen) {
123
+ if (this.#hostPreopen || this.#virtualDirectory) {
99
124
  throw "is-directory";
100
125
  }
101
126
  return outputStreamCreate(FILE, ioCall(OUTPUT_STREAM_CREATE | FILE, null, {
@@ -139,13 +164,13 @@ class Descriptor {
139
164
  return this.#mode;
140
165
  }
141
166
  getType() {
142
- if (this.#hostPreopen) {
167
+ if (this.#hostPreopen || this.#virtualDirectory) {
143
168
  return "directory";
144
169
  }
145
170
  return ioCall(FILESYSTEM_DESCRIPTOR_GET_TYPE, this.#fileResourceId);
146
171
  }
147
172
  setSize(size) {
148
- if (this.#hostPreopen) {
173
+ if (this.#hostPreopen || this.#virtualDirectory) {
149
174
  throw "is-directory";
150
175
  }
151
176
  try {
@@ -187,6 +212,9 @@ class Descriptor {
187
212
  }
188
213
  }
189
214
  read(length, offset) {
215
+ if (this.#virtualDirectory) {
216
+ throw "is-directory";
217
+ }
190
218
  if (!this.#fullPath) {
191
219
  throw "bad-descriptor";
192
220
  }
@@ -197,6 +225,9 @@ class Descriptor {
197
225
  return [bytes, eof];
198
226
  }
199
227
  write(buffer, offset) {
228
+ if (this.#virtualDirectory) {
229
+ throw "is-directory";
230
+ }
200
231
  if (!this.#fullPath) {
201
232
  throw "bad-descriptor";
202
233
  }
@@ -218,7 +249,7 @@ class Descriptor {
218
249
  }
219
250
  }
220
251
  sync() {
221
- if (this.#hostPreopen) {
252
+ if (this.#hostPreopen || this.#virtualDirectory) {
222
253
  throw "invalid";
223
254
  }
224
255
  try {
@@ -246,12 +277,14 @@ class Descriptor {
246
277
  }
247
278
  let stats;
248
279
  try {
249
- stats = ioCall(FILESYSTEM_DESCRIPTOR_STAT, this.#fileResourceId);
280
+ stats = this.#virtualDirectory
281
+ ? statSync(this.#fullPath, { bigint: true })
282
+ : ioCall(FILESYSTEM_DESCRIPTOR_STAT, this.#fileResourceId);
250
283
  }
251
284
  catch (e) {
252
285
  throw convertFsError(e);
253
286
  }
254
- const type = stats.type;
287
+ const type = this.#virtualDirectory ? lookupType(stats) : stats.type;
255
288
  return {
256
289
  type,
257
290
  linkCount: stats.nlink,
@@ -381,6 +414,24 @@ class Descriptor {
381
414
  throw "not-directory";
382
415
  }
383
416
  }
417
+ // Node cannot open a directory with fs.open on Windows (EISDIR).
418
+ // Directory operations use the path, so keep a descriptor without an fd.
419
+ try {
420
+ if (statSync(fullPath).isDirectory()) {
421
+ if (descriptorFlags.write || openFlags.create || openFlags.truncate) {
422
+ throw "is-directory";
423
+ }
424
+ return Descriptor._createVirtualDirectory(descriptorFlags, fullPath);
425
+ }
426
+ }
427
+ catch (e) {
428
+ if (e === "is-directory") {
429
+ throw e;
430
+ }
431
+ if (e.code !== "ENOENT") {
432
+ throw convertFsError(e);
433
+ }
434
+ }
384
435
  }
385
436
  try {
386
437
  const { id, type } = ioCall(FILESYSTEM_DESCRIPTOR_OPEN, null, {
@@ -509,6 +560,10 @@ class Descriptor {
509
560
  if (this.#hostPreopen) {
510
561
  return { upper: 0n, lower: BigInt(this._id) };
511
562
  }
563
+ if (this.#virtualDirectory) {
564
+ const stats = statSync(this.#fullPath, { bigint: true });
565
+ return { upper: stats.mtimeNs, lower: stats.ino };
566
+ }
512
567
  try {
513
568
  return ioCall(FILESYSTEM_DESCRIPTOR_METADATA_HASH, this.#fileResourceId);
514
569
  }
@@ -560,7 +615,7 @@ class Descriptor {
560
615
  }
561
616
  let baseResolved;
562
617
  try {
563
- baseResolved = normalizeHostPath(realpathSync(base));
618
+ baseResolved = realHostPath(base);
564
619
  }
565
620
  catch (error) {
566
621
  throw convertFsError(error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bytecodealliance/preview2-shim",
3
- "version": "0.27.0",
3
+ "version": "0.28.0",
4
4
  "description": "WASI Preview2 shim for JS environments",
5
5
  "homepage": "https://github.com/bytecodealliance/jco#readme",
6
6
  "bugs": {
@@ -64,7 +64,7 @@
64
64
  "@bytecodealliance/jco-node-fs": "0.3.2"
65
65
  },
66
66
  "devDependencies": {
67
- "@bytecodealliance/componentize-js": "0.22.0",
67
+ "@bytecodealliance/componentize-js": "0.23.0",
68
68
  "@bytecodealliance/jco": "1.25.2",
69
69
  "@types/node": "^24.12.4",
70
70
  "mime": "^4.0.7",