@memberjunction/code-execution
Advanced tools
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
| /* @license | ||
| Papa Parse | ||
| v5.5.3 | ||
| https://github.com/mholt/PapaParse | ||
| License: MIT | ||
| */ | ||
| ((e,t)=>{"function"==typeof define&&define.amd?define([],t):"object"==typeof module&&"undefined"!=typeof exports?module.exports=t():e.Papa=t()})(this,function r(){var n="undefined"!=typeof self?self:"undefined"!=typeof window?window:void 0!==n?n:{};var d,s=!n.document&&!!n.postMessage,a=n.IS_PAPA_WORKER||!1,o={},h=0,v={};function u(e){this._handle=null,this._finished=!1,this._completed=!1,this._halted=!1,this._input=null,this._baseIndex=0,this._partialLine="",this._rowCount=0,this._start=0,this._nextChunk=null,this.isFirstChunk=!0,this._completeResults={data:[],errors:[],meta:{}},function(e){var t=b(e);t.chunkSize=parseInt(t.chunkSize),e.step||e.chunk||(t.chunkSize=null);this._handle=new i(t),(this._handle.streamer=this)._config=t}.call(this,e),this.parseChunk=function(t,e){var i=parseInt(this._config.skipFirstNLines)||0;if(this.isFirstChunk&&0<i){let e=this._config.newline;e||(r=this._config.quoteChar||'"',e=this._handle.guessLineEndings(t,r)),t=[...t.split(e).slice(i)].join(e)}this.isFirstChunk&&U(this._config.beforeFirstChunk)&&void 0!==(r=this._config.beforeFirstChunk(t))&&(t=r),this.isFirstChunk=!1,this._halted=!1;var i=this._partialLine+t,r=(this._partialLine="",this._handle.parse(i,this._baseIndex,!this._finished));if(!this._handle.paused()&&!this._handle.aborted()){t=r.meta.cursor,i=(this._finished||(this._partialLine=i.substring(t-this._baseIndex),this._baseIndex=t),r&&r.data&&(this._rowCount+=r.data.length),this._finished||this._config.preview&&this._rowCount>=this._config.preview);if(a)n.postMessage({results:r,workerId:v.WORKER_ID,finished:i});else if(U(this._config.chunk)&&!e){if(this._config.chunk(r,this._handle),this._handle.paused()||this._handle.aborted())return void(this._halted=!0);this._completeResults=r=void 0}return this._config.step||this._config.chunk||(this._completeResults.data=this._completeResults.data.concat(r.data),this._completeResults.errors=this._completeResults.errors.concat(r.errors),this._completeResults.meta=r.meta),this._completed||!i||!U(this._config.complete)||r&&r.meta.aborted||(this._config.complete(this._completeResults,this._input),this._completed=!0),i||r&&r.meta.paused||this._nextChunk(),r}this._halted=!0},this._sendError=function(e){U(this._config.error)?this._config.error(e):a&&this._config.error&&n.postMessage({workerId:v.WORKER_ID,error:e,finished:!1})}}function f(e){var r;(e=e||{}).chunkSize||(e.chunkSize=v.RemoteChunkSize),u.call(this,e),this._nextChunk=s?function(){this._readChunk(),this._chunkLoaded()}:function(){this._readChunk()},this.stream=function(e){this._input=e,this._nextChunk()},this._readChunk=function(){if(this._finished)this._chunkLoaded();else{if(r=new XMLHttpRequest,this._config.withCredentials&&(r.withCredentials=this._config.withCredentials),s||(r.onload=y(this._chunkLoaded,this),r.onerror=y(this._chunkError,this)),r.open(this._config.downloadRequestBody?"POST":"GET",this._input,!s),this._config.downloadRequestHeaders){var e,t=this._config.downloadRequestHeaders;for(e in t)r.setRequestHeader(e,t[e])}var i;this._config.chunkSize&&(i=this._start+this._config.chunkSize-1,r.setRequestHeader("Range","bytes="+this._start+"-"+i));try{r.send(this._config.downloadRequestBody)}catch(e){this._chunkError(e.message)}s&&0===r.status&&this._chunkError()}},this._chunkLoaded=function(){4===r.readyState&&(r.status<200||400<=r.status?this._chunkError():(this._start+=this._config.chunkSize||r.responseText.length,this._finished=!this._config.chunkSize||this._start>=(e=>null!==(e=e.getResponseHeader("Content-Range"))?parseInt(e.substring(e.lastIndexOf("/")+1)):-1)(r),this.parseChunk(r.responseText)))},this._chunkError=function(e){e=r.statusText||e;this._sendError(new Error(e))}}function l(e){(e=e||{}).chunkSize||(e.chunkSize=v.LocalChunkSize),u.call(this,e);var i,r,n="undefined"!=typeof FileReader;this.stream=function(e){this._input=e,r=e.slice||e.webkitSlice||e.mozSlice,n?((i=new FileReader).onload=y(this._chunkLoaded,this),i.onerror=y(this._chunkError,this)):i=new FileReaderSync,this._nextChunk()},this._nextChunk=function(){this._finished||this._config.preview&&!(this._rowCount<this._config.preview)||this._readChunk()},this._readChunk=function(){var e=this._input,t=(this._config.chunkSize&&(t=Math.min(this._start+this._config.chunkSize,this._input.size),e=r.call(e,this._start,t)),i.readAsText(e,this._config.encoding));n||this._chunkLoaded({target:{result:t}})},this._chunkLoaded=function(e){this._start+=this._config.chunkSize,this._finished=!this._config.chunkSize||this._start>=this._input.size,this.parseChunk(e.target.result)},this._chunkError=function(){this._sendError(i.error)}}function c(e){var i;u.call(this,e=e||{}),this.stream=function(e){return i=e,this._nextChunk()},this._nextChunk=function(){var e,t;if(!this._finished)return e=this._config.chunkSize,i=e?(t=i.substring(0,e),i.substring(e)):(t=i,""),this._finished=!i,this.parseChunk(t)}}function p(e){u.call(this,e=e||{});var t=[],i=!0,r=!1;this.pause=function(){u.prototype.pause.apply(this,arguments),this._input.pause()},this.resume=function(){u.prototype.resume.apply(this,arguments),this._input.resume()},this.stream=function(e){this._input=e,this._input.on("data",this._streamData),this._input.on("end",this._streamEnd),this._input.on("error",this._streamError)},this._checkIsFinished=function(){r&&1===t.length&&(this._finished=!0)},this._nextChunk=function(){this._checkIsFinished(),t.length?this.parseChunk(t.shift()):i=!0},this._streamData=y(function(e){try{t.push("string"==typeof e?e:e.toString(this._config.encoding)),i&&(i=!1,this._checkIsFinished(),this.parseChunk(t.shift()))}catch(e){this._streamError(e)}},this),this._streamError=y(function(e){this._streamCleanUp(),this._sendError(e)},this),this._streamEnd=y(function(){this._streamCleanUp(),r=!0,this._streamData("")},this),this._streamCleanUp=y(function(){this._input.removeListener("data",this._streamData),this._input.removeListener("end",this._streamEnd),this._input.removeListener("error",this._streamError)},this)}function i(m){var n,s,a,t,o=Math.pow(2,53),h=-o,u=/^\s*-?(\d+\.?|\.\d+|\d+\.\d+)([eE][-+]?\d+)?\s*$/,d=/^((\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d+([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z))|(\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d([+-][0-2]\d:[0-5]\d|Z)))$/,i=this,r=0,f=0,l=!1,e=!1,c=[],p={data:[],errors:[],meta:{}};function y(e){return"greedy"===m.skipEmptyLines?""===e.join("").trim():1===e.length&&0===e[0].length}function g(){if(p&&a&&(k("Delimiter","UndetectableDelimiter","Unable to auto-detect delimiting character; defaulted to '"+v.DefaultDelimiter+"'"),a=!1),m.skipEmptyLines&&(p.data=p.data.filter(function(e){return!y(e)})),_()){if(p)if(Array.isArray(p.data[0])){for(var e=0;_()&&e<p.data.length;e++)p.data[e].forEach(t);p.data.splice(0,1)}else p.data.forEach(t);function t(e,t){U(m.transformHeader)&&(e=m.transformHeader(e,t)),c.push(e)}}function i(e,t){for(var i=m.header?{}:[],r=0;r<e.length;r++){var n=r,s=e[r],s=((e,t)=>(e=>(m.dynamicTypingFunction&&void 0===m.dynamicTyping[e]&&(m.dynamicTyping[e]=m.dynamicTypingFunction(e)),!0===(m.dynamicTyping[e]||m.dynamicTyping)))(e)?"true"===t||"TRUE"===t||"false"!==t&&"FALSE"!==t&&((e=>{if(u.test(e)){e=parseFloat(e);if(h<e&&e<o)return 1}})(t)?parseFloat(t):d.test(t)?new Date(t):""===t?null:t):t)(n=m.header?r>=c.length?"__parsed_extra":c[r]:n,s=m.transform?m.transform(s,n):s);"__parsed_extra"===n?(i[n]=i[n]||[],i[n].push(s)):i[n]=s}return m.header&&(r>c.length?k("FieldMismatch","TooManyFields","Too many fields: expected "+c.length+" fields but parsed "+r,f+t):r<c.length&&k("FieldMismatch","TooFewFields","Too few fields: expected "+c.length+" fields but parsed "+r,f+t)),i}var r;p&&(m.header||m.dynamicTyping||m.transform)&&(r=1,!p.data.length||Array.isArray(p.data[0])?(p.data=p.data.map(i),r=p.data.length):p.data=i(p.data,0),m.header&&p.meta&&(p.meta.fields=c),f+=r)}function _(){return m.header&&0===c.length}function k(e,t,i,r){e={type:e,code:t,message:i};void 0!==r&&(e.row=r),p.errors.push(e)}U(m.step)&&(t=m.step,m.step=function(e){p=e,_()?g():(g(),0!==p.data.length&&(r+=e.data.length,m.preview&&r>m.preview?s.abort():(p.data=p.data[0],t(p,i))))}),this.parse=function(e,t,i){var r=m.quoteChar||'"',r=(m.newline||(m.newline=this.guessLineEndings(e,r)),a=!1,m.delimiter?U(m.delimiter)&&(m.delimiter=m.delimiter(e),p.meta.delimiter=m.delimiter):((r=((e,t,i,r,n)=>{var s,a,o,h;n=n||[",","\t","|",";",v.RECORD_SEP,v.UNIT_SEP];for(var u=0;u<n.length;u++){for(var d,f=n[u],l=0,c=0,p=0,g=(o=void 0,new E({comments:r,delimiter:f,newline:t,preview:10}).parse(e)),_=0;_<g.data.length;_++)i&&y(g.data[_])?p++:(d=g.data[_].length,c+=d,void 0===o?o=d:0<d&&(l+=Math.abs(d-o),o=d));0<g.data.length&&(c/=g.data.length-p),(void 0===a||l<=a)&&(void 0===h||h<c)&&1.99<c&&(a=l,s=f,h=c)}return{successful:!!(m.delimiter=s),bestDelimiter:s}})(e,m.newline,m.skipEmptyLines,m.comments,m.delimitersToGuess)).successful?m.delimiter=r.bestDelimiter:(a=!0,m.delimiter=v.DefaultDelimiter),p.meta.delimiter=m.delimiter),b(m));return m.preview&&m.header&&r.preview++,n=e,s=new E(r),p=s.parse(n,t,i),g(),l?{meta:{paused:!0}}:p||{meta:{paused:!1}}},this.paused=function(){return l},this.pause=function(){l=!0,s.abort(),n=U(m.chunk)?"":n.substring(s.getCharIndex())},this.resume=function(){i.streamer._halted?(l=!1,i.streamer.parseChunk(n,!0)):setTimeout(i.resume,3)},this.aborted=function(){return e},this.abort=function(){e=!0,s.abort(),p.meta.aborted=!0,U(m.complete)&&m.complete(p),n=""},this.guessLineEndings=function(e,t){e=e.substring(0,1048576);var t=new RegExp(P(t)+"([^]*?)"+P(t),"gm"),i=(e=e.replace(t,"")).split("\r"),t=e.split("\n"),e=1<t.length&&t[0].length<i[0].length;if(1===i.length||e)return"\n";for(var r=0,n=0;n<i.length;n++)"\n"===i[n][0]&&r++;return r>=i.length/2?"\r\n":"\r"}}function P(e){return e.replace(/[.*+?^${}()|[\]\\]/g,"\\$&")}function E(C){var S=(C=C||{}).delimiter,O=C.newline,x=C.comments,I=C.step,A=C.preview,T=C.fastMode,D=null,L=!1,F=null==C.quoteChar?'"':C.quoteChar,j=F;if(void 0!==C.escapeChar&&(j=C.escapeChar),("string"!=typeof S||-1<v.BAD_DELIMITERS.indexOf(S))&&(S=","),x===S)throw new Error("Comment character same as delimiter");!0===x?x="#":("string"!=typeof x||-1<v.BAD_DELIMITERS.indexOf(x))&&(x=!1),"\n"!==O&&"\r"!==O&&"\r\n"!==O&&(O="\n");var z=0,M=!1;this.parse=function(i,t,r){if("string"!=typeof i)throw new Error("Input must be a string");var n=i.length,e=S.length,s=O.length,a=x.length,o=U(I),h=[],u=[],d=[],f=z=0;if(!i)return w();if(T||!1!==T&&-1===i.indexOf(F)){for(var l=i.split(O),c=0;c<l.length;c++){if(d=l[c],z+=d.length,c!==l.length-1)z+=O.length;else if(r)return w();if(!x||d.substring(0,a)!==x){if(o){if(h=[],k(d.split(S)),R(),M)return w()}else k(d.split(S));if(A&&A<=c)return h=h.slice(0,A),w(!0)}}return w()}for(var p=i.indexOf(S,z),g=i.indexOf(O,z),_=new RegExp(P(j)+P(F),"g"),m=i.indexOf(F,z);;)if(i[z]===F)for(m=z,z++;;){if(-1===(m=i.indexOf(F,m+1)))return r||u.push({type:"Quotes",code:"MissingQuotes",message:"Quoted field unterminated",row:h.length,index:z}),E();if(m===n-1)return E(i.substring(z,m).replace(_,F));if(F===j&&i[m+1]===j)m++;else if(F===j||0===m||i[m-1]!==j){-1!==p&&p<m+1&&(p=i.indexOf(S,m+1));var y=v(-1===(g=-1!==g&&g<m+1?i.indexOf(O,m+1):g)?p:Math.min(p,g));if(i.substr(m+1+y,e)===S){d.push(i.substring(z,m).replace(_,F)),i[z=m+1+y+e]!==F&&(m=i.indexOf(F,z)),p=i.indexOf(S,z),g=i.indexOf(O,z);break}y=v(g);if(i.substring(m+1+y,m+1+y+s)===O){if(d.push(i.substring(z,m).replace(_,F)),b(m+1+y+s),p=i.indexOf(S,z),m=i.indexOf(F,z),o&&(R(),M))return w();if(A&&h.length>=A)return w(!0);break}u.push({type:"Quotes",code:"InvalidQuotes",message:"Trailing quote on quoted field is malformed",row:h.length,index:z}),m++}}else if(x&&0===d.length&&i.substring(z,z+a)===x){if(-1===g)return w();z=g+s,g=i.indexOf(O,z),p=i.indexOf(S,z)}else if(-1!==p&&(p<g||-1===g))d.push(i.substring(z,p)),z=p+e,p=i.indexOf(S,z);else{if(-1===g)break;if(d.push(i.substring(z,g)),b(g+s),o&&(R(),M))return w();if(A&&h.length>=A)return w(!0)}return E();function k(e){h.push(e),f=z}function v(e){var t=0;return t=-1!==e&&(e=i.substring(m+1,e))&&""===e.trim()?e.length:t}function E(e){return r||(void 0===e&&(e=i.substring(z)),d.push(e),z=n,k(d),o&&R()),w()}function b(e){z=e,k(d),d=[],g=i.indexOf(O,z)}function w(e){if(C.header&&!t&&h.length&&!L){var s=h[0],a=Object.create(null),o=new Set(s);let n=!1;for(let r=0;r<s.length;r++){let i=s[r];if(a[i=U(C.transformHeader)?C.transformHeader(i,r):i]){let e,t=a[i];for(;e=i+"_"+t,t++,o.has(e););o.add(e),s[r]=e,a[i]++,n=!0,(D=null===D?{}:D)[e]=i}else a[i]=1,s[r]=i;o.add(i)}n&&console.warn("Duplicate headers found and renamed."),L=!0}return{data:h,errors:u,meta:{delimiter:S,linebreak:O,aborted:M,truncated:!!e,cursor:f+(t||0),renamedHeaders:D}}}function R(){I(w()),h=[],u=[]}},this.abort=function(){M=!0},this.getCharIndex=function(){return z}}function g(e){var t=e.data,i=o[t.workerId],r=!1;if(t.error)i.userError(t.error,t.file);else if(t.results&&t.results.data){var n={abort:function(){r=!0,_(t.workerId,{data:[],errors:[],meta:{aborted:!0}})},pause:m,resume:m};if(U(i.userStep)){for(var s=0;s<t.results.data.length&&(i.userStep({data:t.results.data[s],errors:t.results.errors,meta:t.results.meta},n),!r);s++);delete t.results}else U(i.userChunk)&&(i.userChunk(t.results,n,t.file),delete t.results)}t.finished&&!r&&_(t.workerId,t.results)}function _(e,t){var i=o[e];U(i.userComplete)&&i.userComplete(t),i.terminate(),delete o[e]}function m(){throw new Error("Not implemented.")}function b(e){if("object"!=typeof e||null===e)return e;var t,i=Array.isArray(e)?[]:{};for(t in e)i[t]=b(e[t]);return i}function y(e,t){return function(){e.apply(t,arguments)}}function U(e){return"function"==typeof e}return v.parse=function(e,t){var i=(t=t||{}).dynamicTyping||!1;U(i)&&(t.dynamicTypingFunction=i,i={});if(t.dynamicTyping=i,t.transform=!!U(t.transform)&&t.transform,!t.worker||!v.WORKERS_SUPPORTED)return i=null,v.NODE_STREAM_INPUT,"string"==typeof e?(e=(e=>65279!==e.charCodeAt(0)?e:e.slice(1))(e),i=new(t.download?f:c)(t)):!0===e.readable&&U(e.read)&&U(e.on)?i=new p(t):(n.File&&e instanceof File||e instanceof Object)&&(i=new l(t)),i.stream(e);(i=(()=>{var e;return!!v.WORKERS_SUPPORTED&&(e=(()=>{var e=n.URL||n.webkitURL||null,t=r.toString();return v.BLOB_URL||(v.BLOB_URL=e.createObjectURL(new Blob(["var global = (function() { if (typeof self !== 'undefined') { return self; } if (typeof window !== 'undefined') { return window; } if (typeof global !== 'undefined') { return global; } return {}; })(); global.IS_PAPA_WORKER=true; ","(",t,")();"],{type:"text/javascript"})))})(),(e=new n.Worker(e)).onmessage=g,e.id=h++,o[e.id]=e)})()).userStep=t.step,i.userChunk=t.chunk,i.userComplete=t.complete,i.userError=t.error,t.step=U(t.step),t.chunk=U(t.chunk),t.complete=U(t.complete),t.error=U(t.error),delete t.worker,i.postMessage({input:e,config:t,workerId:i.id})},v.unparse=function(e,t){var n=!1,_=!0,m=",",y="\r\n",s='"',a=s+s,i=!1,r=null,o=!1,h=((()=>{if("object"==typeof t){if("string"!=typeof t.delimiter||v.BAD_DELIMITERS.filter(function(e){return-1!==t.delimiter.indexOf(e)}).length||(m=t.delimiter),"boolean"!=typeof t.quotes&&"function"!=typeof t.quotes&&!Array.isArray(t.quotes)||(n=t.quotes),"boolean"!=typeof t.skipEmptyLines&&"string"!=typeof t.skipEmptyLines||(i=t.skipEmptyLines),"string"==typeof t.newline&&(y=t.newline),"string"==typeof t.quoteChar&&(s=t.quoteChar),"boolean"==typeof t.header&&(_=t.header),Array.isArray(t.columns)){if(0===t.columns.length)throw new Error("Option columns is empty");r=t.columns}void 0!==t.escapeChar&&(a=t.escapeChar+s),t.escapeFormulae instanceof RegExp?o=t.escapeFormulae:"boolean"==typeof t.escapeFormulae&&t.escapeFormulae&&(o=/^[=+\-@\t\r].*$/)}})(),new RegExp(P(s),"g"));"string"==typeof e&&(e=JSON.parse(e));if(Array.isArray(e)){if(!e.length||Array.isArray(e[0]))return u(null,e,i);if("object"==typeof e[0])return u(r||Object.keys(e[0]),e,i)}else if("object"==typeof e)return"string"==typeof e.data&&(e.data=JSON.parse(e.data)),Array.isArray(e.data)&&(e.fields||(e.fields=e.meta&&e.meta.fields||r),e.fields||(e.fields=Array.isArray(e.data[0])?e.fields:"object"==typeof e.data[0]?Object.keys(e.data[0]):[]),Array.isArray(e.data[0])||"object"==typeof e.data[0]||(e.data=[e.data])),u(e.fields||[],e.data||[],i);throw new Error("Unable to serialize unrecognized input");function u(e,t,i){var r="",n=("string"==typeof e&&(e=JSON.parse(e)),"string"==typeof t&&(t=JSON.parse(t)),Array.isArray(e)&&0<e.length),s=!Array.isArray(t[0]);if(n&&_){for(var a=0;a<e.length;a++)0<a&&(r+=m),r+=k(e[a],a);0<t.length&&(r+=y)}for(var o=0;o<t.length;o++){var h=(n?e:t[o]).length,u=!1,d=n?0===Object.keys(t[o]).length:0===t[o].length;if(i&&!n&&(u="greedy"===i?""===t[o].join("").trim():1===t[o].length&&0===t[o][0].length),"greedy"===i&&n){for(var f=[],l=0;l<h;l++){var c=s?e[l]:l;f.push(t[o][c])}u=""===f.join("").trim()}if(!u){for(var p=0;p<h;p++){0<p&&!d&&(r+=m);var g=n&&s?e[p]:p;r+=k(t[o][g],p)}o<t.length-1&&(!i||0<h&&!d)&&(r+=y)}}return r}function k(e,t){var i,r;return null==e?"":e.constructor===Date?JSON.stringify(e).slice(1,25):(r=!1,o&&"string"==typeof e&&o.test(e)&&(e="'"+e,r=!0),i=e.toString().replace(h,a),(r=r||!0===n||"function"==typeof n&&n(e,t)||Array.isArray(n)&&n[t]||((e,t)=>{for(var i=0;i<t.length;i++)if(-1<e.indexOf(t[i]))return!0;return!1})(i,v.BAD_DELIMITERS)||-1<i.indexOf(m)||" "===i.charAt(0)||" "===i.charAt(i.length-1))?s+i+s:i)}},v.RECORD_SEP=String.fromCharCode(30),v.UNIT_SEP=String.fromCharCode(31),v.BYTE_ORDER_MARK="\ufeff",v.BAD_DELIMITERS=["\r","\n",'"',v.BYTE_ORDER_MARK],v.WORKERS_SUPPORTED=!s&&!!n.Worker,v.NODE_STREAM_INPUT=1,v.LocalChunkSize=10485760,v.RemoteChunkSize=5242880,v.DefaultDelimiter=",",v.Parser=E,v.ParserHandle=i,v.NetworkStreamer=f,v.FileStreamer=l,v.StringStreamer=c,v.ReadableStreamStreamer=p,n.jQuery&&((d=n.jQuery).fn.parse=function(o){var i=o.config||{},h=[];return this.each(function(e){if(!("INPUT"===d(this).prop("tagName").toUpperCase()&&"file"===d(this).attr("type").toLowerCase()&&n.FileReader)||!this.files||0===this.files.length)return!0;for(var t=0;t<this.files.length;t++)h.push({file:this.files[t],inputElem:this,instanceConfig:d.extend({},i)})}),e(),this;function e(){if(0===h.length)U(o.complete)&&o.complete();else{var e,t,i,r,n=h[0];if(U(o.before)){var s=o.before(n.file,n.inputElem);if("object"==typeof s){if("abort"===s.action)return e="AbortError",t=n.file,i=n.inputElem,r=s.reason,void(U(o.error)&&o.error({name:e},t,i,r));if("skip"===s.action)return void u();"object"==typeof s.config&&(n.instanceConfig=d.extend(n.instanceConfig,s.config))}else if("skip"===s)return void u()}var a=n.instanceConfig.complete;n.instanceConfig.complete=function(e){U(a)&&a(e,n.file,n.inputElem),u()},v.parse(n.file,n.instanceConfig)}}function u(){h.splice(0,1),e()}}),a&&(n.onmessage=function(e){e=e.data;void 0===v.WORKER_ID&&e&&(v.WORKER_ID=e.workerId);"string"==typeof e.input?n.postMessage({workerId:v.WORKER_ID,results:v.parse(e.input,e.config),finished:!0}):(n.File&&e.input instanceof File||e.input instanceof Object)&&(e=v.parse(e.input,e.config))&&n.postMessage({workerId:v.WORKER_ID,results:e,finished:!0})}),(f.prototype=Object.create(u.prototype)).constructor=f,(l.prototype=Object.create(u.prototype)).constructor=l,(c.prototype=Object.create(c.prototype)).constructor=c,(p.prototype=Object.create(u.prototype)).constructor=p,v}); |
| "use strict"; | ||
| Object.defineProperty(exports, "__esModule", { | ||
| value: true | ||
| }); | ||
| exports.default = void 0; | ||
| var _native = _interopRequireDefault(require("./native.js")); | ||
| var _rng = _interopRequireDefault(require("./rng.js")); | ||
| var _stringify = require("./stringify.js"); | ||
| function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } | ||
| function v4(options, buf, offset) { | ||
| if (_native.default.randomUUID && !buf && !options) { | ||
| return _native.default.randomUUID(); | ||
| } | ||
| options = options || {}; | ||
| const rnds = options.random || (options.rng || _rng.default)(); // Per 4.4, set bits for version and `clock_seq_hi_and_reserved` | ||
| rnds[6] = rnds[6] & 0x0f | 0x40; | ||
| rnds[8] = rnds[8] & 0x3f | 0x80; // Copy bytes to buffer, if provided | ||
| if (buf) { | ||
| offset = offset || 0; | ||
| for (let i = 0; i < 16; ++i) { | ||
| buf[offset + i] = rnds[i]; | ||
| } | ||
| return buf; | ||
| } | ||
| return (0, _stringify.unsafeStringify)(rnds); | ||
| } | ||
| var _default = v4; | ||
| exports.default = _default; |
Sorry, the diff of this file is too big to display
| /** | ||
| * End-to-end bridge tests for the CodeExecution sandbox. | ||
| * | ||
| * These tests exercise the REAL isolated-vm + child-process path so we can | ||
| * verify the bridge protocol actually works: sandbox code calls | ||
| * `__bridgeCall(name, args)`, the worker routes it to the host, the host | ||
| * invokes the registered handler, the response travels back, and the | ||
| * sandbox's `await` resolves with the result. | ||
| * | ||
| * These tests import from the compiled `dist/` output rather than `src/` | ||
| * because `WorkerPool.fork()` resolves `worker.js` relative to its own | ||
| * module location via `import.meta.url` — running vitest against the TS | ||
| * source would look for a sibling `src/worker.js` file that doesn't exist. | ||
| * | ||
| * Because they fork real Node processes they're slower than our other unit | ||
| * tests; each test has a generous timeout. | ||
| */ | ||
| import { describe, it, expect, afterAll } from 'vitest'; | ||
| // eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
| // @ts-ignore - built output has no .d.ts index.js entry for tests | ||
| import { CodeExecutionService } from '../../dist/index.js'; | ||
| describe('CodeExecution bridge (end-to-end)', () => { | ||
| // One service for the whole suite so we amortize the worker-pool startup | ||
| // cost across tests. Shut it down in afterAll. | ||
| const service = new CodeExecutionService({ poolSize: 1 }); | ||
| afterAll(async () => { | ||
| await (service as unknown as { workerPool: { shutdown: () => Promise<void> } }).workerPool.shutdown(); | ||
| }); | ||
| it('round-trips a simple bridge call', async () => { | ||
| const result = await service.execute({ | ||
| code: ` | ||
| const echo = await __bridgeCall('__echo', { msg: 'hello' }); | ||
| output = echo; | ||
| `, | ||
| language: 'javascript', | ||
| bridgeHandlers: { | ||
| __echo: async (args: unknown) => args | ||
| } | ||
| }); | ||
| expect(result.success).toBe(true); | ||
| expect(result.output).toEqual({ msg: 'hello' }); | ||
| }, 15000); | ||
| it('rejects when sandbox calls an unregistered handler', async () => { | ||
| const result = await service.execute({ | ||
| code: ` | ||
| try { | ||
| await __bridgeCall('nope', {}); | ||
| output = { unexpected: true }; | ||
| } catch (err) { | ||
| output = { error: err.message }; | ||
| } | ||
| `, | ||
| language: 'javascript', | ||
| bridgeHandlers: { __echo: async (a: unknown) => a } | ||
| }); | ||
| expect(result.success).toBe(true); | ||
| expect(result.output).toHaveProperty('error'); | ||
| expect(String(result.output.error)).toContain("'nope'"); | ||
| }, 15000); | ||
| it('propagates handler-thrown errors to the sandbox', async () => { | ||
| const result = await service.execute({ | ||
| code: ` | ||
| try { | ||
| await __bridgeCall('boom', {}); | ||
| output = { unexpected: true }; | ||
| } catch (err) { | ||
| output = { error: err.message }; | ||
| } | ||
| `, | ||
| language: 'javascript', | ||
| bridgeHandlers: { | ||
| boom: async () => { | ||
| throw new Error('explosion'); | ||
| } | ||
| } | ||
| }); | ||
| expect(result.success).toBe(true); | ||
| expect(result.output.error).toBe('explosion'); | ||
| }, 15000); | ||
| it('supports Promise.all with concurrent bridge calls correlated by callId', async () => { | ||
| const result = await service.execute({ | ||
| code: ` | ||
| const [a, b, c] = await Promise.all([ | ||
| __bridgeCall('double', 3), | ||
| __bridgeCall('double', 7), | ||
| __bridgeCall('double', 11) | ||
| ]); | ||
| output = { a, b, c }; | ||
| `, | ||
| language: 'javascript', | ||
| bridgeHandlers: { | ||
| double: async (args: unknown) => (args as number) * 2 | ||
| } | ||
| }); | ||
| expect(result.success).toBe(true); | ||
| expect(result.output).toEqual({ a: 6, b: 14, c: 22 }); | ||
| }, 15000); | ||
| it('enforces maxBridgeCalls', async () => { | ||
| const result = await service.execute({ | ||
| code: ` | ||
| const attempts = []; | ||
| let failedWith = null; | ||
| for (let i = 0; i < 5; i++) { | ||
| try { | ||
| const r = await __bridgeCall('ping', i); | ||
| attempts.push(r); | ||
| } catch (err) { | ||
| failedWith = err.message; | ||
| break; | ||
| } | ||
| } | ||
| output = { attempts, failedWith }; | ||
| `, | ||
| language: 'javascript', | ||
| maxBridgeCalls: 3, | ||
| bridgeHandlers: { | ||
| ping: async (n: unknown) => `pong-${n}` | ||
| } | ||
| }); | ||
| expect(result.success).toBe(true); | ||
| expect(result.output.attempts).toEqual(['pong-0', 'pong-1', 'pong-2']); | ||
| expect(String(result.output.failedWith)).toContain('maxBridgeCalls=3'); | ||
| }, 15000); | ||
| it('aborts in-flight bridge calls when the caller aborts', async () => { | ||
| const controller = new AbortController(); | ||
| // A handler that never resolves — we want the sandbox to be suspended | ||
| // on the `await` when the abort fires. | ||
| const resultPromise = service.execute({ | ||
| code: ` | ||
| try { | ||
| await __bridgeCall('hang', {}); | ||
| output = { unexpected: true }; | ||
| } catch (err) { | ||
| output = { error: err.message }; | ||
| } | ||
| `, | ||
| language: 'javascript', | ||
| timeoutSeconds: 10, | ||
| abortSignal: controller.signal, | ||
| bridgeHandlers: { | ||
| hang: () => new Promise<never>(() => {}) | ||
| } | ||
| }); | ||
| // Let the sandbox enter the await before aborting. | ||
| await new Promise((r) => setTimeout(r, 200)); | ||
| controller.abort('user cancelled'); | ||
| const result = await resultPromise; | ||
| expect(result.success).toBe(false); | ||
| expect(result.errorType).toBe('TIMEOUT'); | ||
| expect(String(result.error)).toContain('user cancelled'); | ||
| }, 20000); | ||
| it('leaves subsequent executions unaffected after a bridge failure', async () => { | ||
| const failing = await service.execute({ | ||
| code: ` | ||
| try { await __bridgeCall('fail', {}); output = 'no'; } | ||
| catch (e) { output = e.message; } | ||
| `, | ||
| language: 'javascript', | ||
| bridgeHandlers: { fail: async () => { throw new Error('nope'); } } | ||
| }); | ||
| expect(failing.success).toBe(true); | ||
| expect(failing.output).toBe('nope'); | ||
| const recovering = await service.execute({ | ||
| code: `output = await __bridgeCall('ok', 42);`, | ||
| language: 'javascript', | ||
| bridgeHandlers: { ok: async (n: unknown) => (n as number) + 1 } | ||
| }); | ||
| expect(recovering.success).toBe(true); | ||
| expect(recovering.output).toBe(43); | ||
| }, 20000); | ||
| }); |
| > @memberjunction/code-execution@5.29.0 build | ||
| > tsc && tsc-alias -f | ||
| > @memberjunction/code-execution@5.30.0 build | ||
| > tsc && tsc-alias -f && npm run copy:bundled-libs | ||
| > @memberjunction/code-execution@5.30.0 copy:bundled-libs | ||
| > node -e "const fs=require('fs'),p=require('path');const src=p.join('src','libraries','bundled-libs');const dst=p.join('dist','libraries','bundled-libs');fs.mkdirSync(dst,{recursive:true});for(const f of fs.readdirSync(src))fs.copyFileSync(p.join(src,f),p.join(dst,f));" | ||
+16
-0
| # @memberjunction/code-execution | ||
| ## 5.30.0 | ||
| ### Minor Changes | ||
| - 4729398: Runtime Actions — Phase 1 complete. Introduces `Action.Type='Runtime'`, a new action type where agents dynamically generate, test, and persist JavaScript actions that execute in MJ's isolated-vm sandbox with a permissioned bridge to metadata, views, queries, entity CRUD, other actions, agents, and AI prompts. Ships the v5.29.x migration (new `RuntimeActionConfiguration`, universal `MaxExecutionTimeMS`, and `CreatedByAgentID` columns on `Action`), the JSONType-authored config interface, the Zod validator with drift detection, the bidirectional IPC bridge in WorkerPool, the full `utilities.*` handler surface, the ActionSmith meta-agent with `Create Runtime Action` / `Test Runtime Action` helpers, Agent Manager wiring, the generic `Execute Agent` action, and Runtime-aware approval UI enhancements. Minor bumps across all touched packages because the schema migration + metadata records are coupled surface changes. | ||
| ### Patch Changes | ||
| - Updated dependencies [68bf87f] | ||
| - Updated dependencies [963f2df] | ||
| - Updated dependencies [4729398] | ||
| - Updated dependencies [b1f32a4] | ||
| - Updated dependencies [c199f3b] | ||
| - @memberjunction/core@5.30.0 | ||
| - @memberjunction/global@5.30.0 | ||
| ## 5.29.0 | ||
@@ -4,0 +20,0 @@ |
+128
-7
@@ -51,3 +51,3 @@ /** | ||
| case 'lodash': | ||
| return getLodashSource(); | ||
| return getBundledLodashSource(); | ||
| case 'date-fns': | ||
@@ -98,2 +98,52 @@ return getDateFnsSource(); | ||
| // Min/Max — commonly reached for in numeric-summary style Runtime | ||
| // actions. Return undefined for empty arrays (matches real lodash). | ||
| min: function(array) { | ||
| if (!array || array.length === 0) return undefined; | ||
| let m = array[0]; | ||
| for (let i = 1; i < array.length; i++) { | ||
| if (array[i] < m) m = array[i]; | ||
| } | ||
| return m; | ||
| }, | ||
| max: function(array) { | ||
| if (!array || array.length === 0) return undefined; | ||
| let m = array[0]; | ||
| for (let i = 1; i < array.length; i++) { | ||
| if (array[i] > m) m = array[i]; | ||
| } | ||
| return m; | ||
| }, | ||
| minBy: function(array, iteratee) { | ||
| if (!array || array.length === 0) return undefined; | ||
| const fn = typeof iteratee === 'function' ? iteratee : (x) => x[iteratee]; | ||
| let best = array[0]; | ||
| let bestScore = fn(best); | ||
| for (let i = 1; i < array.length; i++) { | ||
| const score = fn(array[i]); | ||
| if (score < bestScore) { | ||
| best = array[i]; | ||
| bestScore = score; | ||
| } | ||
| } | ||
| return best; | ||
| }, | ||
| maxBy: function(array, iteratee) { | ||
| if (!array || array.length === 0) return undefined; | ||
| const fn = typeof iteratee === 'function' ? iteratee : (x) => x[iteratee]; | ||
| let best = array[0]; | ||
| let bestScore = fn(best); | ||
| for (let i = 1; i < array.length; i++) { | ||
| const score = fn(array[i]); | ||
| if (score > bestScore) { | ||
| best = array[i]; | ||
| bestScore = score; | ||
| } | ||
| } | ||
| return best; | ||
| }, | ||
| difference: function(array, ...values) { | ||
@@ -150,2 +200,22 @@ const others = new Set(values.flat()); | ||
| // orderBy is like sortBy but supports per-iteratee asc/desc direction. | ||
| // Signature: orderBy(collection, iteratees, orders) where iteratees | ||
| // and orders are parallel arrays (or both scalars). Direction strings | ||
| // default to 'asc' when not supplied. | ||
| orderBy: function(collection, iteratees, orders) { | ||
| const itList = Array.isArray(iteratees) ? iteratees : [iteratees]; | ||
| const orderList = Array.isArray(orders) ? orders : (orders ? [orders] : []); | ||
| const resolveFn = (it) => typeof it === 'function' ? it : x => x[it]; | ||
| return [...collection].sort((a, b) => { | ||
| for (let i = 0; i < itList.length; i++) { | ||
| const fn = resolveFn(itList[i]); | ||
| const dir = (orderList[i] ?? 'asc') === 'desc' ? -1 : 1; | ||
| const aVal = fn(a); | ||
| const bVal = fn(b); | ||
| if (aVal !== bVal) return aVal < bVal ? -1 * dir : 1 * dir; | ||
| } | ||
| return 0; | ||
| }); | ||
| }, | ||
| // Object functions | ||
@@ -237,11 +307,30 @@ pick: function(object, ...paths) { | ||
| const d = new Date(date); | ||
| const monthsLong = ['January','February','March','April','May','June','July','August','September','October','November','December']; | ||
| const monthsShort = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; | ||
| const daysLong = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; | ||
| const daysShort = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; | ||
| const month = d.getMonth(); | ||
| const dow = d.getDay(); | ||
| // Supported tokens are listed longest-first in the regex so that | ||
| // e.g. 'MMMM' is matched before 'MMM' / 'MM' / 'M'. | ||
| // NOTE: this shim has no literal-escape support, so we deliberately | ||
| // omit ambiguous single-letter tokens from real date-fns (a/h/H/m/s | ||
| // bare) that collide with common English letters in format strings. | ||
| // Users who need time formatting can use the padded HH:mm:ss forms. | ||
| const tokens = { | ||
| 'yyyy': d.getFullYear(), | ||
| 'MM': String(d.getMonth() + 1).padStart(2, '0'), | ||
| 'dd': String(d.getDate()).padStart(2, '0'), | ||
| 'HH': String(d.getHours()).padStart(2, '0'), | ||
| 'mm': String(d.getMinutes()).padStart(2, '0'), | ||
| 'ss': String(d.getSeconds()).padStart(2, '0') | ||
| 'yy': String(d.getFullYear()).slice(-2), | ||
| 'MMMM': monthsLong[month], | ||
| 'MMM': monthsShort[month], | ||
| 'MM': String(month + 1).padStart(2, '0'), | ||
| 'M': String(month + 1), | ||
| 'dd': String(d.getDate()).padStart(2, '0'), | ||
| 'd': String(d.getDate()), | ||
| 'EEEE': daysLong[dow], | ||
| 'EEE': daysShort[dow], | ||
| 'HH': String(d.getHours()).padStart(2, '0'), | ||
| 'mm': String(d.getMinutes()).padStart(2, '0'), | ||
| 'ss': String(d.getSeconds()).padStart(2, '0') | ||
| }; | ||
| return formatStr.replace(/yyyy|MM|dd|HH|mm|ss/g, match => tokens[match]); | ||
| return formatStr.replace(/yyyy|yy|MMMM|MMM|MM|M|dd|d|EEEE|EEE|HH|mm|ss/g, match => tokens[match]); | ||
| }, | ||
@@ -377,2 +466,34 @@ | ||
| /** | ||
| * Load the bundled full lodash UMD build (~73KB). We used to hand-roll | ||
| * a tiny subset here, but the subset was missing the most foundational | ||
| * helpers (`map`, `filter`, `forEach`, `reduce`, `find`, `some`, `every`, | ||
| * `keys`, `values`, `each`, `includes`) — so any Runtime action that | ||
| * called `_.map(...)` crashed with "_.map is not a function". The inline | ||
| * subset stays in this file below as `getLodashSource()` for reference / | ||
| * fallback only; it is no longer wired up. | ||
| * | ||
| * Lodash's UMD epilogue looks for `module.exports` before falling back to | ||
| * a global. We provide a shim `module` and return `module.exports` so the | ||
| * sandbox gets the full lodash object exactly as `require('lodash')` would | ||
| * hand back in node. | ||
| */ | ||
| function getBundledLodashSource() { | ||
| const libPath = path.join(__dirname, 'bundled-libs', 'lodash.js'); | ||
| const source = fs.readFileSync(libPath, 'utf8'); | ||
| // Lodash's UMD defines `ue` (the module-exports branch it actually writes | ||
| // to) as `ee && typeof module == "object" && ... && module`, where | ||
| // `ee = typeof exports == "object" && exports && ...`. So it short-circuits | ||
| // to the global branch (`re._=be`, synthesized root) if `exports` is | ||
| // undefined in scope — even when `module` is present. Both shims are | ||
| // required, and they must share storage (`const exports = module.exports`) | ||
| // so whichever path lodash picks, `module.exports` ends up holding the | ||
| // real lodash object. | ||
| return `(function() { | ||
| const module = { exports: {} }; | ||
| const exports = module.exports; | ||
| ${source} | ||
| return module.exports; | ||
| })()`; | ||
| } | ||
| /** | ||
| * mathjs implementation | ||
@@ -379,0 +500,0 @@ * Provides comprehensive mathematics and statistics functions |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/libraries/index.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/libraries/index.ts"],"names":[],"mappings":"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"} |
+106
-1
| /** | ||
| * Parameters for code execution | ||
| * A host-side bridge handler function. Registered per-execution via | ||
| * `CodeExecutionService.execute({ bridgeHandlers, ... })` and invoked when | ||
| * sandboxed code calls `__bridgeCall(name, args)`. The handler receives the | ||
| * args (already JSON-round-tripped, so primitives + plain objects only) and | ||
| * returns any JSON-serializable value; it may also throw, in which case the | ||
| * sandbox's `await __bridgeCall(...)` rejects with the error's message. | ||
| * | ||
| * The handler runs in the **host process**, not the sandbox. It has full | ||
| * access to MJ's services, DB, and filesystem — so higher layers | ||
| * (RuntimeActionBridge in @memberjunction/action-runtime) are responsible | ||
| * for enforcing per-Runtime-action permission scopes before each call | ||
| * reaches this point. | ||
| */ | ||
| export type BridgeHandler = (args: unknown) => Promise<unknown> | unknown; | ||
| /** | ||
| * A name → handler map passed to `CodeExecutionService.execute()` for a | ||
| * single invocation. The sandbox can only call names that are present in | ||
| * this map; unknown names reject with a "handler not registered" error. | ||
| */ | ||
| export type BridgeHandlerMap = Record<string, BridgeHandler>; | ||
| /** | ||
| * Parameters for code execution. | ||
| * | ||
| * `bridgeHandlers` is the ONLY field that is NOT sent over IPC — functions | ||
| * can't be serialized across the process boundary. The host keeps the map | ||
| * alive for the duration of this execution and uses it to service | ||
| * `bridge-call` messages coming back from the worker. | ||
| */ | ||
| export interface CodeExecutionParams { | ||
@@ -26,2 +52,22 @@ /** | ||
| memoryLimitMB?: number; | ||
| /** | ||
| * Optional map of bridge handlers to expose to sandbox code as | ||
| * `__bridgeCall(name, args)`. When present, the sandbox can make async | ||
| * round-trips to the host for each registered name. Host-side only — not | ||
| * sent over IPC. See {@link BridgeHandler}. | ||
| */ | ||
| bridgeHandlers?: BridgeHandlerMap; | ||
| /** | ||
| * Optional cap on the number of bridge calls a single execution may | ||
| * make (default: 100). The (N+1)th call rejects with a BRIDGE_LIMIT | ||
| * error. Prevents runaway loops inside sandbox code. | ||
| */ | ||
| maxBridgeCalls?: number; | ||
| /** | ||
| * Optional AbortSignal for upstream cancellation. When aborted, any | ||
| * in-flight `await __bridgeCall(...)` inside the sandbox rejects and | ||
| * the sandbox script is left to wind down (we don't dispose the isolate | ||
| * mid-execution — see design notes in the plan). | ||
| */ | ||
| abortSignal?: AbortSignal; | ||
| } | ||
@@ -75,2 +121,61 @@ /** | ||
| /** | ||
| * IPC message types for worker/host communication. | ||
| * | ||
| * Existing: `execute`, `result`, `error`, `ready`. | ||
| * NEW for the bridge: `bridge-call` (worker→host), `bridge-response` | ||
| * (host→worker), `abort` (host→worker). | ||
| */ | ||
| /** Host → Worker: run this code. */ | ||
| export interface ExecuteIpcMessage { | ||
| type: 'execute'; | ||
| requestId: string; | ||
| /** The IPC-safe subset of `CodeExecutionParams` (no `bridgeHandlers`). */ | ||
| params: Omit<CodeExecutionParams, 'bridgeHandlers' | 'abortSignal'>; | ||
| } | ||
| /** Worker → Host: execution finished cleanly. */ | ||
| export interface ResultIpcMessage { | ||
| type: 'result'; | ||
| requestId: string; | ||
| result: CodeExecutionResult; | ||
| } | ||
| /** Worker → Host: execution threw before the handler could assemble a result. */ | ||
| export interface ErrorIpcMessage { | ||
| type: 'error'; | ||
| requestId: string; | ||
| error: string; | ||
| } | ||
| /** Worker → Host: sandbox code wants the host to service a bridge call. */ | ||
| export interface BridgeCallIpcMessage { | ||
| type: 'bridge-call'; | ||
| requestId: string; | ||
| /** Unique per call so Promise.all-style concurrency is correlated correctly. */ | ||
| callId: string; | ||
| /** Name registered in the execution's `bridgeHandlers` map. */ | ||
| functionName: string; | ||
| /** JSON-round-tripped args. */ | ||
| args: unknown; | ||
| } | ||
| /** Host → Worker: result of a bridge call. One of `result` | `error` is populated. */ | ||
| export interface BridgeResponseIpcMessage { | ||
| type: 'bridge-response'; | ||
| requestId: string; | ||
| callId: string; | ||
| result?: unknown; | ||
| error?: string; | ||
| } | ||
| /** Host → Worker: abort in-flight bridge calls for this requestId. */ | ||
| export interface AbortIpcMessage { | ||
| type: 'abort'; | ||
| requestId: string; | ||
| reason: string; | ||
| } | ||
| /** Worker → Host: initial ready signal (no requestId). */ | ||
| export interface ReadyIpcMessage { | ||
| type: 'ready'; | ||
| } | ||
| /** Host → Worker message types. */ | ||
| export type HostToWorkerMessage = ExecuteIpcMessage | BridgeResponseIpcMessage | AbortIpcMessage; | ||
| /** Worker → Host message types. */ | ||
| export type WorkerToHostMessage = ResultIpcMessage | ErrorIpcMessage | BridgeCallIpcMessage | ReadyIpcMessage; | ||
| /** | ||
| * Sandbox context provided to executed code | ||
@@ -77,0 +182,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC;;OAEG;IACH,IAAI,EAAE,MAAM,CAAC;IAEb;;OAEG;IACH,QAAQ,EAAE,YAAY,CAAC;IAEvB;;OAEG;IACH,SAAS,CAAC,EAAE,GAAG,CAAC;IAEhB;;OAEG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB;;;OAGG;IACH,aAAa,CAAC,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC;;OAEG;IACH,OAAO,EAAE,OAAO,CAAC;IAEjB;;OAEG;IACH,MAAM,CAAC,EAAE,GAAG,CAAC;IAEb;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,cAAc,GAAG,cAAc,GAAG,eAAe,GAAG,gBAAgB,CAAC;IAE7F;;OAEG;IACH,eAAe,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED;;GAEG;AACH,MAAM,WAAW,0BAA0B;IACvC;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,gBAAgB,CAAC,EAAE,MAAM,EAAE,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC3B;;OAEG;IACH,KAAK,EAAE,GAAG,CAAC;IAEX;;OAEG;IACH,MAAM,EAAE,GAAG,CAAC;IAEZ;;OAEG;IACH,OAAO,EAAE;QACL,GAAG,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC9B,KAAK,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAChC,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;QAC/B,IAAI,EAAE,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,KAAK,IAAI,CAAC;KAClC,CAAC;CACL"} | ||
| {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/types/index.ts"],"names":[],"mappings":"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"} |
+22
-0
@@ -20,4 +20,26 @@ /** | ||
| * 4. Resource limits - prevents DoS via timeout/memory exhaustion | ||
| * | ||
| * ============================================================================== | ||
| * Bidirectional Bridge (Phase 1d) | ||
| * ============================================================================== | ||
| * Sandbox code can call `__bridgeCall(name, args)` to invoke a host-registered | ||
| * handler and await its result. The flow: | ||
| * | ||
| * 1. Sandbox calls `__bridgeCall('rv.RunView', { EntityName: '...' })` | ||
| * 2. That dispatches to the host-side `_bridgeCall` ivm.Reference via | ||
| * `applyAsync`. The reference's JS body sends a `bridge-call` IPC | ||
| * message to the parent process and waits for the matching | ||
| * `bridge-response`. | ||
| * 3. Parent process routes the request to the registered handler, | ||
| * executes it, and sends `bridge-response` back. | ||
| * 4. The worker's `process.on('message')` handler resolves the pending | ||
| * promise for that callId. | ||
| * 5. `applyAsync` resolves inside the isolate with the JSON-stringified | ||
| * result. Sandbox code parses it and sees a plain object. | ||
| * | ||
| * The bridge is scoped per-requestId: each `execute` message opens a new | ||
| * pending-calls map that is either drained on completion or rejected en | ||
| * masse on `abort`. Nothing persists between executions. | ||
| */ | ||
| export {}; | ||
| //# sourceMappingURL=worker.d.ts.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;GAmBG"} | ||
| {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyCG"} |
+194
-4
@@ -20,5 +20,31 @@ /** | ||
| * 4. Resource limits - prevents DoS via timeout/memory exhaustion | ||
| * | ||
| * ============================================================================== | ||
| * Bidirectional Bridge (Phase 1d) | ||
| * ============================================================================== | ||
| * Sandbox code can call `__bridgeCall(name, args)` to invoke a host-registered | ||
| * handler and await its result. The flow: | ||
| * | ||
| * 1. Sandbox calls `__bridgeCall('rv.RunView', { EntityName: '...' })` | ||
| * 2. That dispatches to the host-side `_bridgeCall` ivm.Reference via | ||
| * `applyAsync`. The reference's JS body sends a `bridge-call` IPC | ||
| * message to the parent process and waits for the matching | ||
| * `bridge-response`. | ||
| * 3. Parent process routes the request to the registered handler, | ||
| * executes it, and sends `bridge-response` back. | ||
| * 4. The worker's `process.on('message')` handler resolves the pending | ||
| * promise for that callId. | ||
| * 5. `applyAsync` resolves inside the isolate with the JSON-stringified | ||
| * result. Sandbox code parses it and sees a plain object. | ||
| * | ||
| * The bridge is scoped per-requestId: each `execute` message opens a new | ||
| * pending-calls map that is either drained on completion or rejected en | ||
| * masse on `abort`. Nothing persists between executions. | ||
| */ | ||
| import ivm from 'isolated-vm'; | ||
| import { getLibrarySource, isModuleAllowed, getAllowedModuleNames } from './libraries/index.js'; | ||
| const bridgeStates = new Map(); | ||
| function getBridgeState(requestId) { | ||
| return bridgeStates.get(requestId); | ||
| } | ||
| /** | ||
@@ -32,6 +58,16 @@ * Execute JavaScript code in an isolated-vm isolate | ||
| */ | ||
| async function executeInIsolate(params) { | ||
| async function executeInIsolate(params, requestId) { | ||
| const startTime = Date.now(); | ||
| const timeoutMs = (params.timeoutSeconds || 30) * 1000; | ||
| const memoryLimitMB = params.memoryLimitMB || 128; | ||
| const maxBridgeCalls = params.maxBridgeCalls ?? 100; | ||
| // Open the bridge state for this execution before the isolate starts so | ||
| // any bridge-response / abort messages that race in are delivered to a | ||
| // real slot rather than dropped. | ||
| bridgeStates.set(requestId, { | ||
| pending: new Map(), | ||
| callCount: 0, | ||
| maxCalls: maxBridgeCalls, | ||
| aborted: false | ||
| }); | ||
| let isolate = null; | ||
@@ -93,2 +129,95 @@ let context = null; | ||
| `); | ||
| // =================================================================== | ||
| // BRIDGE SETUP | ||
| // | ||
| // Inject a single host-backed async function as `_bridgeCall`. The | ||
| // reference's body sends an IPC message to the parent and awaits the | ||
| // matching `bridge-response` via the `bridgeStates` map above. The | ||
| // isolate wraps it as `globalThis.__bridgeCall(name, args)` which | ||
| // returns a Promise resolving to the parsed JSON result (or rejects | ||
| // with the host error message). | ||
| // | ||
| // Args + result cross the boundary as JSON strings so complex object | ||
| // shapes, Dates, etc. behave consistently and no ivm.Reference ever | ||
| // leaks back into user code. | ||
| // =================================================================== | ||
| // The bridge callback NEVER throws — errors are encoded as a JSON | ||
| // envelope `{ __bridgeError: '...' }` and re-thrown inside the | ||
| // isolate. This avoids firing unhandled-rejection in the worker | ||
| // process when the caller exceeds maxBridgeCalls or the host handler | ||
| // throws; isolated-vm would propagate the rejection across the | ||
| // boundary, but Node 24+'s strict unhandled-rejection semantics can | ||
| // fire a process.unhandledRejection handler before isolated-vm's | ||
| // delivery settles, killing the worker. | ||
| const bridgeCall = new ivm.Reference(async (callId, functionName, argsJson) => { | ||
| try { | ||
| const state = getBridgeState(requestId); | ||
| if (!state) { | ||
| throw new Error('Bridge state missing for this execution'); | ||
| } | ||
| if (state.aborted) { | ||
| throw new Error(state.abortReason ?? 'Execution aborted'); | ||
| } | ||
| if (state.callCount >= state.maxCalls) { | ||
| throw new Error(`Bridge call limit exceeded (maxBridgeCalls=${state.maxCalls}). ` + | ||
| 'Runtime action tried to make too many host round-trips.'); | ||
| } | ||
| state.callCount++; | ||
| let args; | ||
| try { | ||
| args = JSON.parse(argsJson); | ||
| } | ||
| catch { | ||
| throw new Error('Bridge call args must be JSON-serializable'); | ||
| } | ||
| const resultPromise = new Promise((resolve, reject) => { | ||
| state.pending.set(callId, { resolve, reject }); | ||
| }); | ||
| const callMessage = { | ||
| type: 'bridge-call', | ||
| requestId, | ||
| callId, | ||
| functionName, | ||
| args | ||
| }; | ||
| // SECURITY: IPC is JSON-serialized by Node's child-process | ||
| // plumbing; no references leak. | ||
| process.send(callMessage); | ||
| const hostResult = await resultPromise; | ||
| // Return as a JSON string so the isolate receives a primitive | ||
| // it can safely JSON.parse — no copyInto / ExternalCopy needed. | ||
| return JSON.stringify({ ok: hostResult === undefined ? null : hostResult }); | ||
| } | ||
| catch (err) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| return JSON.stringify({ __bridgeError: message }); | ||
| } | ||
| }); | ||
| await jail.set('_bridgeCall', bridgeCall); | ||
| // Counter inside the isolate for unique callIds — we use a monotonic | ||
| // counter instead of Math.random() so the sequence is deterministic | ||
| // for debugging and tests. | ||
| await context.eval(` | ||
| globalThis.__bridgeCallSeq = 0; | ||
| globalThis.__bridgeCall = async function(functionName, args) { | ||
| const callId = 'bc-' + (++globalThis.__bridgeCallSeq); | ||
| const argsJson = JSON.stringify(args ?? null); | ||
| // Reference.apply returns a Promise when called from inside | ||
| // an isolate. { result: { promise: true } } tells isolated-vm | ||
| // that the host callback returns a Promise it should await | ||
| // before resolving. The result is a primitive string, which | ||
| // transfers cleanly without needing ExternalCopy. | ||
| const envelope = await _bridgeCall.apply( | ||
| undefined, | ||
| [callId, functionName, argsJson], | ||
| { result: { promise: true } } | ||
| ); | ||
| const parsed = envelope ? JSON.parse(envelope) : null; | ||
| if (parsed && parsed.__bridgeError) { | ||
| throw new Error(parsed.__bridgeError); | ||
| } | ||
| // Envelope is always { ok: value } on success. | ||
| return parsed ? parsed.ok : null; | ||
| }; | ||
| `); | ||
| // Set up module cache for require() | ||
@@ -147,4 +276,10 @@ await jail.set('_moduleCache', new ivm.Reference({})); | ||
| await jail.set('_output', undefined); | ||
| // Now that the bridge can be awaited, user code may be async. Wrap in | ||
| // an async IIFE that exposes its return value via globalThis._output. | ||
| // The script.run() call below awaits the script completion (the IIFE | ||
| // itself returns a Promise, and with `promise: true` script.run | ||
| // awaits it). Back-compat: code that does NOT use await still works | ||
| // because the wrapper is an async function. | ||
| const wrappedCode = ` | ||
| (function() { | ||
| (async function() { | ||
| let output; | ||
@@ -159,3 +294,5 @@ ${params.code} | ||
| const script = await isolate.compileScript(wrappedCode); | ||
| await script.run(context, { timeout: timeoutMs }); | ||
| // `promise: true` ensures script.run awaits the IIFE's returned Promise | ||
| // so await __bridgeCall(...) inside user code works end-to-end. | ||
| await script.run(context, { timeout: timeoutMs, promise: true }); | ||
| // SECURITY: Extract output value - use .copySync() to get plain data, not References | ||
@@ -203,2 +340,11 @@ // This ensures no ivm objects leak back to caller | ||
| finally { | ||
| // Clean up bridge state — reject any promises that didn't resolve | ||
| // naturally (e.g. execution threw before a bridge call returned). | ||
| const state = bridgeStates.get(requestId); | ||
| if (state) { | ||
| for (const [, entry] of state.pending) { | ||
| entry.reject(new Error('Execution ended with pending bridge call')); | ||
| } | ||
| bridgeStates.delete(requestId); | ||
| } | ||
| // Clean up resources | ||
@@ -239,3 +385,3 @@ if (context) { | ||
| try { | ||
| const result = await executeInIsolate(message.params); | ||
| const result = await executeInIsolate(message.params, message.requestId); | ||
| const response = { | ||
@@ -257,6 +403,50 @@ type: 'result', | ||
| } | ||
| return; | ||
| } | ||
| if (message.type === 'bridge-response') { | ||
| const state = bridgeStates.get(message.requestId); | ||
| if (!state) { | ||
| // Stale response — execution already ended. Drop silently. | ||
| return; | ||
| } | ||
| const entry = state.pending.get(message.callId); | ||
| if (!entry) { | ||
| // Unknown callId — either it was already aborted or we've got a | ||
| // bug. Drop silently; the server-side WorkerPool would re-issue | ||
| // if it still cared. | ||
| return; | ||
| } | ||
| state.pending.delete(message.callId); | ||
| if (message.error) { | ||
| entry.reject(new Error(message.error)); | ||
| } | ||
| else { | ||
| entry.resolve(message.result); | ||
| } | ||
| return; | ||
| } | ||
| if (message.type === 'abort') { | ||
| const state = bridgeStates.get(message.requestId); | ||
| if (!state) { | ||
| return; | ||
| } | ||
| state.aborted = true; | ||
| state.abortReason = message.reason; | ||
| const pending = Array.from(state.pending.entries()); | ||
| state.pending.clear(); | ||
| for (const [, entry] of pending) { | ||
| entry.reject(new Error(message.reason || 'Execution aborted')); | ||
| } | ||
| // Note: we do NOT dispose the isolate from here. isolated-vm's | ||
| // `script.run(..., { timeout })` will still terminate the script | ||
| // when its own deadline fires, and rejecting the pending bridge | ||
| // calls is enough for cooperative user code to unwind. | ||
| return; | ||
| } | ||
| }); | ||
| // Export nothing — this file is only executed as a child process entrypoint. | ||
| // The ParentOutboundMessage type is exported via the types module instead. | ||
| void {}; | ||
| // Signal that worker is ready to accept work | ||
| process.send({ type: 'ready' }); | ||
| //# sourceMappingURL=worker.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"worker.js","sourceRoot":"","sources":["../src/worker.ts"],"names":[],"mappings":"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"} |
+39
-1
@@ -13,2 +13,6 @@ /** | ||
| * - Health monitoring and circuit breaker pattern | ||
| * - Bidirectional bridge: sandbox code can call host-registered handlers | ||
| * via `__bridgeCall(name, args)`; the host routes the call, executes the | ||
| * handler, and sends the response back. See worker.ts for the sandbox-side | ||
| * wiring. | ||
| */ | ||
@@ -51,6 +55,27 @@ import { CodeExecutionParams, CodeExecutionResult } from './types/index.js'; | ||
| /** | ||
| * Handle messages from worker process | ||
| * Handle messages from worker process. | ||
| * | ||
| * Types: | ||
| * - `ready` — initial startup / post-restart signal, ignored here. | ||
| * - `result` / `error` — the terminal outcome of the current execute() call. | ||
| * - `bridge-call` — a host-service request from inside the sandbox; we | ||
| * look up the registered handler and send back `bridge-response`. | ||
| * Critically, `bridge-call` does NOT end the request; the worker is | ||
| * still executing until it sends `result`/`error`. | ||
| */ | ||
| private handleWorkerMessage; | ||
| /** | ||
| * Finalize a request once the worker reports `result` or `error`. | ||
| * Clears timeouts, detaches abort listeners, frees the worker, and | ||
| * drains the queue. | ||
| */ | ||
| private settleRequest; | ||
| /** | ||
| * Route a `bridge-call` from the worker to the registered host handler, | ||
| * execute it, and send `bridge-response` back. All error paths here | ||
| * produce a response with `error` populated so the sandbox's | ||
| * `await __bridgeCall(...)` rejects cleanly rather than hanging. | ||
| */ | ||
| private handleBridgeCall; | ||
| /** | ||
| * Handle worker process crash | ||
@@ -64,2 +89,15 @@ */ | ||
| /** | ||
| * Abort an in-flight or queued request. | ||
| * | ||
| * If the request is still queued, removes it and resolves with a TIMEOUT | ||
| * result (no worker ever picked it up). If a worker is actively running | ||
| * it, sends an `abort` IPC message so the worker rejects its pending | ||
| * bridge calls cleanly, then resolves with a TIMEOUT result. The worker | ||
| * will still send its eventual `result` / `error` message when the | ||
| * sandbox unwinds; `handleWorkerMessage` drops it because | ||
| * `worker.currentRequest` is already cleared. | ||
| */ | ||
| private abortRequest; | ||
| private detachAbortListener; | ||
| /** | ||
| * Process queued requests by assigning them to available workers | ||
@@ -66,0 +104,0 @@ */ |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"WorkerPool.d.ts","sourceRoot":"","sources":["../src/WorkerPool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAQH,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,SAAS,CAAC;AAoBnE;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,GAAE,iBAAsB;IAO3C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC;;OAEG;YACW,YAAY;IAiD1B;;OAEG;IACH,OAAO,CAAC,mBAAmB;IA0B3B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IA8CzB;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAoDxE;;OAEG;IACH,OAAO,CAAC,YAAY;IAkCpB;;OAEG;IACH,QAAQ,IAAI;QACR,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACvB;IAYD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CAyClC"} | ||
| {"version":3,"file":"WorkerPool.d.ts","sourceRoot":"","sources":["../src/WorkerPool.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAQH,OAAO,EAEH,mBAAmB,EACnB,mBAAmB,EACtB,MAAM,SAAS,CAAC;AA2CjB;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAC9B,0DAA0D;IAC1D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,uEAAuE;IACvE,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,kEAAkE;IAClE,YAAY,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;GAEG;AACH,qBAAa,UAAU;IACnB,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,YAAY,CAAwB;IAC5C,OAAO,CAAC,aAAa,CAAK;IAC1B,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,eAAe,CAAS;IAChC,OAAO,CAAC,YAAY,CAAS;IAC7B,OAAO,CAAC,cAAc,CAAS;gBAEnB,OAAO,GAAE,iBAAsB;IAO3C;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC;IAQjC;;OAEG;YACW,YAAY;IAsE1B;;;;;;;;;;OAUG;IACH,OAAO,CAAC,mBAAmB;IAiC3B;;;;OAIG;IACH,OAAO,CAAC,aAAa;IAmBrB;;;;;OAKG;YACW,gBAAgB;IA8D9B;;OAEG;IACH,OAAO,CAAC,iBAAiB;IAgDzB;;OAEG;IACG,OAAO,CAAC,MAAM,EAAE,mBAAmB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAoExE;;;;;;;;;;OAUG;IACH,OAAO,CAAC,YAAY;IAyDpB,OAAO,CAAC,mBAAmB;IAO3B;;OAEG;IACH,OAAO,CAAC,YAAY;IAuCpB;;OAEG;IACH,QAAQ,IAAI;QACR,YAAY,EAAE,MAAM,CAAC;QACrB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,WAAW,EAAE,MAAM,CAAC;KACvB;IAYD;;OAEG;IACG,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC;CA0ClC"} |
+305
-22
@@ -13,2 +13,6 @@ /** | ||
| * - Health monitoring and circuit breaker pattern | ||
| * - Bidirectional bridge: sandbox code can call host-registered handlers | ||
| * via `__bridgeCall(name, args)`; the host routes the call, executes the | ||
| * handler, and sends the response back. See worker.ts for the sandbox-side | ||
| * wiring. | ||
| */ | ||
@@ -21,2 +25,6 @@ import { fork } from 'child_process'; | ||
| import { LogError, LogStatus } from '@memberjunction/core'; | ||
| /** Max bytes of stderr we keep per worker for crash diagnostics. 64KB is | ||
| * big enough for a full V8 fatal-error banner + stack trace, small | ||
| * enough that the host can't be starved by pathological output. */ | ||
| const STDERR_TAIL_BYTES = 64 * 1024; | ||
| /** | ||
@@ -50,4 +58,7 @@ * Manages a pool of worker processes for code execution | ||
| const workerPath = path.join(__dirname, 'worker.js'); // Use .js because worker.ts is compiled | ||
| // stdout 'inherit' so debug logs still reach MJAPI's console. | ||
| // stderr 'pipe' so we can capture a rolling tail for crash diagnostics | ||
| // AND still forward it to MJAPI's stderr — we listen below and echo. | ||
| const childProcess = fork(workerPath, [], { | ||
| stdio: ['ignore', 'inherit', 'inherit', 'ipc'] // Inherit stdout/stderr for debugging | ||
| stdio: ['ignore', 'inherit', 'pipe', 'ipc'] | ||
| }); | ||
@@ -60,4 +71,21 @@ const worker = { | ||
| crashCount: 0, | ||
| lastCrashTime: 0 | ||
| lastCrashTime: 0, | ||
| stderrTail: '' | ||
| }; | ||
| // Capture stderr for crash diagnostics + forward to MJAPI stderr so | ||
| // operators still see it live in logs during non-crash scenarios. | ||
| if (childProcess.stderr) { | ||
| childProcess.stderr.on('data', (chunk) => { | ||
| // Forward to parent stderr — preserves existing debug behavior. | ||
| // Convert to string first since Node's ArrayBuffer types differ | ||
| // between versions and writing the Buffer directly trips TS. | ||
| const str = chunk.toString('utf8'); | ||
| process.stderr.write(str); | ||
| // Append to rolling tail, truncating from the front. | ||
| worker.stderrTail += str; | ||
| if (worker.stderrTail.length > STDERR_TAIL_BYTES) { | ||
| worker.stderrTail = worker.stderrTail.slice(-STDERR_TAIL_BYTES); | ||
| } | ||
| }); | ||
| } | ||
| this.workers[id] = worker; | ||
@@ -91,3 +119,11 @@ // Wait for ready message | ||
| /** | ||
| * Handle messages from worker process | ||
| * Handle messages from worker process. | ||
| * | ||
| * Types: | ||
| * - `ready` — initial startup / post-restart signal, ignored here. | ||
| * - `result` / `error` — the terminal outcome of the current execute() call. | ||
| * - `bridge-call` — a host-service request from inside the sandbox; we | ||
| * look up the registered handler and send back `bridge-response`. | ||
| * Critically, `bridge-call` does NOT end the request; the worker is | ||
| * still executing until it sends `result`/`error`. | ||
| */ | ||
@@ -99,2 +135,7 @@ handleWorkerMessage(worker, message) { | ||
| } | ||
| if (message.type === 'bridge-call') { | ||
| // Dispatch to the registered handler for this execution. | ||
| void this.handleBridgeCall(worker, message); | ||
| return; | ||
| } | ||
| if (!worker.currentRequest) { | ||
@@ -104,11 +145,29 @@ LogError(new Error(`Worker ${worker.id} sent message but has no current request`)); | ||
| } | ||
| // If the worker included a requestId, make sure it matches — this | ||
| // catches stale messages from aborted executions. Legacy test | ||
| // harnesses (and very old workers) don't include a requestId; in | ||
| // that case fall through and treat the message as belonging to the | ||
| // current request. | ||
| if (message.requestId && message.requestId !== worker.currentRequest.requestId) { | ||
| return; | ||
| } | ||
| const request = worker.currentRequest; | ||
| if (message.type === 'result' || message.type === 'error') { | ||
| this.settleRequest(worker, request, message); | ||
| } | ||
| } | ||
| /** | ||
| * Finalize a request once the worker reports `result` or `error`. | ||
| * Clears timeouts, detaches abort listeners, frees the worker, and | ||
| * drains the queue. | ||
| */ | ||
| settleRequest(worker, request, message) { | ||
| clearTimeout(request.timeout); | ||
| this.detachAbortListener(request); | ||
| if (message.type === 'result') { | ||
| request.resolve(message.result); | ||
| } | ||
| else if (message.type === 'error') { | ||
| request.reject(new Error(message.error)); | ||
| else { | ||
| request.reject(new Error(message.error ?? 'Worker returned error')); | ||
| } | ||
| // Mark worker as available and process queue | ||
| worker.busy = false; | ||
@@ -119,2 +178,50 @@ worker.currentRequest = null; | ||
| /** | ||
| * Route a `bridge-call` from the worker to the registered host handler, | ||
| * execute it, and send `bridge-response` back. All error paths here | ||
| * produce a response with `error` populated so the sandbox's | ||
| * `await __bridgeCall(...)` rejects cleanly rather than hanging. | ||
| */ | ||
| async handleBridgeCall(worker, message) { | ||
| const sendResponse = (payload) => { | ||
| try { | ||
| worker.process.send({ | ||
| type: 'bridge-response', | ||
| requestId: message.requestId, | ||
| callId: message.callId, | ||
| ...payload | ||
| }); | ||
| } | ||
| catch (err) { | ||
| LogError(new Error(`Failed to send bridge-response to worker ${worker.id}: ${err instanceof Error ? err.message : String(err)}`)); | ||
| } | ||
| }; | ||
| if (!worker.currentRequest || worker.currentRequest.requestId !== message.requestId) { | ||
| sendResponse({ error: 'No active execution for this bridge call.' }); | ||
| return; | ||
| } | ||
| const handlers = worker.currentRequest.bridgeHandlers; | ||
| if (!handlers) { | ||
| sendResponse({ | ||
| error: `Runtime action tried to call '${message.functionName}' but no bridge ` + | ||
| 'handlers were registered for this execution.' | ||
| }); | ||
| return; | ||
| } | ||
| const handler = handlers[message.functionName]; | ||
| if (!handler) { | ||
| sendResponse({ | ||
| error: `Bridge handler '${message.functionName}' is not registered. ` + | ||
| `Available: ${Object.keys(handlers).join(', ') || '(none)'}.` | ||
| }); | ||
| return; | ||
| } | ||
| try { | ||
| const result = await handler(message.args); | ||
| sendResponse({ result }); | ||
| } | ||
| catch (err) { | ||
| sendResponse({ error: err instanceof Error ? err.message : String(err) }); | ||
| } | ||
| } | ||
| /** | ||
| * Handle worker process crash | ||
@@ -133,3 +240,5 @@ */ | ||
| worker.lastCrashTime = now; | ||
| const diagnostic = classifyWorkerCrash(code, signal, worker.stderrTail); | ||
| LogError(new Error(`Worker ${worker.id} crashed (code: ${code}, signal: ${signal}). ` + | ||
| `Reason: ${diagnostic.reason}. ` + | ||
| `Crash count: ${worker.crashCount}/${this.maxCrashesPerWorker}`)); | ||
@@ -139,3 +248,4 @@ // Reject current request if any | ||
| clearTimeout(worker.currentRequest.timeout); | ||
| worker.currentRequest.reject(new Error('Worker process crashed during code execution. This may indicate a severe error in the code.')); | ||
| this.detachAbortListener(worker.currentRequest); | ||
| worker.currentRequest.reject(new Error(diagnostic.userMessage)); | ||
| worker.currentRequest = null; | ||
@@ -182,21 +292,33 @@ } | ||
| const totalTimeout = ((params.timeoutSeconds || 30) + 5) * 1000; // Add 5s buffer for overhead | ||
| const timeout = setTimeout(() => { | ||
| // Remove from queue if still there | ||
| const queueIndex = this.requestQueue.findIndex(r => r.requestId === requestId); | ||
| if (queueIndex >= 0) { | ||
| this.requestQueue.splice(queueIndex, 1); | ||
| } | ||
| resolve({ | ||
| success: false, | ||
| error: 'Request timed out waiting for worker availability', | ||
| errorType: 'TIMEOUT' | ||
| }); | ||
| }, totalTimeout); | ||
| const request = { | ||
| requestId, | ||
| params, | ||
| bridgeHandlers: params.bridgeHandlers, | ||
| resolve, | ||
| reject, | ||
| timeout | ||
| // placeholder — reassigned below once we have the request ref | ||
| timeout: null | ||
| }; | ||
| request.timeout = setTimeout(() => { | ||
| // If the worker is still processing, tell it to abort its | ||
| // in-flight bridge calls first — the sandbox's await will | ||
| // reject cleanly. Then reject on our side. | ||
| this.abortRequest(request, 'Request timed out waiting for worker availability'); | ||
| }, totalTimeout); | ||
| // Mirror any caller-supplied AbortSignal into the same abort path. | ||
| if (params.abortSignal) { | ||
| const listener = () => { | ||
| this.abortRequest(request, typeof params.abortSignal.reason === 'string' | ||
| ? params.abortSignal.reason | ||
| : 'Caller aborted'); | ||
| }; | ||
| request.abortListener = listener; | ||
| if (params.abortSignal.aborted) { | ||
| // Fire immediately if caller aborted before we even queued. | ||
| setImmediate(listener); | ||
| } | ||
| else { | ||
| params.abortSignal.addEventListener('abort', listener, { once: true }); | ||
| } | ||
| } | ||
| // Add to queue | ||
@@ -209,2 +331,64 @@ this.requestQueue.push(request); | ||
| /** | ||
| * Abort an in-flight or queued request. | ||
| * | ||
| * If the request is still queued, removes it and resolves with a TIMEOUT | ||
| * result (no worker ever picked it up). If a worker is actively running | ||
| * it, sends an `abort` IPC message so the worker rejects its pending | ||
| * bridge calls cleanly, then resolves with a TIMEOUT result. The worker | ||
| * will still send its eventual `result` / `error` message when the | ||
| * sandbox unwinds; `handleWorkerMessage` drops it because | ||
| * `worker.currentRequest` is already cleared. | ||
| */ | ||
| abortRequest(request, reason) { | ||
| if (request.aborted) | ||
| return; | ||
| request.aborted = true; | ||
| // Remove from queue if still waiting. | ||
| const queueIndex = this.requestQueue.findIndex(r => r.requestId === request.requestId); | ||
| if (queueIndex >= 0) { | ||
| this.requestQueue.splice(queueIndex, 1); | ||
| } | ||
| // If a worker is actively running this request, tell it to abort | ||
| // pending bridge calls. We do NOT kill the worker — the sandbox | ||
| // script will unwind naturally once its awaits reject. | ||
| const activeWorker = this.workers.find(w => w && w.currentRequest && w.currentRequest.requestId === request.requestId); | ||
| if (activeWorker && activeWorker.process.connected) { | ||
| try { | ||
| activeWorker.process.send({ | ||
| type: 'abort', | ||
| requestId: request.requestId, | ||
| reason | ||
| }); | ||
| } | ||
| catch (err) { | ||
| LogError(new Error(`Failed to send abort to worker ${activeWorker.id}: ${err instanceof Error ? err.message : String(err)}`)); | ||
| } | ||
| // Decouple the request from the worker so the eventual | ||
| // result/error message is ignored by handleWorkerMessage. Leave | ||
| // worker.busy=true so we don't queue fresh work onto a worker | ||
| // that's still winding down; it'll flip back when the stale | ||
| // result arrives (and the `requestId !== worker.currentRequest` | ||
| // guard in handleWorkerMessage catches it). | ||
| activeWorker.currentRequest = null; | ||
| // Allow the worker to accept fresh work as soon as it's ready; | ||
| // the pool will swing back around on the next settle from the | ||
| // stale message because we won't match the requestId. | ||
| activeWorker.busy = false; | ||
| } | ||
| clearTimeout(request.timeout); | ||
| this.detachAbortListener(request); | ||
| request.resolve({ | ||
| success: false, | ||
| error: reason, | ||
| errorType: 'TIMEOUT' | ||
| }); | ||
| this.processQueue(); | ||
| } | ||
| detachAbortListener(request) { | ||
| if (request.params.abortSignal && request.abortListener) { | ||
| request.params.abortSignal.removeEventListener('abort', request.abortListener); | ||
| request.abortListener = undefined; | ||
| } | ||
| } | ||
| /** | ||
| * Process queued requests by assigning them to available workers | ||
@@ -229,7 +413,11 @@ */ | ||
| worker.currentRequest = request; | ||
| // Send execution request to worker | ||
| // Send execution request to worker — strip non-serializable fields | ||
| // (`bridgeHandlers` are functions, `abortSignal` is a live object). | ||
| const ipcParams = { ...request.params }; | ||
| delete ipcParams.bridgeHandlers; | ||
| delete ipcParams.abortSignal; | ||
| worker.process.send({ | ||
| type: 'execute', | ||
| requestId: request.requestId, | ||
| params: request.params | ||
| params: ipcParams | ||
| }); | ||
@@ -263,2 +451,3 @@ // Try to process more if queue has items | ||
| clearTimeout(request.timeout); | ||
| this.detachAbortListener(request); | ||
| request.resolve({ | ||
@@ -295,2 +484,96 @@ success: false, | ||
| } | ||
| /** | ||
| * Classify an unexpected worker-process exit into an actionable error. | ||
| * | ||
| * "Worker process crashed" by itself is useless to a downstream AI agent | ||
| * trying to fix its Runtime action code — it doesn't know whether the | ||
| * problem is a memory leak, a bad native call, or a runaway loop. This | ||
| * helper inspects the Node exit `code`, kill `signal`, and the tail of | ||
| * stderr the worker emitted before dying, then returns: | ||
| * | ||
| * - `reason`: short label for logs | ||
| * - `userMessage`: the human-readable message surfaced all the way to | ||
| * the action's `message` field. Includes a "what to | ||
| * do about it" hint so agents can self-correct. | ||
| */ | ||
| function classifyWorkerCrash(code, signal, stderrTail) { | ||
| const stderr = (stderrTail || '').trim(); | ||
| const stderrForUser = stderr | ||
| ? `\n\nWorker stderr (last ${STDERR_TAIL_BYTES / 1024}KB):\n${stderr.length > 4000 ? stderr.slice(-4000) : stderr}` | ||
| : ''; | ||
| // 1) V8 Node-heap OOM — usually `code=134` + stderr with "JavaScript | ||
| // heap out of memory" banner. | ||
| if (/heap out of memory|Allocation failed/i.test(stderr) || code === 134) { | ||
| return { | ||
| reason: 'NODE_HEAP_OOM', | ||
| userMessage: "Sandbox worker ran out of Node.js heap memory. The most common cause is a RunView / RunViews call that returned too many rows (e.g. `MaxRows: 0` against a high-volume table like `MJ: AI Prompt Runs`). Fix the Runtime action code to (a) cap `MaxRows` to a reasonable number, (b) add a time-window filter, or (c) page through results instead of loading everything at once." + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 2) isolated-vm memory-limit OOM propagated from the v8 isolate via | ||
| // the standard "Array buffer allocation failed" / "out of memory" | ||
| // patterns. Separate from node-heap OOM because the fix is the | ||
| // same but the surface is different. | ||
| if (/out of memory|RangeError.*memory/i.test(stderr)) { | ||
| return { | ||
| reason: 'ISOLATE_OOM', | ||
| userMessage: 'Sandbox isolated-vm ran out of memory (128MB default). Reduce the data volume the Runtime action loads — cap `MaxRows`, narrow the ExtraFilter, or process in batches. Avoid pulling whole rows when you only need a few columns; project with `Fields` where supported.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 3) Linux OOM killer — kernel killed the process because system | ||
| // memory was exhausted. code=null + signal='SIGKILL' is the | ||
| // canonical signature; stderr is often empty (the kernel doesn't | ||
| // let the process write anything before the kill). | ||
| if (signal === 'SIGKILL') { | ||
| return { | ||
| reason: 'KERNEL_SIGKILL', | ||
| userMessage: 'Sandbox worker was killed by the OS (SIGKILL). The most likely cause is the Linux OOM killer reclaiming memory under pressure — your Runtime action or the bridge result was large enough to put the host under memory stress. Reduce the data volume: cap `MaxRows`, narrow filters, or page through results.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 4) Native crash — segfault, bus error, abort. ivm / v8 bugs, or | ||
| // passing something ExternalCopy cannot serialize. | ||
| if (signal === 'SIGSEGV' || signal === 'SIGBUS') { | ||
| return { | ||
| reason: 'NATIVE_CRASH', | ||
| userMessage: `Sandbox worker crashed with native signal ${signal} — usually an isolated-vm / V8 fault, not something your code can fix directly. Common triggers include passing very deeply-nested objects or circular references through the utilities bridge. Simplify the values you return from RunView / actions before using them.` + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| if (signal === 'SIGABRT') { | ||
| return { | ||
| reason: 'PROCESS_ABORT', | ||
| userMessage: 'Sandbox worker aborted (SIGABRT). Usually triggered by an unrecoverable v8 assertion or a failed native check — often the same class of issue as a segfault. If this is reproducible, reduce the size and complexity of data your code allocates.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 5) Maximum call stack — v8 throws a catchable RangeError normally, | ||
| // but in deep-native contexts (ivm bridge marshaling, sync | ||
| // callbacks) it can escape and kill the process. Signature is | ||
| // usually stderr mentioning "Maximum call stack size exceeded" | ||
| // with code=1. | ||
| if (/Maximum call stack|stack size exceeded/i.test(stderr)) { | ||
| return { | ||
| reason: 'STACK_OVERFLOW', | ||
| userMessage: 'Sandbox worker hit a stack overflow. Likely a recursive function with no base case, or a call pattern that recurses indirectly through the utilities bridge. Review loops and recursion in your Runtime action code.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 6) Uncaught JS exception — code=1 with stderr containing a stack. | ||
| if (code === 1 && stderr.length > 0) { | ||
| return { | ||
| reason: 'UNCAUGHT_EXCEPTION', | ||
| userMessage: 'Sandbox worker crashed on an uncaught exception before the isolate could wrap it. Check the stderr tail below for the offending JS trace.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 7) Unknown / fallback — still include exit metadata and stderr so | ||
| // the agent has SOMETHING to act on. | ||
| return { | ||
| reason: 'UNKNOWN', | ||
| userMessage: `Sandbox worker process exited unexpectedly (code=${code}, signal=${signal}). The Runtime action code may be triggering a low-level issue in the sandbox. Simplify the code and retry.` + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| //# sourceMappingURL=WorkerPool.js.map |
@@ -1,1 +0,1 @@ | ||
| {"version":3,"file":"WorkerPool.js","sourceRoot":"","sources":["../src/WorkerPool.ts"],"names":[],"mappings":"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"} | ||
| {"version":3,"file":"WorkerPool.js","sourceRoot":"","sources":["../src/WorkerPool.ts"],"names":[],"mappings":"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"} |
+5
-4
| { | ||
| "name": "@memberjunction/code-execution", | ||
| "type": "module", | ||
| "version": "5.29.0", | ||
| "version": "5.30.0", | ||
| "description": "Sandboxed code execution service for MemberJunction actions and agents", | ||
@@ -9,3 +9,4 @@ "main": "dist/index.js", | ||
| "scripts": { | ||
| "build": "tsc && tsc-alias -f", | ||
| "build": "tsc && tsc-alias -f && npm run copy:bundled-libs", | ||
| "copy:bundled-libs": "node -e \"const fs=require('fs'),p=require('path');const src=p.join('src','libraries','bundled-libs');const dst=p.join('dist','libraries','bundled-libs');fs.mkdirSync(dst,{recursive:true});for(const f of fs.readdirSync(src))fs.copyFileSync(p.join(src,f),p.join(dst,f));\"", | ||
| "watch": "tsc --watch", | ||
@@ -16,4 +17,4 @@ "test": "vitest run", | ||
| "dependencies": { | ||
| "@memberjunction/core": "5.29.0", | ||
| "@memberjunction/global": "5.29.0", | ||
| "@memberjunction/core": "5.30.0", | ||
| "@memberjunction/global": "5.30.0", | ||
| "isolated-vm": "^6.0.2", | ||
@@ -20,0 +21,0 @@ "lodash": "^4.17.23", |
@@ -121,18 +121,14 @@ import { describe, it, expect, vi, beforeEach } from 'vitest'; | ||
| it('should return lodash source with expected functions', () => { | ||
| it('should return bundled lodash wrapped in module-shim IIFE', () => { | ||
| // NOTE: fs.readFileSync is mocked at the top of this test file, so the | ||
| // "source" we get here is our IIFE wrapper around the stub content — | ||
| // NOT the real ~73KB lodash bundle. We assert the shape of the wrapper | ||
| // (proves we switched away from the old hand-coded subset). End-to-end | ||
| // behavior of real lodash is exercised by Runtime action integration | ||
| // tests, not here. | ||
| const source = getLibrarySource('lodash')!; | ||
| expect(source).toContain('chunk'); | ||
| expect(source).toContain('compact'); | ||
| expect(source).toContain('flatten'); | ||
| expect(source).toContain('uniq'); | ||
| expect(source).toContain('groupBy'); | ||
| expect(source).toContain('keyBy'); | ||
| expect(source).toContain('sortBy'); | ||
| expect(source).toContain('pick'); | ||
| expect(source).toContain('omit'); | ||
| expect(source).toContain('get'); | ||
| expect(source).toContain('set'); | ||
| expect(source).toContain('sum'); | ||
| expect(source).toContain('mean'); | ||
| expect(source).toContain('cloneDeep'); | ||
| expect(source).toContain('const module = { exports: {} }'); | ||
| expect(source).toContain('return module.exports'); | ||
| // Old subset signature absent — we no longer wire up getLodashSource(). | ||
| expect(source).not.toContain('chunk: function(array'); | ||
| }); | ||
@@ -177,5 +173,5 @@ | ||
| const lodashSource = getLibrarySource('lodash')!; | ||
| // Should be wrapped in an IIFE that returns the object | ||
| // Lodash is now the bundled UMD wrapped in our module-shim IIFE. | ||
| expect(lodashSource).toMatch(/^\(function\(\)/); | ||
| expect(lodashSource).toContain('return _'); | ||
| expect(lodashSource).toContain('return module.exports'); | ||
@@ -182,0 +178,0 @@ const dateFnsSource = getLibrarySource('date-fns')!; |
@@ -128,3 +128,5 @@ import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'; | ||
| expect.objectContaining({ | ||
| stdio: ['ignore', 'inherit', 'inherit', 'ipc'] | ||
| // stderr is 'pipe' so the pool can capture a rolling tail for | ||
| // crash diagnostics while still forwarding it to MJAPI stderr. | ||
| stdio: ['ignore', 'inherit', 'pipe', 'ipc'] | ||
| }) | ||
@@ -346,4 +348,6 @@ ); | ||
| // The promise should reject because the worker crashed | ||
| await expect(executePromise).rejects.toThrow('Worker process crashed'); | ||
| // The promise should reject with the enriched diagnostic — the | ||
| // classifier recognizes SIGSEGV as a native crash and surfaces | ||
| // actionable guidance instead of the old opaque message. | ||
| await expect(executePromise).rejects.toThrow(/SIGSEGV/); | ||
| }); | ||
@@ -350,0 +354,0 @@ |
+129
-7
@@ -60,3 +60,3 @@ /** | ||
| case 'lodash': | ||
| return getLodashSource(); | ||
| return getBundledLodashSource(); | ||
| case 'date-fns': | ||
@@ -108,2 +108,52 @@ return getDateFnsSource(); | ||
| // Min/Max — commonly reached for in numeric-summary style Runtime | ||
| // actions. Return undefined for empty arrays (matches real lodash). | ||
| min: function(array) { | ||
| if (!array || array.length === 0) return undefined; | ||
| let m = array[0]; | ||
| for (let i = 1; i < array.length; i++) { | ||
| if (array[i] < m) m = array[i]; | ||
| } | ||
| return m; | ||
| }, | ||
| max: function(array) { | ||
| if (!array || array.length === 0) return undefined; | ||
| let m = array[0]; | ||
| for (let i = 1; i < array.length; i++) { | ||
| if (array[i] > m) m = array[i]; | ||
| } | ||
| return m; | ||
| }, | ||
| minBy: function(array, iteratee) { | ||
| if (!array || array.length === 0) return undefined; | ||
| const fn = typeof iteratee === 'function' ? iteratee : (x) => x[iteratee]; | ||
| let best = array[0]; | ||
| let bestScore = fn(best); | ||
| for (let i = 1; i < array.length; i++) { | ||
| const score = fn(array[i]); | ||
| if (score < bestScore) { | ||
| best = array[i]; | ||
| bestScore = score; | ||
| } | ||
| } | ||
| return best; | ||
| }, | ||
| maxBy: function(array, iteratee) { | ||
| if (!array || array.length === 0) return undefined; | ||
| const fn = typeof iteratee === 'function' ? iteratee : (x) => x[iteratee]; | ||
| let best = array[0]; | ||
| let bestScore = fn(best); | ||
| for (let i = 1; i < array.length; i++) { | ||
| const score = fn(array[i]); | ||
| if (score > bestScore) { | ||
| best = array[i]; | ||
| bestScore = score; | ||
| } | ||
| } | ||
| return best; | ||
| }, | ||
| difference: function(array, ...values) { | ||
@@ -160,2 +210,22 @@ const others = new Set(values.flat()); | ||
| // orderBy is like sortBy but supports per-iteratee asc/desc direction. | ||
| // Signature: orderBy(collection, iteratees, orders) where iteratees | ||
| // and orders are parallel arrays (or both scalars). Direction strings | ||
| // default to 'asc' when not supplied. | ||
| orderBy: function(collection, iteratees, orders) { | ||
| const itList = Array.isArray(iteratees) ? iteratees : [iteratees]; | ||
| const orderList = Array.isArray(orders) ? orders : (orders ? [orders] : []); | ||
| const resolveFn = (it) => typeof it === 'function' ? it : x => x[it]; | ||
| return [...collection].sort((a, b) => { | ||
| for (let i = 0; i < itList.length; i++) { | ||
| const fn = resolveFn(itList[i]); | ||
| const dir = (orderList[i] ?? 'asc') === 'desc' ? -1 : 1; | ||
| const aVal = fn(a); | ||
| const bVal = fn(b); | ||
| if (aVal !== bVal) return aVal < bVal ? -1 * dir : 1 * dir; | ||
| } | ||
| return 0; | ||
| }); | ||
| }, | ||
| // Object functions | ||
@@ -248,11 +318,30 @@ pick: function(object, ...paths) { | ||
| const d = new Date(date); | ||
| const monthsLong = ['January','February','March','April','May','June','July','August','September','October','November','December']; | ||
| const monthsShort = ['Jan','Feb','Mar','Apr','May','Jun','Jul','Aug','Sep','Oct','Nov','Dec']; | ||
| const daysLong = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday']; | ||
| const daysShort = ['Sun','Mon','Tue','Wed','Thu','Fri','Sat']; | ||
| const month = d.getMonth(); | ||
| const dow = d.getDay(); | ||
| // Supported tokens are listed longest-first in the regex so that | ||
| // e.g. 'MMMM' is matched before 'MMM' / 'MM' / 'M'. | ||
| // NOTE: this shim has no literal-escape support, so we deliberately | ||
| // omit ambiguous single-letter tokens from real date-fns (a/h/H/m/s | ||
| // bare) that collide with common English letters in format strings. | ||
| // Users who need time formatting can use the padded HH:mm:ss forms. | ||
| const tokens = { | ||
| 'yyyy': d.getFullYear(), | ||
| 'MM': String(d.getMonth() + 1).padStart(2, '0'), | ||
| 'dd': String(d.getDate()).padStart(2, '0'), | ||
| 'HH': String(d.getHours()).padStart(2, '0'), | ||
| 'mm': String(d.getMinutes()).padStart(2, '0'), | ||
| 'ss': String(d.getSeconds()).padStart(2, '0') | ||
| 'yy': String(d.getFullYear()).slice(-2), | ||
| 'MMMM': monthsLong[month], | ||
| 'MMM': monthsShort[month], | ||
| 'MM': String(month + 1).padStart(2, '0'), | ||
| 'M': String(month + 1), | ||
| 'dd': String(d.getDate()).padStart(2, '0'), | ||
| 'd': String(d.getDate()), | ||
| 'EEEE': daysLong[dow], | ||
| 'EEE': daysShort[dow], | ||
| 'HH': String(d.getHours()).padStart(2, '0'), | ||
| 'mm': String(d.getMinutes()).padStart(2, '0'), | ||
| 'ss': String(d.getSeconds()).padStart(2, '0') | ||
| }; | ||
| return formatStr.replace(/yyyy|MM|dd|HH|mm|ss/g, match => tokens[match]); | ||
| return formatStr.replace(/yyyy|yy|MMMM|MMM|MM|M|dd|d|EEEE|EEE|HH|mm|ss/g, match => tokens[match]); | ||
| }, | ||
@@ -391,2 +480,35 @@ | ||
| /** | ||
| * Load the bundled full lodash UMD build (~73KB). We used to hand-roll | ||
| * a tiny subset here, but the subset was missing the most foundational | ||
| * helpers (`map`, `filter`, `forEach`, `reduce`, `find`, `some`, `every`, | ||
| * `keys`, `values`, `each`, `includes`) — so any Runtime action that | ||
| * called `_.map(...)` crashed with "_.map is not a function". The inline | ||
| * subset stays in this file below as `getLodashSource()` for reference / | ||
| * fallback only; it is no longer wired up. | ||
| * | ||
| * Lodash's UMD epilogue looks for `module.exports` before falling back to | ||
| * a global. We provide a shim `module` and return `module.exports` so the | ||
| * sandbox gets the full lodash object exactly as `require('lodash')` would | ||
| * hand back in node. | ||
| */ | ||
| function getBundledLodashSource(): string { | ||
| const libPath = path.join(__dirname, 'bundled-libs', 'lodash.js'); | ||
| const source = fs.readFileSync(libPath, 'utf8'); | ||
| // Lodash's UMD defines `ue` (the module-exports branch it actually writes | ||
| // to) as `ee && typeof module == "object" && ... && module`, where | ||
| // `ee = typeof exports == "object" && exports && ...`. So it short-circuits | ||
| // to the global branch (`re._=be`, synthesized root) if `exports` is | ||
| // undefined in scope — even when `module` is present. Both shims are | ||
| // required, and they must share storage (`const exports = module.exports`) | ||
| // so whichever path lodash picks, `module.exports` ends up holding the | ||
| // real lodash object. | ||
| return `(function() { | ||
| const module = { exports: {} }; | ||
| const exports = module.exports; | ||
| ${source} | ||
| return module.exports; | ||
| })()`; | ||
| } | ||
| /** | ||
| * mathjs implementation | ||
@@ -393,0 +515,0 @@ * Provides comprehensive mathematics and statistics functions |
+129
-5
| /** | ||
| * Parameters for code execution | ||
| * A host-side bridge handler function. Registered per-execution via | ||
| * `CodeExecutionService.execute({ bridgeHandlers, ... })` and invoked when | ||
| * sandboxed code calls `__bridgeCall(name, args)`. The handler receives the | ||
| * args (already JSON-round-tripped, so primitives + plain objects only) and | ||
| * returns any JSON-serializable value; it may also throw, in which case the | ||
| * sandbox's `await __bridgeCall(...)` rejects with the error's message. | ||
| * | ||
| * The handler runs in the **host process**, not the sandbox. It has full | ||
| * access to MJ's services, DB, and filesystem — so higher layers | ||
| * (RuntimeActionBridge in @memberjunction/action-runtime) are responsible | ||
| * for enforcing per-Runtime-action permission scopes before each call | ||
| * reaches this point. | ||
| */ | ||
| export type BridgeHandler = (args: unknown) => Promise<unknown> | unknown; | ||
| /** | ||
| * A name → handler map passed to `CodeExecutionService.execute()` for a | ||
| * single invocation. The sandbox can only call names that are present in | ||
| * this map; unknown names reject with a "handler not registered" error. | ||
| */ | ||
| export type BridgeHandlerMap = Record<string, BridgeHandler>; | ||
| /** | ||
| * Parameters for code execution. | ||
| * | ||
| * `bridgeHandlers` is the ONLY field that is NOT sent over IPC — functions | ||
| * can't be serialized across the process boundary. The host keeps the map | ||
| * alive for the duration of this execution and uses it to service | ||
| * `bridge-call` messages coming back from the worker. | ||
| */ | ||
| export interface CodeExecutionParams { | ||
@@ -9,3 +37,3 @@ /** | ||
| code: string; | ||
| /** | ||
@@ -15,3 +43,3 @@ * Programming language (currently only 'javascript' supported) | ||
| language: 'javascript'; | ||
| /** | ||
@@ -21,3 +49,3 @@ * Input data available to the code via 'input' variable | ||
| inputData?: any; | ||
| /** | ||
@@ -27,3 +55,3 @@ * Maximum execution time in seconds (default: 30) | ||
| timeoutSeconds?: number; | ||
| /** | ||
@@ -34,2 +62,25 @@ * Memory limit in MB (default: 128) | ||
| memoryLimitMB?: number; | ||
| /** | ||
| * Optional map of bridge handlers to expose to sandbox code as | ||
| * `__bridgeCall(name, args)`. When present, the sandbox can make async | ||
| * round-trips to the host for each registered name. Host-side only — not | ||
| * sent over IPC. See {@link BridgeHandler}. | ||
| */ | ||
| bridgeHandlers?: BridgeHandlerMap; | ||
| /** | ||
| * Optional cap on the number of bridge calls a single execution may | ||
| * make (default: 100). The (N+1)th call rejects with a BRIDGE_LIMIT | ||
| * error. Prevents runaway loops inside sandbox code. | ||
| */ | ||
| maxBridgeCalls?: number; | ||
| /** | ||
| * Optional AbortSignal for upstream cancellation. When aborted, any | ||
| * in-flight `await __bridgeCall(...)` inside the sandbox rejects and | ||
| * the sandbox script is left to wind down (we don't dispose the isolate | ||
| * mid-execution — see design notes in the plan). | ||
| */ | ||
| abortSignal?: AbortSignal; | ||
| } | ||
@@ -93,2 +144,75 @@ | ||
| /** | ||
| * IPC message types for worker/host communication. | ||
| * | ||
| * Existing: `execute`, `result`, `error`, `ready`. | ||
| * NEW for the bridge: `bridge-call` (worker→host), `bridge-response` | ||
| * (host→worker), `abort` (host→worker). | ||
| */ | ||
| /** Host → Worker: run this code. */ | ||
| export interface ExecuteIpcMessage { | ||
| type: 'execute'; | ||
| requestId: string; | ||
| /** The IPC-safe subset of `CodeExecutionParams` (no `bridgeHandlers`). */ | ||
| params: Omit<CodeExecutionParams, 'bridgeHandlers' | 'abortSignal'>; | ||
| } | ||
| /** Worker → Host: execution finished cleanly. */ | ||
| export interface ResultIpcMessage { | ||
| type: 'result'; | ||
| requestId: string; | ||
| result: CodeExecutionResult; | ||
| } | ||
| /** Worker → Host: execution threw before the handler could assemble a result. */ | ||
| export interface ErrorIpcMessage { | ||
| type: 'error'; | ||
| requestId: string; | ||
| error: string; | ||
| } | ||
| /** Worker → Host: sandbox code wants the host to service a bridge call. */ | ||
| export interface BridgeCallIpcMessage { | ||
| type: 'bridge-call'; | ||
| requestId: string; | ||
| /** Unique per call so Promise.all-style concurrency is correlated correctly. */ | ||
| callId: string; | ||
| /** Name registered in the execution's `bridgeHandlers` map. */ | ||
| functionName: string; | ||
| /** JSON-round-tripped args. */ | ||
| args: unknown; | ||
| } | ||
| /** Host → Worker: result of a bridge call. One of `result` | `error` is populated. */ | ||
| export interface BridgeResponseIpcMessage { | ||
| type: 'bridge-response'; | ||
| requestId: string; | ||
| callId: string; | ||
| result?: unknown; | ||
| error?: string; | ||
| } | ||
| /** Host → Worker: abort in-flight bridge calls for this requestId. */ | ||
| export interface AbortIpcMessage { | ||
| type: 'abort'; | ||
| requestId: string; | ||
| reason: string; | ||
| } | ||
| /** Worker → Host: initial ready signal (no requestId). */ | ||
| export interface ReadyIpcMessage { | ||
| type: 'ready'; | ||
| } | ||
| /** Host → Worker message types. */ | ||
| export type HostToWorkerMessage = ExecuteIpcMessage | BridgeResponseIpcMessage | AbortIpcMessage; | ||
| /** Worker → Host message types. */ | ||
| export type WorkerToHostMessage = | ||
| | ResultIpcMessage | ||
| | ErrorIpcMessage | ||
| | BridgeCallIpcMessage | ||
| | ReadyIpcMessage; | ||
| /** | ||
| * Sandbox context provided to executed code | ||
@@ -95,0 +219,0 @@ */ |
+255
-8
@@ -20,2 +20,24 @@ /** | ||
| * 4. Resource limits - prevents DoS via timeout/memory exhaustion | ||
| * | ||
| * ============================================================================== | ||
| * Bidirectional Bridge (Phase 1d) | ||
| * ============================================================================== | ||
| * Sandbox code can call `__bridgeCall(name, args)` to invoke a host-registered | ||
| * handler and await its result. The flow: | ||
| * | ||
| * 1. Sandbox calls `__bridgeCall('rv.RunView', { EntityName: '...' })` | ||
| * 2. That dispatches to the host-side `_bridgeCall` ivm.Reference via | ||
| * `applyAsync`. The reference's JS body sends a `bridge-call` IPC | ||
| * message to the parent process and waits for the matching | ||
| * `bridge-response`. | ||
| * 3. Parent process routes the request to the registered handler, | ||
| * executes it, and sends `bridge-response` back. | ||
| * 4. The worker's `process.on('message')` handler resolves the pending | ||
| * promise for that callId. | ||
| * 5. `applyAsync` resolves inside the isolate with the JSON-stringified | ||
| * result. Sandbox code parses it and sees a plain object. | ||
| * | ||
| * The bridge is scoped per-requestId: each `execute` message opens a new | ||
| * pending-calls map that is either drained on completion or rejected en | ||
| * masse on `abort`. Nothing persists between executions. | ||
| */ | ||
@@ -33,3 +55,3 @@ | ||
| type: 'execute'; | ||
| params: CodeExecutionParams; | ||
| params: Omit<CodeExecutionParams, 'bridgeHandlers' | 'abortSignal'>; | ||
| requestId: string; | ||
@@ -54,5 +76,50 @@ } | ||
| type WorkerMessage = ExecuteMessage; | ||
| type ParentMessage = ResultMessage | ErrorMessage | ReadyMessage; | ||
| interface BridgeCallMessage { | ||
| type: 'bridge-call'; | ||
| requestId: string; | ||
| callId: string; | ||
| functionName: string; | ||
| args: unknown; | ||
| } | ||
| interface BridgeResponseMessage { | ||
| type: 'bridge-response'; | ||
| requestId: string; | ||
| callId: string; | ||
| result?: unknown; | ||
| error?: string; | ||
| } | ||
| interface AbortMessage { | ||
| type: 'abort'; | ||
| requestId: string; | ||
| reason: string; | ||
| } | ||
| type ParentInboundMessage = ExecuteMessage | BridgeResponseMessage | AbortMessage; | ||
| type ParentOutboundMessage = ResultMessage | ErrorMessage | ReadyMessage | BridgeCallMessage; | ||
| // --------------------------------------------------------------------------- | ||
| // Bridge state — one entry per in-flight execution. | ||
| // | ||
| // The key is the host-assigned `requestId` for the execute() call. Each entry | ||
| // owns a Map of in-flight bridge calls (callId → { resolve, reject }) so | ||
| // multiple concurrent `await __bridgeCall(...)` invocations from inside one | ||
| // execution (e.g. Promise.all) are correlated by callId. The `callCount` + | ||
| // `maxCalls` fields enforce the per-execution bridge-call cap. | ||
| // --------------------------------------------------------------------------- | ||
| interface BridgeExecutionState { | ||
| pending: Map<string, { resolve: (value: unknown) => void; reject: (err: Error) => void }>; | ||
| callCount: number; | ||
| maxCalls: number; | ||
| aborted: boolean; | ||
| abortReason?: string; | ||
| } | ||
| const bridgeStates = new Map<string, BridgeExecutionState>(); | ||
| function getBridgeState(requestId: string): BridgeExecutionState | undefined { | ||
| return bridgeStates.get(requestId); | ||
| } | ||
| /** | ||
@@ -66,7 +133,21 @@ * Execute JavaScript code in an isolated-vm isolate | ||
| */ | ||
| async function executeInIsolate(params: CodeExecutionParams): Promise<CodeExecutionResult> { | ||
| async function executeInIsolate( | ||
| params: ExecuteMessage['params'], | ||
| requestId: string | ||
| ): Promise<CodeExecutionResult> { | ||
| const startTime = Date.now(); | ||
| const timeoutMs = (params.timeoutSeconds || 30) * 1000; | ||
| const memoryLimitMB = params.memoryLimitMB || 128; | ||
| const maxBridgeCalls = params.maxBridgeCalls ?? 100; | ||
| // Open the bridge state for this execution before the isolate starts so | ||
| // any bridge-response / abort messages that race in are delivered to a | ||
| // real slot rather than dropped. | ||
| bridgeStates.set(requestId, { | ||
| pending: new Map(), | ||
| callCount: 0, | ||
| maxCalls: maxBridgeCalls, | ||
| aborted: false | ||
| }); | ||
| let isolate: ivm.Isolate | null = null; | ||
@@ -138,2 +219,104 @@ let context: ivm.Context | null = null; | ||
| // =================================================================== | ||
| // BRIDGE SETUP | ||
| // | ||
| // Inject a single host-backed async function as `_bridgeCall`. The | ||
| // reference's body sends an IPC message to the parent and awaits the | ||
| // matching `bridge-response` via the `bridgeStates` map above. The | ||
| // isolate wraps it as `globalThis.__bridgeCall(name, args)` which | ||
| // returns a Promise resolving to the parsed JSON result (or rejects | ||
| // with the host error message). | ||
| // | ||
| // Args + result cross the boundary as JSON strings so complex object | ||
| // shapes, Dates, etc. behave consistently and no ivm.Reference ever | ||
| // leaks back into user code. | ||
| // =================================================================== | ||
| // The bridge callback NEVER throws — errors are encoded as a JSON | ||
| // envelope `{ __bridgeError: '...' }` and re-thrown inside the | ||
| // isolate. This avoids firing unhandled-rejection in the worker | ||
| // process when the caller exceeds maxBridgeCalls or the host handler | ||
| // throws; isolated-vm would propagate the rejection across the | ||
| // boundary, but Node 24+'s strict unhandled-rejection semantics can | ||
| // fire a process.unhandledRejection handler before isolated-vm's | ||
| // delivery settles, killing the worker. | ||
| const bridgeCall = new ivm.Reference( | ||
| async (callId: string, functionName: string, argsJson: string): Promise<string> => { | ||
| try { | ||
| const state = getBridgeState(requestId); | ||
| if (!state) { | ||
| throw new Error('Bridge state missing for this execution'); | ||
| } | ||
| if (state.aborted) { | ||
| throw new Error(state.abortReason ?? 'Execution aborted'); | ||
| } | ||
| if (state.callCount >= state.maxCalls) { | ||
| throw new Error( | ||
| `Bridge call limit exceeded (maxBridgeCalls=${state.maxCalls}). ` + | ||
| 'Runtime action tried to make too many host round-trips.' | ||
| ); | ||
| } | ||
| state.callCount++; | ||
| let args: unknown; | ||
| try { | ||
| args = JSON.parse(argsJson); | ||
| } catch { | ||
| throw new Error('Bridge call args must be JSON-serializable'); | ||
| } | ||
| const resultPromise = new Promise<unknown>((resolve, reject) => { | ||
| state.pending.set(callId, { resolve, reject }); | ||
| }); | ||
| const callMessage: BridgeCallMessage = { | ||
| type: 'bridge-call', | ||
| requestId, | ||
| callId, | ||
| functionName, | ||
| args | ||
| }; | ||
| // SECURITY: IPC is JSON-serialized by Node's child-process | ||
| // plumbing; no references leak. | ||
| process.send!(callMessage); | ||
| const hostResult = await resultPromise; | ||
| // Return as a JSON string so the isolate receives a primitive | ||
| // it can safely JSON.parse — no copyInto / ExternalCopy needed. | ||
| return JSON.stringify({ ok: hostResult === undefined ? null : hostResult }); | ||
| } catch (err) { | ||
| const message = err instanceof Error ? err.message : String(err); | ||
| return JSON.stringify({ __bridgeError: message }); | ||
| } | ||
| } | ||
| ); | ||
| await jail.set('_bridgeCall', bridgeCall); | ||
| // Counter inside the isolate for unique callIds — we use a monotonic | ||
| // counter instead of Math.random() so the sequence is deterministic | ||
| // for debugging and tests. | ||
| await context.eval(` | ||
| globalThis.__bridgeCallSeq = 0; | ||
| globalThis.__bridgeCall = async function(functionName, args) { | ||
| const callId = 'bc-' + (++globalThis.__bridgeCallSeq); | ||
| const argsJson = JSON.stringify(args ?? null); | ||
| // Reference.apply returns a Promise when called from inside | ||
| // an isolate. { result: { promise: true } } tells isolated-vm | ||
| // that the host callback returns a Promise it should await | ||
| // before resolving. The result is a primitive string, which | ||
| // transfers cleanly without needing ExternalCopy. | ||
| const envelope = await _bridgeCall.apply( | ||
| undefined, | ||
| [callId, functionName, argsJson], | ||
| { result: { promise: true } } | ||
| ); | ||
| const parsed = envelope ? JSON.parse(envelope) : null; | ||
| if (parsed && parsed.__bridgeError) { | ||
| throw new Error(parsed.__bridgeError); | ||
| } | ||
| // Envelope is always { ok: value } on success. | ||
| return parsed ? parsed.ok : null; | ||
| }; | ||
| `); | ||
| // Set up module cache for require() | ||
@@ -201,4 +384,10 @@ await jail.set('_moduleCache', new ivm.Reference({})); | ||
| // Now that the bridge can be awaited, user code may be async. Wrap in | ||
| // an async IIFE that exposes its return value via globalThis._output. | ||
| // The script.run() call below awaits the script completion (the IIFE | ||
| // itself returns a Promise, and with `promise: true` script.run | ||
| // awaits it). Back-compat: code that does NOT use await still works | ||
| // because the wrapper is an async function. | ||
| const wrappedCode = ` | ||
| (function() { | ||
| (async function() { | ||
| let output; | ||
@@ -214,3 +403,5 @@ ${params.code} | ||
| const script = await isolate.compileScript(wrappedCode); | ||
| await script.run(context, { timeout: timeoutMs }); | ||
| // `promise: true` ensures script.run awaits the IIFE's returned Promise | ||
| // so await __bridgeCall(...) inside user code works end-to-end. | ||
| await script.run(context, { timeout: timeoutMs, promise: true }); | ||
@@ -258,2 +449,12 @@ // SECURITY: Extract output value - use .copySync() to get plain data, not References | ||
| } finally { | ||
| // Clean up bridge state — reject any promises that didn't resolve | ||
| // naturally (e.g. execution threw before a bridge call returned). | ||
| const state = bridgeStates.get(requestId); | ||
| if (state) { | ||
| for (const [, entry] of state.pending) { | ||
| entry.reject(new Error('Execution ended with pending bridge call')); | ||
| } | ||
| bridgeStates.delete(requestId); | ||
| } | ||
| // Clean up resources | ||
@@ -289,6 +490,6 @@ if (context) { | ||
| */ | ||
| process.on('message', async (message: WorkerMessage) => { | ||
| process.on('message', async (message: ParentInboundMessage) => { | ||
| if (message.type === 'execute') { | ||
| try { | ||
| const result = await executeInIsolate(message.params); | ||
| const result = await executeInIsolate(message.params, message.requestId); | ||
| const response: ResultMessage = { | ||
@@ -309,6 +510,52 @@ type: 'result', | ||
| } | ||
| return; | ||
| } | ||
| if (message.type === 'bridge-response') { | ||
| const state = bridgeStates.get(message.requestId); | ||
| if (!state) { | ||
| // Stale response — execution already ended. Drop silently. | ||
| return; | ||
| } | ||
| const entry = state.pending.get(message.callId); | ||
| if (!entry) { | ||
| // Unknown callId — either it was already aborted or we've got a | ||
| // bug. Drop silently; the server-side WorkerPool would re-issue | ||
| // if it still cared. | ||
| return; | ||
| } | ||
| state.pending.delete(message.callId); | ||
| if (message.error) { | ||
| entry.reject(new Error(message.error)); | ||
| } else { | ||
| entry.resolve(message.result); | ||
| } | ||
| return; | ||
| } | ||
| if (message.type === 'abort') { | ||
| const state = bridgeStates.get(message.requestId); | ||
| if (!state) { | ||
| return; | ||
| } | ||
| state.aborted = true; | ||
| state.abortReason = message.reason; | ||
| const pending = Array.from(state.pending.entries()); | ||
| state.pending.clear(); | ||
| for (const [, entry] of pending) { | ||
| entry.reject(new Error(message.reason || 'Execution aborted')); | ||
| } | ||
| // Note: we do NOT dispose the isolate from here. isolated-vm's | ||
| // `script.run(..., { timeout })` will still terminate the script | ||
| // when its own deadline fires, and rejecting the pending bridge | ||
| // calls is enough for cooperative user code to unwind. | ||
| return; | ||
| } | ||
| }); | ||
| // Export nothing — this file is only executed as a child process entrypoint. | ||
| // The ParentOutboundMessage type is exported via the types module instead. | ||
| void ({} as ParentOutboundMessage); | ||
| // Signal that worker is ready to accept work | ||
| process.send!({ type: 'ready' }); |
+398
-26
@@ -13,2 +13,6 @@ /** | ||
| * - Health monitoring and circuit breaker pattern | ||
| * - Bidirectional bridge: sandbox code can call host-registered handlers | ||
| * via `__bridgeCall(name, args)`; the host routes the call, executes the | ||
| * handler, and sends the response back. See worker.ts for the sandbox-side | ||
| * wiring. | ||
| */ | ||
@@ -22,3 +26,7 @@ | ||
| const __dirname = path.dirname(__filename); | ||
| import { CodeExecutionParams, CodeExecutionResult } from './types'; | ||
| import { | ||
| BridgeHandlerMap, | ||
| CodeExecutionParams, | ||
| CodeExecutionResult | ||
| } from './types'; | ||
| import { LogError, LogStatus } from '@memberjunction/core'; | ||
@@ -29,5 +37,14 @@ | ||
| params: CodeExecutionParams; | ||
| /** Bridge handlers scoped to this execution only (never sent over IPC). */ | ||
| bridgeHandlers?: BridgeHandlerMap; | ||
| resolve: (result: CodeExecutionResult) => void; | ||
| reject: (error: Error) => void; | ||
| timeout: NodeJS.Timeout; | ||
| /** | ||
| * Listener we register on `params.abortSignal` so we can remove it again | ||
| * when the request settles. Kept here to make cleanup easy. | ||
| */ | ||
| abortListener?: () => void; | ||
| /** Tracks whether we already sent an `abort` IPC message for this request. */ | ||
| aborted?: boolean; | ||
| } | ||
@@ -42,4 +59,18 @@ | ||
| lastCrashTime: number; | ||
| /** | ||
| * Rolling tail of the worker's stderr. We keep at most | ||
| * `STDERR_TAIL_BYTES` so a crashing worker that spews a huge stack | ||
| * doesn't balloon memory in the host. Used by `handleWorkerCrash` | ||
| * to enrich the error surfaced to the calling action — agents need | ||
| * to see the actual V8/Node diagnostic (e.g. "JavaScript heap out | ||
| * of memory") rather than an opaque "Worker process crashed". | ||
| */ | ||
| stderrTail: string; | ||
| } | ||
| /** Max bytes of stderr we keep per worker for crash diagnostics. 64KB is | ||
| * big enough for a full V8 fatal-error banner + stack trace, small | ||
| * enough that the host can't be starved by pathological output. */ | ||
| const STDERR_TAIL_BYTES = 64 * 1024; | ||
| /** | ||
@@ -95,4 +126,7 @@ * Configuration options for worker pool | ||
| const workerPath = path.join(__dirname, 'worker.js'); // Use .js because worker.ts is compiled | ||
| // stdout 'inherit' so debug logs still reach MJAPI's console. | ||
| // stderr 'pipe' so we can capture a rolling tail for crash diagnostics | ||
| // AND still forward it to MJAPI's stderr — we listen below and echo. | ||
| const childProcess = fork(workerPath, [], { | ||
| stdio: ['ignore', 'inherit', 'inherit', 'ipc'] // Inherit stdout/stderr for debugging | ||
| stdio: ['ignore', 'inherit', 'pipe', 'ipc'] | ||
| }); | ||
@@ -106,5 +140,23 @@ | ||
| crashCount: 0, | ||
| lastCrashTime: 0 | ||
| lastCrashTime: 0, | ||
| stderrTail: '' | ||
| }; | ||
| // Capture stderr for crash diagnostics + forward to MJAPI stderr so | ||
| // operators still see it live in logs during non-crash scenarios. | ||
| if (childProcess.stderr) { | ||
| childProcess.stderr.on('data', (chunk: Buffer) => { | ||
| // Forward to parent stderr — preserves existing debug behavior. | ||
| // Convert to string first since Node's ArrayBuffer types differ | ||
| // between versions and writing the Buffer directly trips TS. | ||
| const str = chunk.toString('utf8'); | ||
| process.stderr.write(str); | ||
| // Append to rolling tail, truncating from the front. | ||
| worker.stderrTail += str; | ||
| if (worker.stderrTail.length > STDERR_TAIL_BYTES) { | ||
| worker.stderrTail = worker.stderrTail.slice(-STDERR_TAIL_BYTES); | ||
| } | ||
| }); | ||
| } | ||
| this.workers[id] = worker; | ||
@@ -145,3 +197,11 @@ | ||
| /** | ||
| * Handle messages from worker process | ||
| * Handle messages from worker process. | ||
| * | ||
| * Types: | ||
| * - `ready` — initial startup / post-restart signal, ignored here. | ||
| * - `result` / `error` — the terminal outcome of the current execute() call. | ||
| * - `bridge-call` — a host-service request from inside the sandbox; we | ||
| * look up the registered handler and send back `bridge-response`. | ||
| * Critically, `bridge-call` does NOT end the request; the worker is | ||
| * still executing until it sends `result`/`error`. | ||
| */ | ||
@@ -154,2 +214,8 @@ private handleWorkerMessage(worker: Worker, message: any): void { | ||
| if (message.type === 'bridge-call') { | ||
| // Dispatch to the registered handler for this execution. | ||
| void this.handleBridgeCall(worker, message); | ||
| return; | ||
| } | ||
| if (!worker.currentRequest) { | ||
@@ -160,12 +226,37 @@ LogError(new Error(`Worker ${worker.id} sent message but has no current request`)); | ||
| // If the worker included a requestId, make sure it matches — this | ||
| // catches stale messages from aborted executions. Legacy test | ||
| // harnesses (and very old workers) don't include a requestId; in | ||
| // that case fall through and treat the message as belonging to the | ||
| // current request. | ||
| if (message.requestId && message.requestId !== worker.currentRequest.requestId) { | ||
| return; | ||
| } | ||
| const request = worker.currentRequest; | ||
| if (message.type === 'result' || message.type === 'error') { | ||
| this.settleRequest(worker, request, message); | ||
| } | ||
| } | ||
| /** | ||
| * Finalize a request once the worker reports `result` or `error`. | ||
| * Clears timeouts, detaches abort listeners, frees the worker, and | ||
| * drains the queue. | ||
| */ | ||
| private settleRequest( | ||
| worker: Worker, | ||
| request: PendingRequest, | ||
| message: { type: 'result' | 'error'; result?: CodeExecutionResult; error?: string } | ||
| ): void { | ||
| clearTimeout(request.timeout); | ||
| this.detachAbortListener(request); | ||
| if (message.type === 'result') { | ||
| request.resolve(message.result); | ||
| } else if (message.type === 'error') { | ||
| request.reject(new Error(message.error)); | ||
| request.resolve(message.result!); | ||
| } else { | ||
| request.reject(new Error(message.error ?? 'Worker returned error')); | ||
| } | ||
| // Mark worker as available and process queue | ||
| worker.busy = false; | ||
@@ -177,2 +268,70 @@ worker.currentRequest = null; | ||
| /** | ||
| * Route a `bridge-call` from the worker to the registered host handler, | ||
| * execute it, and send `bridge-response` back. All error paths here | ||
| * produce a response with `error` populated so the sandbox's | ||
| * `await __bridgeCall(...)` rejects cleanly rather than hanging. | ||
| */ | ||
| private async handleBridgeCall( | ||
| worker: Worker, | ||
| message: { | ||
| type: 'bridge-call'; | ||
| requestId: string; | ||
| callId: string; | ||
| functionName: string; | ||
| args: unknown; | ||
| } | ||
| ): Promise<void> { | ||
| const sendResponse = (payload: { result?: unknown; error?: string }) => { | ||
| try { | ||
| worker.process.send({ | ||
| type: 'bridge-response', | ||
| requestId: message.requestId, | ||
| callId: message.callId, | ||
| ...payload | ||
| }); | ||
| } catch (err) { | ||
| LogError( | ||
| new Error( | ||
| `Failed to send bridge-response to worker ${worker.id}: ${ | ||
| err instanceof Error ? err.message : String(err) | ||
| }` | ||
| ) | ||
| ); | ||
| } | ||
| }; | ||
| if (!worker.currentRequest || worker.currentRequest.requestId !== message.requestId) { | ||
| sendResponse({ error: 'No active execution for this bridge call.' }); | ||
| return; | ||
| } | ||
| const handlers = worker.currentRequest.bridgeHandlers; | ||
| if (!handlers) { | ||
| sendResponse({ | ||
| error: | ||
| `Runtime action tried to call '${message.functionName}' but no bridge ` + | ||
| 'handlers were registered for this execution.' | ||
| }); | ||
| return; | ||
| } | ||
| const handler = handlers[message.functionName]; | ||
| if (!handler) { | ||
| sendResponse({ | ||
| error: | ||
| `Bridge handler '${message.functionName}' is not registered. ` + | ||
| `Available: ${Object.keys(handlers).join(', ') || '(none)'}.` | ||
| }); | ||
| return; | ||
| } | ||
| try { | ||
| const result = await handler(message.args); | ||
| sendResponse({ result }); | ||
| } catch (err) { | ||
| sendResponse({ error: err instanceof Error ? err.message : String(err) }); | ||
| } | ||
| } | ||
| /** | ||
| * Handle worker process crash | ||
@@ -192,4 +351,7 @@ */ | ||
| const diagnostic = classifyWorkerCrash(code, signal, worker.stderrTail); | ||
| LogError(new Error( | ||
| `Worker ${worker.id} crashed (code: ${code}, signal: ${signal}). ` + | ||
| `Reason: ${diagnostic.reason}. ` + | ||
| `Crash count: ${worker.crashCount}/${this.maxCrashesPerWorker}` | ||
@@ -201,5 +363,4 @@ )); | ||
| clearTimeout(worker.currentRequest.timeout); | ||
| worker.currentRequest.reject(new Error( | ||
| 'Worker process crashed during code execution. This may indicate a severe error in the code.' | ||
| )); | ||
| this.detachAbortListener(worker.currentRequest); | ||
| worker.currentRequest.reject(new Error(diagnostic.userMessage)); | ||
| worker.currentRequest = null; | ||
@@ -254,14 +415,2 @@ } | ||
| const totalTimeout = ((params.timeoutSeconds || 30) + 5) * 1000; // Add 5s buffer for overhead | ||
| const timeout = setTimeout(() => { | ||
| // Remove from queue if still there | ||
| const queueIndex = this.requestQueue.findIndex(r => r.requestId === requestId); | ||
| if (queueIndex >= 0) { | ||
| this.requestQueue.splice(queueIndex, 1); | ||
| } | ||
| resolve({ | ||
| success: false, | ||
| error: 'Request timed out waiting for worker availability', | ||
| errorType: 'TIMEOUT' | ||
| }); | ||
| }, totalTimeout); | ||
@@ -271,7 +420,35 @@ const request: PendingRequest = { | ||
| params, | ||
| bridgeHandlers: params.bridgeHandlers, | ||
| resolve, | ||
| reject, | ||
| timeout | ||
| // placeholder — reassigned below once we have the request ref | ||
| timeout: null as unknown as NodeJS.Timeout | ||
| }; | ||
| request.timeout = setTimeout(() => { | ||
| // If the worker is still processing, tell it to abort its | ||
| // in-flight bridge calls first — the sandbox's await will | ||
| // reject cleanly. Then reject on our side. | ||
| this.abortRequest(request, 'Request timed out waiting for worker availability'); | ||
| }, totalTimeout); | ||
| // Mirror any caller-supplied AbortSignal into the same abort path. | ||
| if (params.abortSignal) { | ||
| const listener = () => { | ||
| this.abortRequest( | ||
| request, | ||
| typeof params.abortSignal!.reason === 'string' | ||
| ? params.abortSignal!.reason | ||
| : 'Caller aborted' | ||
| ); | ||
| }; | ||
| request.abortListener = listener; | ||
| if (params.abortSignal.aborted) { | ||
| // Fire immediately if caller aborted before we even queued. | ||
| setImmediate(listener); | ||
| } else { | ||
| params.abortSignal.addEventListener('abort', listener, { once: true }); | ||
| } | ||
| } | ||
| // Add to queue | ||
@@ -286,2 +463,77 @@ this.requestQueue.push(request); | ||
| /** | ||
| * Abort an in-flight or queued request. | ||
| * | ||
| * If the request is still queued, removes it and resolves with a TIMEOUT | ||
| * result (no worker ever picked it up). If a worker is actively running | ||
| * it, sends an `abort` IPC message so the worker rejects its pending | ||
| * bridge calls cleanly, then resolves with a TIMEOUT result. The worker | ||
| * will still send its eventual `result` / `error` message when the | ||
| * sandbox unwinds; `handleWorkerMessage` drops it because | ||
| * `worker.currentRequest` is already cleared. | ||
| */ | ||
| private abortRequest(request: PendingRequest, reason: string): void { | ||
| if (request.aborted) return; | ||
| request.aborted = true; | ||
| // Remove from queue if still waiting. | ||
| const queueIndex = this.requestQueue.findIndex(r => r.requestId === request.requestId); | ||
| if (queueIndex >= 0) { | ||
| this.requestQueue.splice(queueIndex, 1); | ||
| } | ||
| // If a worker is actively running this request, tell it to abort | ||
| // pending bridge calls. We do NOT kill the worker — the sandbox | ||
| // script will unwind naturally once its awaits reject. | ||
| const activeWorker = this.workers.find( | ||
| w => w && w.currentRequest && w.currentRequest.requestId === request.requestId | ||
| ); | ||
| if (activeWorker && activeWorker.process.connected) { | ||
| try { | ||
| activeWorker.process.send({ | ||
| type: 'abort', | ||
| requestId: request.requestId, | ||
| reason | ||
| }); | ||
| } catch (err) { | ||
| LogError( | ||
| new Error( | ||
| `Failed to send abort to worker ${activeWorker.id}: ${ | ||
| err instanceof Error ? err.message : String(err) | ||
| }` | ||
| ) | ||
| ); | ||
| } | ||
| // Decouple the request from the worker so the eventual | ||
| // result/error message is ignored by handleWorkerMessage. Leave | ||
| // worker.busy=true so we don't queue fresh work onto a worker | ||
| // that's still winding down; it'll flip back when the stale | ||
| // result arrives (and the `requestId !== worker.currentRequest` | ||
| // guard in handleWorkerMessage catches it). | ||
| activeWorker.currentRequest = null; | ||
| // Allow the worker to accept fresh work as soon as it's ready; | ||
| // the pool will swing back around on the next settle from the | ||
| // stale message because we won't match the requestId. | ||
| activeWorker.busy = false; | ||
| } | ||
| clearTimeout(request.timeout); | ||
| this.detachAbortListener(request); | ||
| request.resolve({ | ||
| success: false, | ||
| error: reason, | ||
| errorType: 'TIMEOUT' | ||
| }); | ||
| this.processQueue(); | ||
| } | ||
| private detachAbortListener(request: PendingRequest): void { | ||
| if (request.params.abortSignal && request.abortListener) { | ||
| request.params.abortSignal.removeEventListener('abort', request.abortListener); | ||
| request.abortListener = undefined; | ||
| } | ||
| } | ||
| /** | ||
| * Process queued requests by assigning them to available workers | ||
@@ -310,7 +562,12 @@ */ | ||
| // Send execution request to worker | ||
| // Send execution request to worker — strip non-serializable fields | ||
| // (`bridgeHandlers` are functions, `abortSignal` is a live object). | ||
| const ipcParams = { ...request.params } as CodeExecutionParams; | ||
| delete (ipcParams as { bridgeHandlers?: unknown }).bridgeHandlers; | ||
| delete (ipcParams as { abortSignal?: unknown }).abortSignal; | ||
| worker.process.send({ | ||
| type: 'execute', | ||
| requestId: request.requestId, | ||
| params: request.params | ||
| params: ipcParams | ||
| }); | ||
@@ -354,2 +611,3 @@ | ||
| clearTimeout(request.timeout); | ||
| this.detachAbortListener(request); | ||
| request.resolve({ | ||
@@ -390,1 +648,115 @@ success: false, | ||
| } | ||
| /** | ||
| * Classify an unexpected worker-process exit into an actionable error. | ||
| * | ||
| * "Worker process crashed" by itself is useless to a downstream AI agent | ||
| * trying to fix its Runtime action code — it doesn't know whether the | ||
| * problem is a memory leak, a bad native call, or a runaway loop. This | ||
| * helper inspects the Node exit `code`, kill `signal`, and the tail of | ||
| * stderr the worker emitted before dying, then returns: | ||
| * | ||
| * - `reason`: short label for logs | ||
| * - `userMessage`: the human-readable message surfaced all the way to | ||
| * the action's `message` field. Includes a "what to | ||
| * do about it" hint so agents can self-correct. | ||
| */ | ||
| function classifyWorkerCrash( | ||
| code: number | null, | ||
| signal: string | null, | ||
| stderrTail: string | ||
| ): { reason: string; userMessage: string } { | ||
| const stderr = (stderrTail || '').trim(); | ||
| const stderrForUser = stderr | ||
| ? `\n\nWorker stderr (last ${STDERR_TAIL_BYTES / 1024}KB):\n${stderr.length > 4000 ? stderr.slice(-4000) : stderr}` | ||
| : ''; | ||
| // 1) V8 Node-heap OOM — usually `code=134` + stderr with "JavaScript | ||
| // heap out of memory" banner. | ||
| if (/heap out of memory|Allocation failed/i.test(stderr) || code === 134) { | ||
| return { | ||
| reason: 'NODE_HEAP_OOM', | ||
| userMessage: | ||
| "Sandbox worker ran out of Node.js heap memory. The most common cause is a RunView / RunViews call that returned too many rows (e.g. `MaxRows: 0` against a high-volume table like `MJ: AI Prompt Runs`). Fix the Runtime action code to (a) cap `MaxRows` to a reasonable number, (b) add a time-window filter, or (c) page through results instead of loading everything at once." + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 2) isolated-vm memory-limit OOM propagated from the v8 isolate via | ||
| // the standard "Array buffer allocation failed" / "out of memory" | ||
| // patterns. Separate from node-heap OOM because the fix is the | ||
| // same but the surface is different. | ||
| if (/out of memory|RangeError.*memory/i.test(stderr)) { | ||
| return { | ||
| reason: 'ISOLATE_OOM', | ||
| userMessage: | ||
| 'Sandbox isolated-vm ran out of memory (128MB default). Reduce the data volume the Runtime action loads — cap `MaxRows`, narrow the ExtraFilter, or process in batches. Avoid pulling whole rows when you only need a few columns; project with `Fields` where supported.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 3) Linux OOM killer — kernel killed the process because system | ||
| // memory was exhausted. code=null + signal='SIGKILL' is the | ||
| // canonical signature; stderr is often empty (the kernel doesn't | ||
| // let the process write anything before the kill). | ||
| if (signal === 'SIGKILL') { | ||
| return { | ||
| reason: 'KERNEL_SIGKILL', | ||
| userMessage: | ||
| 'Sandbox worker was killed by the OS (SIGKILL). The most likely cause is the Linux OOM killer reclaiming memory under pressure — your Runtime action or the bridge result was large enough to put the host under memory stress. Reduce the data volume: cap `MaxRows`, narrow filters, or page through results.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 4) Native crash — segfault, bus error, abort. ivm / v8 bugs, or | ||
| // passing something ExternalCopy cannot serialize. | ||
| if (signal === 'SIGSEGV' || signal === 'SIGBUS') { | ||
| return { | ||
| reason: 'NATIVE_CRASH', | ||
| userMessage: | ||
| `Sandbox worker crashed with native signal ${signal} — usually an isolated-vm / V8 fault, not something your code can fix directly. Common triggers include passing very deeply-nested objects or circular references through the utilities bridge. Simplify the values you return from RunView / actions before using them.` + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| if (signal === 'SIGABRT') { | ||
| return { | ||
| reason: 'PROCESS_ABORT', | ||
| userMessage: | ||
| 'Sandbox worker aborted (SIGABRT). Usually triggered by an unrecoverable v8 assertion or a failed native check — often the same class of issue as a segfault. If this is reproducible, reduce the size and complexity of data your code allocates.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 5) Maximum call stack — v8 throws a catchable RangeError normally, | ||
| // but in deep-native contexts (ivm bridge marshaling, sync | ||
| // callbacks) it can escape and kill the process. Signature is | ||
| // usually stderr mentioning "Maximum call stack size exceeded" | ||
| // with code=1. | ||
| if (/Maximum call stack|stack size exceeded/i.test(stderr)) { | ||
| return { | ||
| reason: 'STACK_OVERFLOW', | ||
| userMessage: | ||
| 'Sandbox worker hit a stack overflow. Likely a recursive function with no base case, or a call pattern that recurses indirectly through the utilities bridge. Review loops and recursion in your Runtime action code.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 6) Uncaught JS exception — code=1 with stderr containing a stack. | ||
| if (code === 1 && stderr.length > 0) { | ||
| return { | ||
| reason: 'UNCAUGHT_EXCEPTION', | ||
| userMessage: | ||
| 'Sandbox worker crashed on an uncaught exception before the isolate could wrap it. Check the stderr tail below for the offending JS trace.' + | ||
| stderrForUser | ||
| }; | ||
| } | ||
| // 7) Unknown / fallback — still include exit metadata and stderr so | ||
| // the agent has SOMETHING to act on. | ||
| return { | ||
| reason: 'UNKNOWN', | ||
| userMessage: | ||
| `Sandbox worker process exited unexpectedly (code=${code}, signal=${signal}). The Runtime action code may be triggering a low-level issue in the sandbox. Simplify the code and retry.` + | ||
| stderrForUser | ||
| }; | ||
| } |
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
2549195
87.42%69
13.11%13641
68.3%13
85.71%21
23.53%21
31.25%+ Added
+ Added
- Removed
- Removed
Updated