thebird 1.2.79 → 1.2.81

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 (79) hide show
  1. package/.github/workflows/publish.yml +9 -1
  2. package/CHANGELOG.md +217 -0
  3. package/CLAUDE.md +16 -0
  4. package/docs/agent-chat.js +7 -4
  5. package/docs/app.js +14 -11
  6. package/docs/defaults.json +1 -1
  7. package/docs/index.html +23 -6
  8. package/docs/kilo-fs-mirror.js +15 -0
  9. package/docs/kilo-http-stream.js +47 -0
  10. package/docs/node-builtins.js +24 -0
  11. package/docs/preview/index.html +32 -0
  12. package/docs/preview-sw-client.js +37 -6
  13. package/docs/preview-sw.js +55 -51
  14. package/docs/shell-awk.js +113 -0
  15. package/docs/shell-builtins-extra.js +121 -0
  16. package/docs/shell-builtins-text.js +109 -0
  17. package/docs/shell-builtins-util.js +112 -0
  18. package/docs/shell-builtins.js +183 -0
  19. package/docs/shell-bun.js +45 -0
  20. package/docs/shell-control.js +132 -0
  21. package/docs/shell-deno.js +54 -0
  22. package/docs/shell-exec.js +85 -0
  23. package/docs/shell-expand.js +164 -0
  24. package/docs/shell-fd.js +86 -0
  25. package/docs/shell-jobs.js +86 -0
  26. package/docs/shell-node-advanced.js +86 -0
  27. package/docs/shell-node-brotli.js +22 -0
  28. package/docs/shell-node-busnet.js +90 -0
  29. package/docs/shell-node-cipher.js +61 -0
  30. package/docs/shell-node-cluster.js +33 -0
  31. package/docs/shell-node-coreutils.js +36 -0
  32. package/docs/shell-node-crypto.js +137 -0
  33. package/docs/shell-node-dns.js +41 -0
  34. package/docs/shell-node-extras.js +148 -0
  35. package/docs/shell-node-firefox.js +95 -0
  36. package/docs/shell-node-git.js +60 -0
  37. package/docs/shell-node-inspector.js +39 -0
  38. package/docs/shell-node-io.js +131 -0
  39. package/docs/shell-node-ipc.js +15 -0
  40. package/docs/shell-node-keyobject.js +60 -0
  41. package/docs/shell-node-modules.js +157 -0
  42. package/docs/shell-node-native.js +31 -0
  43. package/docs/shell-node-net.js +71 -0
  44. package/docs/shell-node-observe.js +80 -0
  45. package/docs/shell-node-opfs.js +54 -0
  46. package/docs/shell-node-procfs.js +42 -0
  47. package/docs/shell-node-profiler.js +50 -0
  48. package/docs/shell-node-registry.js +24 -0
  49. package/docs/shell-node-resolve.js +147 -0
  50. package/docs/shell-node-runtime.js +83 -0
  51. package/docs/shell-node-srcmap.js +52 -0
  52. package/docs/shell-node-stdlib.js +103 -0
  53. package/docs/shell-node-streams.js +66 -0
  54. package/docs/shell-node-tar.js +47 -0
  55. package/docs/shell-node-testrunner.js +35 -0
  56. package/docs/shell-node-util-extras.js +66 -0
  57. package/docs/shell-node.js +175 -169
  58. package/docs/shell-npm.js +173 -0
  59. package/docs/shell-parser.js +122 -0
  60. package/docs/shell-pm-layout.js +62 -0
  61. package/docs/shell-pm.js +39 -0
  62. package/docs/shell-posix.js +70 -0
  63. package/docs/shell-procsub.js +65 -0
  64. package/docs/shell-readline.js +59 -4
  65. package/docs/shell-runtime.js +37 -0
  66. package/docs/shell-sed.js +83 -0
  67. package/docs/shell-signals.js +54 -0
  68. package/docs/shell-sw-jobs.js +76 -0
  69. package/docs/shell-ts.js +30 -0
  70. package/docs/shell.js +161 -152
  71. package/docs/terminal.js +9 -11
  72. package/docs/todo.html +211 -0
  73. package/package.json +1 -1
  74. package/server.js +43 -4
  75. package/start-kilo.js +45 -0
  76. package/test.js +199 -0
  77. package/.codeinsight +0 -73
  78. package/docs/acp-stream.js +0 -102
  79. package/docs/coi-serviceworker.js +0 -2
@@ -1,193 +1,199 @@
1
1
  import { createPath, createFs, createEvents, createUrl, createQuerystring, createBuffer } from './node-builtins.js';
