38 lines
2.7 KiB
JavaScript
38 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};
|