thebird 1.2.79 → 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 +217 -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 +24 -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 +175 -169
- 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 +161 -152
- 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,60 @@
|
|
|
1
|
+
let gitMod=null;let gitPromise=null;
|
|
2
|
+
|
|
3
|
+
async function loadGit(){
|
|
4
|
+
if(!gitPromise)gitPromise=Promise.all([
|
|
5
|
+
import('https://esm.sh/isomorphic-git@1.27.1/es2022/isomorphic-git.mjs').then(m=>m.default||m),
|
|
6
|
+
import('https://esm.sh/isomorphic-git@1.27.1/http/web').then(m=>m.default||m).catch(()=>null),
|
|
7
|
+
]).then(([git,http])=>({git,http}));
|
|
8
|
+
return gitPromise;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export async function preloadGit(){gitMod=await loadGit();return gitMod;}
|
|
12
|
+
|
|
13
|
+
export function makeGit(fs){
|
|
14
|
+
const fsAdapter={
|
|
15
|
+
promises:{
|
|
16
|
+
async readFile(path,opts){try{const enc=typeof opts==='string'?opts:opts?.encoding;const d=fs.readFileSync(path);if(enc==='utf8'||enc==='utf-8')return typeof d==='string'?d:new TextDecoder().decode(d);return typeof d==='string'?new TextEncoder().encode(d):d;}catch(e){e.code='ENOENT';throw e;}},
|
|
17
|
+
async writeFile(path,data){const parts=path.split('/');for(let i=1;i<parts.length;i++){const d=parts.slice(0,i).join('/');if(d&&!fs.existsSync(d))fs.mkdirSync(d,{recursive:true});}fs.writeFileSync(path,data);},
|
|
18
|
+
async unlink(path){fs.unlinkSync(path);},
|
|
19
|
+
async readdir(path){return fs.readdirSync(path);},
|
|
20
|
+
async mkdir(path){fs.mkdirSync(path,{recursive:true});},
|
|
21
|
+
async rmdir(path){fs.rmSync?.(path,{recursive:true})||fs.unlinkSync(path);},
|
|
22
|
+
async stat(path){return fs.statSync(path);},
|
|
23
|
+
async lstat(path){return fs.lstatSync?.(path)||fs.statSync(path);},
|
|
24
|
+
async readlink(path){return fs.readlinkSync?.(path);},
|
|
25
|
+
async symlink(target,path){fs.symlinkSync?.(target,path);},
|
|
26
|
+
async chmod(){},
|
|
27
|
+
},
|
|
28
|
+
};
|
|
29
|
+
const need=()=>{if(!gitMod)throw new Error('git: call await preloadGit() once before git ops');return gitMod;};
|
|
30
|
+
const wrap=method=>async opts=>{const{git,http}=need();return git[method]({fs:fsAdapter,http,...opts});};
|
|
31
|
+
return{
|
|
32
|
+
clone:wrap('clone'),
|
|
33
|
+
init:wrap('init'),
|
|
34
|
+
add:wrap('add'),
|
|
35
|
+
commit:wrap('commit'),
|
|
36
|
+
push:wrap('push'),
|
|
37
|
+
pull:wrap('pull'),
|
|
38
|
+
fetch:wrap('fetch'),
|
|
39
|
+
status:wrap('status'),
|
|
40
|
+
statusMatrix:wrap('statusMatrix'),
|
|
41
|
+
log:wrap('log'),
|
|
42
|
+
listBranches:wrap('listBranches'),
|
|
43
|
+
branch:wrap('branch'),
|
|
44
|
+
checkout:wrap('checkout'),
|
|
45
|
+
resolveRef:wrap('resolveRef'),
|
|
46
|
+
currentBranch:wrap('currentBranch'),
|
|
47
|
+
remove:wrap('remove'),
|
|
48
|
+
listRemotes:wrap('listRemotes'),
|
|
49
|
+
addRemote:wrap('addRemote'),
|
|
50
|
+
deleteRemote:wrap('deleteRemote'),
|
|
51
|
+
merge:wrap('merge'),
|
|
52
|
+
readBlob:wrap('readBlob'),
|
|
53
|
+
readTree:wrap('readTree'),
|
|
54
|
+
readCommit:wrap('readCommit'),
|
|
55
|
+
tag:wrap('tag'),
|
|
56
|
+
listTags:wrap('listTags'),
|
|
57
|
+
diff:async opts=>{const s=await wrap('statusMatrix')(opts);return s.filter(([,,w,s])=>w!==s).map(([path])=>path);},
|
|
58
|
+
preload:preloadGit,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
export function makeInspector(debugReg){
|
|
2
|
+
let opened=false;let url=null;let port=null;const sessions=new Set();
|
|
3
|
+
const targets=()=>[{description:'thebird browser runtime',devtoolsFrontendUrl:'',id:'thebird-1',title:'thebird',type:'node',url:'file://thebird',webSocketDebuggerUrl:url}];
|
|
4
|
+
const handlers={};
|
|
5
|
+
const post=(sess,msg)=>{try{sess.send(JSON.stringify(msg));}catch{}};
|
|
6
|
+
const installServer=u=>{
|
|
7
|
+
if(typeof globalThis.addEventListener!=='function')return;
|
|
8
|
+
globalThis.addEventListener('message',e=>{if(e.data?.type!=='cdp:connect')return;const chan=e.data.channel;const sess={send:m=>globalThis.postMessage({type:'cdp:msg',channel:chan,msg:m},'*')};sessions.add(sess);});
|
|
9
|
+
};
|
|
10
|
+
const dispatch=(sess,raw)=>{
|
|
11
|
+
let msg;try{msg=JSON.parse(raw);}catch{return;}
|
|
12
|
+
const {id,method,params}=msg;
|
|
13
|
+
const send=result=>post(sess,{id,result});
|
|
14
|
+
const err=code=>post(sess,{id,error:{code,message:'not implemented'}});
|
|
15
|
+
const map={
|
|
16
|
+
'Runtime.enable':()=>send({}),
|
|
17
|
+
'Runtime.evaluate':()=>{try{const r=(0,eval)(params.expression);send({result:{type:typeof r,value:r,description:String(r)}});}catch(e){send({exceptionDetails:{exception:{type:'object',className:'Error',description:e.stack}}});}},
|
|
18
|
+
'Debugger.enable':()=>send({debuggerId:'thebird-dbg-1'}),
|
|
19
|
+
'Debugger.getScriptSource':()=>send({scriptSource:''}),
|
|
20
|
+
'Profiler.enable':()=>send({}),
|
|
21
|
+
'Profiler.start':()=>send({}),
|
|
22
|
+
'Profiler.stop':()=>send({profile:{nodes:[],startTime:0,endTime:0,samples:[],timeDeltas:[]}}),
|
|
23
|
+
'HeapProfiler.enable':()=>send({}),
|
|
24
|
+
'HeapProfiler.takeHeapSnapshot':()=>send({}),
|
|
25
|
+
};
|
|
26
|
+
const h=map[method];h?h():err(-32601);
|
|
27
|
+
};
|
|
28
|
+
return{
|
|
29
|
+
open(p=9229,host='127.0.0.1',wait=false){if(opened)return;opened=true;port=p;url=`ws://${host}:${p}/${crypto.randomUUID()}`;installServer(url);debugReg.polyfills=debugReg.polyfills||{};debugReg.polyfills.inspector={active:true,backing:'postMessage-CDP',reason:'real WS debugger unavailable in sandbox'};debugReg.inspector={url,port,targets:targets(),sessions};if(wait)throw new Error('inspector.waitForDebugger: sync block not supported — use openAsync()');return{url};},
|
|
30
|
+
async openAsync(p=9229,host='127.0.0.1'){return this.open(p,host,false);},
|
|
31
|
+
close(){opened=false;url=null;sessions.clear();},
|
|
32
|
+
url:()=>url,
|
|
33
|
+
waitForDebugger(){throw new Error('inspector.waitForDebugger: synchronous block not supported in browser — attach before starting work');},
|
|
34
|
+
Session:class Session{constructor(){this._h={};}connect(){return this;}post(method,params,cb){queueMicrotask(()=>dispatch({send:m=>{const p=JSON.parse(m);cb&&cb(p.error||null,p.result);}},JSON.stringify({id:1,method,params})));}on(ev,fn){(this._h[ev]=this._h[ev]||[]).push(fn);return this;}disconnect(){return this;}},
|
|
35
|
+
console:{context:{}},
|
|
36
|
+
_dispatch:dispatch,
|
|
37
|
+
_targets:targets,
|
|
38
|
+
};
|
|
39
|
+
}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export function createChildProcess(ctx) {
|
|
2
|
+
async function runThroughShell(cmd) {
|
|
3
|
+
const shell = window.__debug?.shell;
|
|
4
|
+
if (!shell?.run) throw new Error('child_process: shell not ready');
|
|
5
|
+
let captured = '';
|
|
6
|
+
const origWrite = ctx.term.write.bind(ctx.term);
|
|
7
|
+
ctx.term.write = s => { captured += s; };
|
|
8
|
+
try { await shell.run(cmd); } finally { ctx.term.write = origWrite; }
|
|
9
|
+
return { stdout: captured.replace(/\r\n/g, '\n').replace(/\x1b\[\d+m/g, ''), code: ctx.lastExitCode | 0 };
|
|
10
|
+
}
|
|
11
|
+
return {
|
|
12
|
+
spawn: (cmd, args = [], opts = {}) => {
|
|
13
|
+
const handlers = { stdout: [], stderr: [], exit: [], close: [], error: [] };
|
|
14
|
+
const emit = (ev, ...a) => { for (const h of handlers[ev] || []) h(...a); };
|
|
15
|
+
const emitter = {
|
|
16
|
+
stdout: { on: (ev, fn) => { if (ev === 'data') handlers.stdout.push(fn); return emitter.stdout; }, pipe: () => emitter.stdout },
|
|
17
|
+
stderr: { on: (ev, fn) => { if (ev === 'data') handlers.stderr.push(fn); return emitter.stderr; } },
|
|
18
|
+
stdin: { write: () => true, end: () => {} },
|
|
19
|
+
on: (ev, fn) => { (handlers[ev] = handlers[ev] || []).push(fn); return emitter; },
|
|
20
|
+
once: (ev, fn) => emitter.on(ev, fn),
|
|
21
|
+
kill: () => {},
|
|
22
|
+
pid: Math.floor(Math.random() * 65535) + 1,
|
|
23
|
+
};
|
|
24
|
+
const line = [cmd, ...args].join(' ');
|
|
25
|
+
queueMicrotask(async () => {
|
|
26
|
+
try { const r = await runThroughShell(line); if (r.stdout) emit('stdout', r.stdout); emit('exit', r.code, null); emit('close', r.code, null); }
|
|
27
|
+
catch (e) { emit('error', e); emit('exit', 1, null); emit('close', 1, null); }
|
|
28
|
+
});
|
|
29
|
+
return emitter;
|
|
30
|
+
},
|
|
31
|
+
exec: (cmd, opts, cb) => {
|
|
32
|
+
if (typeof opts === 'function') { cb = opts; opts = {}; }
|
|
33
|
+
queueMicrotask(async () => { try { const r = await runThroughShell(cmd); cb?.(r.code === 0 ? null : Object.assign(new Error('exit ' + r.code), { code: r.code }), r.stdout, ''); } catch (e) { cb?.(e, '', String(e.message)); } });
|
|
34
|
+
},
|
|
35
|
+
execSync: cmd => { throw new Error('child_process.execSync: use exec() with callback in browser — sync subprocess impossible'); },
|
|
36
|
+
fork: () => { throw new Error('child_process.fork: not supported in browser'); },
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function createHttpClient(Buf) {
|
|
41
|
+
function makeReq(urlOrOpts, cb) {
|
|
42
|
+
const u = typeof urlOrOpts === 'string' ? urlOrOpts : ('http://' + (urlOrOpts.hostname || 'localhost') + ':' + (urlOrOpts.port || 80) + (urlOrOpts.path || '/'));
|
|
43
|
+
const opts = typeof urlOrOpts === 'object' ? urlOrOpts : {};
|
|
44
|
+
const handlers = { response: [], error: [], finish: [] };
|
|
45
|
+
const emit = (ev, ...a) => { for (const h of handlers[ev] || []) h(...a); };
|
|
46
|
+
let body = '';
|
|
47
|
+
const req = {
|
|
48
|
+
on: (ev, fn) => { (handlers[ev] = handlers[ev] || []).push(fn); return req; },
|
|
49
|
+
write: chunk => { body += String(chunk); return true; },
|
|
50
|
+
end: async chunk => {
|
|
51
|
+
if (chunk != null) body += String(chunk);
|
|
52
|
+
try {
|
|
53
|
+
const res = await fetch(u, { method: opts.method || 'GET', headers: opts.headers || {}, body: body || undefined });
|
|
54
|
+
const text = await res.text();
|
|
55
|
+
const resObj = {
|
|
56
|
+
statusCode: res.status, statusMessage: res.statusText, headers: Object.fromEntries(res.headers.entries()),
|
|
57
|
+
on: (ev, fn) => { if (ev === 'data') queueMicrotask(() => fn(Buf.from(text))); if (ev === 'end') queueMicrotask(() => fn()); return resObj; },
|
|
58
|
+
setEncoding: () => {}, pipe: () => {},
|
|
59
|
+
};
|
|
60
|
+
cb?.(resObj); emit('response', resObj);
|
|
61
|
+
} catch (e) { emit('error', e); }
|
|
62
|
+
},
|
|
63
|
+
setHeader: () => {}, getHeader: () => undefined, abort: () => {}, destroy: () => {},
|
|
64
|
+
};
|
|
65
|
+
return req;
|
|
66
|
+
}
|
|
67
|
+
return {
|
|
68
|
+
request: (urlOrOpts, cb) => makeReq(urlOrOpts, cb),
|
|
69
|
+
get: (urlOrOpts, cb) => { const r = makeReq(urlOrOpts, cb); r.end(); return r; },
|
|
70
|
+
Agent: class Agent {},
|
|
71
|
+
STATUS_CODES: { 200: 'OK', 201: 'Created', 204: 'No Content', 301: 'Moved Permanently', 302: 'Found', 400: 'Bad Request', 401: 'Unauthorized', 403: 'Forbidden', 404: 'Not Found', 500: 'Internal Server Error' },
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function extendProcess(proc, ctx) {
|
|
76
|
+
proc.execPath = '/usr/local/bin/node';
|
|
77
|
+
proc.argv0 = 'node';
|
|
78
|
+
proc.title = 'node';
|
|
79
|
+
if (!ctx.env.PATH) ctx.env.PATH = '/usr/local/bin:/usr/bin:/bin';
|
|
80
|
+
if (!ctx.env.HOME) ctx.env.HOME = '/root';
|
|
81
|
+
if (!ctx.env.USER) ctx.env.USER = 'root';
|
|
82
|
+
if (!ctx.env.SHELL) ctx.env.SHELL = '/bin/jsh';
|
|
83
|
+
if (!ctx.env.TERM) ctx.env.TERM = 'xterm-256color';
|
|
84
|
+
if (!ctx.env.LANG) ctx.env.LANG = 'C.UTF-8';
|
|
85
|
+
proc.memoryUsage = () => ({ rss: 50000000, heapTotal: 20000000, heapUsed: 10000000, external: 0, arrayBuffers: 0 });
|
|
86
|
+
proc.uptime = () => performance.now() / 1000;
|
|
87
|
+
proc.cpuUsage = () => ({ user: 0, system: 0 });
|
|
88
|
+
proc.getuid = () => 0; proc.getgid = () => 0; proc.geteuid = () => 0; proc.getegid = () => 0;
|
|
89
|
+
proc.umask = () => 0o022;
|
|
90
|
+
proc.features = { tls: false };
|
|
91
|
+
proc.release = { name: 'node', lts: false, sourceUrl: '', headersUrl: '' };
|
|
92
|
+
return proc;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function rewriteStack(err, filename) {
|
|
96
|
+
if (!err.stack) return err.message;
|
|
97
|
+
const lines = err.stack.split('\n');
|
|
98
|
+
const first = lines[0];
|
|
99
|
+
const fname = filename || '[eval]';
|
|
100
|
+
const frames = lines.slice(1)
|
|
101
|
+
.filter(l => !l.includes('new Function') && !l.includes('AsyncFunction') && !l.includes('<anonymous>'))
|
|
102
|
+
.map(l => l.replace(/\bat eval \(eval at[^)]*\), /, 'at ').replace(/:(\d+):(\d+)\)?$/, (_, ln, col) => ':' + ln + ':' + col))
|
|
103
|
+
.slice(0, 5);
|
|
104
|
+
return [first, ...frames].join('\n') + '\n\nNode.js v23.10.0';
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
export function isEsmCode(code) {
|
|
108
|
+
const stripped = code.replace(/\/\*[\s\S]*?\*\//g, '').replace(/\/\/.*$/gm, '');
|
|
109
|
+
return /^\s*(import\s+[\w*{]|import\s*['"`]|export\s+(default|const|function|class|let|var|\{))/m.test(stripped);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export async function runEsm(code, scope) {
|
|
113
|
+
const injectionKeys = Object.keys(scope);
|
|
114
|
+
const preamble = injectionKeys.map(k => `const ${k} = globalThis.__esmScope__.${k};`).join('\n');
|
|
115
|
+
globalThis.__esmScope__ = scope;
|
|
116
|
+
const blob = new Blob([preamble + '\n' + code], { type: 'text/javascript' });
|
|
117
|
+
const url = URL.createObjectURL(blob);
|
|
118
|
+
try { return await import(url); } finally { URL.revokeObjectURL(url); }
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function parseDotEnv(text) {
|
|
122
|
+
const out = {};
|
|
123
|
+
for (const line of text.split(/\r?\n/)) {
|
|
124
|
+
const m = line.match(/^\s*([A-Za-z_][A-Za-z0-9_]*)\s*=\s*(.*?)\s*$/);
|
|
125
|
+
if (!m) continue;
|
|
126
|
+
let v = m[2];
|
|
127
|
+
if ((v.startsWith('"') && v.endsWith('"')) || (v.startsWith("'") && v.endsWith("'"))) v = v.slice(1, -1);
|
|
128
|
+
out[m[1]] = v;
|
|
129
|
+
}
|
|
130
|
+
return out;
|
|
131
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const CHAN='plugkit-ipc';
|
|
2
|
+
|
|
3
|
+
export function makeForkIpc(proc){
|
|
4
|
+
if(typeof BroadcastChannel==='undefined')return{enabled:false};
|
|
5
|
+
const bc=new BroadcastChannel(CHAN);
|
|
6
|
+
const childId=globalThis.location?.hash==='#ipc-child'?1:0;
|
|
7
|
+
const isChild=childId>0;
|
|
8
|
+
const handlers={message:[],disconnect:[]};
|
|
9
|
+
bc.addEventListener('message',e=>{const{from,to,msg}=e.data||{};if(isChild&&to==='child'){for(const h of handlers.message)h(msg,{});}else if(!isChild&&to==='parent'){for(const h of handlers.message)h(msg,{});}});
|
|
10
|
+
proc.send=msg=>{bc.postMessage({from:isChild?'child':'parent',to:isChild?'parent':'child',msg});return true;};
|
|
11
|
+
proc.on=(ev,fn)=>{if(ev==='message'||ev==='disconnect')(handlers[ev]=handlers[ev]||[]).push(fn);return proc;};
|
|
12
|
+
proc.disconnect=()=>{bc.postMessage({from:isChild?'child':'parent',to:isChild?'parent':'child',type:'disconnect'});for(const h of handlers.disconnect)h();};
|
|
13
|
+
proc.connected=true;
|
|
14
|
+
return{enabled:true,bc,isChild};
|
|
15
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
const pemLabel=pem=>{const m=pem.match(/-----BEGIN ([^-]+)-----/);return m?m[1]:null;};
|
|
2
|
+
const pemToBytes=pem=>{const m=pem.match(/-----BEGIN [^-]+-----([\s\S]+?)-----END/);if(!m)throw new Error('invalid PEM');return Uint8Array.from(atob(m[1].replace(/\s/g,'')),c=>c.charCodeAt(0));};
|
|
3
|
+
const bytesToPem=(bytes,label)=>`-----BEGIN ${label}-----\n${btoa(String.fromCharCode(...bytes)).match(/.{1,64}/g).join('\n')}\n-----END ${label}-----\n`;
|
|
4
|
+
|
|
5
|
+
export function makeKeyObject(pem,type){
|
|
6
|
+
const label=pemLabel(pem);
|
|
7
|
+
const inferredType=type||(label==='PRIVATE KEY'||label==='RSA PRIVATE KEY'||label==='EC PRIVATE KEY'?'private':label==='PUBLIC KEY'?'public':'secret');
|
|
8
|
+
const bytes=pemToBytes(pem);
|
|
9
|
+
const asymType=(()=>{const s=String.fromCharCode(...bytes.slice(0,Math.min(bytes.length,80)));if(s.includes('\x2A\x86\x48\xCE\x3D\x02\x01'))return'ec';if(s.includes('\x2A\x86\x48\x86\xF7\x0D\x01\x01\x01'))return'rsa';return'unknown';})();
|
|
10
|
+
return{
|
|
11
|
+
type:inferredType,
|
|
12
|
+
asymmetricKeyType:asymType,
|
|
13
|
+
export({format='pem',type:expType}={}){
|
|
14
|
+
if(format==='pem'){const l=inferredType==='private'?'PRIVATE KEY':'PUBLIC KEY';return bytesToPem(bytes,l);}
|
|
15
|
+
if(format==='der')return bytes;
|
|
16
|
+
if(format==='jwk')throw new Error('KeyObject.export jwk: use webcrypto exportKey(\'jwk\') directly');
|
|
17
|
+
throw new Error('unsupported format: '+format);
|
|
18
|
+
},
|
|
19
|
+
toCryptoKey(){throw new Error('KeyObject.toCryptoKey: import via webcrypto.importKey instead');}
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
let x509Mod=null;let x509Promise=null;
|
|
24
|
+
async function getX509(){
|
|
25
|
+
if(!x509Promise)x509Promise=import('https://esm.sh/@peculiar/x509@1.9.7/es2022/x509.mjs').then(m=>m.default||m);
|
|
26
|
+
return x509Promise;
|
|
27
|
+
}
|
|
28
|
+
export async function preloadX509(){x509Mod=await getX509();return x509Mod;}
|
|
29
|
+
|
|
30
|
+
export class X509Certificate{
|
|
31
|
+
constructor(pem){
|
|
32
|
+
this._pem=typeof pem==='string'?pem:'-----BEGIN CERTIFICATE-----\n'+btoa(String.fromCharCode(...pem)).match(/.{1,64}/g).join('\n')+'\n-----END CERTIFICATE-----\n';
|
|
33
|
+
if(x509Mod){this._parsed=new x509Mod.X509Certificate(this._pem);}else{this._parsed=null;}
|
|
34
|
+
}
|
|
35
|
+
async _parse(){
|
|
36
|
+
if(this._parsed)return this._parsed;
|
|
37
|
+
const x509=await getX509();x509Mod=x509;
|
|
38
|
+
this._parsed=new x509.X509Certificate(this._pem);
|
|
39
|
+
return this._parsed;
|
|
40
|
+
}
|
|
41
|
+
async fingerprint256Async(){const p=await this._parse();const hash=await crypto.subtle.digest('SHA-256',p.rawData);return [...new Uint8Array(hash)].map(b=>b.toString(16).padStart(2,'0').toUpperCase()).join(':');}
|
|
42
|
+
_need(){if(!this._parsed)throw new Error('X509Certificate: call await crypto.preloadX509() once before sync access, or await cert._parse()');return this._parsed;}
|
|
43
|
+
get subject(){return this._need().subject;}
|
|
44
|
+
get issuer(){return this._need().issuer;}
|
|
45
|
+
get validFrom(){return this._need().notBefore.toISOString();}
|
|
46
|
+
get validTo(){return this._need().notAfter.toISOString();}
|
|
47
|
+
get serialNumber(){return this._need().serialNumber;}
|
|
48
|
+
get raw(){return new Uint8Array(this._need().rawData);}
|
|
49
|
+
toString(){return this._pem;}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function extendKeys(cryptoMod){
|
|
53
|
+
cryptoMod.createPrivateKey=input=>{const pem=typeof input==='string'?input:input.key;return makeKeyObject(pem,'private');};
|
|
54
|
+
cryptoMod.createPublicKey=input=>{const pem=typeof input==='string'?input:input.key;return makeKeyObject(pem,'public');};
|
|
55
|
+
cryptoMod.createSecretKey=buf=>({type:'secret',symmetricKeySize:buf.length,export:()=>buf});
|
|
56
|
+
cryptoMod.X509Certificate=X509Certificate;
|
|
57
|
+
cryptoMod.KeyObject={from:pem=>makeKeyObject(pem)};
|
|
58
|
+
cryptoMod.preloadX509=preloadX509;
|
|
59
|
+
return cryptoMod;
|
|
60
|
+
}
|
|
@@ -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
|
+
}
|