2
-
3
- function serializeRoutes(routes) {
4
- const out = {};
5
- for (const [method, arr] of Object.entries(routes)) out[method] = arr.map(r => ({ path: r.path }));
6
- return out;
7
- }
8
-
9
- function matchRoute(pattern, path) {
10
- if (pattern === '*') return {};
11
- const pp = pattern.split('/'), rp = path.split('/');
12
- if (pp.length !== rp.length) return null;
13
- const params = {};
14
- for (let i = 0; i < pp.length; i++) {
15
- if (pp[i].startsWith(':')) params[pp[i].slice(1)] = rp[i];
16
- else if (pp[i] !== rp[i]) return null;
17
- }
18
- return params;
19
- }
20
-
21
- function createExpress(term, fsmod) {
22
- return () => {
23
- const routes = { GET: [], POST: [], PUT: [], DELETE: [], USE: [] };
24
- const middlewares = [];
25
- const app = fn => middlewares.push(fn);
26
- app.get = (p, ...fns) => routes.GET.push({ path: p, fns });
27
- app.post = (p, ...fns) => routes.POST.push({ path: p, fns });
28
- app.put = (p, ...fns) => routes.PUT.push({ path: p, fns });
29
- app.delete = (p, ...fns) => routes.DELETE.push({ path: p, fns });
30
- app.use = (...args) => {
31
- if (typeof args[0] === 'function') middlewares.push(args[0]);
32
- else routes.USE.push({ path: args[0], fn: args[1] });
33
- };
34
- app.listen = (port, cb) => {
35
- window.__debug.shell.httpHandlers[port] = { routes, middlewares };
36
- navigator.serviceWorker?.controller?.postMessage({ type: 'REGISTER_ROUTES', port, routes: serializeRoutes(routes) });
37
- term.write('Express listening on :' + port + '\r\n');
38
- cb?.();
39
- };
40
- app.json = () => (req, res, next) => {
41
- if (typeof req.body === 'string') try { req.body = JSON.parse(req.body); } catch {}
42
- next?.();
43
- };
44
- app.static = dir => (req, res) => {
45
- const fp = dir.replace(/\/$/, '') + req.path;
46
- try { res.send(fsmod.readFileSync(fp)); } catch { res.status(404).send('Not Found'); }
47
- };
48
- return app;
49
- };
50
- }
51
-
52
- function createSqlite() {
53
- return class Database {
54
- constructor(name) {
55
- this._name = name;
56
- if (!window.__sqlJs) throw new Error('sql.js not loaded');
57
- this._db = new window.__sqlJs.Database();
58
- }
59
- prepare(sql) {
60
- const db = this._db;
61
- return {
62
- run: (...p) => { db.run(sql, p); return { changes: 1 }; },
63
- get: (...p) => { const r = db.exec(sql, p); return r[0]?.values[0] ? Object.fromEntries(r[0].columns.map((c, i) => [c, r[0].values[0][i]])) : undefined; },
64
- all: (...p) => { const r = db.exec(sql, p); if (!r[0]) return []; return r[0].values.map(row => Object.fromEntries(r[0].columns.map((c, i) => [c, row[i]]))); },
65
- };
66
- }
67
- close() {}
68
- };
69
- }
70
-
71
- function createConsole(term) {
72
- const w = s => term.write(s + '\r\n');
73
- const timers = {};
74
- return {
75
- log: (...a) => w(a.map(v => typeof v === 'object' ? JSON.stringify(v, null, 2) : String(v)).join(' ')),
76
- error: (...a) => term.write('\x1b[31m' + a.map(String).join(' ') + '\x1b[0m\r\n'),
77
- warn: (...a) => term.write('\x1b[33m' + a.map(String).join(' ') + '\x1b[0m\r\n'),
78
- info: (...a) => w(a.map(String).join(' ')),
79
- dir: (o, opts) => w(JSON.stringify(o, null, 2)),
80
- table: data => {
81
- if (!Array.isArray(data)) { w(JSON.stringify(data, null, 2)); return; }
82
- if (!data.length) { w('(empty)'); return; }
83
- const cols = Object.keys(data[0]);
84
- w(cols.join('\t'));
85
- for (const row of data) w(cols.map(c => String(row[c] ?? '')).join('\t'));
86
- },
87
- time: label => { timers[label || 'default'] = performance.now(); },
88
- timeEnd: label => {
89
- const k = label || 'default';
90
- const ms = timers[k] ? (performance.now() - timers[k]).toFixed(3) : 0;
91
- delete timers[k];
92
- w(k + ': ' + ms + 'ms');
93
- },
94
- assert: (cond, ...a) => { if (!cond) term.write('\x1b[31mAssertion failed: ' + a.join(' ') + '\x1b[0m\r\n'); },
95
- count: (() => { const c = {}; return label => { const k = label || 'default'; c[k] = (c[k] || 0) + 1; w(k + ': ' + c[k]); }; })(),
96
- clear: () => term.clear(),
97
- trace: (...a) => w('Trace: ' + a.map(String).join(' ')),
98
- group: () => {},
99
- groupEnd: () => {},
100
- };
101
- }
102
-
103
- function createProcess(term, ctx) {
104
- return {
105
- argv: ['node'],
106
- env: ctx.env,
107
- cwd: () => ctx.cwd,
108
- chdir: d => { ctx.cwd = d; },
109
- exit: code => term.write('[exit ' + (code || 0) + ']\r\n'),
110
- platform: 'browser',
111
- version: 'v20.0.0',
112
- versions: { node: '20.0.0' },
113
- pid: 1,
114
- nextTick: fn => Promise.resolve().then(fn),
115
- stdout: { write: s => term.write(String(s)) },
116
- stderr: { write: s => term.write('\x1b[31m' + String(s) + '\x1b[0m') },
117
- stdin: { on: () => {} },
118
- on: () => {},
119
- off: () => {},
120
- hrtime: { bigint: () => BigInt(Math.round(performance.now() * 1e6)) },
121
- };
122
- }
2
+ import { createExpress, createHttp, createSqlite, createConsole, createProcess, NODE_VERSION, NODE_VERSIONS, NodeExit } from './shell-node-modules.js';
3
+ import { inspect, format, createZlib, preloadFflate } from './shell-node-stdlib.js';
4
+ import { createHash, createHmac, pbkdf2Sync, randomBytes } from './shell-node-crypto.js';
5
+ import { createChildProcess, createHttpClient, extendProcess, rewriteStack, isEsmCode, runEsm, parseDotEnv } from './shell-node-io.js';
6
+ import { resolveExports, resolveImports, walkUpNodeModules, resolvePackageEntry, makeModuleModule, makeModuleNotFoundError, makeFsPromises, makeFsWatch, makeNetStub, makeDgramStub, makeWorkerThreadsStub } from './shell-node-resolve.js';
7
+ import { extendBuffer, extendPath, createUrlExt, makeStringDecoder, makeReadline, makeTimersMod, makePerfHooks, makeV8Mod, makeAsyncHooks, makeStubs, makeErrorCodes, extendProcessExtras, makeStreamConsumers } from './shell-node-extras.js';
8
+ import { makeStream, extendFsStreams } from './shell-node-streams.js';
9
+ import { extendCrypto } from './shell-node-cipher.js';
10
+ import { extendKeys } from './shell-node-keyobject.js';
11
+ import { makeStreamingZlib, makeVmModule, makeModuleRegister, makeHttp2, makeWasi } from './shell-node-advanced.js';
12
+ import { makeDebugRegistry, makeDiagnosticsChannel, makeTraceEvents, makeBufferPool, makeProcessBindings, makePerfMemory, makeFetchPool, makeFsWatchReal, installPrepareStackTraceHook, installCaptureStackTrace } from './shell-node-observe.js';
13
+ import { makeWorkerThreads, makeChildProcessReal, makeRepl } from './shell-node-runtime.js';
14
+ import { detectBrowser, registerPolyfill, makeCompressionStreamZlib, makeWebCodecs, makeWebPush, makeStorageHelpers } from './shell-node-firefox.js';
15
+ import { makeOpfsBackend, wireOpfsIntoFs } from './shell-node-opfs.js';
16
+ import { preloadBrotli, makeBrotli } from './shell-node-brotli.js';
17
+ import { preloadSourceMap, installSourceMapStacks } from './shell-node-srcmap.js';
18
+ import { makeNet, makeTls, makeDgram } from './shell-node-net.js';
19
+ import { makeInspector } from './shell-node-inspector.js';
20
+ import { makeV8Profiler, makeHeapSnapshot } from './shell-node-profiler.js';
21
+ import { makeCluster } from './shell-node-cluster.js';
22
+ import { preloadX509 } from './shell-node-keyobject.js';
23
+ import { detectRuntime, registerRuntime, switchRuntime, logRuntimeSwitch } from './shell-runtime.js';
24
+ import { makeDenoGlobal } from './shell-deno.js'; import { makeBunGlobal } from './shell-bun.js';
25
+ import { makePmDispatcher, detectPm, makeCorepackStub } from './shell-pm.js';
26
+ import { isTsFile, preprocessSource } from './shell-ts.js'; import { installPosixFs, installFds, installTmpAndMisc } from './shell-posix.js';
27
+ import { makeTestRunner, makeTapReporter } from './shell-node-testrunner.js'; import { makeForkIpc } from './shell-node-ipc.js';
28
+ import { styleText, stripVTControlCharacters, getCallSites, MIMEType, MIMEParams, makeConsoleExtras } from './shell-node-util-extras.js';
29
+ import { makeProcFs, wireProcFs } from './shell-node-procfs.js'; import { makeGit } from './shell-node-git.js'; import { makeTar } from './shell-node-tar.js'; import { makeDns } from './shell-node-dns.js'; import { makeNativeLoader } from './shell-node-native.js'; import { makeRegistry } from './shell-node-registry.js';
30
+ import { makeBusnet, makeBusHttp } from './shell-node-busnet.js';
123
31
 
