sandboxedjs 0.1.0 → 0.1.1
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/README.md +329 -4
- package/dist/index.cjs +355 -56
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +15 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +355 -56
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -1435,6 +1435,21 @@ interface ContainerOptions {
|
|
|
1435
1435
|
onStderr?: (chunk: string) => void;
|
|
1436
1436
|
/** Invoked when an in-container HTTP server starts listening. */
|
|
1437
1437
|
onServerReady?: (port: number, url: string) => void;
|
|
1438
|
+
/**
|
|
1439
|
+
* Supply the Nodepod instance instead of letting the container boot one.
|
|
1440
|
+
*
|
|
1441
|
+
* The default path imports `@scelar/nodepod/headless`, which installs a
|
|
1442
|
+
* `worker_threads` host and is the right choice on Node. In a browser you
|
|
1443
|
+
* boot Nodepod's browser build yourself (it needs a service worker) and hand
|
|
1444
|
+
* the instance over:
|
|
1445
|
+
*
|
|
1446
|
+
* ```ts
|
|
1447
|
+
* import { Nodepod } from "@scelar/nodepod";
|
|
1448
|
+
* const pod = await Nodepod.boot({ ... });
|
|
1449
|
+
* const box = await createContainer({ pod });
|
|
1450
|
+
* ```
|
|
1451
|
+
*/
|
|
1452
|
+
pod?: Nodepod;
|
|
1438
1453
|
}
|
|
1439
1454
|
interface ExecOptions {
|
|
1440
1455
|
cwd?: string;
|
package/dist/index.d.ts
CHANGED
|
@@ -1435,6 +1435,21 @@ interface ContainerOptions {
|
|
|
1435
1435
|
onStderr?: (chunk: string) => void;
|
|
1436
1436
|
/** Invoked when an in-container HTTP server starts listening. */
|
|
1437
1437
|
onServerReady?: (port: number, url: string) => void;
|
|
1438
|
+
/**
|
|
1439
|
+
* Supply the Nodepod instance instead of letting the container boot one.
|
|
1440
|
+
*
|
|
1441
|
+
* The default path imports `@scelar/nodepod/headless`, which installs a
|
|
1442
|
+
* `worker_threads` host and is the right choice on Node. In a browser you
|
|
1443
|
+
* boot Nodepod's browser build yourself (it needs a service worker) and hand
|
|
1444
|
+
* the instance over:
|
|
1445
|
+
*
|
|
1446
|
+
* ```ts
|
|
1447
|
+
* import { Nodepod } from "@scelar/nodepod";
|
|
1448
|
+
* const pod = await Nodepod.boot({ ... });
|
|
1449
|
+
* const box = await createContainer({ pod });
|
|
1450
|
+
* ```
|
|
1451
|
+
*/
|
|
1452
|
+
pod?: Nodepod;
|
|
1438
1453
|
}
|
|
1439
1454
|
interface ExecOptions {
|
|
1440
1455
|
cwd?: string;
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
1
|
import { Nodepod, DependencyInstaller } from '@scelar/nodepod/headless';
|
|
2
|
-
import { gzipSync, gunzipSync, inflateRawSync, inflateSync, deflateSync } from 'zlib';
|
|
3
|
-
import { createHash, randomUUID } from 'crypto';
|
|
4
2
|
|
|
5
3
|
var __defProp = Object.defineProperty;
|
|
6
4
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
@@ -15133,6 +15131,206 @@ var commands5 = [grep, find, xargs, diff, cmp];
|
|
|
15133
15131
|
// src/bin/archive.ts
|
|
15134
15132
|
init_mode();
|
|
15135
15133
|
init_path();
|
|
15134
|
+
|
|
15135
|
+
// src/util/binary.ts
|
|
15136
|
+
var isNode = typeof process !== "undefined" && process.versions != null && process.versions.node != null;
|
|
15137
|
+
async function nodeBuiltin(name) {
|
|
15138
|
+
const specifier = `node:${name}`;
|
|
15139
|
+
return await import(
|
|
15140
|
+
/* @vite-ignore */
|
|
15141
|
+
/* webpackIgnore: true */
|
|
15142
|
+
specifier
|
|
15143
|
+
);
|
|
15144
|
+
}
|
|
15145
|
+
var zlibPromise = null;
|
|
15146
|
+
function nodeZlib() {
|
|
15147
|
+
zlibPromise ??= nodeBuiltin("zlib");
|
|
15148
|
+
return zlibPromise;
|
|
15149
|
+
}
|
|
15150
|
+
async function throughStream(data, stream) {
|
|
15151
|
+
const source = new Blob([data]).stream();
|
|
15152
|
+
const piped = source.pipeThrough(stream);
|
|
15153
|
+
return new Uint8Array(await new Response(piped).arrayBuffer());
|
|
15154
|
+
}
|
|
15155
|
+
async function gzip(data) {
|
|
15156
|
+
if (isNode) return new Uint8Array((await nodeZlib()).gzipSync(data));
|
|
15157
|
+
return throughStream(data, new CompressionStream("gzip"));
|
|
15158
|
+
}
|
|
15159
|
+
async function gunzip(data) {
|
|
15160
|
+
if (isNode) return new Uint8Array((await nodeZlib()).gunzipSync(data));
|
|
15161
|
+
return throughStream(data, new DecompressionStream("gzip"));
|
|
15162
|
+
}
|
|
15163
|
+
async function deflate(data) {
|
|
15164
|
+
if (isNode) return new Uint8Array((await nodeZlib()).deflateSync(data));
|
|
15165
|
+
return throughStream(data, new CompressionStream("deflate"));
|
|
15166
|
+
}
|
|
15167
|
+
async function inflate(data) {
|
|
15168
|
+
if (isNode) return new Uint8Array((await nodeZlib()).inflateSync(data));
|
|
15169
|
+
return throughStream(data, new DecompressionStream("deflate"));
|
|
15170
|
+
}
|
|
15171
|
+
async function inflateRaw(data) {
|
|
15172
|
+
if (isNode) return new Uint8Array((await nodeZlib()).inflateRawSync(data));
|
|
15173
|
+
return throughStream(data, new DecompressionStream("deflate-raw"));
|
|
15174
|
+
}
|
|
15175
|
+
var cryptoPromise = null;
|
|
15176
|
+
function nodeCrypto() {
|
|
15177
|
+
cryptoPromise ??= nodeBuiltin("crypto");
|
|
15178
|
+
return cryptoPromise;
|
|
15179
|
+
}
|
|
15180
|
+
var SUBTLE_NAMES = {
|
|
15181
|
+
sha1: "SHA-1",
|
|
15182
|
+
sha256: "SHA-256",
|
|
15183
|
+
sha384: "SHA-384",
|
|
15184
|
+
sha512: "SHA-512"
|
|
15185
|
+
};
|
|
15186
|
+
var UnsupportedAlgorithmError = class extends Error {
|
|
15187
|
+
constructor(algorithm) {
|
|
15188
|
+
super(`${algorithm} is not available in this environment`);
|
|
15189
|
+
this.name = "UnsupportedAlgorithmError";
|
|
15190
|
+
}
|
|
15191
|
+
};
|
|
15192
|
+
async function digestHex(algorithm, data) {
|
|
15193
|
+
if (isNode) {
|
|
15194
|
+
const { createHash } = await nodeCrypto();
|
|
15195
|
+
return createHash(algorithm).update(data).digest("hex");
|
|
15196
|
+
}
|
|
15197
|
+
if (algorithm === "md5") return md5Hex(data);
|
|
15198
|
+
const name = SUBTLE_NAMES[algorithm];
|
|
15199
|
+
if (!name) throw new UnsupportedAlgorithmError(algorithm);
|
|
15200
|
+
const buffer = await crypto.subtle.digest(name, data);
|
|
15201
|
+
return toHex(new Uint8Array(buffer));
|
|
15202
|
+
}
|
|
15203
|
+
async function randomUuid() {
|
|
15204
|
+
if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function") return crypto.randomUUID();
|
|
15205
|
+
const { randomUUID } = await nodeCrypto();
|
|
15206
|
+
return randomUUID();
|
|
15207
|
+
}
|
|
15208
|
+
function toHex(bytes) {
|
|
15209
|
+
let out = "";
|
|
15210
|
+
for (const byte of bytes) out += byte.toString(16).padStart(2, "0");
|
|
15211
|
+
return out;
|
|
15212
|
+
}
|
|
15213
|
+
function md5Hex(input) {
|
|
15214
|
+
const S = [
|
|
15215
|
+
7,
|
|
15216
|
+
12,
|
|
15217
|
+
17,
|
|
15218
|
+
22,
|
|
15219
|
+
7,
|
|
15220
|
+
12,
|
|
15221
|
+
17,
|
|
15222
|
+
22,
|
|
15223
|
+
7,
|
|
15224
|
+
12,
|
|
15225
|
+
17,
|
|
15226
|
+
22,
|
|
15227
|
+
7,
|
|
15228
|
+
12,
|
|
15229
|
+
17,
|
|
15230
|
+
22,
|
|
15231
|
+
5,
|
|
15232
|
+
9,
|
|
15233
|
+
14,
|
|
15234
|
+
20,
|
|
15235
|
+
5,
|
|
15236
|
+
9,
|
|
15237
|
+
14,
|
|
15238
|
+
20,
|
|
15239
|
+
5,
|
|
15240
|
+
9,
|
|
15241
|
+
14,
|
|
15242
|
+
20,
|
|
15243
|
+
5,
|
|
15244
|
+
9,
|
|
15245
|
+
14,
|
|
15246
|
+
20,
|
|
15247
|
+
4,
|
|
15248
|
+
11,
|
|
15249
|
+
16,
|
|
15250
|
+
23,
|
|
15251
|
+
4,
|
|
15252
|
+
11,
|
|
15253
|
+
16,
|
|
15254
|
+
23,
|
|
15255
|
+
4,
|
|
15256
|
+
11,
|
|
15257
|
+
16,
|
|
15258
|
+
23,
|
|
15259
|
+
4,
|
|
15260
|
+
11,
|
|
15261
|
+
16,
|
|
15262
|
+
23,
|
|
15263
|
+
6,
|
|
15264
|
+
10,
|
|
15265
|
+
15,
|
|
15266
|
+
21,
|
|
15267
|
+
6,
|
|
15268
|
+
10,
|
|
15269
|
+
15,
|
|
15270
|
+
21,
|
|
15271
|
+
6,
|
|
15272
|
+
10,
|
|
15273
|
+
15,
|
|
15274
|
+
21,
|
|
15275
|
+
6,
|
|
15276
|
+
10,
|
|
15277
|
+
15,
|
|
15278
|
+
21
|
|
15279
|
+
];
|
|
15280
|
+
const K = new Uint32Array(64);
|
|
15281
|
+
for (let i = 0; i < 64; i++) K[i] = Math.floor(Math.abs(Math.sin(i + 1)) * 2 ** 32);
|
|
15282
|
+
const bitLength = input.length * 8;
|
|
15283
|
+
const padded = new Uint8Array(((input.length + 8 >> 6) + 1) * 64);
|
|
15284
|
+
padded.set(input);
|
|
15285
|
+
padded[input.length] = 128;
|
|
15286
|
+
const view = new DataView(padded.buffer);
|
|
15287
|
+
view.setUint32(padded.length - 8, bitLength >>> 0, true);
|
|
15288
|
+
view.setUint32(padded.length - 4, Math.floor(bitLength / 2 ** 32), true);
|
|
15289
|
+
let a0 = 1732584193;
|
|
15290
|
+
let b0 = 4023233417;
|
|
15291
|
+
let c0 = 2562383102;
|
|
15292
|
+
let d0 = 271733878;
|
|
15293
|
+
for (let chunk = 0; chunk < padded.length; chunk += 64) {
|
|
15294
|
+
const M = new Uint32Array(16);
|
|
15295
|
+
for (let i = 0; i < 16; i++) M[i] = view.getUint32(chunk + i * 4, true);
|
|
15296
|
+
let [a, b, c, d] = [a0, b0, c0, d0];
|
|
15297
|
+
for (let i = 0; i < 64; i++) {
|
|
15298
|
+
let f;
|
|
15299
|
+
let g;
|
|
15300
|
+
if (i < 16) {
|
|
15301
|
+
f = b & c | ~b & d;
|
|
15302
|
+
g = i;
|
|
15303
|
+
} else if (i < 32) {
|
|
15304
|
+
f = d & b | ~d & c;
|
|
15305
|
+
g = (5 * i + 1) % 16;
|
|
15306
|
+
} else if (i < 48) {
|
|
15307
|
+
f = b ^ c ^ d;
|
|
15308
|
+
g = (3 * i + 5) % 16;
|
|
15309
|
+
} else {
|
|
15310
|
+
f = c ^ (b | ~d);
|
|
15311
|
+
g = 7 * i % 16;
|
|
15312
|
+
}
|
|
15313
|
+
const tmp = d;
|
|
15314
|
+
d = c;
|
|
15315
|
+
c = b;
|
|
15316
|
+
const sum = a + f + K[i] + M[g] >>> 0;
|
|
15317
|
+
b = b + (sum << S[i] | sum >>> 32 - S[i]) >>> 0;
|
|
15318
|
+
a = tmp;
|
|
15319
|
+
}
|
|
15320
|
+
a0 = a0 + a >>> 0;
|
|
15321
|
+
b0 = b0 + b >>> 0;
|
|
15322
|
+
c0 = c0 + c >>> 0;
|
|
15323
|
+
d0 = d0 + d >>> 0;
|
|
15324
|
+
}
|
|
15325
|
+
const out = new Uint8Array(16);
|
|
15326
|
+
new DataView(out.buffer).setUint32(0, a0, true);
|
|
15327
|
+
new DataView(out.buffer).setUint32(4, b0, true);
|
|
15328
|
+
new DataView(out.buffer).setUint32(8, c0, true);
|
|
15329
|
+
new DataView(out.buffer).setUint32(12, d0, true);
|
|
15330
|
+
return toHex(out);
|
|
15331
|
+
}
|
|
15332
|
+
|
|
15333
|
+
// src/bin/archive.ts
|
|
15136
15334
|
var BLOCK = 512;
|
|
15137
15335
|
var encoder4 = new TextEncoder();
|
|
15138
15336
|
var decoder6 = new TextDecoder();
|
|
@@ -15290,7 +15488,7 @@ var tar = defineCommand({
|
|
|
15290
15488
|
};
|
|
15291
15489
|
for (const operand of args.positional) add(resolve(baseDir, operand), operand);
|
|
15292
15490
|
let bytes2 = createTar(entries2);
|
|
15293
|
-
if (compressed) bytes2 =
|
|
15491
|
+
if (compressed) bytes2 = await gzip(bytes2);
|
|
15294
15492
|
if (archive === void 0 || archive === "-") ctx.write(bytes2);
|
|
15295
15493
|
else ctx.vfs.writeFile(ctx.path(archive), bytes2, { cred: ctx.cred, mode: 420 });
|
|
15296
15494
|
return 0;
|
|
@@ -15304,7 +15502,7 @@ var tar = defineCommand({
|
|
|
15304
15502
|
return ctx.reportError(e, archive);
|
|
15305
15503
|
}
|
|
15306
15504
|
}
|
|
15307
|
-
if (bytes[0] === 31 && bytes[1] === 139) bytes =
|
|
15505
|
+
if (bytes[0] === 31 && bytes[1] === 139) bytes = await gunzip(bytes);
|
|
15308
15506
|
const entries = readTar(bytes);
|
|
15309
15507
|
const strip = args.num("strip-components", 0);
|
|
15310
15508
|
if (args.has("list")) {
|
|
@@ -15350,7 +15548,7 @@ var tar = defineCommand({
|
|
|
15350
15548
|
return 2;
|
|
15351
15549
|
}
|
|
15352
15550
|
});
|
|
15353
|
-
var
|
|
15551
|
+
var gzip2 = defineCommand({
|
|
15354
15552
|
name: "gzip",
|
|
15355
15553
|
path: "/bin/gzip",
|
|
15356
15554
|
summary: "compress files",
|
|
@@ -15373,28 +15571,28 @@ var gzip = defineCommand({
|
|
|
15373
15571
|
toStdout,
|
|
15374
15572
|
keep: args.has("keep") || ctx.name === "zcat",
|
|
15375
15573
|
suffix: ".gz",
|
|
15376
|
-
compress: (data) =>
|
|
15377
|
-
expand: (data) =>
|
|
15574
|
+
compress: (data) => gzip(data),
|
|
15575
|
+
expand: (data) => gunzip(data)
|
|
15378
15576
|
});
|
|
15379
15577
|
}
|
|
15380
15578
|
});
|
|
15381
|
-
var
|
|
15579
|
+
var gunzip2 = defineCommand({
|
|
15382
15580
|
name: "gunzip",
|
|
15383
15581
|
path: "/bin/gunzip",
|
|
15384
15582
|
summary: "decompress files",
|
|
15385
|
-
run: (ctx) =>
|
|
15583
|
+
run: (ctx) => gzip2.run(ctx)
|
|
15386
15584
|
});
|
|
15387
15585
|
var zcat = defineCommand({
|
|
15388
15586
|
name: "zcat",
|
|
15389
15587
|
path: "/bin/zcat",
|
|
15390
15588
|
summary: "decompress files to standard output",
|
|
15391
|
-
run: (ctx) =>
|
|
15589
|
+
run: (ctx) => gzip2.run(ctx)
|
|
15392
15590
|
});
|
|
15393
15591
|
async function runCompressor(ctx, operands, opts) {
|
|
15394
15592
|
if (operands.length === 0) {
|
|
15395
15593
|
const data = await ctx.stdin.readAll();
|
|
15396
15594
|
try {
|
|
15397
|
-
ctx.write(opts.decompress ? opts.expand(data) : opts.compress(data));
|
|
15595
|
+
ctx.write(opts.decompress ? await opts.expand(data) : await opts.compress(data));
|
|
15398
15596
|
return 0;
|
|
15399
15597
|
} catch (e) {
|
|
15400
15598
|
return ctx.fail(e instanceof Error ? e.message : String(e));
|
|
@@ -15411,7 +15609,7 @@ async function runCompressor(ctx, operands, opts) {
|
|
|
15411
15609
|
continue;
|
|
15412
15610
|
}
|
|
15413
15611
|
try {
|
|
15414
|
-
const result = opts.decompress ? opts.expand(data) : opts.compress(data);
|
|
15612
|
+
const result = opts.decompress ? await opts.expand(data) : await opts.compress(data);
|
|
15415
15613
|
if (opts.toStdout) {
|
|
15416
15614
|
ctx.write(result);
|
|
15417
15615
|
continue;
|
|
@@ -15437,12 +15635,14 @@ var zlibCompress = defineCommand({
|
|
|
15437
15635
|
toStdout: args.has("stdout"),
|
|
15438
15636
|
keep: false,
|
|
15439
15637
|
suffix: ".Z",
|
|
15440
|
-
compress: (data) =>
|
|
15441
|
-
expand: (data) =>
|
|
15638
|
+
compress: (data) => deflate(data),
|
|
15639
|
+
expand: (data) => inflate(data)
|
|
15442
15640
|
});
|
|
15443
15641
|
}
|
|
15444
15642
|
});
|
|
15445
|
-
var commands6 = [tar,
|
|
15643
|
+
var commands6 = [tar, gzip2, gunzip2, zcat, zlibCompress];
|
|
15644
|
+
|
|
15645
|
+
// src/bin/hash.ts
|
|
15446
15646
|
function makeSum(name, algorithm, path) {
|
|
15447
15647
|
return defineCommand({
|
|
15448
15648
|
name,
|
|
@@ -15461,7 +15661,7 @@ function makeSum(name, algorithm, path) {
|
|
|
15461
15661
|
if (args.has("check")) return checkSums(ctx, algorithm, args.positional, args.has("quiet") || args.has("status"));
|
|
15462
15662
|
const { sources, ok } = await readInputs(ctx, args.positional, { stdinName: "-" });
|
|
15463
15663
|
for (const source of sources) {
|
|
15464
|
-
const digest =
|
|
15664
|
+
const digest = await digestHex(algorithm, source.bytes);
|
|
15465
15665
|
if (args.has("tag")) ctx.line(`${algorithm.toUpperCase()} (${source.name}) = ${digest}`);
|
|
15466
15666
|
else ctx.line(`${digest} ${source.name}`);
|
|
15467
15667
|
}
|
|
@@ -15482,7 +15682,7 @@ async function checkSums(ctx, algorithm, files, quiet) {
|
|
|
15482
15682
|
const [, expected, name] = m;
|
|
15483
15683
|
try {
|
|
15484
15684
|
const bytes = ctx.vfs.readFile(ctx.path(name), ctx.cred);
|
|
15485
|
-
const actual =
|
|
15685
|
+
const actual = await digestHex(algorithm, bytes);
|
|
15486
15686
|
if (actual === expected.toLowerCase()) {
|
|
15487
15687
|
if (!quiet) ctx.line(`${name}: OK`);
|
|
15488
15688
|
} else {
|
|
@@ -15613,8 +15813,8 @@ var uuidgen = defineCommand({
|
|
|
15613
15813
|
name: "uuidgen",
|
|
15614
15814
|
path: "/usr/bin/uuidgen",
|
|
15615
15815
|
summary: "create a new UUID value",
|
|
15616
|
-
run(ctx) {
|
|
15617
|
-
ctx.line(
|
|
15816
|
+
async run(ctx) {
|
|
15817
|
+
ctx.line(await randomUuid());
|
|
15618
15818
|
return 0;
|
|
15619
15819
|
}
|
|
15620
15820
|
});
|
|
@@ -17688,7 +17888,7 @@ var pip = defineCommand({
|
|
|
17688
17888
|
}
|
|
17689
17889
|
const data = new Uint8Array(await (await fetch(wheel.url)).arrayBuffer());
|
|
17690
17890
|
ctx.line(` Downloading ${wheel.filename} (${Math.round(data.length / 1024)} kB)`);
|
|
17691
|
-
installWheel(ctx, siteDir, data, name, chosen);
|
|
17891
|
+
await installWheel(ctx, siteDir, data, name, chosen);
|
|
17692
17892
|
ctx.line(`Successfully installed ${name}-${chosen}`);
|
|
17693
17893
|
} catch (e) {
|
|
17694
17894
|
ctx.warn(`ERROR: Could not install ${spec}: ${e instanceof Error ? e.message : String(e)}`);
|
|
@@ -17698,8 +17898,8 @@ var pip = defineCommand({
|
|
|
17698
17898
|
return status;
|
|
17699
17899
|
}
|
|
17700
17900
|
});
|
|
17701
|
-
function installWheel(ctx, siteDir, data, name, version) {
|
|
17702
|
-
const entries = readZip(data);
|
|
17901
|
+
async function installWheel(ctx, siteDir, data, name, version) {
|
|
17902
|
+
const entries = await readZip(data);
|
|
17703
17903
|
for (const entry of entries) {
|
|
17704
17904
|
if (entry.name.endsWith("/")) continue;
|
|
17705
17905
|
const target = join(siteDir, entry.name);
|
|
@@ -17713,7 +17913,7 @@ function installWheel(ctx, siteDir, data, name, version) {
|
|
|
17713
17913
|
Version: ${version}
|
|
17714
17914
|
`, { cred: ctx.cred });
|
|
17715
17915
|
}
|
|
17716
|
-
function readZip(data) {
|
|
17916
|
+
async function readZip(data) {
|
|
17717
17917
|
const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
|
|
17718
17918
|
const entries = [];
|
|
17719
17919
|
let eocd = -1;
|
|
@@ -17739,7 +17939,7 @@ function readZip(data) {
|
|
|
17739
17939
|
const localExtraLength = view.getUint16(localOffset + 28, true);
|
|
17740
17940
|
const dataStart = localOffset + 30 + localNameLength + localExtraLength;
|
|
17741
17941
|
const raw = data.subarray(dataStart, dataStart + compressedSize);
|
|
17742
|
-
entries.push({ name, data: method === 0 ? raw.slice() :
|
|
17942
|
+
entries.push({ name, data: method === 0 ? raw.slice() : await inflateRaw(raw) });
|
|
17743
17943
|
offset += 46 + nameLength + extraLength + commentLength;
|
|
17744
17944
|
}
|
|
17745
17945
|
return entries;
|
|
@@ -17770,9 +17970,17 @@ function findProjectRoot(ctx) {
|
|
|
17770
17970
|
function writeManifest(ctx, path, manifest) {
|
|
17771
17971
|
ctx.vfs.writeFile(path, JSON.stringify(manifest, null, 2) + "\n", { cred: ctx.cred, mode: 420 });
|
|
17772
17972
|
}
|
|
17973
|
+
function splitPackageSpec(spec) {
|
|
17974
|
+
const at = spec.lastIndexOf("@");
|
|
17975
|
+
if (at <= 0) return { name: spec };
|
|
17976
|
+
return { name: spec.slice(0, at), version: spec.slice(at + 1) };
|
|
17977
|
+
}
|
|
17773
17978
|
async function installPackages(ctx, specs, opts) {
|
|
17774
17979
|
const installer = opts.cwd === "/" ? ctx.kernel.pod.packages : new DependencyInstaller(ctx.vfs.volume, { cwd: opts.cwd });
|
|
17775
|
-
const onProgress = (message) =>
|
|
17980
|
+
const onProgress = (message) => {
|
|
17981
|
+
if (!opts.quiet) ctx.stderr.write(message.endsWith("\n") ? message : message + "\n");
|
|
17982
|
+
};
|
|
17983
|
+
const note = (text) => opts.quiet ? ctx.stderr.write(text + "\n") : ctx.line(text);
|
|
17776
17984
|
if (!ctx.kernel.net.options.allowOutbound) {
|
|
17777
17985
|
ctx.warn("npm error code ENOTFOUND");
|
|
17778
17986
|
ctx.warn("npm error network request to https://registry.npmjs.org failed");
|
|
@@ -17782,7 +17990,7 @@ async function installPackages(ctx, specs, opts) {
|
|
|
17782
17990
|
}
|
|
17783
17991
|
try {
|
|
17784
17992
|
if (specs.length === 0) {
|
|
17785
|
-
|
|
17993
|
+
note(`npm install (project at ${opts.cwd})`);
|
|
17786
17994
|
await installer.installFromManifest(join(opts.cwd, "package.json"), {
|
|
17787
17995
|
withDevDeps: true,
|
|
17788
17996
|
onProgress,
|
|
@@ -17790,11 +17998,8 @@ async function installPackages(ctx, specs, opts) {
|
|
|
17790
17998
|
});
|
|
17791
17999
|
} else {
|
|
17792
18000
|
for (const spec of specs) {
|
|
17793
|
-
const
|
|
17794
|
-
|
|
17795
|
-
const name = at > 0 && !(scoped && at === 0) ? spec.slice(0, at) : spec;
|
|
17796
|
-
const version = at > 0 && !(scoped && at === 0) ? spec.slice(at + 1) : void 0;
|
|
17797
|
-
ctx.line(`added ${name}${version ? `@${version}` : ""}`);
|
|
18001
|
+
const { name, version } = splitPackageSpec(spec);
|
|
18002
|
+
note(`added ${name}${version ? `@${version}` : ""}`);
|
|
17798
18003
|
await installer.install(name, version, {
|
|
17799
18004
|
onProgress,
|
|
17800
18005
|
persist: opts.save !== false,
|
|
@@ -18038,33 +18243,127 @@ function printNpmHelp(ctx) {
|
|
|
18038
18243
|
ctx.line("");
|
|
18039
18244
|
ctx.line(`npm@${NPM_VERSION} /usr/lib/node_modules/npm`);
|
|
18040
18245
|
}
|
|
18246
|
+
function packageBinaries(ctx, root, packageName) {
|
|
18247
|
+
const manifestPath = join(root, "node_modules", packageName, "package.json");
|
|
18248
|
+
try {
|
|
18249
|
+
const manifest = JSON.parse(ctx.vfs.readText(manifestPath, ctx.cred));
|
|
18250
|
+
if (typeof manifest.bin === "string") return [basename(packageName)];
|
|
18251
|
+
if (manifest.bin && typeof manifest.bin === "object") return Object.keys(manifest.bin);
|
|
18252
|
+
} catch {
|
|
18253
|
+
}
|
|
18254
|
+
return [];
|
|
18255
|
+
}
|
|
18256
|
+
function findLocalBin(ctx, name) {
|
|
18257
|
+
let dir3 = ctx.cwd;
|
|
18258
|
+
for (let i = 0; i < 64; i++) {
|
|
18259
|
+
const candidate = join(dir3, "node_modules", ".bin", name);
|
|
18260
|
+
if (ctx.vfs.lexists(candidate)) return candidate;
|
|
18261
|
+
const parent = dirname(dir3);
|
|
18262
|
+
if (parent === dir3) break;
|
|
18263
|
+
dir3 = parent;
|
|
18264
|
+
}
|
|
18265
|
+
return null;
|
|
18266
|
+
}
|
|
18041
18267
|
var npx = defineCommand({
|
|
18042
18268
|
name: "npx",
|
|
18043
18269
|
path: "/usr/bin/npx",
|
|
18044
18270
|
summary: "run a command from a local or remote npm package",
|
|
18271
|
+
usage: "npx [-y] [-p package] <command> [args]",
|
|
18272
|
+
manual: `Runs a package binary, installing the package first if it is not
|
|
18273
|
+
already present. Installation needs outbound network access, which is off
|
|
18274
|
+
unless the container was created with network: { allowOutbound: true }.`,
|
|
18045
18275
|
async run(ctx) {
|
|
18046
|
-
const args = parseArgs(
|
|
18047
|
-
|
|
18048
|
-
|
|
18049
|
-
|
|
18276
|
+
const args = parseArgs(
|
|
18277
|
+
ctx.args,
|
|
18278
|
+
[
|
|
18279
|
+
{ short: "y", long: "yes" },
|
|
18280
|
+
{ long: "no-install" },
|
|
18281
|
+
{ short: "p", long: "package", arg: true, multiple: true },
|
|
18282
|
+
{ short: "q", long: "quiet" },
|
|
18283
|
+
{ short: "c", long: "call", arg: true },
|
|
18284
|
+
{ long: "version" },
|
|
18285
|
+
{ long: "help" }
|
|
18286
|
+
],
|
|
18287
|
+
{ stopAtFirstPositional: true, allowUnknown: true }
|
|
18288
|
+
);
|
|
18289
|
+
if (args.has("version")) {
|
|
18290
|
+
ctx.line(NPM_VERSION);
|
|
18291
|
+
return 0;
|
|
18292
|
+
}
|
|
18293
|
+
if (args.has("help")) {
|
|
18294
|
+
ctx.line("Usage: npx [options] <command>[@version] [command-arg]...");
|
|
18295
|
+
ctx.line("");
|
|
18296
|
+
ctx.line("Options:");
|
|
18297
|
+
ctx.line(" -y, --yes skip the install confirmation (always implied here)");
|
|
18298
|
+
ctx.line(" -p, --package <pkg> package providing the command (repeatable)");
|
|
18299
|
+
ctx.line(" --no-install fail instead of installing a missing package");
|
|
18300
|
+
ctx.line(" -q, --quiet suppress install progress");
|
|
18301
|
+
return 0;
|
|
18302
|
+
}
|
|
18303
|
+
const call = args.str("call");
|
|
18304
|
+
const operands = call ? call.split(/\s+/).filter(Boolean) : args.positional;
|
|
18305
|
+
if (operands.length === 0) {
|
|
18306
|
+
ctx.warn("a command is required");
|
|
18307
|
+
ctx.warn("usage: npx [options] <command>[@version] [command-arg]...");
|
|
18308
|
+
return 1;
|
|
18309
|
+
}
|
|
18050
18310
|
const root = findProjectRoot(ctx);
|
|
18051
|
-
const
|
|
18052
|
-
const
|
|
18053
|
-
|
|
18054
|
-
|
|
18311
|
+
const binDir = join(root, "node_modules", ".bin");
|
|
18312
|
+
const [spec, ...rest] = operands;
|
|
18313
|
+
const { name: specName, version } = splitPackageSpec(spec);
|
|
18314
|
+
const packages = args.list("package");
|
|
18315
|
+
const packageSpec = packages[0] ?? spec;
|
|
18316
|
+
const packageName = packages[0] ? splitPackageSpec(packages[0]).name : specName;
|
|
18317
|
+
let command = packages.length > 0 ? spec : basename(specName);
|
|
18318
|
+
const run = async (binary) => {
|
|
18319
|
+
const env2 = {
|
|
18320
|
+
...ctx.env,
|
|
18321
|
+
PATH: `${binDir}:${ctx.env.PATH ?? ""}`,
|
|
18322
|
+
npm_config_user_agent: `npm/${NPM_VERSION} node/${NODE_VERSION} linux x64`
|
|
18323
|
+
};
|
|
18324
|
+
return await ctx.kernel.spawn([binary, ...rest], {
|
|
18325
|
+
cwd: ctx.cwd,
|
|
18326
|
+
env: env2,
|
|
18327
|
+
cred: ctx.cred,
|
|
18328
|
+
ppid: ctx.proc.pid,
|
|
18329
|
+
stdin: ctx.stdin,
|
|
18330
|
+
stdout: ctx.stdout,
|
|
18331
|
+
stderr: ctx.stderr
|
|
18332
|
+
}).wait();
|
|
18333
|
+
};
|
|
18334
|
+
if (!version) {
|
|
18335
|
+
if (findLocalBin(ctx, command)) return run(command);
|
|
18336
|
+
if (ctx.kernel.which(command, ctx.cwd, ctx.env, ctx.cred)) return run(command);
|
|
18337
|
+
}
|
|
18338
|
+
if (args.has("no-install")) {
|
|
18339
|
+
ctx.warn(`command not found: ${command}`);
|
|
18340
|
+
return 127;
|
|
18341
|
+
}
|
|
18342
|
+
if (!ctx.kernel.net.options.allowOutbound) {
|
|
18343
|
+
ctx.warn(`could not determine executable to run: ${command}`);
|
|
18344
|
+
ctx.warn(`'${packageName}' is not installed, and installing it needs network access.`);
|
|
18345
|
+
ctx.warn("Outbound network access is disabled for this container.");
|
|
18346
|
+
ctx.warn("Enable it with createContainer({ network: { allowOutbound: true } }).");
|
|
18347
|
+
return 127;
|
|
18348
|
+
}
|
|
18349
|
+
ctx.stderr.write(`npx: installing ${packageSpec}...
|
|
18055
18350
|
`);
|
|
18056
|
-
|
|
18351
|
+
const installed = await installPackages(ctx, [packageSpec], {
|
|
18352
|
+
cwd: root,
|
|
18353
|
+
save: false,
|
|
18354
|
+
quiet: true
|
|
18355
|
+
});
|
|
18356
|
+
if (installed !== 0) return installed;
|
|
18357
|
+
if (!findLocalBin(ctx, command)) {
|
|
18358
|
+
const binaries = packageBinaries(ctx, root, packageName);
|
|
18359
|
+
const chosen = binaries.includes(command) ? command : binaries[0];
|
|
18360
|
+
if (chosen === void 0) {
|
|
18361
|
+
ctx.warn(`could not determine executable to run: ${packageName} provides no binary`);
|
|
18362
|
+
return 127;
|
|
18363
|
+
}
|
|
18364
|
+
command = chosen;
|
|
18057
18365
|
}
|
|
18058
|
-
|
|
18059
|
-
return await ctx.kernel.spawn([name, ...rest], {
|
|
18060
|
-
cwd: ctx.cwd,
|
|
18061
|
-
env: env2,
|
|
18062
|
-
cred: ctx.cred,
|
|
18063
|
-
ppid: ctx.proc.pid,
|
|
18064
|
-
stdin: ctx.stdin,
|
|
18065
|
-
stdout: ctx.stdout,
|
|
18066
|
-
stderr: ctx.stderr
|
|
18067
|
-
}).wait();
|
|
18366
|
+
return run(command);
|
|
18068
18367
|
}
|
|
18069
18368
|
});
|
|
18070
18369
|
function makeNpmAlias(name, path) {
|
|
@@ -18483,7 +18782,7 @@ var Container = class _Container {
|
|
|
18483
18782
|
}
|
|
18484
18783
|
// ── boot ──────────────────────────────────────────────────────────────────
|
|
18485
18784
|
static async create(opts = {}) {
|
|
18486
|
-
const pod = await Nodepod.boot({
|
|
18785
|
+
const pod = opts.pod ?? await Nodepod.boot({
|
|
18487
18786
|
headless: true,
|
|
18488
18787
|
serviceWorker: false,
|
|
18489
18788
|
env: opts.env ?? {},
|
|
@@ -18534,8 +18833,8 @@ var Container = class _Container {
|
|
|
18534
18833
|
/** Copy a directory tree from the host filesystem into the container. */
|
|
18535
18834
|
async copyIn(hostPath, containerPath) {
|
|
18536
18835
|
this.assertActive();
|
|
18537
|
-
const { readdir, readFile, stat: stat2 } = await
|
|
18538
|
-
const nodePath = await
|
|
18836
|
+
const { readdir, readFile, stat: stat2 } = await nodeBuiltin("fs/promises");
|
|
18837
|
+
const nodePath = await nodeBuiltin("path");
|
|
18539
18838
|
const walk = async (src, dest) => {
|
|
18540
18839
|
const st = await stat2(src);
|
|
18541
18840
|
if (st.isDirectory()) {
|
|
@@ -18562,8 +18861,8 @@ var Container = class _Container {
|
|
|
18562
18861
|
/** Copy a file or directory out of the container onto the host. */
|
|
18563
18862
|
async copyOut(containerPath, hostPath) {
|
|
18564
18863
|
this.assertActive();
|
|
18565
|
-
const { mkdir: mkdir2, writeFile } = await
|
|
18566
|
-
const nodePath = await
|
|
18864
|
+
const { mkdir: mkdir2, writeFile } = await nodeBuiltin("fs/promises");
|
|
18865
|
+
const nodePath = await nodeBuiltin("path");
|
|
18567
18866
|
const src = resolve(this.defaults.cwd, containerPath);
|
|
18568
18867
|
const walk = async (from, to) => {
|
|
18569
18868
|
const st = this.kernel.vfs.lstat(from);
|
|
@@ -18787,7 +19086,7 @@ var Container = class _Container {
|
|
|
18787
19086
|
*/
|
|
18788
19087
|
async expose(port, opts = {}) {
|
|
18789
19088
|
this.assertActive();
|
|
18790
|
-
const http = await
|
|
19089
|
+
const http = await nodeBuiltin("http");
|
|
18791
19090
|
const container = this;
|
|
18792
19091
|
const server = http.createServer((req, res) => {
|
|
18793
19092
|
const chunks = [];
|