Files

39 lines
2.7 KiB
JavaScript

'use strict';
/** Inherited private parent IPC only. The reviewed launcher supplies a fixed
* factory in code; IPC cannot select modules, executable paths or commands.
* This supplies lifecycle isolation, not a packaged/WFP-constrained broker.
*/
function runBrokerProcess(factory,{maxMessageBytes=65536}={}) {
if(typeof factory!=='function'||typeof process.send!=='function'||!process.connected||!Number.isSafeInteger(maxMessageBytes)||maxMessageBytes<1||maxMessageBytes>65536)throw Error('broker-process-invalid');
let state='waiting',broker,closing;
const send=value=>{if(process.connected)process.send(value,()=>{});};
const close=()=>{
if(closing)return closing;
state='closing';
closing=Promise.resolve().then(()=>broker?.close()).catch(()=>{}).finally(()=>{if(process.connected)process.disconnect();});
return closing;
};
process.once('disconnect',()=>{void close();});
process.on('message',message=>{
let serialized;
try {serialized=JSON.stringify(message);} catch {serialized='';}
if(!serialized||Buffer.byteLength(serialized)>maxMessageBytes||!message||typeof message!=='object'||Array.isArray(message)){send({type:'error',code:'invalid-message'});void close();return;}
if(message.type==='stop'&&Object.keys(message).length===1){void close();return;}
if(state!=='waiting'||message.type!=='start'||Object.keys(message).sort().join(',')!=='config,type'||!message.config||typeof message.config!=='object'||Array.isArray(message.config)){send({type:'error',code:'invalid-message'});void close();return;}
state='starting';
Promise.resolve().then(()=>factory(message.config)).then(async created=>{
if(!created||typeof created.listen!=='function'||typeof created.close!=='function'||typeof created.capability!=='string'||!/^[a-zA-Z0-9_-]{43}$/.test(created.capability)||!['basic','bearer'].includes(created.authMode))throw Error('invalid-factory');
if(created.authMode==='basic'&&(!created.credentials||!/^safetynet-[a-f0-9]{32}$/.test(created.credentials.username)||created.credentials.password!==created.capability))throw Error('invalid-factory');
broker=created;
if(state==='closing'){await broker.close();return;}
const port=await broker.listen();
if(state==='closing'){await broker.close();return;}
if(!Number.isSafeInteger(port)||port<1||port>65535)throw Error('invalid-listener');
state='ready';send({type:'ready',port,authMode:broker.authMode,capability:broker.capability,...(broker.authMode==='basic'?{credentials:{username:broker.credentials.username,password:broker.credentials.password}}:{})});
}).catch(()=>{send({type:'error',code:'startup-failed'});void close();});
});
return Object.freeze({close});
}
module.exports={runBrokerProcess};