124
32
  export function createNodeEnv({ ctx, term }) {
125
- const pathmod = createPath();
126
- const fsmod = createFs();
127
- const Buf = createBuffer();
33
+ const pathmod = extendPath(createPath()); const Buf = makeBufferPool(extendBuffer(createBuffer())); const debugReg = makeDebugRegistry();
34
+ const browserInfo = detectBrowser(); debugReg.browser = browserInfo; const snapFn = () => window.__debug?.idbSnapshot || {};
35
+ const fsmod = installTmpAndMisc(installFds(installPosixFs(extendFsStreams(createFs(), Buf), Buf, ctx), Buf), Buf, ctx);
36
+ const opfs = makeOpfsBackend(Buf); if (opfs) wireOpfsIntoFs(fsmod, opfs, debugReg);
37
+ const runtime = detectRuntime(); registerRuntime(debugReg, runtime); fsmod.promises = makeFsPromises(fsmod); fsmod.watch = makeFsWatchReal(snapFn);
38
+ fsmod.glob = (pat, opts, cb) => { if (typeof opts === 'function') { cb = opts; opts = {}; } const matches = Object.keys(window.__debug?.idbSnapshot || {}).filter(k => new RegExp('^' + pat.replace(/\*\*/g, '.+').replace(/\*/g, '[^/]*') + '$').test(k)); queueMicrotask(() => cb?.(null, matches)); };
39
+ fsmod.globSync = pat => Object.keys(window.__debug?.idbSnapshot || {}).filter(k => new RegExp('^' + pat.replace(/\*\*/g, '.+').replace(/\*/g, '[^/]*') + '$').test(k));
40
+ const zlibMod = createZlib(Buf); const httpClient = createHttpClient(Buf); const cpMod = createChildProcess(ctx); const streamMod = makeStream();
41
+ const cpReal = makeChildProcessReal(Buf, streamMod); Object.assign(cpMod, { exec: cpReal.exec.bind(cpReal), spawn: cpReal.spawn.bind(cpReal), execFile: cpReal.execFile.bind(cpReal), execSync: cpReal.execSync, spawnSync: cpReal.spawnSync, fork: cpReal.fork });
42
+ let cryptoMod = { createHash, createHmac, pbkdf2Sync, pbkdf2: (pw, salt, iter, len, dig, cb) => queueMicrotask(() => { try { cb(null, Buf.from(pbkdf2Sync(pw, salt, iter, len, dig))); } catch (e) { cb(e); } }), randomBytes: n => Buf.from(randomBytes(n)), randomUUID: () => crypto.randomUUID(), randomInt: (a, b) => Math.floor(Math.random() * (b - a) + a), webcrypto: globalThis.crypto, constants: {} };
43
+ cryptoMod = extendKeys(extendCrypto(cryptoMod, Buf)); cryptoMod._ops = () => ++debugReg.cryptoOps; cryptoMod.secureHeapUsed = () => ({ total: 0, min: 0, used: 0, utilization: 0 });
44
+ const errorCodes = makeErrorCodes(); const stubs = makeStubs(ctx); const diagCh = makeDiagnosticsChannel(); const traceEv = makeTraceEvents(debugReg);
45
+ const vmMod = makeVmModule(); const http2Mod = makeHttp2(); const wasiMod = makeWasi(); const moduleRegister = makeModuleRegister(); const workerThreads = makeWorkerThreads(snapFn, Buf);
46
+ const getMem = makePerfMemory(performance); const FetchAgent = makeFetchPool(); const netMod = makeNet(Buf); const tlsMod = makeTls(netMod, Buf); const dgramMod = makeDgram(Buf);
47
+ const v8Real = makeV8Profiler(debugReg); const heapSnap = makeHeapSnapshot(); const clusterReal = makeCluster(); const inspector = makeInspector(debugReg);
48
+ const nativeCS = makeCompressionStreamZlib(streamMod, Buf); const webCodecs = makeWebCodecs(); const webPush = makeWebPush(); const storage = makeStorageHelpers();
49
+ const gitMod = makeGit(fsmod); const tarMod = makeTar(fsmod, null, Buf); const dnsMod = makeDns(); const nativeLoader = makeNativeLoader(); const registryMod = makeRegistry(); const busnet = makeBusnet(); globalThis.__busnet = busnet; const busHttp = makeBusHttp(busnet); debugReg.busnet = busnet;
50
+ if (nativeCS) registerPolyfill(debugReg, 'compressionStream', 'native', 'CompressionStream available'); if (browserInfo.capabilities.webCodecs) registerPolyfill(debugReg, 'webCodecs', 'native', 'WebCodecs available');
51
+ const proc = extendProcessExtras(extendProcess(createProcess(term, ctx), ctx), ctx);
52
+ proc.stdin.setRawMode = () => proc.stdin; proc.stdin.isRaw = false; proc.binding = makeProcessBindings(); proc.memoryUsage = getMem; proc.storage = storage; proc.storageBuckets = storage.buckets; proc.cwd = () => ctx.cwd; proc.chdir = p => { ctx.cwd = p.startsWith('/') ? p : pathmod.resolve(ctx.cwd, p); }; proc.umask = m => { const prev = ctx.umask || 0o022; if (m != null) ctx.umask = m; return prev; }; makeForkIpc(proc); proc.dlopen = (t, p) => nativeLoader.dlopen(t, p); proc.resourceUsage = () => { const m = performance.memory || {}; return { userCPUTime: performance.now() * 1000 | 0, systemCPUTime: 0, maxRSS: (m.totalJSHeapSize || 0) / 1024 | 0, sharedMemorySize: 0, unsharedDataSize: 0, unsharedStackSize: 0, minorPageFault: 0, majorPageFault: 0, swappedOut: 0, fsRead: 0, fsWrite: 0, ipcSent: 0, ipcReceived: 0, signalsCount: 0, voluntaryContextSwitches: 0, involuntaryContextSwitches: 0 }; };
53
+ wireProcFs(fsmod, makeProcFs(proc)); const denoGlobal = makeDenoGlobal(fsmod, proc, cpMod, ctx.httpHandlers || {}, Buf); const bunGlobal = makeBunGlobal(fsmod, proc, cpMod, ctx.httpHandlers || {}, Buf, streamMod, cryptoMod);
128
54
  const MODULES = {
129
- path: () => pathmod,
130
- fs: () => fsmod,
131
- events: () => createEvents(),
132
- url: () => createUrl(),
133
- querystring: () => createQuerystring(),
134
- os: () => ({ platform: () => 'browser', homedir: () => '/', tmpdir: () => '/tmp', cpus: () => [{}], totalmem: () => 1073741824, freemem: () => 536870912, hostname: () => 'thebird', EOL: '\n' }),
135
- util: () => ({ format: (...a) => a.join(' '), inspect: o => JSON.stringify(o, null, 2), promisify: fn => (...a) => new Promise((r, j) => fn(...a, (e, v) => e ? j(e) : r(v))), types: { isPromise: p => p instanceof Promise } }),
136
- crypto: () => ({ randomBytes: n => Buf.from(Array.from({ length: n }, () => Math.random() * 256 | 0)), randomUUID: () => crypto.randomUUID(), createHash: () => ({ update: () => ({ digest: () => 'stub' }) }) }),
137
- stream: () => ({ Readable: createEvents(), Writable: createEvents(), Transform: createEvents(), pipeline: (...a) => a.pop()(null) }),
138
- express: createExpress(term, fsmod),
139
- 'better-sqlite3': createSqlite,
55
+ path: () => pathmod, fs: () => fsmod, events: () => createEvents(), url: () => createUrlExt(), querystring: () => createQuerystring(),
56
+ os: () => { const n=navigator?.hardwareConcurrency||1; const mem=performance.memory||{}; return { platform: () => 'linux', arch: () => 'x64', homedir: () => ctx.env.HOME || '/root', tmpdir: () => '/tmp', cpus: () => Array.from({length:n},(_,i)=>({model:'Browser CPU',speed:3000,times:{user:0,nice:0,sys:0,idle:0,irq:0}})), totalmem: () => mem.jsHeapSizeLimit || 1073741824, freemem: () => (mem.jsHeapSizeLimit || 1073741824) - (mem.usedJSHeapSize || 0), hostname: () => ctx.env.HOSTNAME || 'thebird', EOL: '\n', release: () => '6.0.0-browser', type: () => 'Linux', uptime: () => performance.now() / 1000, networkInterfaces: () => ({ lo: [{ address: '127.0.0.1', netmask: '255.0.0.0', family: 'IPv4', mac: '00:00:00:00:00:00', internal: true, cidr: '127.0.0.1/8' }] }), loadavg: () => [0, 0, 0], userInfo: () => ({ username: ctx.env.USER || 'root', uid: 0, gid: 0, shell: ctx.env.SHELL || '/bin/sh', homedir: ctx.env.HOME || '/root' }), endianness: () => 'LE', version: () => '#1 SMP', machine: () => 'x86_64', devNull: '/dev/null', availableParallelism: () => n, constants: { signals: { SIGINT: 2, SIGTERM: 15, SIGKILL: 9, SIGHUP: 1 }, errno: { EACCES: 13, EEXIST: 17, ENOENT: 2, EISDIR: 21, ENOTDIR: 20 } } }; },
57
+ util: () => ({ inspect, format, promisify: fn => (...a) => new Promise((r, j) => fn(...a, (e, v) => e ? j(e) : r(v))), callbackify: fn => (...a) => { const cb = a.pop(); fn(...a).then(v => cb(null, v), e => cb(e)); }, types: { isPromise: p => p instanceof Promise, isDate: v => v instanceof Date, isRegExp: v => v instanceof RegExp, isBuffer: v => v instanceof Uint8Array, isTypedArray: v => ArrayBuffer.isView(v) && !(v instanceof DataView), isAsyncFunction: f => f?.constructor?.name === 'AsyncFunction', isNativeError: e => e instanceof Error }, deprecate: fn => fn, inherits: (a, b) => { Object.setPrototypeOf(a.prototype, b.prototype); }, debuglog: () => () => {}, isDeepStrictEqual: (a, b) => JSON.stringify(a) === JSON.stringify(b), styleText, stripVTControlCharacters, getCallSites, MIMEType, MIMEParams, parseArgs: ({ args = [], options = {} }) => { const values = {}, positionals = []; for (let i = 0; i < args.length; i++) { const a = args[i]; if (a.startsWith('--')) { const [k, v] = a.slice(2).split('='); if (v !== undefined) values[k] = v; else if (options[k]?.type === 'string') values[k] = args[++i]; else values[k] = true; } else positionals.push(a); } return { values, positionals }; } }),
58
+ crypto: () => cryptoMod,
59
+ stream: () => streamMod, 'stream/promises': () => streamMod.promises, 'stream/consumers': () => makeStreamConsumers(), 'stream/web': () => ({ ReadableStream, WritableStream, TransformStream }),
60
+ http: () => ({ ...httpClient, Agent: FetchAgent, globalAgent: new FetchAgent() }), https: () => ({ ...httpClient, Agent: FetchAgent, globalAgent: new FetchAgent() }),
61
+ http2: () => http2Mod, 'node:http2': () => http2Mod,
62
+ vm: () => vmMod, 'node:vm': () => vmMod,
63
+ buffer: () => ({ Buffer: Buf, constants: { MAX_LENGTH: 4294967295, MAX_STRING_LENGTH: 536870888 }, kMaxLength: 4294967295, Blob, File }),
64
+ child_process: () => cpMod,
65
+ net: () => netMod, dgram: () => dgramMod, tls: () => tlsMod, worker_threads: () => workerThreads,
66
+ zlib: () => ({ ...zlibMod, ...makeStreamingZlib(streamMod, Buf, globalThis.__fflate || {}), ...(nativeCS || {}), ...makeBrotli(streamMod, Buf) }),
67
+ assert: () => { const a = (v, m) => { if (!v) throw new Error(m || 'assertion failed'); }; a.ok = a; a.equal = (x, y, m) => a(x === y, m); a.deepEqual = (x, y, m) => a(JSON.stringify(x) === JSON.stringify(y), m); a.deepStrictEqual = a.deepEqual; a.strictEqual = a.equal; a.notEqual = (x, y, m) => a(x !== y, m); a.notDeepEqual = (x, y, m) => a(JSON.stringify(x) !== JSON.stringify(y), m); a.notStrictEqual = a.notEqual; a.throws = (fn, m) => { try { fn(); throw new Error('did not throw'); } catch (e) {} }; a.doesNotThrow = fn => fn(); a.rejects = async fn => { try { await (typeof fn === 'function' ? fn() : fn); throw new Error('did not reject'); } catch {} }; a.fail = m => { throw new Error(m || 'failed'); }; a.match = (s, re) => a(re.test(s)); return a; },
68
+ string_decoder: () => stubs.string_decoder, readline: () => makeReadline(term, proc), 'readline/promises': () => stubs.readline_promises,
69
+ timers: () => makeTimersMod(), 'timers/promises': () => makeTimersMod().promises, perf_hooks: () => makePerfHooks(),
70
+ v8: () => ({ ...makeV8Mod(), ...v8Real, ...heapSnap }), async_hooks: () => makeAsyncHooks(),
71
+ inspector: () => inspector, cluster: () => clusterReal || stubs.cluster,
72
+ codecs: () => { if (!webCodecs) throw makeModuleNotFoundError('codecs', []); return webCodecs; }, 'web-push': () => webPush,
73
+ sea: () => stubs.sea, 'node:sea': () => stubs.sea, test: () => makeTestRunner(term), 'node:test': () => makeTestRunner(term),
74
+ 'node:test/reporters': () => ({ tap: makeTapReporter(term), spec: class {}, dot: class {} }), tty: () => stubs.tty, domain: () => stubs.domain,
75
+ diagnostics_channel: () => diagCh, punycode: () => stubs.punycode, errors: () => errorCodes, trace_events: () => traceEv,
76
+ wasi: () => wasiMod, module: () => ({ ...makeModuleModule(() => {}, MODULES), register: moduleRegister.register, _registerHooks: moduleRegister._hooks }), express: () => createExpress(term, fsmod),
77
+ 'better-sqlite3': createSqlite, sqlite: () => ({ DatabaseSync: createSqlite, StatementSync: class {} }), 'node:sqlite': () => ({ DatabaseSync: createSqlite, StatementSync: class {} }),
78
+ dns: () => dnsMod, 'dns/promises': () => dnsMod.promises, 'node:dns': () => dnsMod, 'isomorphic-git': () => gitMod, git: () => gitMod, tar: () => tarMod, 'npm-registry-fetch': () => registryMod,
79
+ busnet: () => busnet, 'bus-http': () => busHttp,
140
80
  };
141
-
81
+ for (const k of Object.keys(MODULES)) if (!k.startsWith('node:')) MODULES['node:' + k] = MODULES[k];
142
82
  const cons = createConsole(term);
143
- const proc = createProcess(term, ctx);
83
+ cons.log = (...a) => term.write(format(...a) + '\r\n'); cons.info = cons.log; cons.error = (...a) => term.write('\x1b[31m' + format(...a) + '\x1b[0m\r\n'); cons.warn = (...a) => term.write('\x1b[33m' + format(...a) + '\x1b[0m\r\n'); cons.debug = cons.log; Object.assign(cons, makeConsoleExtras(cons, term));
84
+ const pkgCache = {}; const reqCache = {}; let requireStack = [];
85
+ function loadDotEnv() { const envFile = snapFn()[ctx.cwd.replace(/^\//, '').replace(/\/$/, '') + '/.env'] || snapFn()['.env']; if (!envFile) return; for (const [k, v] of Object.entries(parseDotEnv(envFile))) if (!(k in ctx.env)) ctx.env[k] = v; }
86
+
87
+ const resolveCandidates = (dir, id) => [pathmod.resolve(dir, id) + '.js', pathmod.resolve(dir, id), pathmod.resolve(dir, id) + '/index.js', pathmod.resolve(dir, id, 'index.js')];
88
+ function findPkgJsonDir(s, dir) { let d = dir.replace(/^\//, '').replace(/\/$/, ''); while (true) { const k = (d ? d + '/' : '') + 'package.json'; if (k in s) return d; if (!d) return null; const up = d.slice(0, d.lastIndexOf('/')); if (up === d) return null; d = up; } }
89
+ function isEsmPkg(s, filePath) { const pjDir = findPkgJsonDir(s, pathmod.dirname(filePath)); try { const pj = JSON.parse(s[(pjDir || '') + (pjDir ? '/' : '') + 'package.json']); return pj.type === 'module'; } catch { return false; } }
144
90
 
145
91
  function makeRequire(dir) {
146
- return function require(id) {
92
+ const req = function require(id) {
93
+ if (id === 'module') return makeModuleModule(req, MODULES);
147
94
  if (MODULES[id]) return MODULES[id]();
148
- const candidates = id.startsWith('.') ? [
149
- pathmod.resolve(dir, id) + '.js',
150
- pathmod.resolve(dir, id),
151
- pathmod.resolve(dir, id) + '/index.js',
152
- pathmod.resolve(dir, id, 'index.js'),
153
- ] : ['node_modules/' + id + '/index.js'];
154
- const s = snap();
155
- for (const c of candidates) {
95
+ const s = snapFn();
96
+ if (id.startsWith('#')) {
97
+ const pjRoot = findPkgJsonDir(s, dir);
98
+ if (pjRoot) { const pj = JSON.parse(s[pjRoot + '/package.json']); const target = resolveImports(pj, id); if (target) { const resolved = pathmod.resolve('/' + pjRoot, target); return loadFile(resolved.replace(/^\//, ''), s); } }
99
+ throw makeModuleNotFoundError(id, requireStack);
100
+ }
101
+ if (!id.startsWith('.')) {
102
+ if (pkgCache[id]) return pkgCache[id];
103
+ const pkgDir = walkUpNodeModules(s, dir, id);
104
+ if (pkgDir) { const entry = resolvePackageEntry(s, pkgDir); if (entry) { const m = loadFile(entry.replace(/^\//, ''), s); if (m) return m; } }
105
+ throw makeModuleNotFoundError(id, requireStack);
106
+ }
107
+ for (const c of resolveCandidates(dir, id)) {
156
108
  const key = c.replace(/^\//, '');
157
- if (key in s) {
158
- if (key.endsWith('.json')) return JSON.parse(s[key]);
159
- const mod = { exports: {} };
160
- const modDir = pathmod.dirname('/' + key);
161
- new Function('module', 'exports', 'require', '__filename', '__dirname', 'process', 'console', 'Buffer', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', 'fetch', s[key])(mod, mod.exports, makeRequire(modDir), '/' + key, modDir, proc, cons, Buf, setTimeout, setInterval, clearTimeout, clearInterval, fetch);
162
- return mod.exports;
163
- }
109
+ if (key in s) { const loaded = loadFile(key, s); if (loaded !== undefined) return loaded; }
110
+ }
111
+ throw makeModuleNotFoundError(id, requireStack);
112
+ };
113
+ function loadFile(key, s) {
114
+ if (key.endsWith('.json')) return JSON.parse(s[key]);
115
+ if (reqCache[key]) return reqCache[key].exports;
116
+ const mod = { exports: {} };
117
+ reqCache[key] = mod;
118
+ const modDir = pathmod.dirname('/' + key);
119
+ requireStack.push('/' + key);
120
+ try {
121
+ const src = s[key]; const esm = key.endsWith('.mjs') || (key.endsWith('.js') && isEsmPkg(s, '/' + key)) || isEsmCode(src);
122
+ if (esm) throw new Error("ESM module '" + key + "' requested via require() — use dynamic import() or run entry as ESM");
123
+ new Function('module', 'exports', 'require', '__filename', '__dirname', 'process', 'console', 'Buffer', 'setTimeout', 'setInterval', 'clearTimeout', 'clearInterval', 'fetch', src)(mod, mod.exports, makeRequire(modDir), '/' + key, modDir, proc, cons, Buf, setTimeout, setInterval, clearTimeout, clearInterval, fetch);
164
124
  }
165
- throw new Error('Cannot find module: ' + id);
125
+ finally { requireStack.pop(); }
126
+ mod.loaded = true;
127
+ return mod.exports;
128
+ }
129
+ req.resolve = id => {
130
+ if (MODULES[id] || id === 'module') return id;
131
+ const s = snapFn();
132
+ if (!id.startsWith('.')) { const pkgDir = walkUpNodeModules(s, dir, id); if (pkgDir) return resolvePackageEntry(s, pkgDir) || pkgDir; throw makeModuleNotFoundError(id, requireStack); }
133
+ for (const c of resolveCandidates(dir, id)) { const key = c.replace(/^\//, ''); if (key in s) return '/' + key; }
134
+ throw makeModuleNotFoundError(id, requireStack);
166
135
  };
136
+ req.cache = reqCache;
137
+ return req;
167
138
  }
168
139
 
169
- const snap = () => window.__debug?.idbSnapshot || {};
170
-
171
- async function loadSql() {
172
- if (window.__sqlJs) return window.__sqlJs;
173
- await new Promise((res, rej) => { const s = document.createElement('script'); s.src = './vendor/sql-wasm.js'; s.onload = res; s.onerror = rej; document.head.appendChild(s); });
174
- window.__sqlJs = await initSqlJs({ locateFile: f => './vendor/' + f });
175
- return window.__sqlJs;
140
+ async function preloadAsyncPkgs(entryCode, entryDir) {
141
+ const s = snapFn();
142
+ const visited = new Set(); const queue = [{ code: entryCode, dir: entryDir }]; const pkgIds = new Set();
143
+ const re = /require\(\s*['"]([^'"]+)['"]\s*\)/g;
144
+ while (queue.length) {
145
+ const { code, dir } = queue.shift(); let m; re.lastIndex = 0;
146
+ while ((m = re.exec(code))) { const id = m[1]; if (MODULES[id]) continue; if (!id.startsWith('.')) { pkgIds.add(id); continue; } for (const c of resolveCandidates(dir, id)) { const key = c.replace(/^\//, ''); if (visited.has(key) || !(key in s)) continue; visited.add(key); queue.push({ code: s[key], dir: pathmod.dirname('/' + key) }); break; } }
147
+ }
148
+ for (const id of pkgIds) {
149
+ if (pkgCache[id]) continue;
150
+ const key = 'node_modules/' + id + '/index.js'; if (!(key in s)) continue;
151
+ const urlMatch = s[key].match(/import\((".+?")\)/); if (!urlMatch) continue;
152
+ const url = JSON.parse(urlMatch[1]);
153
+ try { const mod = await import(url); const exp = { ...mod }; if (mod.default && typeof mod.default === 'object') Object.assign(exp, mod.default); pkgCache[id] = mod.default && Object.keys(mod).length === 1 ? mod.default : exp; }
154
+ catch (e) { term.write('\x1b[31mfailed to load ' + id + ': ' + e.message + '\x1b[0m\r\n'); }
155
+ }
176
156
  }
177
157
 
178
- return async function nodeEval(code, filename, argv) {
158
+ return async function nodeEval(code, filename, argv, stdinBuf) {
179
159
  const dir = filename ? pathmod.dirname(filename) : ctx.cwd;
180
- const fpath = filename || ctx.cwd + '/repl';
181
- proc.argv = ['node', fpath, ...(argv || [])];
182
- const scope = { process: proc, console: cons, require: makeRequire(dir), Buffer: Buf, __filename: fpath, __dirname: dir, setTimeout, setInterval, clearTimeout, clearInterval, fetch, loadSql, module: { exports: {} }, exports: {} };
160
+ const fpath = filename || '[eval]';
161
+ proc.argv = filename ? ['node', fpath, ...(argv || [])] : ['node'];
162
+ proc.exitCode = 0;
163
+ loadDotEnv();
164
+ globalThis.__fflate = await preloadFflate().catch(() => ({}));
165
+ if (proc.sourceMapsEnabled) { await preloadSourceMap().catch(() => {}); installSourceMapStacks(snapFn); }
166
+ const rtName = switchRuntime(code.startsWith('#!') ? code.slice(0, code.indexOf('\n')) : ''); logRuntimeSwitch(debugReg, debugReg.runtime.active, rtName, fpath);
167
+ if (isTsFile(fpath)) code = await preprocessSource(fpath, code);
168
+ await preloadAsyncPkgs(code, dir);
169
+ const reqFn = makeRequire(dir);
170
+ const scope = { process: proc, console: cons, require: reqFn, Buffer: Buf, __filename: fpath, __dirname: dir, setTimeout, setInterval, clearTimeout, clearInterval, fetch, module: { exports: {} }, exports: {}, global: globalThis, URL, URLSearchParams, TextEncoder, TextDecoder };
171
+ const prevGlobals = { process: globalThis.process, Buffer: globalThis.Buffer, Deno: globalThis.Deno, Bun: globalThis.Bun };
172
+ globalThis.process = proc; globalThis.Buffer = Buf;
173
+ if (rtName === 'deno') globalThis.Deno = denoGlobal; else delete globalThis.Deno;
174
+ if (rtName === 'bun') globalThis.Bun = bunGlobal; else delete globalThis.Bun;
175
+ installCaptureStackTrace(); installPrepareStackTraceHook();
176
+ const unhandledH = e => { e.preventDefault?.(); const err = e.reason || e; term.write('\x1b[31m' + rewriteStack(err, fpath) + '\x1b[0m\r\n'); ctx.lastExitCode = 1; };
177
+ window.addEventListener('unhandledrejection', unhandledH);
183
178
  try {
184
- const keys = Object.keys(scope);
185
- const vals = Object.values(scope);
179
+ if (isEsmCode(code)) { const preamble = '\nconst __filename = ' + JSON.stringify(fpath) + ';\nconst __dirname = ' + JSON.stringify(dir) + ';\n'; const mod = await runEsm(preamble + code, scope); if (mod && !filename) { for (const [k, v] of Object.entries(mod)) if (k !== 'default') cons.log(k + ':', v); } ctx.lastExitCode = proc.exitCode | 0; return; }
180
+ const keys = Object.keys(scope), vals = Object.values(scope);
186
181
  const fn = new Function(...keys, 'return (async () => {\n' + code + '\n})()');
187
- const result = await fn(...vals);
182
+ const pending = fn(...vals);
183
+ if (stdinBuf) queueMicrotask(() => proc.stdin._feed(stdinBuf));
184
+ const result = await pending;
188
185
  if (result !== undefined && !filename) cons.log(result);
186
+ ctx.lastExitCode = proc.exitCode | 0;
189
187
  } catch (e) {
190
- term.write('\x1b[31m' + (filename ? filename + ': ' : '') + e.message + '\x1b[0m\r\n');
188
+ if (e && e.__nodeExit) { ctx.lastExitCode = e.code | 0; return; }
189
+ term.write('\x1b[31m' + rewriteStack(e, fpath) + '\x1b[0m\r\n');
190
+ ctx.lastExitCode = 1;
191
+ } finally {
192
+ window.removeEventListener('unhandledrejection', unhandledH);
193
+ if (prevGlobals.process !== undefined) globalThis.process = prevGlobals.process; else delete globalThis.process;
194
+ if (prevGlobals.Buffer !== undefined) globalThis.Buffer = prevGlobals.Buffer; else delete globalThis.Buffer;
195
+ if (prevGlobals.Deno !== undefined) globalThis.Deno = prevGlobals.Deno; else delete globalThis.Deno;
196
+ if (prevGlobals.Bun !== undefined) globalThis.Bun = prevGlobals.Bun; else delete globalThis.Bun;
191
197
  }
192
198
  };
193
199
  }
@@ -0,0 +1,173 @@
1
+ import { NPM_VERSION } from './shell-node-modules.js';
2
+
3
+ const toKey = p => p.replace(/^\//, '');
4
+ const snap = () => window.__debug.idbSnapshot || {};
5
+ const persist = () => window.__debug.idbPersist?.();
6
+
7
+ function resolvePkgJson(cwd, ctx) {
8
+ const path = cwd.replace(/\/$/, '') + '/package.json';
9
+ const raw = snap()[toKey(path)];
10
+ if (!raw) throw new Error('npm: no package.json in ' + cwd);
11
+ try { return { path, data: JSON.parse(raw) }; } catch (e) { throw new Error('npm: invalid package.json: ' + e.message); }
12
+ }
13
+
14
+ async function installOne(pkg, version, term) {
15
+ const spec = version && version !== 'latest' ? pkg + '@' + version.replace(/^[\^~]/, '') : pkg;
16
+ const url = 'https://esm.sh/' + spec + '?bundle&target=es2022';
17
+ term.write(' → ' + spec + '\r\n');
18
+ await import(url);
19
+ const stubPath = 'node_modules/' + pkg + '/index.js';
20
+ snap()[stubPath] = '// esm.sh async stub\nawait import(' + JSON.stringify(url) + ');';
21
+ const meta = { name: pkg, version: version || 'latest', _resolved: url, _from: 'esm.sh' };
22
+ snap()['node_modules/' + pkg + '/package.json'] = JSON.stringify(meta, null, 2);
23
+ persist();
24
+ }
25
+
26
+ function writePkgJson(pkgPath, data) {
27
+ snap()[toKey(pkgPath)] = JSON.stringify(data, null, 2);
28
+ persist();
29
+ }
30
+
31
+ function injectNpmEnv(ctx, data, scriptName) {
32
+ const prev = {};
33
+ const set = (k, v) => { prev[k] = ctx.env[k]; ctx.env[k] = v; };
34
+ set('npm_lifecycle_event', scriptName);
35
+ set('npm_package_name', data.name || '');
36
+ set('npm_package_version', data.version || '');
37
+ set('npm_config_user_agent', 'npm/' + NPM_VERSION + ' node/v23.10.0');
38
+ set('NODE_ENV', ctx.env.NODE_ENV || 'development');
39
+ for (const [k, v] of Object.entries(data.scripts || {})) set('npm_package_scripts_' + k.replace(/[^a-z0-9_]/gi, '_'), v);
40
+ return () => { for (const k of Object.keys(prev)) { if (prev[k] === undefined) delete ctx.env[k]; else ctx.env[k] = prev[k]; } };
41
+ }
42
+
43
+ export function makeNpm(ctx) {
44
+ const w = s => ctx.term.write(s);
45
+ const wl = s => w(s + '\r\n');
46
+
47
+ async function cmdInstall(args) {
48
+ const saveDev = args.includes('--save-dev') || args.includes('-D');
49
+ const noSave = args.includes('--no-save');
50
+ const pkgs = args.filter(a => !a.startsWith('-'));
51
+ if (!pkgs.length) {
52
+ const { data } = resolvePkgJson(ctx.cwd, ctx);
53
+ const all = { ...(data.dependencies || {}), ...(data.devDependencies || {}), ...(data.peerDependencies || {}) };
54
+ const entries = Object.entries(all);
55
+ if (!entries.length) { wl('up to date, 0 packages'); return; }
56
+ wl('installing ' + entries.length + ' packages from package.json');
57
+ for (const [name, ver] of entries) await installOne(name, ver, ctx.term);
58
+ wl('added ' + entries.length + ' package' + (entries.length === 1 ? '' : 's'));
59
+ return;
60
+ }
61
+ for (const spec of pkgs) {
62
+ const m = spec.match(/^(@?[^@]+?)(?:@(.+))?$/);
63
+ const [, name, version] = m;
64
+ await installOne(name, version, ctx.term);
65
+ }
66
+ if (!noSave) {
67
+ const { path: pkgPath, data } = resolvePkgJson(ctx.cwd, ctx);
68
+ const target = saveDev ? 'devDependencies' : 'dependencies';
69
+ data[target] = data[target] || {};
70
+ for (const spec of pkgs) {
71
+ const m = spec.match(/^(@?[^@]+?)(?:@(.+))?$/);
72
+ data[target][m[1]] = m[2] || 'latest';
73
+ }
74
+ writePkgJson(pkgPath, data);
75
+ }
76
+ wl('added ' + pkgs.length + ' package' + (pkgs.length === 1 ? '' : 's'));
77
+ }
78
+
79
+ function cmdUninstall(args) {
80
+ const pkgs = args.filter(a => !a.startsWith('-'));
81
+ if (!pkgs.length) throw new Error('npm uninstall <pkg>');
82
+ for (const pkg of pkgs) {
83
+ const s = snap();
84
+ let n = 0;
85
+ for (const k of Object.keys(s)) {
86
+ if (k === 'node_modules/' + pkg + '/index.js' || k.startsWith('node_modules/' + pkg + '/')) { delete s[k]; n++; }
87
+ }
88
+ wl(n ? 'removed ' + pkg : pkg + ' not installed');
89
+ }
90
+ const { path: pkgPath, data } = resolvePkgJson(ctx.cwd, ctx);
91
+ for (const pkg of pkgs) { delete data.dependencies?.[pkg]; delete data.devDependencies?.[pkg]; }
92
+ writePkgJson(pkgPath, data);
93
+ persist();
94
+ }
95
+
96
+ function cmdList(args) {
97
+ const filter = args.find(a => !a.startsWith('-'));
98
+ const s = snap();
99
+ const installed = Object.keys(s).filter(k => k.match(/^node_modules\/[^/]+\/package\.json$/) || k.match(/^node_modules\/@[^/]+\/[^/]+\/package\.json$/));
100
+ try {
101
+ const { data } = resolvePkgJson(ctx.cwd, ctx);
102
+ wl(data.name + '@' + (data.version || '1.0.0') + ' ' + ctx.cwd);
103
+ } catch { wl('(no package.json)'); }
104
+ for (const k of installed) {
105
+ const name = k.replace(/^node_modules\//, '').replace(/\/package\.json$/, '');
106
+ if (filter && !name.includes(filter)) continue;
107
+ const pj = JSON.parse(s[k]);
108
+ wl('├── ' + name + '@' + (pj.version || 'latest'));
109
+ }
110
+ }
111
+
112
+ async function cmdRun(args) {
113
+ const [scriptName, ...rest] = args;
114
+ const { data } = resolvePkgJson(ctx.cwd, ctx);
115
+ if (!scriptName) {
116
+ wl('Lifecycle scripts included in ' + (data.name || 'package') + '@' + (data.version || '') + ':');
117
+ for (const [n, s] of Object.entries(data.scripts || {})) wl(' ' + n + '\r\n ' + s);
118
+ return null;
119
+ }
120
+ const cmd = data.scripts?.[scriptName];
121
+ if (!cmd) throw new Error('npm: Missing script: "' + scriptName + '"');
122
+ const pre = data.scripts?.['pre' + scriptName];
123
+ const post = data.scripts?.['post' + scriptName];
124
+ const restore = injectNpmEnv(ctx, data, scriptName);
125
+ try {
126
+ const chain = [];
127
+ if (pre) chain.push({ name: 'pre' + scriptName, cmd: pre });
128
+ chain.push({ name: scriptName, cmd: cmd + (rest.length ? ' ' + rest.join(' ') : '') });
129
+ if (post) chain.push({ name: 'post' + scriptName, cmd: post });
130
+ return { runInShell: null, npmChain: chain, pkgName: data.name || 'package', pkgVersion: data.version || '' };
131
+ } finally { queueMicrotask(restore); }
132
+ }
133
+
134
+ function cmdInit(args) {
135
+ const yes = args.includes('-y') || args.includes('--yes');
136
+ if (!yes) { wl('npm init -y — use -y for non-interactive'); return; }
137
+ const pj = { name: ctx.cwd.split('/').filter(Boolean).pop() || 'project', version: '1.0.0', main: 'index.js', scripts: { start: 'node index.js', test: 'echo "Error: no test specified" && exit 1' }, dependencies: {} };
138
+ writePkgJson(ctx.cwd.replace(/\/$/, '') + '/package.json', pj);
139
+ wl('Wrote to ' + ctx.cwd.replace(/\/$/, '') + '/package.json');
140
+ }
141
+
142
+ async function cmdExec(args) {
143
+ const pkg = args[0];
144
+ if (!pkg) throw new Error('npx: package required');
145
+ const s = snap();
146
+ if (!s['node_modules/' + pkg + '/index.js']) await installOne(pkg, null, ctx.term);
147
+ const binPath = 'node_modules/.bin/' + pkg;
148
+ if (s[binPath]) return { runInShell: 'node /' + binPath + (args.length > 1 ? ' ' + args.slice(1).join(' ') : '') };
149
+ return { runInShell: 'node -e "require(\'' + pkg + '\')"' };
150
+ }
151
+
152
+ return async function npm(args) {
153
+ const sub = args[0];
154
+ const rest = args.slice(1);
155
+ if (sub === 'install' || sub === 'i' || sub === 'add') return cmdInstall(rest);
156
+ if (sub === 'uninstall' || sub === 'remove' || sub === 'rm') return cmdUninstall(rest);
157
+ if (sub === 'ls' || sub === 'list') return cmdList(rest);
158
+ if (sub === 'run' || sub === 'run-script') return cmdRun(rest);
159
+ if (sub === 'start') return cmdRun(['start', ...rest]);
160
+ if (sub === 'test' || sub === 't') return cmdRun(['test', ...rest]);
161
+ if (sub === 'init' || sub === 'create') return cmdInit(rest);
162
+ if (sub === 'exec' || sub === 'x') return cmdExec(rest);
163
+ if (sub === '--version' || sub === '-v') { wl(NPM_VERSION); return; }
164
+ if (sub === 'prefix') { wl(ctx.cwd); return; }
165
+ if (sub === 'root') { wl(ctx.cwd.replace(/\/$/, '') + '/node_modules'); return; }
166
+ if (sub === 'view' || sub === 'info' || sub === 'show') { const p = rest[0]; wl(p + ' — use esm.sh to inspect'); return; }
167
+ throw new Error('npm: unknown command "' + sub + '"');
168
+ };
169
+ }
170
+
171
+ export function makeNpx(npmCmd) {
172
+ return args => npmCmd(['exec', ...args]);
173
+ }