thebird 1.2.78 → 1.2.80
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/.github/workflows/publish.yml +9 -1
- package/CHANGELOG.md +226 -0
- package/CLAUDE.md +16 -0
- package/docs/agent-chat.js +7 -4
- package/docs/app.js +14 -11
- package/docs/defaults.json +1 -1
- package/docs/index.html +23 -6
- package/docs/kilo-http-stream.js +24 -0
- package/docs/node-builtins.js +194 -0
- package/docs/preview/index.html +32 -0
- package/docs/preview-sw-client.js +37 -6
- package/docs/preview-sw.js +55 -51
- package/docs/shell-awk.js +113 -0
- package/docs/shell-builtins-extra.js +121 -0
- package/docs/shell-builtins-text.js +109 -0
- package/docs/shell-builtins-util.js +112 -0
- package/docs/shell-builtins.js +183 -0
- package/docs/shell-bun.js +45 -0
- package/docs/shell-control.js +132 -0
- package/docs/shell-deno.js +54 -0
- package/docs/shell-exec.js +85 -0
- package/docs/shell-expand.js +164 -0
- package/docs/shell-fd.js +86 -0
- package/docs/shell-jobs.js +86 -0
- package/docs/shell-node-advanced.js +86 -0
- package/docs/shell-node-brotli.js +22 -0
- package/docs/shell-node-busnet.js +90 -0
- package/docs/shell-node-cipher.js +61 -0
- package/docs/shell-node-cluster.js +33 -0
- package/docs/shell-node-coreutils.js +36 -0
- package/docs/shell-node-crypto.js +137 -0
- package/docs/shell-node-dns.js +41 -0
- package/docs/shell-node-extras.js +148 -0
- package/docs/shell-node-firefox.js +95 -0
- package/docs/shell-node-git.js +60 -0
- package/docs/shell-node-inspector.js +39 -0
- package/docs/shell-node-io.js +131 -0
- package/docs/shell-node-ipc.js +15 -0
- package/docs/shell-node-keyobject.js +60 -0
- package/docs/shell-node-modules.js +157 -0
- package/docs/shell-node-native.js +31 -0
- package/docs/shell-node-net.js +71 -0
- package/docs/shell-node-observe.js +80 -0
- package/docs/shell-node-opfs.js +54 -0
- package/docs/shell-node-procfs.js +42 -0
- package/docs/shell-node-profiler.js +50 -0
- package/docs/shell-node-registry.js +24 -0
- package/docs/shell-node-resolve.js +147 -0
- package/docs/shell-node-runtime.js +83 -0
- package/docs/shell-node-srcmap.js +52 -0
- package/docs/shell-node-stdlib.js +103 -0
- package/docs/shell-node-streams.js +66 -0
- package/docs/shell-node-tar.js +47 -0
- package/docs/shell-node-testrunner.js +35 -0
- package/docs/shell-node-util-extras.js +66 -0
- package/docs/shell-node.js +188 -97
- package/docs/shell-npm.js +173 -0
- package/docs/shell-parser.js +122 -0
- package/docs/shell-pm-layout.js +62 -0
- package/docs/shell-pm.js +39 -0
- package/docs/shell-posix.js +70 -0
- package/docs/shell-procsub.js +65 -0
- package/docs/shell-readline.js +59 -4
- package/docs/shell-runtime.js +37 -0
- package/docs/shell-sed.js +83 -0
- package/docs/shell-signals.js +54 -0
- package/docs/shell-sw-jobs.js +76 -0
- package/docs/shell-ts.js +30 -0
- package/docs/shell.js +159 -167
- package/docs/terminal.js +9 -11
- package/docs/todo.html +211 -0
- package/package.json +1 -1
- package/server.js +43 -4
- package/start-kilo.js +17 -0
- package/test.js +199 -0
- package/.codeinsight +0 -73
- package/docs/acp-stream.js +0 -102
- package/docs/coi-serviceworker.js +0 -2
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
function serializeRoutes(routes) {
|
|
2
|
+
const out = {};
|
|
3
|
+
for (const [method, arr] of Object.entries(routes)) out[method] = arr.map(r => ({ path: r.path }));
|
|
4
|
+
return out;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function runFns(fns, req, res) {
|
|
8
|
+
let i = 0;
|
|
9
|
+
const next = err => {
|
|
10
|
+
if (err) { res.status?.(500).send?.(String(err)); return; }
|
|
11
|
+
const fn = fns[i++];
|
|
12
|
+
if (fn) fn(req, res, next);
|
|
13
|
+
};
|
|
14
|
+
next();
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function createExpress(term, fsmod) {
|
|
18
|
+
return () => {
|
|
19
|
+
const routes = { GET: [], POST: [], PUT: [], DELETE: [], USE: [] };
|
|
20
|
+
const middlewares = [];
|
|
21
|
+
const app = fn => middlewares.push(fn);
|
|
22
|
+
const addRoute = method => (p, ...fns) => routes[method].push({ path: p, fn: (req, res) => runFns([...middlewares, ...fns], req, res) });
|
|
23
|
+
app.get = addRoute('GET');
|
|
24
|
+
app.post = addRoute('POST');
|
|
25
|
+
app.put = addRoute('PUT');
|
|
26
|
+
app.delete = addRoute('DELETE');
|
|
27
|
+
app.use = (...args) => {
|
|
28
|
+
if (typeof args[0] === 'function') middlewares.push(args[0]);
|
|
29
|
+
else routes.USE.push({ path: args[0], fn: args[1] });
|
|
30
|
+
};
|
|
31
|
+
app.listen = (port, cb) => {
|
|
32
|
+
window.__debug.shell.httpHandlers[port] = { routes, middlewares };
|
|
33
|
+
navigator.serviceWorker?.controller?.postMessage({ type: 'REGISTER_ROUTES', port, routes: serializeRoutes(routes) });
|
|
34
|
+
term.write('Express listening on :' + port + '\r\n');
|
|
35
|
+
cb?.();
|
|
36
|
+
};
|
|
37
|
+
app.json = () => (req, res, next) => {
|
|
38
|
+
if (typeof req.body === 'string') try { req.body = JSON.parse(req.body); } catch {}
|
|
39
|
+
next?.();
|
|
40
|
+
};
|
|
41
|
+
app.static = dir => (req, res) => {
|
|
42
|
+
const fp = dir.replace(/\/$/, '') + req.path;
|
|
43
|
+
try { res.send(fsmod.readFileSync(fp)); } catch { res.status(404).send('Not Found'); }
|
|
44
|
+
};
|
|
45
|
+
return app;
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createHttp(term) {
|
|
50
|
+
return () => ({
|
|
51
|
+
createServer(handler) {
|
|
52
|
+
const routes = { GET: [{ path: '*', fn: (req, res) => handler(req, res) }], POST: [], PUT: [], DELETE: [], USE: [] };
|
|
53
|
+
return {
|
|
54
|
+
listen(port, cb) {
|
|
55
|
+
window.__debug.shell.httpHandlers[port] = { routes, middlewares: [] };
|
|
56
|
+
term.write('http listening on :' + port + '\r\n');
|
|
57
|
+
(typeof cb === 'function' ? cb : (typeof port === 'function' ? port : null))?.();
|
|
58
|
+
return this;
|
|
59
|
+
},
|
|
60
|
+
close(cb) { cb?.(); },
|
|
61
|
+
on() { return this; },
|
|
62
|
+
};
|
|
63
|
+
},
|
|
64
|
+
request: () => { throw new Error('http.request: not supported in browser — use fetch()'); },
|
|
65
|
+
get: () => { throw new Error('http.get: not supported in browser — use fetch()'); },
|
|
66
|
+
STATUS_CODES: { 200: 'OK', 404: 'Not Found', 500: 'Internal Server Error' },
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function createSqlite() {
|
|
71
|
+
return class Database {
|
|
72
|
+
constructor(name) {
|
|
73
|
+
this._name = name;
|
|
74
|
+
if (!window.__sqlJs) throw new Error('sql.js not loaded');
|
|
75
|
+
this._db = new window.__sqlJs.Database();
|
|
76
|
+
}
|
|
77
|
+
prepare(sql) {
|
|
78
|
+
const db = this._db;
|
|
79
|
+
return {
|
|
80
|
+
run: (...p) => { db.run(sql, p); return { changes: 1 }; },
|
|
81
|
+
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; },
|
|
82
|
+
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]]))); },
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
close() {}
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
export function createConsole(term) {
|
|
90
|
+
const w = s => term.write(s + '\r\n');
|
|
91
|
+
const timers = {};
|
|
92
|
+
return {
|
|
93
|
+
log: (...a) => w(a.map(v => typeof v === 'object' ? JSON.stringify(v, null, 2) : String(v)).join(' ')),
|
|
94
|
+
error: (...a) => term.write('\x1b[31m' + a.map(String).join(' ') + '\x1b[0m\r\n'),
|
|
95
|
+
warn: (...a) => term.write('\x1b[33m' + a.map(String).join(' ') + '\x1b[0m\r\n'),
|
|
96
|
+
info: (...a) => w(a.map(String).join(' ')),
|
|
97
|
+
dir: o => w(JSON.stringify(o, null, 2)),
|
|
98
|
+
table: data => {
|
|
99
|
+
if (!Array.isArray(data)) { w(JSON.stringify(data, null, 2)); return; }
|
|
100
|
+
if (!data.length) { w('(empty)'); return; }
|
|
101
|
+
const cols = Object.keys(data[0]);
|
|
102
|
+
w(cols.join('\t'));
|
|
103
|
+
for (const row of data) w(cols.map(c => String(row[c] ?? '')).join('\t'));
|
|
104
|
+
},
|
|
105
|
+
time: label => { timers[label || 'default'] = performance.now(); },
|
|
106
|
+
timeEnd: label => {
|
|
107
|
+
const k = label || 'default';
|
|
108
|
+
const ms = timers[k] ? (performance.now() - timers[k]).toFixed(3) : 0;
|
|
109
|
+
delete timers[k];
|
|
110
|
+
w(k + ': ' + ms + 'ms');
|
|
111
|
+
},
|
|
112
|
+
assert: (cond, ...a) => { if (!cond) term.write('\x1b[31mAssertion failed: ' + a.join(' ') + '\x1b[0m\r\n'); },
|
|
113
|
+
count: (() => { const c = {}; return label => { const k = label || 'default'; c[k] = (c[k] || 0) + 1; w(k + ': ' + c[k]); }; })(),
|
|
114
|
+
clear: () => term.clear(),
|
|
115
|
+
trace: (...a) => w('Trace: ' + a.map(String).join(' ')),
|
|
116
|
+
group: () => {},
|
|
117
|
+
groupEnd: () => {},
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export const NODE_VERSION = 'v23.10.0';
|
|
122
|
+
export const NODE_VERSIONS = { node: '23.10.0', acorn: '8.14.0', ada: '3.1.3', amaro: '0.4.1', ares: '1.34.4', brotli: '1.1.0', cjs_module_lexer: '2.1.0', cldr: '46.0', icu: '76.1', llhttp: '9.2.1', modules: '131', napi: '10', nbytes: '0.1.1', ncrypto: '0.0.1', nghttp2: '1.64.0', openssl: '3.0.16', simdjson: '3.12.2', simdutf: '6.0.3', sqlite: '3.49.1', tz: '2025a', undici: '6.21.1', unicode: '16.0', uv: '1.50.0', uvwasi: '0.0.21', v8: '12.9.202.28-node.13', zlib: '1.3.0.1-motley-788cb3c', zstd: '1.5.6' };
|
|
123
|
+
export const NPM_VERSION = '10.9.2';
|
|
124
|
+
|
|
125
|
+
export class NodeExit extends Error { constructor(code) { super('__NodeExit:' + code); this.code = code | 0; this.__nodeExit = true; } }
|
|
126
|
+
|
|
127
|
+
export function createProcess(term, ctx) {
|
|
128
|
+
const stdinHandlers = { data: [], end: [] };
|
|
129
|
+
return {
|
|
130
|
+
argv: ['node'],
|
|
131
|
+
env: ctx.env,
|
|
132
|
+
cwd: () => ctx.cwd,
|
|
133
|
+
chdir: d => { ctx.cwd = d; },
|
|
134
|
+
exit: code => { throw new NodeExit(code || 0); },
|
|
135
|
+
platform: 'linux',
|
|
136
|
+
arch: 'x64',
|
|
137
|
+
version: NODE_VERSION,
|
|
138
|
+
versions: { ...NODE_VERSIONS },
|
|
139
|
+
pid: 1,
|
|
140
|
+
ppid: 0,
|
|
141
|
+
nextTick: fn => Promise.resolve().then(fn),
|
|
142
|
+
stdout: { write: s => { term.write(String(s)); return true; }, isTTY: true, columns: 80, rows: 24 },
|
|
143
|
+
stderr: { write: s => { term.write('\x1b[31m' + String(s) + '\x1b[0m'); return true; }, isTTY: true, columns: 80, rows: 24 },
|
|
144
|
+
stdin: {
|
|
145
|
+
on: (ev, fn) => { (stdinHandlers[ev] || (stdinHandlers[ev] = [])).push(fn); return this; },
|
|
146
|
+
once: (ev, fn) => { (stdinHandlers[ev] || (stdinHandlers[ev] = [])).push(fn); },
|
|
147
|
+
_feed: buf => { if (buf) for (const h of stdinHandlers.data) h(buf); for (const h of stdinHandlers.end) h(); },
|
|
148
|
+
isTTY: false, setEncoding: () => {}, resume: () => {}, pause: () => {},
|
|
149
|
+
},
|
|
150
|
+
on: () => {},
|
|
151
|
+
off: () => {},
|
|
152
|
+
emit: () => {},
|
|
153
|
+
hrtime: Object.assign(() => [0, 0], { bigint: () => BigInt(Math.round(performance.now() * 1e6)) }),
|
|
154
|
+
exitCode: 0,
|
|
155
|
+
_stdinHandlers: stdinHandlers,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
const NATIVE_DISPATCH={
|
|
2
|
+
'better_sqlite3.node':()=>import('https://esm.sh/sql.js@1.11.0/dist/sql-wasm.js').then(m=>{const Lib=m.default||m;return{__native:true,Database:Lib.Database||Lib};}),
|
|
3
|
+
'bcrypt_lib.node':()=>import('https://esm.sh/bcryptjs@2.4.3').then(m=>({__native:true,...(m.default||m)})),
|
|
4
|
+
'sharp.node':()=>({__native:true,resize:()=>{throw new Error('sharp native requires WASM variant — use @img/sharp-wasm32');}}),
|
|
5
|
+
'argon2.node':()=>import('https://esm.sh/argon2-browser@1.18.0').then(m=>({__native:true,hash:m.default.hash,verify:m.default.verify})),
|
|
6
|
+
'bufferutil.node':()=>({__native:true,mask:(src,mask,out,offset,length)=>{for(let i=0;i<length;i++)out[offset+i]=src[i]^mask[i&3];},unmask:(buf,mask)=>{for(let i=0;i<buf.length;i++)buf[i]^=mask[i&3];}}),
|
|
7
|
+
'utf_8_validate.node':()=>({__native:true,default:(s,buf)=>{try{new TextDecoder('utf-8',{fatal:true}).decode(buf);return true;}catch{return false;}}}),
|
|
8
|
+
'farmhash.node':()=>({__native:true,hash32:s=>{let h=2166136261;for(const c of String(s))h=Math.imul(h^c.charCodeAt(0),16777619)>>>0;return h;},hash64:s=>BigInt(NATIVE_DISPATCH['farmhash.node']().hash32(s))}),
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export function makeNativeLoader(){
|
|
12
|
+
return{
|
|
13
|
+
async dlopen(target,path){const key=path.split('/').pop();const loader=NATIVE_DISPATCH[key];if(!loader)throw new Error(`process.dlopen: no WASM/browser equivalent for ${path}`);const mod=await loader();target.exports=mod;return target;},
|
|
14
|
+
resolve:key=>NATIVE_DISPATCH[key]?'virtual:native/'+key:null,
|
|
15
|
+
register:(name,loader)=>{NATIVE_DISPATCH[name]=loader;},
|
|
16
|
+
list:()=>Object.keys(NATIVE_DISPATCH),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function wireNativeRequire(makeRequire,nativeLoader){
|
|
21
|
+
const origRequire=makeRequire;
|
|
22
|
+
return dir=>{
|
|
23
|
+
const req=origRequire(dir);
|
|
24
|
+
const wrapped=function(id){
|
|
25
|
+
if(id.endsWith('.node')){const key=id.split('/').pop();if(nativeLoader.resolve(key)){const target={exports:{}};nativeLoader.dlopen(target,id);return target.exports;}throw new Error(`Cannot find native addon: ${id}`);}
|
|
26
|
+
return req(id);
|
|
27
|
+
};
|
|
28
|
+
wrapped.resolve=req.resolve;wrapped.cache=req.cache;
|
|
29
|
+
return wrapped;
|
|
30
|
+
};
|
|
31
|
+
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
const relayUrl=()=>{const g=globalThis;return g.__plugkit_tcp_relay||g.window?.__plugkit_tcp_relay||null;};
|
|
2
|
+
const udpRelayUrl=()=>{const g=globalThis;return g.__plugkit_udp_relay||g.window?.__plugkit_udp_relay||null;};
|
|
3
|
+
|
|
4
|
+
export function makeNet(Buf){
|
|
5
|
+
class Socket{
|
|
6
|
+
constructor(){this._h={};this._ws=null;this.bufferedAmount=0;this.writable=true;this.readable=true;this.remoteAddress=null;this.remotePort=null;this.destroyed=false;}
|
|
7
|
+
_emit(ev,...a){for(const f of this._h[ev]||[])f(...a);}
|
|
8
|
+
on(ev,fn){(this._h[ev]=this._h[ev]||[]).push(fn);return this;}
|
|
9
|
+
once(ev,fn){const w=(...a)=>{this.off(ev,w);fn(...a);};return this.on(ev,w);}
|
|
10
|
+
off(ev,fn){this._h[ev]=(this._h[ev]||[]).filter(x=>x!==fn);return this;}
|
|
11
|
+
connect(opts,listener){const{port,host='127.0.0.1',tls=false}=typeof opts==='object'?opts:{port:opts,host:arguments[1]};const relay=relayUrl();if(!relay)throw new Error('net.Socket.connect: set window.__plugkit_tcp_relay to a WSS URL that tunnels TCP — not configured');this.remoteAddress=host;this.remotePort=port;const url=`${relay}${relay.includes('?')?'&':'?'}host=${encodeURIComponent(host)}&port=${port}${tls?'&tls=1':''}`;this._ws=new WebSocket(url);this._ws.binaryType='arraybuffer';this._ws.onopen=()=>{this._emit('connect');listener&&listener();};this._ws.onmessage=e=>{this._emit('data',Buf.from(e.data instanceof ArrayBuffer?new Uint8Array(e.data):new TextEncoder().encode(String(e.data))));};this._ws.onclose=()=>{this.destroyed=true;this._emit('end');this._emit('close');};this._ws.onerror=e=>this._emit('error',e);return this;}
|
|
12
|
+
write(chunk,enc,cb){if(!this._ws||this._ws.readyState!==1){if(cb)cb(new Error('not connected'));return false;}const b=chunk instanceof Uint8Array?chunk:new TextEncoder().encode(String(chunk));this._ws.send(b);this.bufferedAmount=this._ws.bufferedAmount;if(cb)cb();return this.bufferedAmount<65536;}
|
|
13
|
+
end(chunk){if(chunk)this.write(chunk);if(this._ws)this._ws.close();}
|
|
14
|
+
destroy(err){this.destroyed=true;if(err)this._emit('error',err);if(this._ws)this._ws.close();}
|
|
15
|
+
setEncoding(){return this;}
|
|
16
|
+
setKeepAlive(){return this;}
|
|
17
|
+
setNoDelay(){return this;}
|
|
18
|
+
setTimeout(){return this;}
|
|
19
|
+
pipe(dest){this.on('data',c=>{if(dest.write(c)===false)this._ws.send('__BACKPRESSURE__');});this.on('end',()=>dest.end?.());return dest;}
|
|
20
|
+
}
|
|
21
|
+
return{
|
|
22
|
+
Socket,
|
|
23
|
+
createConnection(...args){const s=new Socket();s.connect(...args);return s;},
|
|
24
|
+
connect(...args){return this.createConnection(...args);},
|
|
25
|
+
createServer(onConn){const bn=globalThis.__busnet;if(!bn)throw new Error('net.createServer: busnet not initialized');const handlers={connection:onConn?[onConn]:[]};let bnHandle=null;return{listen(port,host,cb){if(typeof host==='function'){cb=host;host=null;}bnHandle=bn.listen(port,'tcp',c=>{for(const h of handlers.connection)h(c);});cb?.();return this;},close(cb){bnHandle?.close();cb?.();},on(ev,fn){(handlers[ev]=handlers[ev]||[]).push(fn);return this;},address(){return bnHandle?{address:'127.0.0.1',family:'IPv4',port:bnHandle.port}:null;},unref(){return this;},ref(){return this;}};},
|
|
26
|
+
isIP:ip=>/^\d+\.\d+\.\d+\.\d+$/.test(ip)?4:ip.includes(':')?6:0,
|
|
27
|
+
isIPv4:ip=>/^\d+\.\d+\.\d+\.\d+$/.test(ip),
|
|
28
|
+
isIPv6:ip=>ip.includes(':'),
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function makeTls(netMod,Buf){
|
|
33
|
+
class TLSSocket extends netMod.Socket{
|
|
34
|
+
constructor(){super();this.authorized=true;this.encrypted=true;}
|
|
35
|
+
}
|
|
36
|
+
return{
|
|
37
|
+
TLSSocket,
|
|
38
|
+
connect(opts,listener){const s=new TLSSocket();const port=typeof opts==='object'?opts.port:opts;const host=typeof opts==='object'?opts.host:arguments[1];s.connect({port,host,tls:true},listener);return s;},
|
|
39
|
+
createServer(){throw new Error('tls.createServer: server sockets not supported');},
|
|
40
|
+
DEFAULT_ECDH_CURVE:'auto',
|
|
41
|
+
DEFAULT_MAX_VERSION:'TLSv1.3',
|
|
42
|
+
DEFAULT_MIN_VERSION:'TLSv1.2',
|
|
43
|
+
CLIENT_RENEG_LIMIT:3,
|
|
44
|
+
rootCertificates:[],
|
|
45
|
+
checkServerIdentity:()=>undefined,
|
|
46
|
+
createSecureContext:()=>({}),
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export function makeDgram(Buf){
|
|
51
|
+
class Dgram{
|
|
52
|
+
constructor(type='udp4'){this.type=type;this._h={};this._ws=null;this._addr=null;}
|
|
53
|
+
on(ev,fn){(this._h[ev]=this._h[ev]||[]).push(fn);return this;}
|
|
54
|
+
_emit(ev,...a){for(const f of this._h[ev]||[])f(...a);}
|
|
55
|
+
bind(port,addr,cb){this._addr={port:port||0,address:addr||'0.0.0.0'};queueMicrotask(()=>{this._emit('listening');cb&&cb();});const relay=udpRelayUrl();if(!relay)return;this._ws=new WebSocket(relay);this._ws.binaryType='arraybuffer';this._ws.onmessage=e=>{const view=new DataView(e.data);const srcPortLen=view.getUint16(0);const portBytes=new Uint8Array(e.data,2,srcPortLen);const addrStr=new TextDecoder().decode(portBytes);const[ah,ap]=addrStr.split(':');const payload=new Uint8Array(e.data,2+srcPortLen);this._emit('message',Buf.from(payload),{address:ah,port:+ap,family:'IPv4',size:payload.length});};return this;}
|
|
56
|
+
send(msg,offset,length,port,addr,cb){if(typeof offset==='number'&&typeof length==='number'){msg=msg.slice(offset,offset+length);}else{cb=addr;addr=port;port=length;}const relay=udpRelayUrl();if(!relay){if(cb)cb(new Error('dgram: set window.__plugkit_udp_relay'));return;}if(!this._ws){this._ws=new WebSocket(relay);this._ws.binaryType='arraybuffer';}const send=()=>{const target=`${addr}:${port}`;const tb=new TextEncoder().encode(target);const buf=new Uint8Array(2+tb.length+msg.length);new DataView(buf.buffer).setUint16(0,tb.length);buf.set(tb,2);buf.set(msg instanceof Uint8Array?msg:new TextEncoder().encode(String(msg)),2+tb.length);this._ws.send(buf);cb&&cb(null);};if(this._ws.readyState===1)send();else this._ws.addEventListener('open',send,{once:true});}
|
|
57
|
+
address(){return this._addr||{address:'0.0.0.0',port:0,family:'IPv4'};}
|
|
58
|
+
close(cb){if(this._ws)this._ws.close();this._emit('close');cb&&cb();}
|
|
59
|
+
addMembership(){}
|
|
60
|
+
dropMembership(){}
|
|
61
|
+
setBroadcast(){}
|
|
62
|
+
setTTL(){}
|
|
63
|
+
setMulticastTTL(){}
|
|
64
|
+
ref(){return this;}
|
|
65
|
+
unref(){return this;}
|
|
66
|
+
}
|
|
67
|
+
return{
|
|
68
|
+
Socket:Dgram,
|
|
69
|
+
createSocket(type,cb){const s=new Dgram(typeof type==='object'?type.type:type);if(typeof cb==='function')s.on('message',cb);return s;},
|
|
70
|
+
};
|
|
71
|
+
}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export function makeDebugRegistry(){
|
|
2
|
+
const reg={modules:{},streamsOpen:new Set(),workersActive:new Set(),cryptoOps:0,requireCount:0,zlibBytes:{in:0,out:0},http2Sessions:0,vmContexts:0,activeRequires:[]};
|
|
3
|
+
if(typeof window!=='undefined'){window.__debug=window.__debug||{};window.__debug.node=reg;}
|
|
4
|
+
return reg;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export function makeDiagnosticsChannel(){
|
|
8
|
+
const channels=new Map();
|
|
9
|
+
const ch=name=>{if(!channels.has(name))channels.set(name,{name,subs:new Set(),subscribe(fn){this.subs.add(fn);return this;},unsubscribe(fn){this.subs.delete(fn);return this;},publish(msg){for(const fn of this.subs)try{fn(msg,name);}catch{}},hasSubscribers(){return this.subs.size>0;}});return channels.get(name);};
|
|
10
|
+
return{
|
|
11
|
+
channel:ch,
|
|
12
|
+
subscribe(name,fn){ch(name).subscribe(fn);},
|
|
13
|
+
unsubscribe(name,fn){ch(name).unsubscribe(fn);},
|
|
14
|
+
hasSubscribers(name){return ch(name).hasSubscribers();},
|
|
15
|
+
tracingChannel(name){const start=ch(name+':start');const end=ch(name+':end');const asyncStart=ch(name+':asyncStart');const asyncEnd=ch(name+':asyncEnd');const error=ch(name+':error');return{start,end,asyncStart,asyncEnd,error,traceSync(fn,ctx){start.publish(ctx);try{const r=fn.call(ctx);end.publish({...ctx,result:r});return r;}catch(e){error.publish({...ctx,error:e});throw e;}},async tracePromise(fn,ctx){start.publish(ctx);try{const r=await fn.call(ctx);asyncEnd.publish({...ctx,result:r});return r;}catch(e){error.publish({...ctx,error:e});throw e;}}};},
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function makeTraceEvents(reg){
|
|
20
|
+
const events=[];
|
|
21
|
+
reg.traceEvents=events;
|
|
22
|
+
const tracings=[];
|
|
23
|
+
return{
|
|
24
|
+
createTracing({categories=[]}={}){const t={categories,enabled:false,enable(){this.enabled=true;tracings.push(this);},disable(){this.enabled=false;const i=tracings.indexOf(this);if(i>=0)tracings.splice(i,1);}};return t;},
|
|
25
|
+
getEnabledCategories(){const out=new Set();for(const t of tracings)if(t.enabled)for(const c of t.categories)out.add(c);return[...out];},
|
|
26
|
+
_emit(cat,name,data){if(events.length>=10000)events.shift();events.push({cat,name,data,ts:performance.now()});},
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export function makeBufferPool(Buf,poolSize=8192){
|
|
31
|
+
let pool=new Uint8Array(poolSize);
|
|
32
|
+
let offset=0;
|
|
33
|
+
Buf.poolSize=poolSize;
|
|
34
|
+
const origAllocUnsafe=Buf.allocUnsafe;
|
|
35
|
+
Buf.allocUnsafe=size=>{if(size>=poolSize>>>1)return origAllocUnsafe(size);if(offset+size>poolSize){pool=new Uint8Array(poolSize);offset=0;}const slice=Buf.from(pool.buffer,pool.byteOffset+offset,size);offset+=size;offset=(offset+7)&~7;return slice;};
|
|
36
|
+
Buf.allocUnsafeSlow=size=>origAllocUnsafe(size);
|
|
37
|
+
return Buf;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function makeProcessBindings(util){
|
|
41
|
+
const bindings={util:{isDate:v=>v instanceof Date,isRegExp:v=>v instanceof RegExp,isMap:v=>v instanceof Map,isSet:v=>v instanceof Set,isPromise:v=>v instanceof Promise,isNativeError:v=>v instanceof Error,isArrayBuffer:v=>v instanceof ArrayBuffer,isTypedArray:v=>ArrayBuffer.isView(v)&&!(v instanceof DataView),getHiddenValue:()=>undefined,setHiddenValue:()=>{}}};
|
|
42
|
+
return name=>{if(bindings[name])return bindings[name];throw new Error(`process.binding('${name}'): internal binding not exposed`);};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function makePerfMemory(perf){
|
|
46
|
+
const getMemory=()=>{const m=performance.memory||{usedJSHeapSize:0,totalJSHeapSize:0,jsHeapSizeLimit:1073741824};return{rss:m.totalJSHeapSize||50000000,heapTotal:m.totalJSHeapSize||10000000,heapUsed:m.usedJSHeapSize||5000000,external:0,arrayBuffers:0};};
|
|
47
|
+
perf.measureUserAgentSpecificMemory=performance.measureUserAgentSpecificMemory?.bind(performance)||(async()=>({bytes:getMemory().heapUsed,breakdown:[]}));
|
|
48
|
+
return getMemory;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function makeFetchPool(){
|
|
52
|
+
return class Agent{
|
|
53
|
+
constructor(opts={}){this.maxSockets=opts.maxSockets||Infinity;this.keepAlive=opts.keepAlive!==false;this._queue=[];this._active=0;this._controllers=new Set();}
|
|
54
|
+
_acquire(){if(this._active<this.maxSockets){this._active++;return Promise.resolve();}return new Promise(r=>this._queue.push(r));}
|
|
55
|
+
_release(){this._active--;const next=this._queue.shift();if(next){this._active++;next();}}
|
|
56
|
+
async fetch(url,opts={}){await this._acquire();const ctrl=new AbortController();this._controllers.add(ctrl);const signal=opts.signal?AbortSignal.any?.([opts.signal,ctrl.signal])||opts.signal:ctrl.signal;try{return await fetch(url,{...opts,signal});}finally{this._controllers.delete(ctrl);this._release();}}
|
|
57
|
+
destroy(){for(const c of this._controllers)c.abort();this._controllers.clear();}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function installPrepareStackTraceHook(){
|
|
62
|
+
if(Error._psHooked)return;Error._psHooked=true;
|
|
63
|
+
const origGetStack=Object.getOwnPropertyDescriptor(Error.prototype,'stack');
|
|
64
|
+
const parseFrame=l=>{const m=l.match(/at\s+(?:(.+?)\s+\()?(.+?):(\d+):(\d+)\)?/);return m?{getFileName:()=>m[2],getLineNumber:()=>+m[3],getColumnNumber:()=>+m[4],getFunctionName:()=>m[1]||null,isNative:()=>false,isEval:()=>false,toString:()=>l.trim()}:{getFileName:()=>null,getLineNumber:()=>0,getColumnNumber:()=>0,getFunctionName:()=>null,isNative:()=>false,toString:()=>l.trim()};};
|
|
65
|
+
if(!('prepareStackTrace'in Error))Error.prepareStackTrace=null;
|
|
66
|
+
Object.defineProperty(Error.prototype,'stack',{configurable:true,get(){const raw=origGetStack?.get?.call(this)||'';if(typeof Error.prepareStackTrace==='function'){const lines=raw.split('\n').slice(1).filter(l=>l.trim().startsWith('at '));const frames=lines.map(parseFrame);try{return Error.prepareStackTrace(this,frames);}catch{return raw;}}return raw;},set(v){Object.defineProperty(this,'stack',{value:v,writable:true,configurable:true});}});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function installCaptureStackTrace(){
|
|
70
|
+
if(Error.captureStackTrace)return;
|
|
71
|
+
Error.captureStackTrace=(target,ctor)=>{const e=new Error();const lines=(e.stack||'').split('\n');target.stack=(ctor?.name?ctor.name:'Error')+(target.message?': '+target.message:'')+'\n'+lines.slice(2).join('\n');};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export function makeFsWatchReal(getSnap){
|
|
75
|
+
const watchers=[];
|
|
76
|
+
let lastSnap=null;
|
|
77
|
+
const tick=()=>{const cur=getSnap();if(!lastSnap){lastSnap={...cur};return;}const curKeys=new Set(Object.keys(cur));const prevKeys=new Set(Object.keys(lastSnap));const changed=[];for(const k of curKeys)if(!prevKeys.has(k)||cur[k]!==lastSnap[k])changed.push({type:prevKeys.has(k)?'change':'rename',path:k});for(const k of prevKeys)if(!curKeys.has(k))changed.push({type:'rename',path:k});for(const {type,path} of changed)for(const w of watchers)if(path===w.path||(w.recursive&&path.startsWith(w.path+'/')))for(const h of w.handlers.change)h(type,path.split('/').pop());lastSnap={...cur};};
|
|
78
|
+
setInterval(tick,500);
|
|
79
|
+
return (path,opts={},listener)=>{if(typeof opts==='function'){listener=opts;opts={};}const normalized=path.replace(/^\/+/,'').replace(/\/$/,'');const handlers={change:listener?[listener]:[],error:[],close:[]};const w={path:normalized,recursive:!!opts.recursive,handlers,on:(ev,fn)=>{(handlers[ev]=handlers[ev]||[]).push(fn);return w;},close:()=>{const i=watchers.indexOf(w);if(i>=0)watchers.splice(i,1);for(const h of handlers.close)h();},ref:()=>w,unref:()=>w};watchers.push(w);return w;};
|
|
80
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
const WORKER_SRC=`
|
|
2
|
+
self.addEventListener('message',async e=>{
|
|
3
|
+
const {id,op,path,data,flags}=e.data;
|
|
4
|
+
try{
|
|
5
|
+
const root=await navigator.storage.getDirectory();
|
|
6
|
+
const parts=path.replace(/^\\/+/,'').split('/');
|
|
7
|
+
const fname=parts.pop();
|
|
8
|
+
let dir=root;
|
|
9
|
+
for(const p of parts){if(!p)continue;try{dir=await dir.getDirectoryHandle(p,{create:op==='write'||op==='mkdir'});}catch(err){if(op==='read'||op==='stat'||op==='delete'){self.postMessage({id,error:'ENOENT: '+path});return;}throw err;}}
|
|
10
|
+
if(op==='mkdir'){await dir.getDirectoryHandle(fname,{create:true});self.postMessage({id,ok:true});return;}
|
|
11
|
+
if(op==='read'){const h=await dir.getFileHandle(fname);const f=await h.getFile();const buf=new Uint8Array(await f.arrayBuffer());self.postMessage({id,data:buf},[buf.buffer]);return;}
|
|
12
|
+
if(op==='write'){const h=await dir.getFileHandle(fname,{create:true});const sync=await h.createSyncAccessHandle();const bytes=data instanceof Uint8Array?data:new TextEncoder().encode(String(data));sync.truncate(0);sync.write(bytes,{at:0});sync.flush();sync.close();self.postMessage({id,ok:true});return;}
|
|
13
|
+
if(op==='stat'){const h=await dir.getFileHandle(fname).catch(()=>dir.getDirectoryHandle(fname));const f=await h.getFile?.();self.postMessage({id,size:f?.size||0,mtime:f?.lastModified||0,isFile:!!f,isDirectory:!f});return;}
|
|
14
|
+
if(op==='delete'){await dir.removeEntry(fname,{recursive:flags?.recursive||false});self.postMessage({id,ok:true});return;}
|
|
15
|
+
if(op==='list'){const out=[];for await(const [name,h] of dir.entries())out.push({name,kind:h.kind});self.postMessage({id,entries:out});return;}
|
|
16
|
+
self.postMessage({id,error:'unknown op: '+op});
|
|
17
|
+
}catch(err){self.postMessage({id,error:err.message});}
|
|
18
|
+
});
|
|
19
|
+
`;
|
|
20
|
+
|
|
21
|
+
export function makeOpfsBackend(Buf){
|
|
22
|
+
if(typeof navigator==='undefined'||!navigator.storage?.getDirectory)return null;
|
|
23
|
+
const blob=new Blob([WORKER_SRC],{type:'application/javascript'});
|
|
24
|
+
const url=URL.createObjectURL(blob);
|
|
25
|
+
const worker=new Worker(url);
|
|
26
|
+
const pending=new Map();let nextId=1;
|
|
27
|
+
worker.addEventListener('message',e=>{const {id,...rest}=e.data;const p=pending.get(id);if(!p)return;pending.delete(id);if(rest.error)p.reject(new Error(rest.error));else p.resolve(rest);});
|
|
28
|
+
const call=(op,path,data,flags)=>new Promise((resolve,reject)=>{const id=nextId++;pending.set(id,{resolve,reject});worker.postMessage({id,op,path,data,flags});});
|
|
29
|
+
return{
|
|
30
|
+
readFile:async(path,enc)=>{const r=await call('read',path);return enc?new TextDecoder(enc==='utf-8'?'utf-8':enc).decode(r.data):Buf.from(r.data);},
|
|
31
|
+
writeFile:async(path,data)=>{await call('write',path,data);},
|
|
32
|
+
mkdir:async path=>{await call('mkdir',path);},
|
|
33
|
+
rm:async(path,opts)=>{await call('delete',path,null,opts);},
|
|
34
|
+
stat:async path=>call('stat',path),
|
|
35
|
+
list:async path=>{const r=await call('list',path);return r.entries;},
|
|
36
|
+
_worker:worker,
|
|
37
|
+
_url:url,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function wireOpfsIntoFs(fs,opfs,reg){
|
|
42
|
+
if(!opfs)return fs;
|
|
43
|
+
const orig={readFileSync:fs.readFileSync,writeFileSync:fs.writeFileSync,existsSync:fs.existsSync,statSync:fs.statSync,mkdirSync:fs.mkdirSync,rmSync:fs.rmSync,unlinkSync:fs.unlinkSync};
|
|
44
|
+
fs.promises=fs.promises||{};
|
|
45
|
+
fs.promises.readFile=async(p,enc)=>opfs.readFile(p,enc).catch(()=>orig.readFileSync(p,enc));
|
|
46
|
+
fs.promises.writeFile=async(p,d)=>{await opfs.writeFile(p,d).catch(()=>orig.writeFileSync(p,d));};
|
|
47
|
+
fs.promises.mkdir=async(p,o)=>{await opfs.mkdir(p).catch(()=>orig.mkdirSync(p,o));};
|
|
48
|
+
fs.promises.rm=async(p,o)=>{await opfs.rm(p,o).catch(()=>orig.rmSync(p,o));};
|
|
49
|
+
fs.promises.stat=async p=>opfs.stat(p).catch(()=>orig.statSync(p));
|
|
50
|
+
fs.promises.readdir=async p=>opfs.list(p).then(es=>es.map(e=>e.name)).catch(()=>fs.readdirSync(p));
|
|
51
|
+
reg.polyfills=reg.polyfills||{};
|
|
52
|
+
reg.polyfills.opfs={active:true,backing:'native',reason:'OPFS available'};
|
|
53
|
+
return fs;
|
|
54
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export function makeProcFs(proc){
|
|
2
|
+
const g=globalThis;
|
|
3
|
+
const gen={
|
|
4
|
+
'proc/self/cmdline':()=>(proc.argv||[]).join('\0')+'\0',
|
|
5
|
+
'proc/self/environ':()=>Object.entries(proc.env||{}).map(([k,v])=>`${k}=${v}`).join('\0')+'\0',
|
|
6
|
+
'proc/self/cwd':()=>proc.cwd?.()||'/',
|
|
7
|
+
'proc/self/exe':()=>proc.execPath||'/usr/local/bin/node',
|
|
8
|
+
'proc/self/status':()=>{const m=performance.memory||{usedJSHeapSize:0,totalJSHeapSize:0};return`Name:\tnode\nState:\tR (running)\nPid:\t${proc.pid||1}\nPPid:\t${proc.ppid||0}\nUid:\t0\t0\t0\t0\nGid:\t0\t0\t0\t0\nVmRSS:\t${(m.totalJSHeapSize/1024)|0} kB\nVmPeak:\t${(m.totalJSHeapSize/1024)|0} kB\n`;},
|
|
9
|
+
'proc/self/stat':()=>`${proc.pid||1} (node) R ${proc.ppid||0} ${proc.pid||1} ${proc.pid||1} 0 -1 4194304 0 0 0 0 0 0 0 0 20 0 1 0 ${performance.now()|0} ${(performance.memory?.totalJSHeapSize||0)} 0 18446744073709551615 0 0 0 0 0 0 0 0 0 0 0 0 17 0 0 0 0 0 0\n`,
|
|
10
|
+
'proc/self/maps':()=>'00400000-00500000 r-xp 00000000 00:00 0 [text]\n',
|
|
11
|
+
'proc/self/limits':()=>'Limit Soft Limit Hard Limit\nMax open files 1024 4096\n',
|
|
12
|
+
'proc/cpuinfo':()=>{const n=navigator?.hardwareConcurrency||4;let out='';for(let i=0;i<n;i++)out+=`processor\t: ${i}\nvendor_id\t: BrowserCPU\nmodel name\t: Browser JS Engine\ncpu MHz\t\t: 3000.000\ncache size\t: 8192 KB\ncores\t\t: ${n}\n\n`;return out;},
|
|
13
|
+
'proc/meminfo':()=>{const m=performance.memory||{totalJSHeapSize:1e9,jsHeapSizeLimit:2e9,usedJSHeapSize:5e8};return`MemTotal: ${(m.jsHeapSizeLimit/1024)|0} kB\nMemFree: ${((m.jsHeapSizeLimit-m.usedJSHeapSize)/1024)|0} kB\nMemAvailable: ${((m.jsHeapSizeLimit-m.usedJSHeapSize)/1024)|0} kB\nBuffers: 0 kB\nCached: 0 kB\n`;},
|
|
14
|
+
'proc/uptime':()=>`${(performance.now()/1000).toFixed(2)} ${(performance.now()/1000).toFixed(2)}\n`,
|
|
15
|
+
'proc/loadavg':()=>'0.00 0.00 0.00 1/1 '+(proc.pid||1)+'\n',
|
|
16
|
+
'proc/version':()=>`Linux version 6.0.0-browser (node@thebird) #1 SMP ${new Date().toUTCString()}\n`,
|
|
17
|
+
'proc/stat':()=>'cpu 0 0 0 0 0 0 0 0 0 0\nbtime '+Math.floor(Date.now()/1000)+'\n',
|
|
18
|
+
'proc/mounts':()=>'idbfs / idbfs rw,relatime 0 0\n',
|
|
19
|
+
'proc/filesystems':()=>'nodev\tidbfs\nnodev\topfs\n',
|
|
20
|
+
'etc/hosts':()=>'127.0.0.1 localhost\n::1 localhost\n',
|
|
21
|
+
'etc/resolv.conf':()=>'nameserver 1.1.1.1\nnameserver 8.8.8.8\n',
|
|
22
|
+
'etc/passwd':()=>'root:x:0:0:root:/root:/bin/sh\n',
|
|
23
|
+
'etc/group':()=>'root:x:0:\n',
|
|
24
|
+
'etc/os-release':()=>'NAME="thebird"\nPRETTY_NAME="thebird browser runtime"\nID=thebird\nID_LIKE=linux\nVERSION_ID="1.0"\n',
|
|
25
|
+
'etc/hostname':()=>'thebird\n',
|
|
26
|
+
'etc/machine-id':()=>'00000000000000000000000000000000\n',
|
|
27
|
+
'etc/shells':()=>'/bin/sh\n/bin/bash\n',
|
|
28
|
+
};
|
|
29
|
+
return{
|
|
30
|
+
handles(path){const k=path.replace(/^\//,'').replace(/\/$/,'');return k in gen;},
|
|
31
|
+
read(path){const k=path.replace(/^\//,'').replace(/\/$/,'');const fn=gen[k];if(!fn)return null;try{return fn();}catch{return'';}},
|
|
32
|
+
list(){return Object.keys(gen).map(k=>'/'+k);},
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function wireProcFs(fs,procFs){
|
|
37
|
+
const origRead=fs.readFileSync, origExists=fs.existsSync, origStat=fs.statSync;
|
|
38
|
+
fs.readFileSync=(p,enc)=>{if(procFs.handles(p)){const s=procFs.read(p);return enc?s:new TextEncoder().encode(s);}return origRead(p,enc);};
|
|
39
|
+
fs.existsSync=p=>procFs.handles(p)||origExists(p);
|
|
40
|
+
fs.statSync=p=>{if(procFs.handles(p)){const s=procFs.read(p);return{size:s.length,mode:0o100444,isFile:()=>true,isDirectory:()=>false,isSymbolicLink:()=>false,isFIFO:()=>false,isBlockDevice:()=>false,isCharacterDevice:()=>false,isSocket:()=>false,mtimeMs:Date.now(),mtime:new Date(),atimeMs:Date.now(),ctimeMs:Date.now(),birthtimeMs:Date.now(),uid:0,gid:0,nlink:1,dev:0,ino:0,rdev:0,blksize:4096,blocks:1};}return origStat(p);};
|
|
41
|
+
return fs;
|
|
42
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export function makeV8Profiler(debugReg){
|
|
2
|
+
const samples=[];let observer=null;let running=false;let startT=0;
|
|
3
|
+
const startObserver=()=>{if(observer||typeof PerformanceObserver==='undefined')return;try{observer=new PerformanceObserver(list=>{for(const e of list.getEntries())if(running)samples.push({name:e.name,ts:e.startTime,dur:e.duration,entryType:e.entryType});});observer.observe({entryTypes:['measure','longtask','function']});}catch{}};
|
|
4
|
+
const stopObserver=()=>{if(observer){observer.disconnect();observer=null;}};
|
|
5
|
+
return{
|
|
6
|
+
CPUProfile:class CPUProfile{
|
|
7
|
+
constructor(){this.nodes=[];this.samples=[];this.timeDeltas=[];this.startTime=0;this.endTime=0;this.title='';}
|
|
8
|
+
startProfiling(title){this.title=title||'';samples.length=0;startT=performance.now();running=true;startObserver();}
|
|
9
|
+
stopProfiling(){running=false;stopObserver();const end=performance.now();this.startTime=startT*1000;this.endTime=end*1000;this.nodes=samples.map((s,i)=>({id:i+1,callFrame:{functionName:s.name||'(anonymous)',scriptId:'0',url:'',lineNumber:0,columnNumber:0},hitCount:1,children:[]}));this.samples=samples.map((_,i)=>i+1);this.timeDeltas=samples.map(s=>Math.round(s.dur*1000));return this;}
|
|
10
|
+
},
|
|
11
|
+
startProfiling(){const p=new this.CPUProfile();p.startProfiling();return p;},
|
|
12
|
+
stopProfiling(p){return p?p.stopProfiling():null;},
|
|
13
|
+
getHeapStatistics(){const m=performance.memory||{usedJSHeapSize:5e6,totalJSHeapSize:10e6,jsHeapSizeLimit:1e9};return{total_heap_size:m.totalJSHeapSize,total_heap_size_executable:0,total_physical_size:m.totalJSHeapSize,total_available_size:m.jsHeapSizeLimit-m.usedJSHeapSize,used_heap_size:m.usedJSHeapSize,heap_size_limit:m.jsHeapSizeLimit,malloced_memory:0,peak_malloced_memory:0,does_zap_garbage:0,number_of_native_contexts:1,number_of_detached_contexts:0};},
|
|
14
|
+
getHeapSpaceStatistics(){return[{space_name:'new_space',space_size:1e6,space_used_size:5e5,space_available_size:5e5,physical_space_size:1e6}];},
|
|
15
|
+
getHeapCodeStatistics(){return{code_and_metadata_size:0,bytecode_and_metadata_size:0,external_script_source_size:0};},
|
|
16
|
+
cachedDataVersionTag:()=>0,
|
|
17
|
+
setFlagsFromString(){},
|
|
18
|
+
startupSnapshot:{isBuildingSnapshot:()=>false,addSerializeCallback(){},addDeserializeCallback(){}},
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function makeHeapSnapshot(){
|
|
23
|
+
const NODE_FIELDS=['type','name','id','self_size','edge_count','trace_node_id','detachedness'];
|
|
24
|
+
const NODE_TYPES=[['hidden','array','string','object','code','closure','regexp','number','native','synthetic','concatenated string','sliced string','symbol','bigint','object shape'],'string','number','number','number','number','number'];
|
|
25
|
+
const EDGE_FIELDS=['type','name_or_index','to_node'];
|
|
26
|
+
const EDGE_TYPES=[['context','element','property','internal','hidden','shortcut','weak'],'string_or_number','node'];
|
|
27
|
+
const META={node_fields:NODE_FIELDS,node_types:NODE_TYPES,edge_fields:EDGE_FIELDS,edge_types:EDGE_TYPES,trace_function_info_fields:[],trace_node_fields:[],sample_fields:[],location_fields:[]};
|
|
28
|
+
const walk=(root,maxNodes=5000)=>{
|
|
29
|
+
const visited=new WeakMap();const strings=[''];const stringIdx=new Map([['',0]]);
|
|
30
|
+
const addStr=s=>{if(stringIdx.has(s))return stringIdx.get(s);const i=strings.length;strings.push(s);stringIdx.set(s,i);return i;};
|
|
31
|
+
const nodes=[];const edges=[];const queue=[[root,'root']];
|
|
32
|
+
while(queue.length&&nodes.length<maxNodes){
|
|
33
|
+
const [obj,name]=queue.shift();
|
|
34
|
+
const isObj=obj!==null&&(typeof obj==='object'||typeof obj==='function');
|
|
35
|
+
if(isObj&&visited.has(obj))continue;
|
|
36
|
+
const id=nodes.length+1;if(isObj)visited.set(obj,id);
|
|
37
|
+
const t=obj===null?9:typeof obj==='string'?2:typeof obj==='number'?7:typeof obj==='bigint'?13:typeof obj==='symbol'?12:Array.isArray(obj)?1:typeof obj==='function'?5:3;
|
|
38
|
+
let childEdges=0;
|
|
39
|
+
if(isObj){try{for(const k of Object.keys(obj).slice(0,32)){queue.push([obj[k],k]);childEdges++;}}catch{}}
|
|
40
|
+
nodes.push([t,addStr(name),id,typeof obj==='string'?obj.length:64,childEdges,0,0]);
|
|
41
|
+
}
|
|
42
|
+
let cursor=0;
|
|
43
|
+
for(const n of nodes){const childCount=n[4];for(let i=0;i<childCount;i++){edges.push([2,cursor+i+1,(cursor+i+1)*NODE_FIELDS.length]);}cursor+=childCount;}
|
|
44
|
+
return{snapshot:{meta:META,node_count:nodes.length,edge_count:edges.length,trace_function_count:0},nodes:nodes.flat(),edges:edges.flat(),trace_function_infos:[],trace_tree:[],samples:[],locations:[],strings};
|
|
45
|
+
};
|
|
46
|
+
return{
|
|
47
|
+
writeHeapSnapshot(filename){const snap=walk(globalThis);const json=JSON.stringify(snap);if(typeof filename==='string'&&globalThis.window?.__debug?.idbSnapshot){globalThis.window.__debug.idbSnapshot[filename.replace(/^\/+/,'')]=json;}return filename;},
|
|
48
|
+
getHeapSnapshot(){const snap=walk(globalThis);return{read(){return JSON.stringify(snap);}};},
|
|
49
|
+
};
|
|
50
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
const ESM_API='https://esm.sh';
|
|
2
|
+
const cache=new Map();
|
|
3
|
+
|
|
4
|
+
async function esmMeta(name){
|
|
5
|
+
if(cache.has(name))return cache.get(name);
|
|
6
|
+
try{
|
|
7
|
+
const url=`${ESM_API}/${name}/package.json`;
|
|
8
|
+
const r=await fetch(url);
|
|
9
|
+
if(!r.ok)throw new Error('not found');
|
|
10
|
+
const pj=await r.json();
|
|
11
|
+
cache.set(name,pj);return pj;
|
|
12
|
+
}catch(e){throw new Error(`registry: cannot fetch ${name} — ${e.message}`);}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function makeRegistry(){
|
|
16
|
+
return{
|
|
17
|
+
async view(spec){const[name,field]=spec.split(/\s+/);const pj=await esmMeta(name);if(field){const parts=field.split('.');let v=pj;for(const p of parts)v=v?.[p];return v;}return pj;},
|
|
18
|
+
async search(q){try{const r=await fetch(`https://registry.npmjs.org/-/v1/search?text=${encodeURIComponent(q)}&size=10`);const j=await r.json();return j.objects?.map(o=>({name:o.package.name,version:o.package.version,description:o.package.description}))||[];}catch{return[];}},
|
|
19
|
+
async deps(name,version='latest'){try{const pj=await esmMeta(version==='latest'?name:`${name}@${version}`);return{dependencies:pj.dependencies||{},devDependencies:pj.devDependencies||{},peerDependencies:pj.peerDependencies||{}};}catch{return{dependencies:{},devDependencies:{},peerDependencies:{}};}},
|
|
20
|
+
async tarballUrl(name,version){const pj=await esmMeta(`${name}@${version}`);return`https://registry.npmjs.org/${name}/-/${name.split('/').pop()}-${pj.version}.tgz`;},
|
|
21
|
+
async fetchTarball(name,version){const url=await this.tarballUrl(name,version);const r=await fetch(url);return new Uint8Array(await r.arrayBuffer());},
|
|
22
|
+
clearCache(){cache.clear();},
|
|
23
|
+
};
|
|
24
|
+
}
|