@ttsc/wasm 0.11.0-dev.20260517

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.
Files changed (80) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +125 -0
  3. package/build/build-wasm.cjs +240 -0
  4. package/build/pack-prepare.cjs +68 -0
  5. package/cmd/ttsc-wasm/main.go +130 -0
  6. package/cmd/ttsc-wasm/main_wasm.go +17 -0
  7. package/dist/go.mod +54 -0
  8. package/dist/ttsc.wasm +0 -0
  9. package/dist/wasm_exec.js +575 -0
  10. package/go.mod +54 -0
  11. package/go.sum +26 -0
  12. package/host/api.go +205 -0
  13. package/host/doc.go +31 -0
  14. package/host/host.go +317 -0
  15. package/host/host_native.go +12 -0
  16. package/host/plugin.go +59 -0
  17. package/lib/src/MemFS.d.ts +68 -0
  18. package/lib/src/MemFS.js +384 -0
  19. package/lib/src/MemFS.js.map +1 -0
  20. package/lib/src/api.d.ts +87 -0
  21. package/lib/src/api.js +20 -0
  22. package/lib/src/api.js.map +1 -0
  23. package/lib/src/index.d.ts +6 -0
  24. package/lib/src/index.js +10 -0
  25. package/lib/src/index.js.map +1 -0
  26. package/lib/src/instantiate.d.ts +27 -0
  27. package/lib/src/instantiate.js +71 -0
  28. package/lib/src/instantiate.js.map +1 -0
  29. package/package.json +49 -0
  30. package/src/MemFS.ts +566 -0
  31. package/src/api.ts +112 -0
  32. package/src/index.ts +28 -0
  33. package/src/instantiate.ts +114 -0
  34. package/vendor/shim/ast/extra-shim.json +4 -0
  35. package/vendor/shim/ast/go.mod +5 -0
  36. package/vendor/shim/ast/lint.go +328 -0
  37. package/vendor/shim/ast/shim.go +220 -0
  38. package/vendor/shim/ast/surface.go +685 -0
  39. package/vendor/shim/bundled/extra-shim.json +1 -0
  40. package/vendor/shim/bundled/go.mod +5 -0
  41. package/vendor/shim/bundled/shim.go +23 -0
  42. package/vendor/shim/checker/extra-shim.json +31 -0
  43. package/vendor/shim/checker/go.mod +5 -0
  44. package/vendor/shim/checker/shim.go +174 -0
  45. package/vendor/shim/compiler/extra-shim.json +4 -0
  46. package/vendor/shim/compiler/go.mod +5 -0
  47. package/vendor/shim/compiler/shim.go +65 -0
  48. package/vendor/shim/core/extra-shim.json +4 -0
  49. package/vendor/shim/core/go.mod +5 -0
  50. package/vendor/shim/core/shim.go +29 -0
  51. package/vendor/shim/diagnosticwriter/go.mod +5 -0
  52. package/vendor/shim/diagnosticwriter/lint.go +145 -0
  53. package/vendor/shim/diagnosticwriter/shim.go +29 -0
  54. package/vendor/shim/lsp/extra-shim.json +4 -0
  55. package/vendor/shim/lsp/go.mod +16 -0
  56. package/vendor/shim/lsp/go.sum +26 -0
  57. package/vendor/shim/lsp/shim.go +42 -0
  58. package/vendor/shim/parser/extra-shim.json +4 -0
  59. package/vendor/shim/parser/go.mod +5 -0
  60. package/vendor/shim/parser/shim.go +61 -0
  61. package/vendor/shim/printer/go.mod +5 -0
  62. package/vendor/shim/printer/shim.go +23 -0
  63. package/vendor/shim/scanner/extra-shim.json +4 -0
  64. package/vendor/shim/scanner/go.mod +5 -0
  65. package/vendor/shim/scanner/shim.go +134 -0
  66. package/vendor/shim/tsoptions/extra-shim.json +4 -0
  67. package/vendor/shim/tsoptions/go.mod +5 -0
  68. package/vendor/shim/tsoptions/shim.go +173 -0
  69. package/vendor/shim/tspath/extra-shim.json +4 -0
  70. package/vendor/shim/tspath/go.mod +5 -0
  71. package/vendor/shim/tspath/shim.go +236 -0
  72. package/vendor/shim/vfs/cachedvfs/extra-shim.json +4 -0
  73. package/vendor/shim/vfs/cachedvfs/go.mod +5 -0
  74. package/vendor/shim/vfs/cachedvfs/shim.go +12 -0
  75. package/vendor/shim/vfs/extra-shim.json +4 -0
  76. package/vendor/shim/vfs/go.mod +5 -0
  77. package/vendor/shim/vfs/osvfs/extra-shim.json +4 -0
  78. package/vendor/shim/vfs/osvfs/go.mod +5 -0
  79. package/vendor/shim/vfs/osvfs/shim.go +13 -0
  80. package/vendor/shim/vfs/shim.go +22 -0
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@ttsc/wasm",
3
+ "version": "0.11.0-dev.20260517",
4
+ "description": "Build in-browser ttsc playgrounds. Compose ttsc + typescript-go with your own plugins via the Go host helper.",
5
+ "main": "lib/src/index.js",
6
+ "types": "lib/src/index.d.ts",
7
+ "files": [
8
+ "dist",
9
+ "lib",
10
+ "src",
11
+ "host",
12
+ "cmd",
13
+ "build",
14
+ "vendor",
15
+ "go.mod",
16
+ "go.sum",
17
+ "README.md"
18
+ ],
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "exports": {
23
+ ".": {
24
+ "types": "./lib/src/index.d.ts",
25
+ "default": "./lib/src/index.js"
26
+ },
27
+ "./package.json": "./package.json"
28
+ },
29
+ "repository": {
30
+ "type": "git",
31
+ "url": "https://github.com/samchon/ttsc"
32
+ },
33
+ "author": "Jeongho Nam",
34
+ "license": "MIT",
35
+ "bugs": {
36
+ "url": "https://github.com/samchon/ttsc/issues"
37
+ },
38
+ "homepage": "https://github.com/samchon/ttsc",
39
+ "devDependencies": {
40
+ "@types/node": "^25.3.0",
41
+ "@typescript/native-preview": "7.0.0-dev.20260515.1",
42
+ "rimraf": "^6.1.2"
43
+ },
44
+ "scripts": {
45
+ "build": "node build/build-wasm.cjs && rimraf lib && tsgo -p .",
46
+ "build:wasm": "node build/build-wasm.cjs",
47
+ "build:ts": "rimraf lib && tsgo -p ."
48
+ }
49
+ }
package/src/MemFS.ts ADDED
@@ -0,0 +1,566 @@
1
+ // MemFS implements just enough of the wasm_exec.js fs interface for an
2
+ // in-browser ttsc wasm to run.
3
+ //
4
+ // Why this exists: wasm_exec.js routes Go's syscalls (open / stat / read /
5
+ // write / readdir / mkdir / ...) to `globalThis.fs`. In Node, that maps to the
6
+ // real fs module. In browsers we have to supply our own. The set we implement
7
+ // here is the smallest subset typescript-go actually exercises for a project
8
+ // compile of a tiny in-memory project.
9
+ //
10
+ // All callbacks follow the Node "errback" convention: callback(err, result).
11
+ // Errors carry a `code` (e.g. ENOENT, EBADF) so Go's os package sees them as
12
+ // proper os.PathError values.
13
+ //
14
+ // Files are stored as Uint8Array. Text files written via writeFile are
15
+ // encoded by the caller; reads return the raw bytes.
16
+
17
+ /** Mode bits exposed by stat. We only differentiate file vs directory. */
18
+ const S_IFDIR = 0o040000;
19
+ const S_IFREG = 0o100000;
20
+ const DEFAULT_FILE_MODE = S_IFREG | 0o644;
21
+ const DEFAULT_DIR_MODE = S_IFDIR | 0o755;
22
+
23
+ export interface IWasmExecFS {
24
+ constants: Record<string, number>;
25
+ writeSync(fd: number, buf: Uint8Array): number;
26
+ write(
27
+ fd: number,
28
+ buf: Uint8Array,
29
+ offset: number,
30
+ length: number,
31
+ position: number | null,
32
+ callback: (err: NodeJS.ErrnoException | null, n: number) => void,
33
+ ): void;
34
+ open(
35
+ path: string,
36
+ flags: number,
37
+ mode: number,
38
+ callback: (err: NodeJS.ErrnoException | null, fd: number) => void,
39
+ ): void;
40
+ close(fd: number, callback: (err: NodeJS.ErrnoException | null) => void): void;
41
+ read(
42
+ fd: number,
43
+ buffer: Uint8Array,
44
+ offset: number,
45
+ length: number,
46
+ position: number | null,
47
+ callback: (err: NodeJS.ErrnoException | null, n: number) => void,
48
+ ): void;
49
+ readdir(
50
+ path: string,
51
+ callback: (err: NodeJS.ErrnoException | null, entries: string[]) => void,
52
+ ): void;
53
+ mkdir(
54
+ path: string,
55
+ perm: number,
56
+ callback: (err: NodeJS.ErrnoException | null) => void,
57
+ ): void;
58
+ stat(
59
+ path: string,
60
+ callback: (err: NodeJS.ErrnoException | null, stats: IFileStats) => void,
61
+ ): void;
62
+ lstat(
63
+ path: string,
64
+ callback: (err: NodeJS.ErrnoException | null, stats: IFileStats) => void,
65
+ ): void;
66
+ fstat(
67
+ fd: number,
68
+ callback: (err: NodeJS.ErrnoException | null, stats: IFileStats) => void,
69
+ ): void;
70
+ fsync(fd: number, callback: (err: NodeJS.ErrnoException | null) => void): void;
71
+ unlink(
72
+ path: string,
73
+ callback: (err: NodeJS.ErrnoException | null) => void,
74
+ ): void;
75
+ rename(
76
+ from: string,
77
+ to: string,
78
+ callback: (err: NodeJS.ErrnoException | null) => void,
79
+ ): void;
80
+ rmdir(
81
+ path: string,
82
+ callback: (err: NodeJS.ErrnoException | null) => void,
83
+ ): void;
84
+ chmod(
85
+ path: string,
86
+ mode: number,
87
+ callback: (err: NodeJS.ErrnoException | null) => void,
88
+ ): void;
89
+ fchmod(
90
+ fd: number,
91
+ mode: number,
92
+ callback: (err: NodeJS.ErrnoException | null) => void,
93
+ ): void;
94
+ chown(
95
+ path: string,
96
+ uid: number,
97
+ gid: number,
98
+ callback: (err: NodeJS.ErrnoException | null) => void,
99
+ ): void;
100
+ fchown(
101
+ fd: number,
102
+ uid: number,
103
+ gid: number,
104
+ callback: (err: NodeJS.ErrnoException | null) => void,
105
+ ): void;
106
+ lchown(
107
+ path: string,
108
+ uid: number,
109
+ gid: number,
110
+ callback: (err: NodeJS.ErrnoException | null) => void,
111
+ ): void;
112
+ utimes(
113
+ path: string,
114
+ atime: number,
115
+ mtime: number,
116
+ callback: (err: NodeJS.ErrnoException | null) => void,
117
+ ): void;
118
+ link(
119
+ path: string,
120
+ link: string,
121
+ callback: (err: NodeJS.ErrnoException | null) => void,
122
+ ): void;
123
+ symlink(
124
+ path: string,
125
+ link: string,
126
+ callback: (err: NodeJS.ErrnoException | null) => void,
127
+ ): void;
128
+ readlink(
129
+ path: string,
130
+ callback: (err: NodeJS.ErrnoException | null, link: string) => void,
131
+ ): void;
132
+ truncate(
133
+ path: string,
134
+ length: number,
135
+ callback: (err: NodeJS.ErrnoException | null) => void,
136
+ ): void;
137
+ ftruncate(
138
+ fd: number,
139
+ length: number,
140
+ callback: (err: NodeJS.ErrnoException | null) => void,
141
+ ): void;
142
+ }
143
+
144
+ export interface IFileStats {
145
+ isDirectory(): boolean;
146
+ isFile(): boolean;
147
+ size: number;
148
+ mode: number;
149
+ mtimeMs: number;
150
+ atimeMs: number;
151
+ ctimeMs: number;
152
+ dev: number;
153
+ ino: number;
154
+ nlink: number;
155
+ uid: number;
156
+ gid: number;
157
+ rdev: number;
158
+ blksize: number;
159
+ blocks: number;
160
+ }
161
+
162
+ interface INode {
163
+ kind: "file" | "dir";
164
+ data: Uint8Array;
165
+ mtimeMs: number;
166
+ }
167
+
168
+ export class MemFSError extends Error {
169
+ public code: string;
170
+ public errno: number;
171
+ public path?: string;
172
+ public syscall?: string;
173
+ constructor(code: string, syscall: string, path?: string) {
174
+ super(`${code}: ${syscall} ${path ?? ""}`.trim());
175
+ this.code = code;
176
+ this.errno = errnoForCode(code);
177
+ this.path = path;
178
+ this.syscall = syscall;
179
+ }
180
+ }
181
+
182
+ function errnoForCode(code: string): number {
183
+ switch (code) {
184
+ case "ENOENT":
185
+ return -2;
186
+ case "EBADF":
187
+ return -9;
188
+ case "EEXIST":
189
+ return -17;
190
+ case "ENOTDIR":
191
+ return -20;
192
+ case "EISDIR":
193
+ return -21;
194
+ default:
195
+ return -1;
196
+ }
197
+ }
198
+
199
+ export interface IMemFSHost {
200
+ fs: IWasmExecFS;
201
+ writeFile(path: string, data: string | Uint8Array): void;
202
+ readFile(path: string): Uint8Array | null;
203
+ readFileText(path: string): string | null;
204
+ exists(path: string): boolean;
205
+ mkdirp(path: string): void;
206
+ stdout: { buffer: string };
207
+ stderr: { buffer: string };
208
+ resetStdio(): void;
209
+ }
210
+
211
+ const encoder = new TextEncoder();
212
+ const decoder = new TextDecoder();
213
+
214
+ function normalize(p: string): string {
215
+ if (!p) return "/";
216
+ const parts = p.replace(/\\/g, "/").split("/").filter(Boolean);
217
+ const stack: string[] = [];
218
+ for (const part of parts) {
219
+ if (part === ".") continue;
220
+ if (part === "..") {
221
+ stack.pop();
222
+ continue;
223
+ }
224
+ stack.push(part);
225
+ }
226
+ return "/" + stack.join("/");
227
+ }
228
+
229
+ export function createMemFS(): IMemFSHost {
230
+ const nodes = new Map<string, INode>();
231
+ nodes.set("/", { kind: "dir", data: new Uint8Array(), mtimeMs: Date.now() });
232
+
233
+ const stdout = { buffer: "" };
234
+ const stderr = { buffer: "" };
235
+
236
+ const fdTable = new Map<
237
+ number,
238
+ { path: string; position: number; isStdout?: boolean; isStderr?: boolean }
239
+ >();
240
+ let nextFd = 100;
241
+ // Reserve 1/2 for stdout/stderr writeSync routing.
242
+ fdTable.set(1, { path: "/dev/stdout", position: 0, isStdout: true });
243
+ fdTable.set(2, { path: "/dev/stderr", position: 0, isStderr: true });
244
+
245
+ function ensureParentDirs(p: string): void {
246
+ const segments = normalize(p).split("/").filter(Boolean);
247
+ segments.pop();
248
+ let cursor = "";
249
+ for (const seg of segments) {
250
+ cursor += "/" + seg;
251
+ if (!nodes.has(cursor)) {
252
+ nodes.set(cursor, {
253
+ kind: "dir",
254
+ data: new Uint8Array(),
255
+ mtimeMs: Date.now(),
256
+ });
257
+ }
258
+ }
259
+ }
260
+
261
+ function mkdirp(p: string): void {
262
+ const segments = normalize(p).split("/").filter(Boolean);
263
+ let cursor = "";
264
+ for (const seg of segments) {
265
+ cursor += "/" + seg;
266
+ const existing = nodes.get(cursor);
267
+ if (!existing) {
268
+ nodes.set(cursor, {
269
+ kind: "dir",
270
+ data: new Uint8Array(),
271
+ mtimeMs: Date.now(),
272
+ });
273
+ } else if (existing.kind !== "dir") {
274
+ throw new MemFSError("ENOTDIR", "mkdir", cursor);
275
+ }
276
+ }
277
+ }
278
+
279
+ function writeFile(p: string, data: string | Uint8Array): void {
280
+ const norm = normalize(p);
281
+ ensureParentDirs(norm);
282
+ const bytes = typeof data === "string" ? encoder.encode(data) : data;
283
+ nodes.set(norm, { kind: "file", data: bytes, mtimeMs: Date.now() });
284
+ }
285
+
286
+ function readFile(p: string): Uint8Array | null {
287
+ const node = nodes.get(normalize(p));
288
+ if (!node || node.kind !== "file") return null;
289
+ return node.data;
290
+ }
291
+
292
+ function readFileText(p: string): string | null {
293
+ const bytes = readFile(p);
294
+ return bytes === null ? null : decoder.decode(bytes);
295
+ }
296
+
297
+ function exists(p: string): boolean {
298
+ return nodes.has(normalize(p));
299
+ }
300
+
301
+ function statSync(p: string): IFileStats {
302
+ const norm = normalize(p);
303
+ const node = nodes.get(norm);
304
+ if (!node) throw new MemFSError("ENOENT", "stat", norm);
305
+ return makeStats(node);
306
+ }
307
+
308
+ function makeStats(node: INode): IFileStats {
309
+ const isDir = node.kind === "dir";
310
+ return {
311
+ isDirectory: () => isDir,
312
+ isFile: () => !isDir,
313
+ size: node.data.byteLength,
314
+ mode: isDir ? DEFAULT_DIR_MODE : DEFAULT_FILE_MODE,
315
+ mtimeMs: node.mtimeMs,
316
+ atimeMs: node.mtimeMs,
317
+ ctimeMs: node.mtimeMs,
318
+ dev: 0,
319
+ ino: 0,
320
+ nlink: 1,
321
+ uid: 0,
322
+ gid: 0,
323
+ rdev: 0,
324
+ blksize: 4096,
325
+ blocks: Math.ceil(node.data.byteLength / 512),
326
+ };
327
+ }
328
+
329
+ function readdirSync(p: string): string[] {
330
+ const norm = normalize(p);
331
+ const node = nodes.get(norm);
332
+ if (!node) throw new MemFSError("ENOENT", "readdir", norm);
333
+ if (node.kind !== "dir") throw new MemFSError("ENOTDIR", "readdir", norm);
334
+ const prefix = norm === "/" ? "/" : norm + "/";
335
+ const direct = new Set<string>();
336
+ for (const key of nodes.keys()) {
337
+ if (!key.startsWith(prefix) || key === norm) continue;
338
+ const rest = key.slice(prefix.length);
339
+ const cut = rest.indexOf("/");
340
+ direct.add(cut === -1 ? rest : rest.slice(0, cut));
341
+ }
342
+ return [...direct].sort();
343
+ }
344
+
345
+ const fs: IWasmExecFS = {
346
+ constants: {
347
+ O_WRONLY: 1,
348
+ O_RDWR: 2,
349
+ O_CREAT: 64,
350
+ O_TRUNC: 512,
351
+ O_APPEND: 1024,
352
+ O_EXCL: 128,
353
+ O_DIRECTORY: 65536,
354
+ },
355
+
356
+ writeSync(fd, buf) {
357
+ const text = decoder.decode(buf);
358
+ if (fd === 1) {
359
+ stdout.buffer += text;
360
+ } else if (fd === 2) {
361
+ stderr.buffer += text;
362
+ } else {
363
+ stderr.buffer += text;
364
+ }
365
+ return buf.length;
366
+ },
367
+
368
+ write(fd, buf, offset, length, position, callback) {
369
+ try {
370
+ if (position !== null && position !== 0) {
371
+ callback(new MemFSError("ESPIPE", "write"), 0);
372
+ return;
373
+ }
374
+ const view = buf.subarray(offset, offset + length);
375
+ const written = this.writeSync(fd, view);
376
+ callback(null, written);
377
+ } catch (err) {
378
+ callback(err as NodeJS.ErrnoException, 0);
379
+ }
380
+ },
381
+
382
+ open(p, flags, _mode, callback) {
383
+ const norm = normalize(p);
384
+ const node = nodes.get(norm);
385
+ const creating = (flags & (this.constants.O_CREAT ?? 0)) !== 0;
386
+ if (!node) {
387
+ if (!creating) {
388
+ callback(new MemFSError("ENOENT", "open", norm), -1);
389
+ return;
390
+ }
391
+ ensureParentDirs(norm);
392
+ nodes.set(norm, {
393
+ kind: "file",
394
+ data: new Uint8Array(),
395
+ mtimeMs: Date.now(),
396
+ });
397
+ }
398
+ const fd = nextFd++;
399
+ fdTable.set(fd, { path: norm, position: 0 });
400
+ if ((flags & (this.constants.O_TRUNC ?? 0)) !== 0) {
401
+ nodes.set(norm, {
402
+ kind: "file",
403
+ data: new Uint8Array(),
404
+ mtimeMs: Date.now(),
405
+ });
406
+ }
407
+ callback(null, fd);
408
+ },
409
+
410
+ close(fd, callback) {
411
+ if (!fdTable.has(fd)) {
412
+ callback(new MemFSError("EBADF", "close"));
413
+ return;
414
+ }
415
+ if (fd > 2) fdTable.delete(fd);
416
+ callback(null);
417
+ },
418
+
419
+ read(fd, buffer, offset, length, position, callback) {
420
+ const entry = fdTable.get(fd);
421
+ if (!entry) {
422
+ callback(new MemFSError("EBADF", "read"), 0);
423
+ return;
424
+ }
425
+ const node = nodes.get(entry.path);
426
+ if (!node || node.kind !== "file") {
427
+ callback(new MemFSError("ENOENT", "read", entry.path), 0);
428
+ return;
429
+ }
430
+ const start = position ?? entry.position;
431
+ const end = Math.min(start + length, node.data.byteLength);
432
+ const slice = node.data.subarray(start, end);
433
+ buffer.set(slice, offset);
434
+ if (position === null) entry.position = end;
435
+ callback(null, slice.byteLength);
436
+ },
437
+
438
+ readdir(p, callback) {
439
+ try {
440
+ callback(null, readdirSync(p));
441
+ } catch (err) {
442
+ callback(err as NodeJS.ErrnoException, []);
443
+ }
444
+ },
445
+
446
+ mkdir(p, _perm, callback) {
447
+ try {
448
+ mkdirp(p);
449
+ callback(null);
450
+ } catch (err) {
451
+ callback(err as NodeJS.ErrnoException);
452
+ }
453
+ },
454
+
455
+ stat(p, callback) {
456
+ try {
457
+ callback(null, statSync(p));
458
+ } catch (err) {
459
+ callback(err as NodeJS.ErrnoException, undefined as unknown as IFileStats);
460
+ }
461
+ },
462
+
463
+ lstat(p, callback) {
464
+ this.stat(p, callback);
465
+ },
466
+
467
+ fstat(fd, callback) {
468
+ const entry = fdTable.get(fd);
469
+ if (!entry) {
470
+ callback(
471
+ new MemFSError("EBADF", "fstat"),
472
+ undefined as unknown as IFileStats,
473
+ );
474
+ return;
475
+ }
476
+ try {
477
+ callback(null, statSync(entry.path));
478
+ } catch (err) {
479
+ callback(
480
+ err as NodeJS.ErrnoException,
481
+ undefined as unknown as IFileStats,
482
+ );
483
+ }
484
+ },
485
+
486
+ fsync(_fd, callback) {
487
+ callback(null);
488
+ },
489
+
490
+ unlink(p, callback) {
491
+ const norm = normalize(p);
492
+ if (!nodes.has(norm)) {
493
+ callback(new MemFSError("ENOENT", "unlink", norm));
494
+ return;
495
+ }
496
+ nodes.delete(norm);
497
+ callback(null);
498
+ },
499
+
500
+ rename(from, to, callback) {
501
+ const src = normalize(from);
502
+ const node = nodes.get(src);
503
+ if (!node) {
504
+ callback(new MemFSError("ENOENT", "rename", src));
505
+ return;
506
+ }
507
+ const dest = normalize(to);
508
+ nodes.delete(src);
509
+ nodes.set(dest, node);
510
+ callback(null);
511
+ },
512
+
513
+ rmdir(p, callback) {
514
+ this.unlink(p, callback);
515
+ },
516
+
517
+ chmod(_p, _mode, callback) {
518
+ callback(null);
519
+ },
520
+ fchmod(_fd, _mode, callback) {
521
+ callback(null);
522
+ },
523
+ chown(_p, _uid, _gid, callback) {
524
+ callback(null);
525
+ },
526
+ fchown(_fd, _uid, _gid, callback) {
527
+ callback(null);
528
+ },
529
+ lchown(_p, _uid, _gid, callback) {
530
+ callback(null);
531
+ },
532
+ utimes(_p, _atime, _mtime, callback) {
533
+ callback(null);
534
+ },
535
+ link(_p, _link, callback) {
536
+ callback(new MemFSError("EPERM", "link"));
537
+ },
538
+ symlink(_p, _link, callback) {
539
+ callback(new MemFSError("EPERM", "symlink"));
540
+ },
541
+ readlink(_p, callback) {
542
+ callback(new MemFSError("EINVAL", "readlink"), "");
543
+ },
544
+ truncate(_p, _length, callback) {
545
+ callback(null);
546
+ },
547
+ ftruncate(_fd, _length, callback) {
548
+ callback(null);
549
+ },
550
+ };
551
+
552
+ return {
553
+ fs,
554
+ writeFile,
555
+ readFile,
556
+ readFileText,
557
+ exists,
558
+ mkdirp,
559
+ stdout,
560
+ stderr,
561
+ resetStdio() {
562
+ stdout.buffer = "";
563
+ stderr.buffer = "";
564
+ },
565
+ };
566
+ }