@volter/tabnode 0.5.14 → 0.5.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/assets/{runtime-worker-C5pP4aT7.js → runtime-worker-Bw8vbYik.js} +2562 -2494
- package/dist/index.mjs +2573 -2505
- package/dist/node-lib/binding/fs.d.ts +10 -0
- package/package.json +1 -1
- package/src/node-lib/binding/fs.ts +34 -0
- package/src/node-lib/binding/process_wrap.ts +3 -1
- package/src/server-bridge.ts +4 -2
- package/src/shims/child_process.ts +44 -0
|
@@ -3,6 +3,16 @@ import { Buffer as NodeBuffer } from '../buffer-module';
|
|
|
3
3
|
/** The key the engine hangs a run's filesystem off its guest process. */
|
|
4
4
|
export declare const kRunFilesystem: unique symbol;
|
|
5
5
|
export declare function withFilesystem<T>(tree: VirtualFS, work: () => T): T;
|
|
6
|
+
/**
|
|
7
|
+
* Where a child's output goes when its stdio names a descriptor of the
|
|
8
|
+
* parent's (`stdio: ['ignore', fd, fd]` with `fd` from `openSync`): the file
|
|
9
|
+
* that descriptor has open, as the child's own duplicate of it, so it keeps
|
|
10
|
+
* writing after the parent closes its copy. Appending descriptors append;
|
|
11
|
+
* others write on from the position the parent's copy had. A descriptor that
|
|
12
|
+
* is a stream (a socket, a pipe) is written as a stream. Anything else is
|
|
13
|
+
* not a sink, and the child's output there goes nowhere, as `ignore` does.
|
|
14
|
+
*/
|
|
15
|
+
export declare function descriptorWriter(fd: number): ((text: string) => void) | null;
|
|
6
16
|
/** A request object `fs.js` hands an asynchronous call, answered on a tick. */
|
|
7
17
|
interface FSReq {
|
|
8
18
|
oncomplete?: (error: Error | null, ...rest: unknown[]) => void;
|
package/package.json
CHANGED
|
@@ -168,6 +168,40 @@ function fileFor(fd: number): OpenFile {
|
|
|
168
168
|
return file;
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
/**
|
|
172
|
+
* Where a child's output goes when its stdio names a descriptor of the
|
|
173
|
+
* parent's (`stdio: ['ignore', fd, fd]` with `fd` from `openSync`): the file
|
|
174
|
+
* that descriptor has open, as the child's own duplicate of it, so it keeps
|
|
175
|
+
* writing after the parent closes its copy. Appending descriptors append;
|
|
176
|
+
* others write on from the position the parent's copy had. A descriptor that
|
|
177
|
+
* is a stream (a socket, a pipe) is written as a stream. Anything else is
|
|
178
|
+
* not a sink, and the child's output there goes nowhere, as `ignore` does.
|
|
179
|
+
*/
|
|
180
|
+
export function descriptorWriter(fd: number): ((text: string) => void) | null {
|
|
181
|
+
const stream = handleForFd(fd);
|
|
182
|
+
if (stream instanceof LibuvStreamWrap) {
|
|
183
|
+
return (text) => { try { stream.writeBuffer(new WriteWrap(), new TextEncoder().encode(text)); } catch { /* a closed stream drops the child's output, as a closed pipe does */ } };
|
|
184
|
+
}
|
|
185
|
+
const file = openFiles.get(fd);
|
|
186
|
+
if (!file || file.directory || (file.flags & 3) === 0) return null;
|
|
187
|
+
const { tree, path } = file;
|
|
188
|
+
const append = (file.flags & flagBits().append) !== 0;
|
|
189
|
+
let position = file.position;
|
|
190
|
+
return (text) => {
|
|
191
|
+
const bytes = new TextEncoder().encode(text);
|
|
192
|
+
// the parent's copy, while it is open, holds the file's bytes as it last wrote them
|
|
193
|
+
const shared = openFiles.get(fd) === file ? file : null;
|
|
194
|
+
const existing = shared?.cached ?? (tree.existsSync(path) ? tree.readFileSync(path) as Uint8Array : new Uint8Array(0));
|
|
195
|
+
const at = append ? existing.length : position;
|
|
196
|
+
const next = new Uint8Array(Math.max(existing.length, at + bytes.length));
|
|
197
|
+
next.set(existing, 0);
|
|
198
|
+
next.set(bytes, at);
|
|
199
|
+
tree.writeFileSync(path, next);
|
|
200
|
+
position = at + bytes.length;
|
|
201
|
+
if (shared) shared.cached = next;
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
|
|
171
205
|
/** Node's stat array: eighteen numbers, with each time a second and a nanosecond. */
|
|
172
206
|
function statArray(stats: VfsStats, bigint: boolean, path?: string): Float64Array | BigInt64Array {
|
|
173
207
|
const mtime = stats.mtime instanceof Date ? stats.mtime.getTime() : Number(stats.mtimeMs ?? 0);
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
} from './handles';
|
|
30
30
|
import { __runFor, mintPid, type ProcessToken } from '../../process-tokens';
|
|
31
31
|
import { handleForFd } from './fds';
|
|
32
|
+
import { descriptorWriter } from './fs';
|
|
32
33
|
import { TTY, type TerminalState } from './tty_wrap';
|
|
33
34
|
|
|
34
35
|
/** One entry of Node's `options.stdio`, as `getValidStdio` builds it. */
|
|
@@ -178,7 +179,8 @@ function spawningDirectory(token: ProcessToken | null): string | undefined {
|
|
|
178
179
|
|
|
179
180
|
/** Where an `inherit` entry's bytes go: the spawning program's own stream. */
|
|
180
181
|
function inheritedWriter(fd: number, token: ProcessToken | null): ((text: string) => void) | null {
|
|
181
|
-
|
|
182
|
+
// a descriptor of the parent's own (a log file it opened) is written as the file
|
|
183
|
+
if (fd !== 1 && fd !== 2) return descriptorWriter(fd);
|
|
182
184
|
// Inherit the descriptor's original sink, not a guest replacement of
|
|
183
185
|
// process.stderr.write (a child commonly redirects its console over IPC).
|
|
184
186
|
if (token !== null) {
|
package/src/server-bridge.ts
CHANGED
|
@@ -774,7 +774,9 @@ export class ServerBridge extends EventEmitter {
|
|
|
774
774
|
const virtualServer = this.servers.get(port);
|
|
775
775
|
if (!virtualServer) return false;
|
|
776
776
|
const bodyBuffer = body ? Buffer.from(new Uint8Array(body)) : undefined;
|
|
777
|
-
|
|
777
|
+
// A guest's listener (the old null sentinel, now a request adapter) is
|
|
778
|
+
// streamed off its connection; its adapter would answer whole.
|
|
779
|
+
if (virtualServer.server === null || this.guestServers.has(virtualServer.server)) {
|
|
778
780
|
const named = Object.keys(headers).some((name) => name.toLowerCase() === 'host');
|
|
779
781
|
const authority = `${virtualServer.hostname && virtualServer.hostname !== '0.0.0.0' && virtualServer.hostname !== '::' ? virtualServer.hostname : '127.0.0.1'}:${port}`;
|
|
780
782
|
await __streamOverLoopback(
|
|
@@ -839,7 +841,7 @@ export class ServerBridge extends EventEmitter {
|
|
|
839
841
|
|
|
840
842
|
// A guest's server is a port: its answer is streamed off the connection
|
|
841
843
|
// as it arrives, which is what the page reads chunk by chunk.
|
|
842
|
-
if (virtualServer.server === null) {
|
|
844
|
+
if (virtualServer.server === null || this.guestServers.has(virtualServer.server)) {
|
|
843
845
|
const bodyBuffer = body ? Buffer.from(new Uint8Array(body)) : undefined;
|
|
844
846
|
const named = Object.keys(headers).some((name) => name.toLowerCase() === 'host');
|
|
845
847
|
const authority = `${virtualServer.hostname && virtualServer.hostname !== '0.0.0.0' && virtualServer.hostname !== '::' ? virtualServer.hostname : '127.0.0.1'}:${port}`;
|
|
@@ -1011,6 +1011,10 @@ export function initChildProcess(vfs: VirtualFS): void {
|
|
|
1011
1011
|
case 'ls':
|
|
1012
1012
|
case 'list':
|
|
1013
1013
|
return handleNpmList(ctx);
|
|
1014
|
+
case 'init':
|
|
1015
|
+
case 'create':
|
|
1016
|
+
case 'innit':
|
|
1017
|
+
return handleNpmInit(args.slice(1), ctx);
|
|
1014
1018
|
default:
|
|
1015
1019
|
return {
|
|
1016
1020
|
stdout: '',
|
|
@@ -1289,6 +1293,46 @@ async function handleNpmInstall(args: string[], ctx: CommandContext): Promise<Ju
|
|
|
1289
1293
|
}
|
|
1290
1294
|
}
|
|
1291
1295
|
|
|
1296
|
+
/**
|
|
1297
|
+
* `npm init -y`: npm's defaults written to package.json, the fields a
|
|
1298
|
+
* package.json already has kept, and the file printed as npm prints it. The
|
|
1299
|
+
* questionnaire without `-y` needs a person at npm's prompts; it is refused by
|
|
1300
|
+
* name, as is an initializer (`npm init <pkg>`), which runs another package.
|
|
1301
|
+
*/
|
|
1302
|
+
async function handleNpmInit(args: string[], ctx: CommandContext): Promise<JustBashExecResult> {
|
|
1303
|
+
const yes = args.includes('-y') || args.includes('--yes');
|
|
1304
|
+
const initializer = args.find((a) => !a.startsWith('-'));
|
|
1305
|
+
if (initializer) {
|
|
1306
|
+
return { stdout: '', stderr: `npm ERR! npm init ${initializer} runs an initializer package, which this engine's npm does not; write package.json, or run \`npm init -y\`.\n`, exitCode: 1 };
|
|
1307
|
+
}
|
|
1308
|
+
if (!yes) {
|
|
1309
|
+
return { stdout: '', stderr: 'npm ERR! npm init asks its questions at a prompt, which this engine\'s npm does not; `npm init -y` writes the defaults.\n', exitCode: 1 };
|
|
1310
|
+
}
|
|
1311
|
+
const vfs = currentVfs!;
|
|
1312
|
+
const file = __resolvePath(ctx.cwd, 'package.json');
|
|
1313
|
+
let existing: Record<string, unknown> = {};
|
|
1314
|
+
if (vfs.existsSync(file)) {
|
|
1315
|
+
try { existing = JSON.parse(String(vfs.readFileSync(file, 'utf8'))) as Record<string, unknown>; }
|
|
1316
|
+
catch { return { stdout: '', stderr: `npm ERR! ${file} is not valid JSON\n`, exitCode: 1 }; }
|
|
1317
|
+
}
|
|
1318
|
+
const base = ctx.cwd.split('/').filter(Boolean).pop() ?? 'package';
|
|
1319
|
+
const name = base.toLowerCase().replace(/^[._]+/, '').replace(/[^a-z0-9._~-]+/g, '-') || 'package';
|
|
1320
|
+
const manifest = {
|
|
1321
|
+
name,
|
|
1322
|
+
version: '1.0.0',
|
|
1323
|
+
description: '',
|
|
1324
|
+
main: 'index.js',
|
|
1325
|
+
scripts: { test: 'echo "Error: no test specified" && exit 1' },
|
|
1326
|
+
keywords: [],
|
|
1327
|
+
author: '',
|
|
1328
|
+
license: 'ISC',
|
|
1329
|
+
...existing,
|
|
1330
|
+
};
|
|
1331
|
+
const text = `${JSON.stringify(manifest, null, 2)}\n`;
|
|
1332
|
+
vfs.writeFileSync(file, text);
|
|
1333
|
+
return { stdout: `Wrote to ${file}:\n\n${text}\n\n`, stderr: '', exitCode: 0 };
|
|
1334
|
+
}
|
|
1335
|
+
|
|
1292
1336
|
/**
|
|
1293
1337
|
* Handle `npm ls` — list installed packages
|
|
1294
1338
|
*/
|