diff --git a/Dockerfile b/Dockerfile index 064f81e..8720519 100644 --- a/Dockerfile +++ b/Dockerfile @@ -10,9 +10,8 @@ COPY dist dist COPY src src COPY lib lib COPY index.html . -COPY executor.config.js . COPY vite.config.js . -RUN npm run build:all +RUN npm run build:react # Copy bin over COPY bin bin CMD ["npm","start"] diff --git a/ROADMAP.md b/ROADMAP.md index c98bf7f..75b5c97 100644 --- a/ROADMAP.md +++ b/ROADMAP.md @@ -28,7 +28,7 @@ ## v0.0.5 -- [ ] Docker config +- [x] Docker config - [ ] Gitlab Integration - [ ] Garden config diff --git a/bin/executor b/bin/executor deleted file mode 100755 index 6917caf..0000000 Binary files a/bin/executor and /dev/null differ diff --git a/dist/bundles/qualiteer-executor.mjs b/dist/bundles/qualiteer-executor.mjs index 3e6fdd6..6ee8914 100644 --- a/dist/bundles/qualiteer-executor.mjs +++ b/dist/bundles/qualiteer-executor.mjs @@ -1,4 +1,4 @@ -import e from"node:path";import t from"fs";import s from"url";import r from"child_process";import i from"http";import n from"https";import o from"stream";import a from"zlib";import h from"net";import c from"tls";import l from"crypto";import d from"events";function p(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(s){if("default"!==s&&!(s in e)){var r=Object.getOwnPropertyDescriptor(t,s);Object.defineProperty(e,s,r.get?r:{enumerable:!0,get:function(){return t[s]}})}}))})),Object.freeze(e)}const u=Object.create(null);u.open="0",u.close="1",u.ping="2",u.pong="3",u.message="4",u.upgrade="5",u.noop="6";const f=Object.create(null);Object.keys(u).forEach((e=>{f[u[e]]=e}));const _={type:"error",data:"parser error"},m=({type:e,data:t},s,r)=>{if(t instanceof ArrayBuffer||ArrayBuffer.isView(t)){const e=y(t);return r(g(e,s))}return r(u[e]+(t||""))},y=e=>Buffer.isBuffer(e)?e:e instanceof ArrayBuffer?Buffer.from(e):Buffer.from(e.buffer,e.byteOffset,e.byteLength),g=(e,t)=>t?e:"b"+e.toString("base64"),b=(e,t)=>{if("string"!=typeof e)return{type:"message",data:v(e,t)};const s=e.charAt(0);if("b"===s){const s=Buffer.from(e.substring(1),"base64");return{type:"message",data:v(s,t)}}return f[s]?e.length>1?{type:f[s],data:e.substring(1)}:{type:f[s]}:_},v=(e,t)=>{const s=Buffer.isBuffer(e);return"arraybuffer"===t&&s?E(e):e},E=e=>{const t=new ArrayBuffer(e.length),s=new Uint8Array(t);for(let t=0;t(e.hasOwnProperty(s)&&(t[s]=e[s]),t)),{})}const O=setTimeout,T=clearTimeout;function N(e,t){t.useNativeTimers?(e.setTimeoutFn=O.bind(k),e.clearTimeoutFn=T.bind(k)):(e.setTimeoutFn=setTimeout.bind(k),e.clearTimeoutFn=clearTimeout.bind(k))}class R extends Error{constructor(e,t,s){super(e),this.description=t,this.context=s,this.type="TransportError"}}class C extends S{constructor(e){super(),this.writable=!1,N(this,e),this.opts=e,this.query=e.query,this.readyState="",this.socket=e.socket}onError(e,t,s){return super.emitReserved("error",new R(e,t,s)),this}open(){return"closed"!==this.readyState&&""!==this.readyState||(this.readyState="opening",this.doOpen()),this}close(){return"opening"!==this.readyState&&"open"!==this.readyState||(this.doClose(),this.onClose()),this}send(e){"open"===this.readyState&&this.write(e)}onOpen(){this.readyState="open",this.writable=!0,super.emitReserved("open")}onData(e){const t=b(e,this.socket.binaryType);this.onPacket(t)}onPacket(e){super.emitReserved("packet",e)}onClose(e){this.readyState="closed",super.emitReserved("close",e)}}const L="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),A={};let B,P=0,I=0;function D(e){let t="";do{t=L[e%64]+t,e=Math.floor(e/64)}while(e>0);return t}function U(){const e=D(+new Date);return e!==B?(P=0,B=e):e+"."+D(P++)}for(;I<64;I++)A[L[I]]=I;function j(e){let t="";for(let s in e)e.hasOwnProperty(s)&&(t.length&&(t+="&"),t+=encodeURIComponent(s)+"="+encodeURIComponent(e[s]));return t} +import e from"node:path";import t from"fs";import s from"url";import r from"child_process";import i from"http";import n from"https";import o from"stream";import a from"zlib";import h from"net";import c from"tls";import l from"crypto";import d from"events";function p(e,t){return t.forEach((function(t){t&&"string"!=typeof t&&!Array.isArray(t)&&Object.keys(t).forEach((function(s){if("default"!==s&&!(s in e)){var r=Object.getOwnPropertyDescriptor(t,s);Object.defineProperty(e,s,r.get?r:{enumerable:!0,get:function(){return t[s]}})}}))})),Object.freeze(e)}const u=Object.create(null);u.open="0",u.close="1",u.ping="2",u.pong="3",u.message="4",u.upgrade="5",u.noop="6";const f=Object.create(null);Object.keys(u).forEach((e=>{f[u[e]]=e}));const m={type:"error",data:"parser error"},_=({type:e,data:t},s,r)=>{if(t instanceof ArrayBuffer||ArrayBuffer.isView(t)){const e=y(t);return r(g(e,s))}return r(u[e]+(t||""))},y=e=>Buffer.isBuffer(e)?e:e instanceof ArrayBuffer?Buffer.from(e):Buffer.from(e.buffer,e.byteOffset,e.byteLength),g=(e,t)=>t?e:"b"+e.toString("base64"),b=(e,t)=>{if("string"!=typeof e)return{type:"message",data:v(e,t)};const s=e.charAt(0);if("b"===s){const s=Buffer.from(e.substring(1),"base64");return{type:"message",data:v(s,t)}}return f[s]?e.length>1?{type:f[s],data:e.substring(1)}:{type:f[s]}:m},v=(e,t)=>{const s=Buffer.isBuffer(e);return"arraybuffer"===t&&s?E(e):e},E=e=>{const t=new ArrayBuffer(e.length),s=new Uint8Array(t);for(let t=0;t(e.hasOwnProperty(s)&&(t[s]=e[s]),t)),{})}const O=setTimeout,T=clearTimeout;function N(e,t){t.useNativeTimers?(e.setTimeoutFn=O.bind(k),e.clearTimeoutFn=T.bind(k)):(e.setTimeoutFn=setTimeout.bind(k),e.clearTimeoutFn=clearTimeout.bind(k))}class R extends Error{constructor(e,t,s){super(e),this.description=t,this.context=s,this.type="TransportError"}}class C extends S{constructor(e){super(),this.writable=!1,N(this,e),this.opts=e,this.query=e.query,this.readyState="",this.socket=e.socket}onError(e,t,s){return super.emitReserved("error",new R(e,t,s)),this}open(){return"closed"!==this.readyState&&""!==this.readyState||(this.readyState="opening",this.doOpen()),this}close(){return"opening"!==this.readyState&&"open"!==this.readyState||(this.doClose(),this.onClose()),this}send(e){"open"===this.readyState&&this.write(e)}onOpen(){this.readyState="open",this.writable=!0,super.emitReserved("open")}onData(e){const t=b(e,this.socket.binaryType);this.onPacket(t)}onPacket(e){super.emitReserved("packet",e)}onClose(e){this.readyState="closed",super.emitReserved("close",e)}}const L="0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_".split(""),A={};let B,P=0,I=0;function D(e){let t="";do{t=L[e%64]+t,e=Math.floor(e/64)}while(e>0);return t}function U(){const e=D(+new Date);return e!==B?(P=0,B=e):e+"."+D(P++)}for(;I<64;I++)A[L[I]]=I;function j(e){let t="";for(let s in e)e.hasOwnProperty(s)&&(t.length&&(t+="&"),t+=encodeURIComponent(s)+"="+encodeURIComponent(e[s]));return t} /** * Wrapper for built-in http.js to emulate the browser XMLHttpRequest object. * @@ -11,4 +11,4 @@ import e from"node:path";import t from"fs";import s from"url";import r from"chil * @contributor David Ellis * @license MIT */ -var F=t,M=s,q=r.spawn,W=V;function V(e){e=e||{};var t,s,r=this,o=i,a=n,h={},c=!1,l={"User-Agent":"node-XMLHttpRequest",Accept:"*/*"},d=Object.assign({},l),p=["accept-charset","accept-encoding","access-control-request-headers","access-control-request-method","connection","content-length","content-transfer-encoding","cookie","cookie2","date","expect","host","keep-alive","origin","referer","te","trailer","transfer-encoding","upgrade","via"],u=["TRACE","TRACK","CONNECT"],f=!1,_=!1,m=!1,y={};this.UNSENT=0,this.OPENED=1,this.HEADERS_RECEIVED=2,this.LOADING=3,this.DONE=4,this.readyState=this.UNSENT,this.onreadystatechange=null,this.responseText="",this.responseXML="",this.status=null,this.statusText=null;this.open=function(e,t,s,r,i){if(this.abort(),_=!1,m=!1,!function(e){return e&&-1===u.indexOf(e)}(e))throw new Error("SecurityError: Request method not allowed");h={method:e,url:t.toString(),async:"boolean"!=typeof s||s,user:r||null,password:i||null},g(this.OPENED)},this.setDisableHeaderCheck=function(e){c=e},this.setRequestHeader=function(e,t){if(this.readyState!=this.OPENED)throw new Error("INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN");if(!function(e){return c||e&&-1===p.indexOf(e.toLowerCase())}(e))return console.warn('Refused to set unsafe header "'+e+'"'),!1;if(f)throw new Error("INVALID_STATE_ERR: send flag is true");return d[e]=t,!0},this.getResponseHeader=function(e){return"string"==typeof e&&this.readyState>this.OPENED&&s.headers[e.toLowerCase()]&&!_?s.headers[e.toLowerCase()]:null},this.getAllResponseHeaders=function(){if(this.readyState{e.unref()})),i&&t.write(i),t.end(),r.dispatchEvent("loadstart")}else{var k=".node-xmlhttprequest-content-"+process.pid,x=".node-xmlhttprequest-sync-"+process.pid;F.writeFileSync(x,"","utf8");for(var O="var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http"+(c?"s":"")+".request;var options = "+JSON.stringify(v)+";var responseText = '';var req = doRequest(options, function(response) {response.setEncoding('utf8');response.on('data', function(chunk) { responseText += chunk;});response.on('end', function() {fs.writeFileSync('"+k+"', 'NODE-XMLHTTPREQUEST-STATUS:' + response.statusCode + ',' + responseText, 'utf8');fs.unlinkSync('"+x+"');});response.on('error', function(error) {fs.writeFileSync('"+k+"', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('"+x+"');});}).on('error', function(error) {fs.writeFileSync('"+k+"', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('"+x+"');});"+(i?"req.write('"+JSON.stringify(i).slice(1,-1).replace(/'/g,"\\'")+"');":"")+"req.end();",T=q(process.argv[0],["-e",O]);F.existsSync(x););if(r.responseText=F.readFileSync(k,"utf8"),T.stdin.end(),F.unlinkSync(k),r.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)){var N=r.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/,"");r.handleError(N,503)}else r.status=r.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:([0-9]*),.*/,"$1"),r.responseText=r.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:[0-9]*,(.*)/,"$1"),g(r.DONE)}}},this.handleError=function(e,t){this.status=t||0,this.statusText=e,this.responseText=e.stack,_=!0,g(this.DONE)},this.abort=function(){t&&(t.abort(),t=null),d=Object.assign({},l),this.responseText="",this.responseXML="",_=m=!0,this.readyState===this.UNSENT||this.readyState===this.OPENED&&!f||this.readyState===this.DONE||(f=!1,g(this.DONE)),this.readyState=this.UNSENT},this.addEventListener=function(e,t){e in y||(y[e]=[]),y[e].push(t)},this.removeEventListener=function(e,t){e in y&&(y[e]=y[e].filter((function(e){return e!==t})))},this.dispatchEvent=function(e){if("function"==typeof r["on"+e]&&(this.readyState===this.DONE?setImmediate((function(){r["on"+e]()})):r["on"+e]()),e in y)for(let t=0,s=y[e].length;t{4===t.readyState&&(200===t.status||1223===t.status?this.onLoad():this.setTimeoutFn((()=>{this.onError("number"==typeof t.status?t.status:0)}),0))},t.send(this.data)}catch(e){return void this.setTimeoutFn((()=>{this.onError(e)}),0)}"undefined"!=typeof document&&(this.index=z.requestsCount++,z.requests[this.index]=this)}onError(e){this.emitReserved("error",e,this.xhr),this.cleanup(!0)}cleanup(e){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=G,e)try{this.xhr.abort()}catch(e){}"undefined"!=typeof document&&delete z.requests[this.index],this.xhr=null}}onLoad(){const e=this.xhr.responseText;null!==e&&(this.emitReserved("data",e),this.emitReserved("success"),this.cleanup())}abort(){this.cleanup()}}if(z.requestsCount=0,z.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",Y);else if("function"==typeof addEventListener){addEventListener("onpagehide"in k?"pagehide":"unload",Y,!1)}function Y(){for(let e in z.requests)z.requests.hasOwnProperty(e)&&z.requests[e].abort()}var K={exports:{}},X={BINARY_TYPES:["nodebuffer","arraybuffer","fragments"],EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}};const{EMPTY_BUFFER:J}=X;function Q(e,t){if(0===e.length)return J;if(1===e.length)return e[0];const s=Buffer.allocUnsafe(t);let r=0;for(let t=0;t{this.pending--,this[ie]()},this.concurrency=e||1/0,this.jobs=[],this.pending=0}add(e){this.jobs.push(e),this[ie]()}[ie](){if(this.pending!==this.concurrency&&this.jobs.length){const e=this.jobs.shift();this.pending++,e(this[re])}}};const oe=a,ae=K.exports,he=ne,{kStatusCode:ce}=X,le=Buffer.from([0,0,255,255]),de=Symbol("permessage-deflate"),pe=Symbol("total-length"),ue=Symbol("callback"),fe=Symbol("buffers"),_e=Symbol("error");let me;var ye=class{constructor(e,t,s){if(this._maxPayload=0|s,this._options=e||{},this._threshold=void 0!==this._options.threshold?this._options.threshold:1024,this._isServer=!!t,this._deflate=null,this._inflate=null,this.params=null,!me){const e=void 0!==this._options.concurrencyLimit?this._options.concurrencyLimit:10;me=new he(e)}}static get extensionName(){return"permessage-deflate"}offer(){const e={};return this._options.serverNoContextTakeover&&(e.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(e.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(e.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?e.client_max_window_bits=this._options.clientMaxWindowBits:null==this._options.clientMaxWindowBits&&(e.client_max_window_bits=!0),e}accept(e){return e=this.normalizeParams(e),this.params=this._isServer?this.acceptAsServer(e):this.acceptAsClient(e),this.params}cleanup(){if(this._inflate&&(this._inflate.close(),this._inflate=null),this._deflate){const e=this._deflate[ue];this._deflate.close(),this._deflate=null,e&&e(new Error("The deflate stream was closed while data was being processed"))}}acceptAsServer(e){const t=this._options,s=e.find((e=>!(!1===t.serverNoContextTakeover&&e.server_no_context_takeover||e.server_max_window_bits&&(!1===t.serverMaxWindowBits||"number"==typeof t.serverMaxWindowBits&&t.serverMaxWindowBits>e.server_max_window_bits)||"number"==typeof t.clientMaxWindowBits&&!e.client_max_window_bits)));if(!s)throw new Error("None of the extension offers can be accepted");return t.serverNoContextTakeover&&(s.server_no_context_takeover=!0),t.clientNoContextTakeover&&(s.client_no_context_takeover=!0),"number"==typeof t.serverMaxWindowBits&&(s.server_max_window_bits=t.serverMaxWindowBits),"number"==typeof t.clientMaxWindowBits?s.client_max_window_bits=t.clientMaxWindowBits:!0!==s.client_max_window_bits&&!1!==t.clientMaxWindowBits||delete s.client_max_window_bits,s}acceptAsClient(e){const t=e[0];if(!1===this._options.clientNoContextTakeover&&t.client_no_context_takeover)throw new Error('Unexpected parameter "client_no_context_takeover"');if(t.client_max_window_bits){if(!1===this._options.clientMaxWindowBits||"number"==typeof this._options.clientMaxWindowBits&&t.client_max_window_bits>this._options.clientMaxWindowBits)throw new Error('Unexpected or invalid parameter "client_max_window_bits"')}else"number"==typeof this._options.clientMaxWindowBits&&(t.client_max_window_bits=this._options.clientMaxWindowBits);return t}normalizeParams(e){return e.forEach((e=>{Object.keys(e).forEach((t=>{let s=e[t];if(s.length>1)throw new Error(`Parameter "${t}" must have only a single value`);if(s=s[0],"client_max_window_bits"===t){if(!0!==s){const e=+s;if(!Number.isInteger(e)||e<8||e>15)throw new TypeError(`Invalid value for parameter "${t}": ${s}`);s=e}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${t}": ${s}`)}else if("server_max_window_bits"===t){const e=+s;if(!Number.isInteger(e)||e<8||e>15)throw new TypeError(`Invalid value for parameter "${t}": ${s}`);s=e}else{if("client_no_context_takeover"!==t&&"server_no_context_takeover"!==t)throw new Error(`Unknown parameter "${t}"`);if(!0!==s)throw new TypeError(`Invalid value for parameter "${t}": ${s}`)}e[t]=s}))})),e}decompress(e,t,s){me.add((r=>{this._decompress(e,t,((e,t)=>{r(),s(e,t)}))}))}compress(e,t,s){me.add((r=>{this._compress(e,t,((e,t)=>{r(),s(e,t)}))}))}_decompress(e,t,s){const r=this._isServer?"client":"server";if(!this._inflate){const e=`${r}_max_window_bits`,t="number"!=typeof this.params[e]?oe.Z_DEFAULT_WINDOWBITS:this.params[e];this._inflate=oe.createInflateRaw({...this._options.zlibInflateOptions,windowBits:t}),this._inflate[de]=this,this._inflate[pe]=0,this._inflate[fe]=[],this._inflate.on("error",ve),this._inflate.on("data",be)}this._inflate[ue]=s,this._inflate.write(e),t&&this._inflate.write(le),this._inflate.flush((()=>{const e=this._inflate[_e];if(e)return this._inflate.close(),this._inflate=null,void s(e);const i=ae.concat(this._inflate[fe],this._inflate[pe]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[pe]=0,this._inflate[fe]=[],t&&this.params[`${r}_no_context_takeover`]&&this._inflate.reset()),s(null,i)}))}_compress(e,t,s){const r=this._isServer?"server":"client";if(!this._deflate){const e=`${r}_max_window_bits`,t="number"!=typeof this.params[e]?oe.Z_DEFAULT_WINDOWBITS:this.params[e];this._deflate=oe.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:t}),this._deflate[pe]=0,this._deflate[fe]=[],this._deflate.on("data",ge)}this._deflate[ue]=s,this._deflate.write(e),this._deflate.flush(oe.Z_SYNC_FLUSH,(()=>{if(!this._deflate)return;let e=ae.concat(this._deflate[fe],this._deflate[pe]);t&&(e=e.slice(0,e.length-4)),this._deflate[ue]=null,this._deflate[pe]=0,this._deflate[fe]=[],t&&this.params[`${r}_no_context_takeover`]&&this._deflate.reset(),s(null,e)}))}};function ge(e){this[fe].push(e),this[pe]+=e.length}function be(e){this[pe]+=e.length,this[de]._maxPayload<1||this[pe]<=this[de]._maxPayload?this[fe].push(e):(this[_e]=new RangeError("Max payload size exceeded"),this[_e].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[_e][ce]=1009,this.removeListener("data",be),this.reset())}function ve(e){this[de]._inflate=null,e[ce]=1007,this[ue](e)}var Ee={exports:{}};const we=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0];function Se(e){return e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e||e>=3e3&&e<=4999}function ke(e){const t=e.length;let s=0;for(;s=t||128!=(192&e[s+1])||128!=(192&e[s+2])||224===e[s]&&128==(224&e[s+1])||237===e[s]&&160==(224&e[s+1]))return!1;s+=3}else{if(240!=(248&e[s]))return!1;if(s+3>=t||128!=(192&e[s+1])||128!=(192&e[s+2])||128!=(192&e[s+3])||240===e[s]&&128==(240&e[s+1])||244===e[s]&&e[s+1]>143||e[s]>244)return!1;s+=4}return!0}try{const e=require("utf-8-validate");Ee.exports={isValidStatusCode:Se,isValidUTF8:t=>t.length<150?ke(t):e(t),tokenChars:we}}catch(e){Ee.exports={isValidStatusCode:Se,isValidUTF8:ke,tokenChars:we}}const{Writable:xe}=o,Oe=ye,{BINARY_TYPES:Te,EMPTY_BUFFER:Ne,kStatusCode:Re,kWebSocket:Ce}=X,{concat:Le,toArrayBuffer:Ae,unmask:Be}=K.exports,{isValidStatusCode:Pe,isValidUTF8:Ie}=Ee.exports;var De=class extends xe{constructor(e={}){super(),this._binaryType=e.binaryType||Te[0],this._extensions=e.extensions||{},this._isServer=!!e.isServer,this._maxPayload=0|e.maxPayload,this._skipUTF8Validation=!!e.skipUTF8Validation,this[Ce]=void 0,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._fragments=[],this._state=0,this._loop=!1}_write(e,t,s){if(8===this._opcode&&0==this._state)return s();this._bufferedBytes+=e.length,this._buffers.push(e),this.startLoop(s)}consume(e){if(this._bufferedBytes-=e,e===this._buffers[0].length)return this._buffers.shift();if(e=s.length?t.set(this._buffers.shift(),r):(t.set(new Uint8Array(s.buffer,s.byteOffset,e),r),this._buffers[0]=s.slice(e)),e-=s.length}while(e>0);return t}startLoop(e){let t;this._loop=!0;do{switch(this._state){case 0:t=this.getInfo();break;case 1:t=this.getPayloadLength16();break;case 2:t=this.getPayloadLength64();break;case 3:this.getMask();break;case 4:t=this.getData(e);break;default:return void(this._loop=!1)}}while(this._loop);e(t)}getInfo(){if(this._bufferedBytes<2)return void(this._loop=!1);const e=this.consume(2);if(0!=(48&e[0]))return this._loop=!1,Ue(RangeError,"RSV2 and RSV3 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_2_3");const t=64==(64&e[0]);if(t&&!this._extensions[Oe.extensionName])return this._loop=!1,Ue(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");if(this._fin=128==(128&e[0]),this._opcode=15&e[0],this._payloadLength=127&e[1],0===this._opcode){if(t)return this._loop=!1,Ue(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");if(!this._fragmented)return this._loop=!1,Ue(RangeError,"invalid opcode 0",!0,1002,"WS_ERR_INVALID_OPCODE");this._opcode=this._fragmented}else if(1===this._opcode||2===this._opcode){if(this._fragmented)return this._loop=!1,Ue(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");this._compressed=t}else{if(!(this._opcode>7&&this._opcode<11))return this._loop=!1,Ue(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");if(!this._fin)return this._loop=!1,Ue(RangeError,"FIN must be set",!0,1002,"WS_ERR_EXPECTED_FIN");if(t)return this._loop=!1,Ue(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");if(this._payloadLength>125)return this._loop=!1,Ue(RangeError,`invalid payload length ${this._payloadLength}`,!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH")}if(this._fin||this._fragmented||(this._fragmented=this._opcode),this._masked=128==(128&e[1]),this._isServer){if(!this._masked)return this._loop=!1,Ue(RangeError,"MASK must be set",!0,1002,"WS_ERR_EXPECTED_MASK")}else if(this._masked)return this._loop=!1,Ue(RangeError,"MASK must be clear",!0,1002,"WS_ERR_UNEXPECTED_MASK");if(126===this._payloadLength)this._state=1;else{if(127!==this._payloadLength)return this.haveLength();this._state=2}}getPayloadLength16(){if(!(this._bufferedBytes<2))return this._payloadLength=this.consume(2).readUInt16BE(0),this.haveLength();this._loop=!1}getPayloadLength64(){if(this._bufferedBytes<8)return void(this._loop=!1);const e=this.consume(8),t=e.readUInt32BE(0);return t>Math.pow(2,21)-1?(this._loop=!1,Ue(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009,"WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH")):(this._payloadLength=t*Math.pow(2,32)+e.readUInt32BE(4),this.haveLength())}haveLength(){if(this._payloadLength&&this._opcode<8&&(this._totalPayloadLength+=this._payloadLength,this._totalPayloadLength>this._maxPayload&&this._maxPayload>0))return this._loop=!1,Ue(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");this._masked?this._state=3:this._state=4}getMask(){this._bufferedBytes<4?this._loop=!1:(this._mask=this.consume(4),this._state=4)}getData(e){let t=Ne;if(this._payloadLength){if(this._bufferedBytes7?this.controlMessage(t):this._compressed?(this._state=5,void this.decompress(t,e)):(t.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(t)),this.dataMessage())}decompress(e,t){this._extensions[Oe.extensionName].decompress(e,this._fin,((e,s)=>{if(e)return t(e);if(s.length){if(this._messageLength+=s.length,this._messageLength>this._maxPayload&&this._maxPayload>0)return t(Ue(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));this._fragments.push(s)}const r=this.dataMessage();if(r)return t(r);this.startLoop(t)}))}dataMessage(){if(this._fin){const e=this._messageLength,t=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],2===this._opcode){let s;s="nodebuffer"===this._binaryType?Le(t,e):"arraybuffer"===this._binaryType?Ae(Le(t,e)):t,this.emit("message",s,!0)}else{const s=Le(t,e);if(!this._skipUTF8Validation&&!Ie(s))return this._loop=!1,Ue(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");this.emit("message",s,!1)}}this._state=0}controlMessage(e){if(8===this._opcode)if(this._loop=!1,0===e.length)this.emit("conclude",1005,Ne),this.end();else{if(1===e.length)return Ue(RangeError,"invalid payload length 1",!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH");{const t=e.readUInt16BE(0);if(!Pe(t))return Ue(RangeError,`invalid status code ${t}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");const s=e.slice(2);if(!this._skipUTF8Validation&&!Ie(s))return Ue(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");this.emit("conclude",t,s),this.end()}}else 9===this._opcode?this.emit("ping",e):this.emit("pong",e);this._state=0}};function Ue(e,t,s,r,i){const n=new e(s?`Invalid WebSocket frame: ${t}`:t);return Error.captureStackTrace(n,Ue),n.code=i,n[Re]=r,n}const{randomFillSync:je}=l,Fe=ye,{EMPTY_BUFFER:Me}=X,{isValidStatusCode:qe}=Ee.exports,{mask:We,toBuffer:Ve}=K.exports,$e=Buffer.alloc(4);class Ge{constructor(e,t){this._extensions=t||{},this._socket=e,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._deflating=!1,this._queue=[]}static frame(e,t){const s=t.mask&&t.readOnly;let r=t.mask?6:2,i=e.length;e.length>=65536?(r+=8,i=127):e.length>125&&(r+=2,i=126);const n=Buffer.allocUnsafe(s?e.length+r:r);return n[0]=t.fin?128|t.opcode:t.opcode,t.rsv1&&(n[0]|=64),n[1]=i,126===i?n.writeUInt16BE(e.length,2):127===i&&(n.writeUInt32BE(0,2),n.writeUInt32BE(e.length,6)),t.mask?(je($e,0,4),n[1]|=128,n[r-4]=$e[0],n[r-3]=$e[1],n[r-2]=$e[2],n[r-1]=$e[3],s?(We(e,$e,n,r,e.length),[n]):(We(e,$e,e,0,e.length),[n,e])):[n,e]}close(e,t,s,r){let i;if(void 0===e)i=Me;else{if("number"!=typeof e||!qe(e))throw new TypeError("First argument must be a valid error code number");if(void 0!==t&&t.length){const s=Buffer.byteLength(t);if(s>123)throw new RangeError("The message must not be greater than 123 bytes");i=Buffer.allocUnsafe(2+s),i.writeUInt16BE(e,0),"string"==typeof t?i.write(t,2):i.set(t,2)}else i=Buffer.allocUnsafe(2),i.writeUInt16BE(e,0)}this._deflating?this.enqueue([this.doClose,i,s,r]):this.doClose(i,s,r)}doClose(e,t,s){this.sendFrame(Ge.frame(e,{fin:!0,rsv1:!1,opcode:8,mask:t,readOnly:!1}),s)}ping(e,t,s){const r=Ve(e);if(r.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPing,r,t,Ve.readOnly,s]):this.doPing(r,t,Ve.readOnly,s)}doPing(e,t,s,r){this.sendFrame(Ge.frame(e,{fin:!0,rsv1:!1,opcode:9,mask:t,readOnly:s}),r)}pong(e,t,s){const r=Ve(e);if(r.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPong,r,t,Ve.readOnly,s]):this.doPong(r,t,Ve.readOnly,s)}doPong(e,t,s,r){this.sendFrame(Ge.frame(e,{fin:!0,rsv1:!1,opcode:10,mask:t,readOnly:s}),r)}send(e,t,s){const r=Ve(e),i=this._extensions[Fe.extensionName];let n=t.binary?2:1,o=t.compress;if(this._firstFragment?(this._firstFragment=!1,o&&i&&i.params[i._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(o=r.length>=i._threshold),this._compress=o):(o=!1,n=0),t.fin&&(this._firstFragment=!0),i){const e={fin:t.fin,rsv1:o,opcode:n,mask:t.mask,readOnly:Ve.readOnly};this._deflating?this.enqueue([this.dispatch,r,this._compress,e,s]):this.dispatch(r,this._compress,e,s)}else this.sendFrame(Ge.frame(r,{fin:t.fin,rsv1:!1,opcode:n,mask:t.mask,readOnly:Ve.readOnly}),s)}dispatch(e,t,s,r){if(!t)return void this.sendFrame(Ge.frame(e,s),r);const i=this._extensions[Fe.extensionName];this._bufferedBytes+=e.length,this._deflating=!0,i.compress(e,s.fin,((t,i)=>{if(this._socket.destroyed){const e=new Error("The socket was closed while data was being compressed");"function"==typeof r&&r(e);for(let t=0;t{let s=e[t];return Array.isArray(s)||(s=[s]),s.map((e=>[t].concat(Object.keys(e).map((t=>{let s=e[t];return Array.isArray(s)||(s=[s]),s.map((e=>!0===e?t:`${t}=${e}`)).join("; ")}))).join("; "))).join(", ")})).join(", ")},parse:function(e){const t=Object.create(null);let s,r,i=Object.create(null),n=!1,o=!1,a=!1,h=-1,c=-1,l=-1,d=0;for(;d0&&e.unshift(t),e.on("close",Zt),e.on("data",es),e.on("end",ts),e.on("error",ss),this._readyState=Ft.OPEN,this.emit("open")}emitClose(){if(!this._socket)return this._readyState=Ft.CLOSED,void this.emit("close",this._closeCode,this._closeMessage);this._extensions[vt.extensionName]&&this._extensions[vt.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=Ft.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(e,t){if(this.readyState!==Ft.CLOSED){if(this.readyState===Ft.CONNECTING){const e="WebSocket was closed before the connection was established";return Vt(this,this._req,e)}this.readyState!==Ft.CLOSING?(this._readyState=Ft.CLOSING,this._sender.close(e,t,!this._isServer,(e=>{e||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())})),this._closeTimer=setTimeout(this._socket.destroy.bind(this._socket),3e4)):this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end()}}ping(e,t,s){if(this.readyState===Ft.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");"function"==typeof e?(s=e,e=t=void 0):"function"==typeof t&&(s=t,t=void 0),"number"==typeof e&&(e=e.toString()),this.readyState===Ft.OPEN?(void 0===t&&(t=!this._isServer),this._sender.ping(e||kt,t,s)):$t(this,e,s)}pong(e,t,s){if(this.readyState===Ft.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");"function"==typeof e?(s=e,e=t=void 0):"function"==typeof t&&(s=t,t=void 0),"number"==typeof e&&(e=e.toString()),this.readyState===Ft.OPEN?(void 0===t&&(t=!this._isServer),this._sender.pong(e||kt,t,s)):$t(this,e,s)}send(e,t,s){if(this.readyState===Ft.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if("function"==typeof t&&(s=t,t={}),"number"==typeof e&&(e=e.toString()),this.readyState!==Ft.OPEN)return void $t(this,e,s);const r={binary:"string"!=typeof e,mask:!this._isServer,compress:!0,fin:!0,...t};this._extensions[vt.extensionName]||(r.compress=!1),this._sender.send(e||kt,r,s)}terminate(){if(this.readyState!==Ft.CLOSED){if(this.readyState===Ft.CONNECTING){const e="WebSocket was closed before the connection was established";return Vt(this,this._req,e)}this._socket&&(this._readyState=Ft.CLOSING,this._socket.destroy())}}}function Mt(e,t,s,r){const i={protocolVersion:jt[1],maxPayload:104857600,skipUTF8Validation:!1,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...r,createConnection:void 0,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:void 0,host:void 0,path:void 0,port:void 0};if(!jt.includes(i.protocolVersion))throw new RangeError(`Unsupported protocol version: ${i.protocolVersion} (supported versions: ${jt.join(", ")})`);let n;if(t instanceof bt)n=t,e._url=t.href;else{try{n=new bt(t)}catch(e){throw new SyntaxError(`Invalid URL: ${t}`)}e._url=t}const o="wss:"===n.protocol,a="ws+unix:"===n.protocol;if("ws:"!==n.protocol&&!o&&!a)throw new SyntaxError('The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"');if(a&&!n.pathname)throw new SyntaxError("The URL's pathname is empty");if(n.hash)throw new SyntaxError("The URL contains a fragment identifier");const h=o?443:80,c=yt(16).toString("base64"),l=o?ut.get:ft.get,d=new Set;let p;if(i.createConnection=o?Wt:qt,i.defaultPort=i.defaultPort||h,i.port=n.port||h,i.host=n.hostname.startsWith("[")?n.hostname.slice(1,-1):n.hostname,i.headers={"Sec-WebSocket-Version":i.protocolVersion,"Sec-WebSocket-Key":c,Connection:"Upgrade",Upgrade:"websocket",...i.headers},i.path=n.pathname+n.search,i.timeout=i.handshakeTimeout,i.perMessageDeflate&&(p=new vt(!0!==i.perMessageDeflate?i.perMessageDeflate:{},!1,i.maxPayload),i.headers["Sec-WebSocket-Extensions"]=Bt({[vt.extensionName]:p.offer()})),s.length){for(const e of s){if("string"!=typeof e||!Ut.test(e)||d.has(e))throw new SyntaxError("An invalid or duplicated subprotocol was specified");d.add(e)}i.headers["Sec-WebSocket-Protocol"]=s.join(",")}if(i.origin&&(i.protocolVersion<13?i.headers["Sec-WebSocket-Origin"]=i.origin:i.headers.Origin=i.origin),(n.username||n.password)&&(i.auth=`${n.username}:${n.password}`),a){const e=i.path.split(":");i.socketPath=e[0],i.path=e[1]}let u=e._req=l(i);i.timeout&&u.on("timeout",(()=>{Vt(e,u,"Opening handshake has timed out")})),u.on("error",(t=>{null===u||u.aborted||(u=e._req=null,e._readyState=Ft.CLOSING,e.emit("error",t),e.emitClose())})),u.on("response",(n=>{const o=n.headers.location,a=n.statusCode;if(o&&i.followRedirects&&a>=300&&a<400){if(++e._redirects>i.maxRedirects)return void Vt(e,u,"Maximum redirects exceeded");u.abort();const n=new bt(o,t);Mt(e,n,s,r)}else e.emit("unexpected-response",u,n)||Vt(e,u,`Unexpected server response: ${n.statusCode}`)})),u.on("upgrade",((t,s,r)=>{if(e.emit("upgrade",t),e.readyState!==Ft.CONNECTING)return;u=e._req=null;const n=gt("sha1").update(c+xt).digest("base64");if(t.headers["sec-websocket-accept"]!==n)return void Vt(e,s,"Invalid Sec-WebSocket-Accept header");const o=t.headers["sec-websocket-protocol"];let a;if(void 0!==o?d.size?d.has(o)||(a="Server sent an invalid subprotocol"):a="Server sent a subprotocol but none was requested":d.size&&(a="Server sent no subprotocol"),a)return void Vt(e,s,a);o&&(e._protocol=o);const h=t.headers["sec-websocket-extensions"];if(void 0!==h){if(!p){return void Vt(e,s,"Server sent a Sec-WebSocket-Extensions header but no extension was requested")}let t;try{t=Pt(h)}catch(t){return void Vt(e,s,"Invalid Sec-WebSocket-Extensions header")}const r=Object.keys(t);if(1!==r.length||r[0]!==vt.extensionName){return void Vt(e,s,"Server indicated an extension that was not requested")}try{p.accept(t[vt.extensionName])}catch(t){return void Vt(e,s,"Invalid Sec-WebSocket-Extensions header")}e._extensions[vt.extensionName]=p}e.setSocket(s,r,{maxPayload:i.maxPayload,skipUTF8Validation:i.skipUTF8Validation})}))}function qt(e){return e.path=e.socketPath,_t.connect(e)}function Wt(e){return e.path=void 0,e.servername||""===e.servername||(e.servername=_t.isIP(e.host)?"":e.host),mt.connect(e)}function Vt(e,t,s){e._readyState=Ft.CLOSING;const r=new Error(s);Error.captureStackTrace(r,Vt),t.setHeader?(t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),t.once("abort",e.emitClose.bind(e)),e.emit("error",r)):(t.destroy(r),t.once("error",e.emit.bind(e,"error")),t.once("close",e.emitClose.bind(e)))}function $t(e,t,s){if(t){const s=It(t).length;e._socket?e._sender._bufferedBytes+=s:e._bufferedAmount+=s}if(s){s(new Error(`WebSocket is not open: readyState ${e.readyState} (${Dt[e.readyState]})`))}}function Gt(e,t){const s=this[Rt];s._closeFrameReceived=!0,s._closeMessage=t,s._closeCode=e,void 0!==s._socket[Rt]&&(s._socket.removeListener("data",es),process.nextTick(Qt,s._socket),1005===e?s.close():s.close(e,t))}function Ht(){this[Rt]._socket.resume()}function zt(e){const t=this[Rt];void 0!==t._socket[Rt]&&(t._socket.removeListener("data",es),process.nextTick(Qt,t._socket),t.close(e[Nt])),t.emit("error",e)}function Yt(){this[Rt].emitClose()}function Kt(e,t){this[Rt].emit("message",e,t)}function Xt(e){const t=this[Rt];t.pong(e,!t._isServer,Ct),t.emit("ping",e)}function Jt(e){this[Rt].emit("pong",e)}function Qt(e){e.resume()}function Zt(){const e=this[Rt];let t;this.removeListener("close",Zt),this.removeListener("data",es),this.removeListener("end",ts),e._readyState=Ft.CLOSING,this._readableState.endEmitted||e._closeFrameReceived||e._receiver._writableState.errorEmitted||null===(t=e._socket.read())||e._receiver.write(t),e._receiver.end(),this[Rt]=void 0,clearTimeout(e._closeTimer),e._receiver._writableState.finished||e._receiver._writableState.errorEmitted?e.emitClose():(e._receiver.on("error",Yt),e._receiver.on("finish",Yt))}function es(e){this[Rt]._receiver.write(e)||this.pause()}function ts(){const e=this[Rt];e._readyState=Ft.CLOSING,e._receiver.end(),this.end()}function ss(){const e=this[Rt];this.removeListener("error",ss),this.on("error",Ct),e&&(e._readyState=Ft.CLOSING,this.destroy())}Object.defineProperty(Ft,"CONNECTING",{enumerable:!0,value:Dt.indexOf("CONNECTING")}),Object.defineProperty(Ft.prototype,"CONNECTING",{enumerable:!0,value:Dt.indexOf("CONNECTING")}),Object.defineProperty(Ft,"OPEN",{enumerable:!0,value:Dt.indexOf("OPEN")}),Object.defineProperty(Ft.prototype,"OPEN",{enumerable:!0,value:Dt.indexOf("OPEN")}),Object.defineProperty(Ft,"CLOSING",{enumerable:!0,value:Dt.indexOf("CLOSING")}),Object.defineProperty(Ft.prototype,"CLOSING",{enumerable:!0,value:Dt.indexOf("CLOSING")}),Object.defineProperty(Ft,"CLOSED",{enumerable:!0,value:Dt.indexOf("CLOSED")}),Object.defineProperty(Ft.prototype,"CLOSED",{enumerable:!0,value:Dt.indexOf("CLOSED")}),["binaryType","bufferedAmount","extensions","protocol","readyState","url"].forEach((e=>{Object.defineProperty(Ft.prototype,e,{enumerable:!0})})),["open","error","close","message"].forEach((e=>{Object.defineProperty(Ft.prototype,`on${e}`,{enumerable:!0,get(){for(const t of this.listeners(e))if(t[Ot])return t[Tt];return null},set(t){for(const t of this.listeners(e))if(t[Ot]){this.removeListener(e,t);break}"function"==typeof t&&this.addEventListener(e,t,{[Ot]:!0})}})})),Ft.prototype.addEventListener=Lt,Ft.prototype.removeEventListener=At;const rs=Ft,is=process.nextTick,ns="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase();const os={websocket:class extends C{constructor(e){super(e),this.supportsBinary=!e.forceBase64}get name(){return"websocket"}doOpen(){if(!this.check())return;const e=this.uri(),t=this.opts.protocols,s=ns?{}:x(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(s.headers=this.opts.extraHeaders);try{this.ws=new rs(e,t,s)}catch(e){return this.emitReserved("error",e)}this.ws.binaryType=this.socket.binaryType||"nodebuffer",this.addEventListeners()}addEventListeners(){this.ws.onopen=()=>{this.opts.autoUnref&&this.ws._socket.unref(),this.onOpen()},this.ws.onclose=e=>this.onClose({description:"websocket connection closed",context:e}),this.ws.onmessage=e=>this.onData(e.data),this.ws.onerror=e=>this.onError("websocket error",e)}write(e){this.writable=!1;for(let t=0;t{const t={};if(s.options&&(t.compress=s.options.compress),this.opts.perMessageDeflate){("string"==typeof e?Buffer.byteLength(e):e.length){this.writable=!0,this.emitReserved("drain")}),this.setTimeoutFn)}))}}doClose(){void 0!==this.ws&&(this.ws.close(),this.ws=null)}uri(){let e=this.query||{};const t=this.opts.secure?"wss":"ws";let s="";this.opts.port&&("wss"===t&&443!==Number(this.opts.port)||"ws"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port),this.opts.timestampRequests&&(e[this.opts.timestampParam]=U()),this.supportsBinary||(e.b64=1);const r=j(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(r.length?"?"+r:"")}check(){return!!rs}},polling:class extends C{constructor(e){if(super(e),this.polling=!1,"undefined"!=typeof location){const t="https:"===location.protocol;let s=location.port;s||(s=t?"443":"80"),this.xd="undefined"!=typeof location&&e.hostname!==location.hostname||s!==e.port,this.xs=e.secure!==t}const t=e&&e.forceBase64;this.supportsBinary=H&&!t}get name(){return"polling"}doOpen(){this.poll()}pause(e){this.readyState="pausing";const t=()=>{this.readyState="paused",e()};if(this.polling||!this.writable){let e=0;this.polling&&(e++,this.once("pollComplete",(function(){--e||t()}))),this.writable||(e++,this.once("drain",(function(){--e||t()})))}else t()}poll(){this.polling=!0,this.doPoll(),this.emitReserved("poll")}onData(e){((e,t)=>{const s=e.split(w),r=[];for(let e=0;e{if("opening"===this.readyState&&"open"===e.type&&this.onOpen(),"close"===e.type)return this.onClose({description:"transport closed by the server"}),!1;this.onPacket(e)})),"closed"!==this.readyState&&(this.polling=!1,this.emitReserved("pollComplete"),"open"===this.readyState&&this.poll())}doClose(){const e=()=>{this.write([{type:"close"}])};"open"===this.readyState?e():this.once("open",e)}write(e){this.writable=!1,((e,t)=>{const s=e.length,r=new Array(s);let i=0;e.forEach(((e,n)=>{m(e,!1,(e=>{r[n]=e,++i===s&&t(r.join(w))}))}))})(e,(e=>{this.doWrite(e,(()=>{this.writable=!0,this.emitReserved("drain")}))}))}uri(){let e=this.query||{};const t=this.opts.secure?"https":"http";let s="";!1!==this.opts.timestampRequests&&(e[this.opts.timestampParam]=U()),this.supportsBinary||e.sid||(e.b64=1),this.opts.port&&("https"===t&&443!==Number(this.opts.port)||"http"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port);const r=j(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(r.length?"?"+r:"")}request(e={}){return Object.assign(e,{xd:this.xd,xs:this.xs},this.opts),new z(this.uri(),e)}doWrite(e,t){const s=this.request({method:"POST",data:e});s.on("success",t),s.on("error",((e,t)=>{this.onError("xhr post error",e,t)}))}doPoll(){const e=this.request();e.on("data",this.onData.bind(this)),e.on("error",((e,t)=>{this.onError("xhr poll error",e,t)})),this.pollXhr=e}}},as=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,hs=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];function cs(e){const t=e,s=e.indexOf("["),r=e.indexOf("]");-1!=s&&-1!=r&&(e=e.substring(0,s)+e.substring(s,r).replace(/:/g,";")+e.substring(r,e.length));let i=as.exec(e||""),n={},o=14;for(;o--;)n[hs[o]]=i[o]||"";return-1!=s&&-1!=r&&(n.source=t,n.host=n.host.substring(1,n.host.length-1).replace(/;/g,":"),n.authority=n.authority.replace("[","").replace("]","").replace(/;/g,":"),n.ipv6uri=!0),n.pathNames=function(e,t){const s=/\/{2,9}/g,r=t.replace(s,"/").split("/");"/"!=t.substr(0,1)&&0!==t.length||r.splice(0,1);"/"==t.substr(t.length-1,1)&&r.splice(r.length-1,1);return r}(0,n.path),n.queryKey=function(e,t){const s={};return t.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(e,t,r){t&&(s[t]=r)})),s}(0,n.query),n}class ls extends S{constructor(e,t={}){super(),e&&"object"==typeof e&&(t=e,e=null),e?(e=cs(e),t.hostname=e.host,t.secure="https"===e.protocol||"wss"===e.protocol,t.port=e.port,e.query&&(t.query=e.query)):t.host&&(t.hostname=cs(t.host).host),N(this,t),this.secure=null!=t.secure?t.secure:"undefined"!=typeof location&&"https:"===location.protocol,t.hostname&&!t.port&&(t.port=this.secure?"443":"80"),this.hostname=t.hostname||("undefined"!=typeof location?location.hostname:"localhost"),this.port=t.port||("undefined"!=typeof location&&location.port?location.port:this.secure?"443":"80"),this.transports=t.transports||["polling","websocket"],this.readyState="",this.writeBuffer=[],this.prevBufferLen=0,this.opts=Object.assign({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!0},t),this.opts.path=this.opts.path.replace(/\/$/,"")+"/","string"==typeof this.opts.query&&(this.opts.query=function(e){let t={},s=e.split("&");for(let e=0,r=s.length;e{this.transport&&(this.transport.removeAllListeners(),this.transport.close())}),!1),"localhost"!==this.hostname&&(this.offlineEventListener=()=>{this.onClose("transport close",{description:"network connection lost"})},addEventListener("offline",this.offlineEventListener,!1))),this.open()}createTransport(e){const t=Object.assign({},this.opts.query);t.EIO=4,t.transport=e,this.id&&(t.sid=this.id);const s=Object.assign({},this.opts.transportOptions[e],this.opts,{query:t,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return new os[e](s)}open(){let e;if(this.opts.rememberUpgrade&&ls.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))e="websocket";else{if(0===this.transports.length)return void this.setTimeoutFn((()=>{this.emitReserved("error","No transports available")}),0);e=this.transports[0]}this.readyState="opening";try{e=this.createTransport(e)}catch(e){return this.transports.shift(),void this.open()}e.open(),this.setTransport(e)}setTransport(e){this.transport&&this.transport.removeAllListeners(),this.transport=e,e.on("drain",this.onDrain.bind(this)).on("packet",this.onPacket.bind(this)).on("error",this.onError.bind(this)).on("close",(e=>this.onClose("transport close",e)))}probe(e){let t=this.createTransport(e),s=!1;ls.priorWebsocketSuccess=!1;const r=()=>{s||(t.send([{type:"ping",data:"probe"}]),t.once("packet",(e=>{if(!s)if("pong"===e.type&&"probe"===e.data){if(this.upgrading=!0,this.emitReserved("upgrading",t),!t)return;ls.priorWebsocketSuccess="websocket"===t.name,this.transport.pause((()=>{s||"closed"!==this.readyState&&(c(),this.setTransport(t),t.send([{type:"upgrade"}]),this.emitReserved("upgrade",t),t=null,this.upgrading=!1,this.flush())}))}else{const e=new Error("probe error");e.transport=t.name,this.emitReserved("upgradeError",e)}})))};function i(){s||(s=!0,c(),t.close(),t=null)}const n=e=>{const s=new Error("probe error: "+e);s.transport=t.name,i(),this.emitReserved("upgradeError",s)};function o(){n("transport closed")}function a(){n("socket closed")}function h(e){t&&e.name!==t.name&&i()}const c=()=>{t.removeListener("open",r),t.removeListener("error",n),t.removeListener("close",o),this.off("close",a),this.off("upgrading",h)};t.once("open",r),t.once("error",n),t.once("close",o),this.once("close",a),this.once("upgrading",h),t.open()}onOpen(){if(this.readyState="open",ls.priorWebsocketSuccess="websocket"===this.transport.name,this.emitReserved("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade&&this.transport.pause){let e=0;const t=this.upgrades.length;for(;e{this.onClose("ping timeout")}),this.pingInterval+this.pingTimeout),this.opts.autoUnref&&this.pingTimeoutTimer.unref()}onDrain(){this.writeBuffer.splice(0,this.prevBufferLen),this.prevBufferLen=0,0===this.writeBuffer.length?this.emitReserved("drain"):this.flush()}flush(){if("closed"!==this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){const e=this.getWritablePackets();this.transport.send(e),this.prevBufferLen=e.length,this.emitReserved("flush")}}getWritablePackets(){if(!(this.maxPayload&&"polling"===this.transport.name&&this.writeBuffer.length>1))return this.writeBuffer;let e=1;for(let s=0;s=57344?s+=3:(r++,s+=4);return s}(t):Math.ceil(1.33*(t.byteLength||t.size))),s>0&&e>this.maxPayload)return this.writeBuffer.slice(0,s);e+=2}var t;return this.writeBuffer}write(e,t,s){return this.sendPacket("message",e,t,s),this}send(e,t,s){return this.sendPacket("message",e,t,s),this}sendPacket(e,t,s,r){if("function"==typeof t&&(r=t,t=void 0),"function"==typeof s&&(r=s,s=null),"closing"===this.readyState||"closed"===this.readyState)return;(s=s||{}).compress=!1!==s.compress;const i={type:e,data:t,options:s};this.emitReserved("packetCreate",i),this.writeBuffer.push(i),r&&this.once("flush",r),this.flush()}close(){const e=()=>{this.onClose("forced close"),this.transport.close()},t=()=>{this.off("upgrade",t),this.off("upgradeError",t),e()},s=()=>{this.once("upgrade",t),this.once("upgradeError",t)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(()=>{this.upgrading?s():e()})):this.upgrading?s():e()),this}onError(e){ls.priorWebsocketSuccess=!1,this.emitReserved("error",e),this.onClose("transport error",e)}onClose(e,t){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&removeEventListener("offline",this.offlineEventListener,!1),this.readyState="closed",this.id=null,this.emitReserved("close",e,t),this.writeBuffer=[],this.prevBufferLen=0)}filterUpgrades(e){const t=[];let s=0;const r=e.length;for(;s"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(e):e.buffer instanceof ArrayBuffer)(e))||us&&e instanceof Blob||fs&&e instanceof File}function ms(e,t){if(!e||"object"!=typeof e)return!1;if(Array.isArray(e)){for(let t=0,s=e.length;t0;case Es.ACK:case Es.BINARY_ACK:return Array.isArray(t)}}destroy(){this.reconstructor&&this.reconstructor.finishedReconstruction()}}class Ss{constructor(e){this.packet=e,this.buffers=[],this.reconPack=e}takeBinaryData(e){if(this.buffers.push(e),this.buffers.length===this.reconPack.attachments){const e=bs(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}finishedReconstruction(){this.reconPack=null,this.buffers=[]}}var ks=Object.freeze({__proto__:null,protocol:5,get PacketType(){return Es},Encoder:class{constructor(e){this.replacer=e}encode(e){return e.type!==Es.EVENT&&e.type!==Es.ACK||!ms(e)?[this.encodeAsString(e)]:(e.type=e.type===Es.EVENT?Es.BINARY_EVENT:Es.BINARY_ACK,this.encodeAsBinary(e))}encodeAsString(e){let t=""+e.type;return e.type!==Es.BINARY_EVENT&&e.type!==Es.BINARY_ACK||(t+=e.attachments+"-"),e.nsp&&"/"!==e.nsp&&(t+=e.nsp+","),null!=e.id&&(t+=e.id),null!=e.data&&(t+=JSON.stringify(e.data,this.replacer)),t}encodeAsBinary(e){const t=ys(e),s=this.encodeAsString(t.packet),r=t.buffers;return r.unshift(s),r}},Decoder:ws});function xs(e,t,s){return e.on(t,s),function(){e.off(t,s)}}const Os=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1});class Ts extends S{constructor(e,t,s){super(),this.connected=!1,this.receiveBuffer=[],this.sendBuffer=[],this.ids=0,this.acks={},this.flags={},this.io=e,this.nsp=t,s&&s.auth&&(this.auth=s.auth),this.io._autoConnect&&this.open()}get disconnected(){return!this.connected}subEvents(){if(this.subs)return;const e=this.io;this.subs=[xs(e,"open",this.onopen.bind(this)),xs(e,"packet",this.onpacket.bind(this)),xs(e,"error",this.onerror.bind(this)),xs(e,"close",this.onclose.bind(this))]}get active(){return!!this.subs}connect(){return this.connected||(this.subEvents(),this.io._reconnecting||this.io.open(),"open"===this.io._readyState&&this.onopen()),this}open(){return this.connect()}send(...e){return e.unshift("message"),this.emit.apply(this,e),this}emit(e,...t){if(Os.hasOwnProperty(e))throw new Error('"'+e+'" is a reserved event name');t.unshift(e);const s={type:Es.EVENT,data:t,options:{}};if(s.options.compress=!1!==this.flags.compress,"function"==typeof t[t.length-1]){const e=this.ids++,r=t.pop();this._registerAckCallback(e,r),s.id=e}const r=this.io.engine&&this.io.engine.transport&&this.io.engine.transport.writable;return this.flags.volatile&&(!r||!this.connected)||(this.connected?(this.notifyOutgoingListeners(s),this.packet(s)):this.sendBuffer.push(s)),this.flags={},this}_registerAckCallback(e,t){const s=this.flags.timeout;if(void 0===s)return void(this.acks[e]=t);const r=this.io.setTimeoutFn((()=>{delete this.acks[e];for(let t=0;t{this.io.clearTimeoutFn(r),t.apply(this,[null,...e])}}packet(e){e.nsp=this.nsp,this.io._packet(e)}onopen(){"function"==typeof this.auth?this.auth((e=>{this.packet({type:Es.CONNECT,data:e})})):this.packet({type:Es.CONNECT,data:this.auth})}onerror(e){this.connected||this.emitReserved("connect_error",e)}onclose(e,t){this.connected=!1,delete this.id,this.emitReserved("disconnect",e,t)}onpacket(e){if(e.nsp===this.nsp)switch(e.type){case Es.CONNECT:if(e.data&&e.data.sid){const t=e.data.sid;this.onconnect(t)}else this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case Es.EVENT:case Es.BINARY_EVENT:this.onevent(e);break;case Es.ACK:case Es.BINARY_ACK:this.onack(e);break;case Es.DISCONNECT:this.ondisconnect();break;case Es.CONNECT_ERROR:this.destroy();const t=new Error(e.data.message);t.data=e.data.data,this.emitReserved("connect_error",t)}}onevent(e){const t=e.data||[];null!=e.id&&t.push(this.ack(e.id)),this.connected?this.emitEvent(t):this.receiveBuffer.push(Object.freeze(t))}emitEvent(e){if(this._anyListeners&&this._anyListeners.length){const t=this._anyListeners.slice();for(const s of t)s.apply(this,e)}super.emit.apply(this,e)}ack(e){const t=this;let s=!1;return function(...r){s||(s=!0,t.packet({type:Es.ACK,id:e,data:r}))}}onack(e){const t=this.acks[e.id];"function"==typeof t&&(t.apply(this,e.data),delete this.acks[e.id])}onconnect(e){this.id=e,this.connected=!0,this.emitBuffered(),this.emitReserved("connect")}emitBuffered(){this.receiveBuffer.forEach((e=>this.emitEvent(e))),this.receiveBuffer=[],this.sendBuffer.forEach((e=>{this.notifyOutgoingListeners(e),this.packet(e)})),this.sendBuffer=[]}ondisconnect(){this.destroy(),this.onclose("io server disconnect")}destroy(){this.subs&&(this.subs.forEach((e=>e())),this.subs=void 0),this.io._destroy(this)}disconnect(){return this.connected&&this.packet({type:Es.DISCONNECT}),this.destroy(),this.connected&&this.onclose("io client disconnect"),this}close(){return this.disconnect()}compress(e){return this.flags.compress=e,this}get volatile(){return this.flags.volatile=!0,this}timeout(e){return this.flags.timeout=e,this}onAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.push(e),this}prependAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.unshift(e),this}offAny(e){if(!this._anyListeners)return this;if(e){const t=this._anyListeners;for(let s=0;s0&&e.jitter<=1?e.jitter:0,this.attempts=0}Ns.prototype.duration=function(){var e=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var t=Math.random(),s=Math.floor(t*this.jitter*e);e=0==(1&Math.floor(10*t))?e-s:e+s}return 0|Math.min(e,this.max)},Ns.prototype.reset=function(){this.attempts=0},Ns.prototype.setMin=function(e){this.ms=e},Ns.prototype.setMax=function(e){this.max=e},Ns.prototype.setJitter=function(e){this.jitter=e};class Rs extends S{constructor(e,t){var s;super(),this.nsps={},this.subs=[],e&&"object"==typeof e&&(t=e,e=void 0),(t=t||{}).path=t.path||"/socket.io",this.opts=t,N(this,t),this.reconnection(!1!==t.reconnection),this.reconnectionAttempts(t.reconnectionAttempts||1/0),this.reconnectionDelay(t.reconnectionDelay||1e3),this.reconnectionDelayMax(t.reconnectionDelayMax||5e3),this.randomizationFactor(null!==(s=t.randomizationFactor)&&void 0!==s?s:.5),this.backoff=new Ns({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(null==t.timeout?2e4:t.timeout),this._readyState="closed",this.uri=e;const r=t.parser||ks;this.encoder=new r.Encoder,this.decoder=new r.Decoder,this._autoConnect=!1!==t.autoConnect,this._autoConnect&&this.open()}reconnection(e){return arguments.length?(this._reconnection=!!e,this):this._reconnection}reconnectionAttempts(e){return void 0===e?this._reconnectionAttempts:(this._reconnectionAttempts=e,this)}reconnectionDelay(e){var t;return void 0===e?this._reconnectionDelay:(this._reconnectionDelay=e,null===(t=this.backoff)||void 0===t||t.setMin(e),this)}randomizationFactor(e){var t;return void 0===e?this._randomizationFactor:(this._randomizationFactor=e,null===(t=this.backoff)||void 0===t||t.setJitter(e),this)}reconnectionDelayMax(e){var t;return void 0===e?this._reconnectionDelayMax:(this._reconnectionDelayMax=e,null===(t=this.backoff)||void 0===t||t.setMax(e),this)}timeout(e){return arguments.length?(this._timeout=e,this):this._timeout}maybeReconnectOnOpen(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}open(e){if(~this._readyState.indexOf("open"))return this;this.engine=new ls(this.uri,this.opts);const t=this.engine,s=this;this._readyState="opening",this.skipReconnect=!1;const r=xs(t,"open",(function(){s.onopen(),e&&e()})),i=xs(t,"error",(t=>{s.cleanup(),s._readyState="closed",this.emitReserved("error",t),e?e(t):s.maybeReconnectOnOpen()}));if(!1!==this._timeout){const e=this._timeout;0===e&&r();const s=this.setTimeoutFn((()=>{r(),t.close(),t.emit("error",new Error("timeout"))}),e);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}return this.subs.push(r),this.subs.push(i),this}connect(e){return this.open(e)}onopen(){this.cleanup(),this._readyState="open",this.emitReserved("open");const e=this.engine;this.subs.push(xs(e,"ping",this.onping.bind(this)),xs(e,"data",this.ondata.bind(this)),xs(e,"error",this.onerror.bind(this)),xs(e,"close",this.onclose.bind(this)),xs(this.decoder,"decoded",this.ondecoded.bind(this)))}onping(){this.emitReserved("ping")}ondata(e){this.decoder.add(e)}ondecoded(e){this.emitReserved("packet",e)}onerror(e){this.emitReserved("error",e)}socket(e,t){let s=this.nsps[e];return s||(s=new Ts(this,e,t),this.nsps[e]=s),s}_destroy(e){const t=Object.keys(this.nsps);for(const e of t){if(this.nsps[e].active)return}this._close()}_packet(e){const t=this.encoder.encode(e);for(let s=0;se())),this.subs.length=0,this.decoder.destroy()}_close(){this.skipReconnect=!0,this._reconnecting=!1,this.onclose("forced close"),this.engine&&this.engine.close()}disconnect(){return this._close()}onclose(e,t){this.cleanup(),this.backoff.reset(),this._readyState="closed",this.emitReserved("close",e,t),this._reconnection&&!this.skipReconnect&&this.reconnect()}reconnect(){if(this._reconnecting||this.skipReconnect)return this;const e=this;if(this.backoff.attempts>=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{const t=this.backoff.duration();this._reconnecting=!0;const s=this.setTimeoutFn((()=>{e.skipReconnect||(this.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((t=>{t?(e._reconnecting=!1,e.reconnect(),this.emitReserved("reconnect_error",t)):e.onreconnect()})))}),t);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}}onreconnect(){const e=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",e)}}const Cs={};function Ls(e,t){"object"==typeof e&&(t=e,e=void 0);const s=function(e,t="",s){let r=e;s=s||"undefined"!=typeof location&&location,null==e&&(e=s.protocol+"//"+s.host),"string"==typeof e&&("/"===e.charAt(0)&&(e="/"===e.charAt(1)?s.protocol+e:s.host+e),/^(https?|wss?):\/\//.test(e)||(e=void 0!==s?s.protocol+"//"+e:"https://"+e),r=cs(e)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";const i=-1!==r.host.indexOf(":")?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+i+":"+r.port+t,r.href=r.protocol+"://"+i+(s&&s.port===r.port?"":":"+r.port),r}(e,(t=t||{}).path||"/socket.io"),r=s.source,i=s.id,n=s.path,o=Cs[i]&&n in Cs[i].nsps;let a;return t.forceNew||t["force new connection"]||!1===t.multiplex||o?a=new Rs(r,t):(Cs[i]||(Cs[i]=new Rs(r,t)),a=Cs[i]),s.query&&!t.query&&(t.query=s.queryKey),a.socket(s.path,t)}Object.assign(Ls,{Manager:Rs,Socket:Ts,io:Ls,connect:Ls});var As="e";var Bs="jr",Ps="jc";const Is=e=>()=>e;const{default:Ds}=await import(e.resolve("executor.config.js")),Us=process.argv.slice(2),js=JSON.parse(Buffer.from(Us[0],"base64").toString("utf8")),Fs=function(e){const t={...e};for(var s in t)"function"!=typeof t[s]&&(t[s]=Is(t[s]));return t}(Ds(js)),Ms=new class{constructor(e,t){this.url=e.url(t)??process.env.QUALITEER_EXECUTOR_URL,this.jobId=e.jobId(t)??process.env.QUALITEER_JOB_ID,this.command=e.command(t)??process.env.QUALITEER_COMMAND,this.mode=As,this.buf={},this.buf.e="",this.buf.o="",this.spawn=this.spawn.bind(this),this.report=this.report.bind(this),this.onProcClose=this.onProcClose.bind(this),this.onClose=this.onClose.bind(this)}spawn(){const e=this.command,t=e.shift();this.proc=r.spawn(t,e),this.proc.stdout.setEncoding("utf8"),this.proc.stderr.setEncoding("utf8"),this.proc.stdout.on("data",(e=>this.report(e.toString(),"o"))),this.proc.stderr.on("data",(e=>this.report(e.toString(),"e"))),this.proc.on("close",this.onProcClose)}runJob(){this.socket=Ls(this.url,{query:{mode:this.mode,jobId:this.jobId}}),this.socket.on("connect",this.spawn),this.socket.on("disconnect",this.onClose)}onClose(){console.log("Server disconnected, terminating process."),this.proc&&this.proc.kill("SIGKILL")}onProcClose(e){this.socket.emit(Ps,e,(()=>this.socket.disconnect())),console.log(`Process finished with code ${e}`)}report(e,t){this.buf[t]+=e,this.buf[t].includes("\n")&&(this.buf[t].endsWith("\n")&&(this.buf[t]=this.buf[t].slice(0,-1)),this.socket.emit(Bs,this.buf[t]),"e"===t?console.error(`err: ${this.buf[t]}`):console.log(`out: ${this.buf[t]}`),this.buf[t]="")}}(Fs,js);Ms.runJob(); +var F=t,M=s,q=r.spawn,W=V;function V(e){e=e||{};var t,s,r=this,o=i,a=n,h={},c=!1,l={"User-Agent":"node-XMLHttpRequest",Accept:"*/*"},d=Object.assign({},l),p=["accept-charset","accept-encoding","access-control-request-headers","access-control-request-method","connection","content-length","content-transfer-encoding","cookie","cookie2","date","expect","host","keep-alive","origin","referer","te","trailer","transfer-encoding","upgrade","via"],u=["TRACE","TRACK","CONNECT"],f=!1,m=!1,_=!1,y={};this.UNSENT=0,this.OPENED=1,this.HEADERS_RECEIVED=2,this.LOADING=3,this.DONE=4,this.readyState=this.UNSENT,this.onreadystatechange=null,this.responseText="",this.responseXML="",this.status=null,this.statusText=null;this.open=function(e,t,s,r,i){if(this.abort(),m=!1,_=!1,!function(e){return e&&-1===u.indexOf(e)}(e))throw new Error("SecurityError: Request method not allowed");h={method:e,url:t.toString(),async:"boolean"!=typeof s||s,user:r||null,password:i||null},g(this.OPENED)},this.setDisableHeaderCheck=function(e){c=e},this.setRequestHeader=function(e,t){if(this.readyState!=this.OPENED)throw new Error("INVALID_STATE_ERR: setRequestHeader can only be called when state is OPEN");if(!function(e){return c||e&&-1===p.indexOf(e.toLowerCase())}(e))return console.warn('Refused to set unsafe header "'+e+'"'),!1;if(f)throw new Error("INVALID_STATE_ERR: send flag is true");return d[e]=t,!0},this.getResponseHeader=function(e){return"string"==typeof e&&this.readyState>this.OPENED&&s.headers[e.toLowerCase()]&&!m?s.headers[e.toLowerCase()]:null},this.getAllResponseHeaders=function(){if(this.readyState{e.unref()})),i&&t.write(i),t.end(),r.dispatchEvent("loadstart")}else{var k=".node-xmlhttprequest-content-"+process.pid,x=".node-xmlhttprequest-sync-"+process.pid;F.writeFileSync(x,"","utf8");for(var O="var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http"+(c?"s":"")+".request;var options = "+JSON.stringify(v)+";var responseText = '';var req = doRequest(options, function(response) {response.setEncoding('utf8');response.on('data', function(chunk) { responseText += chunk;});response.on('end', function() {fs.writeFileSync('"+k+"', 'NODE-XMLHTTPREQUEST-STATUS:' + response.statusCode + ',' + responseText, 'utf8');fs.unlinkSync('"+x+"');});response.on('error', function(error) {fs.writeFileSync('"+k+"', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('"+x+"');});}).on('error', function(error) {fs.writeFileSync('"+k+"', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('"+x+"');});"+(i?"req.write('"+JSON.stringify(i).slice(1,-1).replace(/'/g,"\\'")+"');":"")+"req.end();",T=q(process.argv[0],["-e",O]);F.existsSync(x););if(r.responseText=F.readFileSync(k,"utf8"),T.stdin.end(),F.unlinkSync(k),r.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)){var N=r.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/,"");r.handleError(N,503)}else r.status=r.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:([0-9]*),.*/,"$1"),r.responseText=r.responseText.replace(/^NODE-XMLHTTPREQUEST-STATUS:[0-9]*,(.*)/,"$1"),g(r.DONE)}}},this.handleError=function(e,t){this.status=t||0,this.statusText=e,this.responseText=e.stack,m=!0,g(this.DONE)},this.abort=function(){t&&(t.abort(),t=null),d=Object.assign({},l),this.responseText="",this.responseXML="",m=_=!0,this.readyState===this.UNSENT||this.readyState===this.OPENED&&!f||this.readyState===this.DONE||(f=!1,g(this.DONE)),this.readyState=this.UNSENT},this.addEventListener=function(e,t){e in y||(y[e]=[]),y[e].push(t)},this.removeEventListener=function(e,t){e in y&&(y[e]=y[e].filter((function(e){return e!==t})))},this.dispatchEvent=function(e){if("function"==typeof r["on"+e]&&(this.readyState===this.DONE?setImmediate((function(){r["on"+e]()})):r["on"+e]()),e in y)for(let t=0,s=y[e].length;t{4===t.readyState&&(200===t.status||1223===t.status?this.onLoad():this.setTimeoutFn((()=>{this.onError("number"==typeof t.status?t.status:0)}),0))},t.send(this.data)}catch(e){return void this.setTimeoutFn((()=>{this.onError(e)}),0)}"undefined"!=typeof document&&(this.index=z.requestsCount++,z.requests[this.index]=this)}onError(e){this.emitReserved("error",e,this.xhr),this.cleanup(!0)}cleanup(e){if(void 0!==this.xhr&&null!==this.xhr){if(this.xhr.onreadystatechange=G,e)try{this.xhr.abort()}catch(e){}"undefined"!=typeof document&&delete z.requests[this.index],this.xhr=null}}onLoad(){const e=this.xhr.responseText;null!==e&&(this.emitReserved("data",e),this.emitReserved("success"),this.cleanup())}abort(){this.cleanup()}}if(z.requestsCount=0,z.requests={},"undefined"!=typeof document)if("function"==typeof attachEvent)attachEvent("onunload",Y);else if("function"==typeof addEventListener){addEventListener("onpagehide"in k?"pagehide":"unload",Y,!1)}function Y(){for(let e in z.requests)z.requests.hasOwnProperty(e)&&z.requests[e].abort()}var K={exports:{}},X={BINARY_TYPES:["nodebuffer","arraybuffer","fragments"],EMPTY_BUFFER:Buffer.alloc(0),GUID:"258EAFA5-E914-47DA-95CA-C5AB0DC85B11",kForOnEventAttribute:Symbol("kIsForOnEventAttribute"),kListener:Symbol("kListener"),kStatusCode:Symbol("status-code"),kWebSocket:Symbol("websocket"),NOOP:()=>{}};const{EMPTY_BUFFER:J}=X;function Q(e,t){if(0===e.length)return J;if(1===e.length)return e[0];const s=Buffer.allocUnsafe(t);let r=0;for(let t=0;t{this.pending--,this[ie]()},this.concurrency=e||1/0,this.jobs=[],this.pending=0}add(e){this.jobs.push(e),this[ie]()}[ie](){if(this.pending!==this.concurrency&&this.jobs.length){const e=this.jobs.shift();this.pending++,e(this[re])}}};const oe=a,ae=K.exports,he=ne,{kStatusCode:ce}=X,le=Buffer.from([0,0,255,255]),de=Symbol("permessage-deflate"),pe=Symbol("total-length"),ue=Symbol("callback"),fe=Symbol("buffers"),me=Symbol("error");let _e;var ye=class{constructor(e,t,s){if(this._maxPayload=0|s,this._options=e||{},this._threshold=void 0!==this._options.threshold?this._options.threshold:1024,this._isServer=!!t,this._deflate=null,this._inflate=null,this.params=null,!_e){const e=void 0!==this._options.concurrencyLimit?this._options.concurrencyLimit:10;_e=new he(e)}}static get extensionName(){return"permessage-deflate"}offer(){const e={};return this._options.serverNoContextTakeover&&(e.server_no_context_takeover=!0),this._options.clientNoContextTakeover&&(e.client_no_context_takeover=!0),this._options.serverMaxWindowBits&&(e.server_max_window_bits=this._options.serverMaxWindowBits),this._options.clientMaxWindowBits?e.client_max_window_bits=this._options.clientMaxWindowBits:null==this._options.clientMaxWindowBits&&(e.client_max_window_bits=!0),e}accept(e){return e=this.normalizeParams(e),this.params=this._isServer?this.acceptAsServer(e):this.acceptAsClient(e),this.params}cleanup(){if(this._inflate&&(this._inflate.close(),this._inflate=null),this._deflate){const e=this._deflate[ue];this._deflate.close(),this._deflate=null,e&&e(new Error("The deflate stream was closed while data was being processed"))}}acceptAsServer(e){const t=this._options,s=e.find((e=>!(!1===t.serverNoContextTakeover&&e.server_no_context_takeover||e.server_max_window_bits&&(!1===t.serverMaxWindowBits||"number"==typeof t.serverMaxWindowBits&&t.serverMaxWindowBits>e.server_max_window_bits)||"number"==typeof t.clientMaxWindowBits&&!e.client_max_window_bits)));if(!s)throw new Error("None of the extension offers can be accepted");return t.serverNoContextTakeover&&(s.server_no_context_takeover=!0),t.clientNoContextTakeover&&(s.client_no_context_takeover=!0),"number"==typeof t.serverMaxWindowBits&&(s.server_max_window_bits=t.serverMaxWindowBits),"number"==typeof t.clientMaxWindowBits?s.client_max_window_bits=t.clientMaxWindowBits:!0!==s.client_max_window_bits&&!1!==t.clientMaxWindowBits||delete s.client_max_window_bits,s}acceptAsClient(e){const t=e[0];if(!1===this._options.clientNoContextTakeover&&t.client_no_context_takeover)throw new Error('Unexpected parameter "client_no_context_takeover"');if(t.client_max_window_bits){if(!1===this._options.clientMaxWindowBits||"number"==typeof this._options.clientMaxWindowBits&&t.client_max_window_bits>this._options.clientMaxWindowBits)throw new Error('Unexpected or invalid parameter "client_max_window_bits"')}else"number"==typeof this._options.clientMaxWindowBits&&(t.client_max_window_bits=this._options.clientMaxWindowBits);return t}normalizeParams(e){return e.forEach((e=>{Object.keys(e).forEach((t=>{let s=e[t];if(s.length>1)throw new Error(`Parameter "${t}" must have only a single value`);if(s=s[0],"client_max_window_bits"===t){if(!0!==s){const e=+s;if(!Number.isInteger(e)||e<8||e>15)throw new TypeError(`Invalid value for parameter "${t}": ${s}`);s=e}else if(!this._isServer)throw new TypeError(`Invalid value for parameter "${t}": ${s}`)}else if("server_max_window_bits"===t){const e=+s;if(!Number.isInteger(e)||e<8||e>15)throw new TypeError(`Invalid value for parameter "${t}": ${s}`);s=e}else{if("client_no_context_takeover"!==t&&"server_no_context_takeover"!==t)throw new Error(`Unknown parameter "${t}"`);if(!0!==s)throw new TypeError(`Invalid value for parameter "${t}": ${s}`)}e[t]=s}))})),e}decompress(e,t,s){_e.add((r=>{this._decompress(e,t,((e,t)=>{r(),s(e,t)}))}))}compress(e,t,s){_e.add((r=>{this._compress(e,t,((e,t)=>{r(),s(e,t)}))}))}_decompress(e,t,s){const r=this._isServer?"client":"server";if(!this._inflate){const e=`${r}_max_window_bits`,t="number"!=typeof this.params[e]?oe.Z_DEFAULT_WINDOWBITS:this.params[e];this._inflate=oe.createInflateRaw({...this._options.zlibInflateOptions,windowBits:t}),this._inflate[de]=this,this._inflate[pe]=0,this._inflate[fe]=[],this._inflate.on("error",ve),this._inflate.on("data",be)}this._inflate[ue]=s,this._inflate.write(e),t&&this._inflate.write(le),this._inflate.flush((()=>{const e=this._inflate[me];if(e)return this._inflate.close(),this._inflate=null,void s(e);const i=ae.concat(this._inflate[fe],this._inflate[pe]);this._inflate._readableState.endEmitted?(this._inflate.close(),this._inflate=null):(this._inflate[pe]=0,this._inflate[fe]=[],t&&this.params[`${r}_no_context_takeover`]&&this._inflate.reset()),s(null,i)}))}_compress(e,t,s){const r=this._isServer?"server":"client";if(!this._deflate){const e=`${r}_max_window_bits`,t="number"!=typeof this.params[e]?oe.Z_DEFAULT_WINDOWBITS:this.params[e];this._deflate=oe.createDeflateRaw({...this._options.zlibDeflateOptions,windowBits:t}),this._deflate[pe]=0,this._deflate[fe]=[],this._deflate.on("data",ge)}this._deflate[ue]=s,this._deflate.write(e),this._deflate.flush(oe.Z_SYNC_FLUSH,(()=>{if(!this._deflate)return;let e=ae.concat(this._deflate[fe],this._deflate[pe]);t&&(e=e.slice(0,e.length-4)),this._deflate[ue]=null,this._deflate[pe]=0,this._deflate[fe]=[],t&&this.params[`${r}_no_context_takeover`]&&this._deflate.reset(),s(null,e)}))}};function ge(e){this[fe].push(e),this[pe]+=e.length}function be(e){this[pe]+=e.length,this[de]._maxPayload<1||this[pe]<=this[de]._maxPayload?this[fe].push(e):(this[me]=new RangeError("Max payload size exceeded"),this[me].code="WS_ERR_UNSUPPORTED_MESSAGE_LENGTH",this[me][ce]=1009,this.removeListener("data",be),this.reset())}function ve(e){this[de]._inflate=null,e[ce]=1007,this[ue](e)}var Ee={exports:{}};const we=[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,1,1,1,1,1,0,0,1,1,0,1,1,0,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,0,1,0];function Se(e){return e>=1e3&&e<=1014&&1004!==e&&1005!==e&&1006!==e||e>=3e3&&e<=4999}function ke(e){const t=e.length;let s=0;for(;s=t||128!=(192&e[s+1])||128!=(192&e[s+2])||224===e[s]&&128==(224&e[s+1])||237===e[s]&&160==(224&e[s+1]))return!1;s+=3}else{if(240!=(248&e[s]))return!1;if(s+3>=t||128!=(192&e[s+1])||128!=(192&e[s+2])||128!=(192&e[s+3])||240===e[s]&&128==(240&e[s+1])||244===e[s]&&e[s+1]>143||e[s]>244)return!1;s+=4}return!0}try{const e=require("utf-8-validate");Ee.exports={isValidStatusCode:Se,isValidUTF8:t=>t.length<150?ke(t):e(t),tokenChars:we}}catch(e){Ee.exports={isValidStatusCode:Se,isValidUTF8:ke,tokenChars:we}}const{Writable:xe}=o,Oe=ye,{BINARY_TYPES:Te,EMPTY_BUFFER:Ne,kStatusCode:Re,kWebSocket:Ce}=X,{concat:Le,toArrayBuffer:Ae,unmask:Be}=K.exports,{isValidStatusCode:Pe,isValidUTF8:Ie}=Ee.exports;var De=class extends xe{constructor(e={}){super(),this._binaryType=e.binaryType||Te[0],this._extensions=e.extensions||{},this._isServer=!!e.isServer,this._maxPayload=0|e.maxPayload,this._skipUTF8Validation=!!e.skipUTF8Validation,this[Ce]=void 0,this._bufferedBytes=0,this._buffers=[],this._compressed=!1,this._payloadLength=0,this._mask=void 0,this._fragmented=0,this._masked=!1,this._fin=!1,this._opcode=0,this._totalPayloadLength=0,this._messageLength=0,this._fragments=[],this._state=0,this._loop=!1}_write(e,t,s){if(8===this._opcode&&0==this._state)return s();this._bufferedBytes+=e.length,this._buffers.push(e),this.startLoop(s)}consume(e){if(this._bufferedBytes-=e,e===this._buffers[0].length)return this._buffers.shift();if(e=s.length?t.set(this._buffers.shift(),r):(t.set(new Uint8Array(s.buffer,s.byteOffset,e),r),this._buffers[0]=s.slice(e)),e-=s.length}while(e>0);return t}startLoop(e){let t;this._loop=!0;do{switch(this._state){case 0:t=this.getInfo();break;case 1:t=this.getPayloadLength16();break;case 2:t=this.getPayloadLength64();break;case 3:this.getMask();break;case 4:t=this.getData(e);break;default:return void(this._loop=!1)}}while(this._loop);e(t)}getInfo(){if(this._bufferedBytes<2)return void(this._loop=!1);const e=this.consume(2);if(0!=(48&e[0]))return this._loop=!1,Ue(RangeError,"RSV2 and RSV3 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_2_3");const t=64==(64&e[0]);if(t&&!this._extensions[Oe.extensionName])return this._loop=!1,Ue(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");if(this._fin=128==(128&e[0]),this._opcode=15&e[0],this._payloadLength=127&e[1],0===this._opcode){if(t)return this._loop=!1,Ue(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");if(!this._fragmented)return this._loop=!1,Ue(RangeError,"invalid opcode 0",!0,1002,"WS_ERR_INVALID_OPCODE");this._opcode=this._fragmented}else if(1===this._opcode||2===this._opcode){if(this._fragmented)return this._loop=!1,Ue(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");this._compressed=t}else{if(!(this._opcode>7&&this._opcode<11))return this._loop=!1,Ue(RangeError,`invalid opcode ${this._opcode}`,!0,1002,"WS_ERR_INVALID_OPCODE");if(!this._fin)return this._loop=!1,Ue(RangeError,"FIN must be set",!0,1002,"WS_ERR_EXPECTED_FIN");if(t)return this._loop=!1,Ue(RangeError,"RSV1 must be clear",!0,1002,"WS_ERR_UNEXPECTED_RSV_1");if(this._payloadLength>125)return this._loop=!1,Ue(RangeError,`invalid payload length ${this._payloadLength}`,!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH")}if(this._fin||this._fragmented||(this._fragmented=this._opcode),this._masked=128==(128&e[1]),this._isServer){if(!this._masked)return this._loop=!1,Ue(RangeError,"MASK must be set",!0,1002,"WS_ERR_EXPECTED_MASK")}else if(this._masked)return this._loop=!1,Ue(RangeError,"MASK must be clear",!0,1002,"WS_ERR_UNEXPECTED_MASK");if(126===this._payloadLength)this._state=1;else{if(127!==this._payloadLength)return this.haveLength();this._state=2}}getPayloadLength16(){if(!(this._bufferedBytes<2))return this._payloadLength=this.consume(2).readUInt16BE(0),this.haveLength();this._loop=!1}getPayloadLength64(){if(this._bufferedBytes<8)return void(this._loop=!1);const e=this.consume(8),t=e.readUInt32BE(0);return t>Math.pow(2,21)-1?(this._loop=!1,Ue(RangeError,"Unsupported WebSocket frame: payload length > 2^53 - 1",!1,1009,"WS_ERR_UNSUPPORTED_DATA_PAYLOAD_LENGTH")):(this._payloadLength=t*Math.pow(2,32)+e.readUInt32BE(4),this.haveLength())}haveLength(){if(this._payloadLength&&this._opcode<8&&(this._totalPayloadLength+=this._payloadLength,this._totalPayloadLength>this._maxPayload&&this._maxPayload>0))return this._loop=!1,Ue(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH");this._masked?this._state=3:this._state=4}getMask(){this._bufferedBytes<4?this._loop=!1:(this._mask=this.consume(4),this._state=4)}getData(e){let t=Ne;if(this._payloadLength){if(this._bufferedBytes7?this.controlMessage(t):this._compressed?(this._state=5,void this.decompress(t,e)):(t.length&&(this._messageLength=this._totalPayloadLength,this._fragments.push(t)),this.dataMessage())}decompress(e,t){this._extensions[Oe.extensionName].decompress(e,this._fin,((e,s)=>{if(e)return t(e);if(s.length){if(this._messageLength+=s.length,this._messageLength>this._maxPayload&&this._maxPayload>0)return t(Ue(RangeError,"Max payload size exceeded",!1,1009,"WS_ERR_UNSUPPORTED_MESSAGE_LENGTH"));this._fragments.push(s)}const r=this.dataMessage();if(r)return t(r);this.startLoop(t)}))}dataMessage(){if(this._fin){const e=this._messageLength,t=this._fragments;if(this._totalPayloadLength=0,this._messageLength=0,this._fragmented=0,this._fragments=[],2===this._opcode){let s;s="nodebuffer"===this._binaryType?Le(t,e):"arraybuffer"===this._binaryType?Ae(Le(t,e)):t,this.emit("message",s,!0)}else{const s=Le(t,e);if(!this._skipUTF8Validation&&!Ie(s))return this._loop=!1,Ue(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");this.emit("message",s,!1)}}this._state=0}controlMessage(e){if(8===this._opcode)if(this._loop=!1,0===e.length)this.emit("conclude",1005,Ne),this.end();else{if(1===e.length)return Ue(RangeError,"invalid payload length 1",!0,1002,"WS_ERR_INVALID_CONTROL_PAYLOAD_LENGTH");{const t=e.readUInt16BE(0);if(!Pe(t))return Ue(RangeError,`invalid status code ${t}`,!0,1002,"WS_ERR_INVALID_CLOSE_CODE");const s=e.slice(2);if(!this._skipUTF8Validation&&!Ie(s))return Ue(Error,"invalid UTF-8 sequence",!0,1007,"WS_ERR_INVALID_UTF8");this.emit("conclude",t,s),this.end()}}else 9===this._opcode?this.emit("ping",e):this.emit("pong",e);this._state=0}};function Ue(e,t,s,r,i){const n=new e(s?`Invalid WebSocket frame: ${t}`:t);return Error.captureStackTrace(n,Ue),n.code=i,n[Re]=r,n}const{randomFillSync:je}=l,Fe=ye,{EMPTY_BUFFER:Me}=X,{isValidStatusCode:qe}=Ee.exports,{mask:We,toBuffer:Ve}=K.exports,$e=Buffer.alloc(4);class Ge{constructor(e,t){this._extensions=t||{},this._socket=e,this._firstFragment=!0,this._compress=!1,this._bufferedBytes=0,this._deflating=!1,this._queue=[]}static frame(e,t){const s=t.mask&&t.readOnly;let r=t.mask?6:2,i=e.length;e.length>=65536?(r+=8,i=127):e.length>125&&(r+=2,i=126);const n=Buffer.allocUnsafe(s?e.length+r:r);return n[0]=t.fin?128|t.opcode:t.opcode,t.rsv1&&(n[0]|=64),n[1]=i,126===i?n.writeUInt16BE(e.length,2):127===i&&(n.writeUInt32BE(0,2),n.writeUInt32BE(e.length,6)),t.mask?(je($e,0,4),n[1]|=128,n[r-4]=$e[0],n[r-3]=$e[1],n[r-2]=$e[2],n[r-1]=$e[3],s?(We(e,$e,n,r,e.length),[n]):(We(e,$e,e,0,e.length),[n,e])):[n,e]}close(e,t,s,r){let i;if(void 0===e)i=Me;else{if("number"!=typeof e||!qe(e))throw new TypeError("First argument must be a valid error code number");if(void 0!==t&&t.length){const s=Buffer.byteLength(t);if(s>123)throw new RangeError("The message must not be greater than 123 bytes");i=Buffer.allocUnsafe(2+s),i.writeUInt16BE(e,0),"string"==typeof t?i.write(t,2):i.set(t,2)}else i=Buffer.allocUnsafe(2),i.writeUInt16BE(e,0)}this._deflating?this.enqueue([this.doClose,i,s,r]):this.doClose(i,s,r)}doClose(e,t,s){this.sendFrame(Ge.frame(e,{fin:!0,rsv1:!1,opcode:8,mask:t,readOnly:!1}),s)}ping(e,t,s){const r=Ve(e);if(r.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPing,r,t,Ve.readOnly,s]):this.doPing(r,t,Ve.readOnly,s)}doPing(e,t,s,r){this.sendFrame(Ge.frame(e,{fin:!0,rsv1:!1,opcode:9,mask:t,readOnly:s}),r)}pong(e,t,s){const r=Ve(e);if(r.length>125)throw new RangeError("The data size must not be greater than 125 bytes");this._deflating?this.enqueue([this.doPong,r,t,Ve.readOnly,s]):this.doPong(r,t,Ve.readOnly,s)}doPong(e,t,s,r){this.sendFrame(Ge.frame(e,{fin:!0,rsv1:!1,opcode:10,mask:t,readOnly:s}),r)}send(e,t,s){const r=Ve(e),i=this._extensions[Fe.extensionName];let n=t.binary?2:1,o=t.compress;if(this._firstFragment?(this._firstFragment=!1,o&&i&&i.params[i._isServer?"server_no_context_takeover":"client_no_context_takeover"]&&(o=r.length>=i._threshold),this._compress=o):(o=!1,n=0),t.fin&&(this._firstFragment=!0),i){const e={fin:t.fin,rsv1:o,opcode:n,mask:t.mask,readOnly:Ve.readOnly};this._deflating?this.enqueue([this.dispatch,r,this._compress,e,s]):this.dispatch(r,this._compress,e,s)}else this.sendFrame(Ge.frame(r,{fin:t.fin,rsv1:!1,opcode:n,mask:t.mask,readOnly:Ve.readOnly}),s)}dispatch(e,t,s,r){if(!t)return void this.sendFrame(Ge.frame(e,s),r);const i=this._extensions[Fe.extensionName];this._bufferedBytes+=e.length,this._deflating=!0,i.compress(e,s.fin,((t,i)=>{if(this._socket.destroyed){const e=new Error("The socket was closed while data was being compressed");"function"==typeof r&&r(e);for(let t=0;t{let s=e[t];return Array.isArray(s)||(s=[s]),s.map((e=>[t].concat(Object.keys(e).map((t=>{let s=e[t];return Array.isArray(s)||(s=[s]),s.map((e=>!0===e?t:`${t}=${e}`)).join("; ")}))).join("; "))).join(", ")})).join(", ")},parse:function(e){const t=Object.create(null);let s,r,i=Object.create(null),n=!1,o=!1,a=!1,h=-1,c=-1,l=-1,d=0;for(;d0&&e.unshift(t),e.on("close",Zt),e.on("data",es),e.on("end",ts),e.on("error",ss),this._readyState=Ft.OPEN,this.emit("open")}emitClose(){if(!this._socket)return this._readyState=Ft.CLOSED,void this.emit("close",this._closeCode,this._closeMessage);this._extensions[vt.extensionName]&&this._extensions[vt.extensionName].cleanup(),this._receiver.removeAllListeners(),this._readyState=Ft.CLOSED,this.emit("close",this._closeCode,this._closeMessage)}close(e,t){if(this.readyState!==Ft.CLOSED){if(this.readyState===Ft.CONNECTING){const e="WebSocket was closed before the connection was established";return Vt(this,this._req,e)}this.readyState!==Ft.CLOSING?(this._readyState=Ft.CLOSING,this._sender.close(e,t,!this._isServer,(e=>{e||(this._closeFrameSent=!0,(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end())})),this._closeTimer=setTimeout(this._socket.destroy.bind(this._socket),3e4)):this._closeFrameSent&&(this._closeFrameReceived||this._receiver._writableState.errorEmitted)&&this._socket.end()}}ping(e,t,s){if(this.readyState===Ft.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");"function"==typeof e?(s=e,e=t=void 0):"function"==typeof t&&(s=t,t=void 0),"number"==typeof e&&(e=e.toString()),this.readyState===Ft.OPEN?(void 0===t&&(t=!this._isServer),this._sender.ping(e||kt,t,s)):$t(this,e,s)}pong(e,t,s){if(this.readyState===Ft.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");"function"==typeof e?(s=e,e=t=void 0):"function"==typeof t&&(s=t,t=void 0),"number"==typeof e&&(e=e.toString()),this.readyState===Ft.OPEN?(void 0===t&&(t=!this._isServer),this._sender.pong(e||kt,t,s)):$t(this,e,s)}send(e,t,s){if(this.readyState===Ft.CONNECTING)throw new Error("WebSocket is not open: readyState 0 (CONNECTING)");if("function"==typeof t&&(s=t,t={}),"number"==typeof e&&(e=e.toString()),this.readyState!==Ft.OPEN)return void $t(this,e,s);const r={binary:"string"!=typeof e,mask:!this._isServer,compress:!0,fin:!0,...t};this._extensions[vt.extensionName]||(r.compress=!1),this._sender.send(e||kt,r,s)}terminate(){if(this.readyState!==Ft.CLOSED){if(this.readyState===Ft.CONNECTING){const e="WebSocket was closed before the connection was established";return Vt(this,this._req,e)}this._socket&&(this._readyState=Ft.CLOSING,this._socket.destroy())}}}function Mt(e,t,s,r){const i={protocolVersion:jt[1],maxPayload:104857600,skipUTF8Validation:!1,perMessageDeflate:!0,followRedirects:!1,maxRedirects:10,...r,createConnection:void 0,socketPath:void 0,hostname:void 0,protocol:void 0,timeout:void 0,method:void 0,host:void 0,path:void 0,port:void 0};if(!jt.includes(i.protocolVersion))throw new RangeError(`Unsupported protocol version: ${i.protocolVersion} (supported versions: ${jt.join(", ")})`);let n;if(t instanceof bt)n=t,e._url=t.href;else{try{n=new bt(t)}catch(e){throw new SyntaxError(`Invalid URL: ${t}`)}e._url=t}const o="wss:"===n.protocol,a="ws+unix:"===n.protocol;if("ws:"!==n.protocol&&!o&&!a)throw new SyntaxError('The URL\'s protocol must be one of "ws:", "wss:", or "ws+unix:"');if(a&&!n.pathname)throw new SyntaxError("The URL's pathname is empty");if(n.hash)throw new SyntaxError("The URL contains a fragment identifier");const h=o?443:80,c=yt(16).toString("base64"),l=o?ut.get:ft.get,d=new Set;let p;if(i.createConnection=o?Wt:qt,i.defaultPort=i.defaultPort||h,i.port=n.port||h,i.host=n.hostname.startsWith("[")?n.hostname.slice(1,-1):n.hostname,i.headers={"Sec-WebSocket-Version":i.protocolVersion,"Sec-WebSocket-Key":c,Connection:"Upgrade",Upgrade:"websocket",...i.headers},i.path=n.pathname+n.search,i.timeout=i.handshakeTimeout,i.perMessageDeflate&&(p=new vt(!0!==i.perMessageDeflate?i.perMessageDeflate:{},!1,i.maxPayload),i.headers["Sec-WebSocket-Extensions"]=Bt({[vt.extensionName]:p.offer()})),s.length){for(const e of s){if("string"!=typeof e||!Ut.test(e)||d.has(e))throw new SyntaxError("An invalid or duplicated subprotocol was specified");d.add(e)}i.headers["Sec-WebSocket-Protocol"]=s.join(",")}if(i.origin&&(i.protocolVersion<13?i.headers["Sec-WebSocket-Origin"]=i.origin:i.headers.Origin=i.origin),(n.username||n.password)&&(i.auth=`${n.username}:${n.password}`),a){const e=i.path.split(":");i.socketPath=e[0],i.path=e[1]}let u=e._req=l(i);i.timeout&&u.on("timeout",(()=>{Vt(e,u,"Opening handshake has timed out")})),u.on("error",(t=>{null===u||u.aborted||(u=e._req=null,e._readyState=Ft.CLOSING,e.emit("error",t),e.emitClose())})),u.on("response",(n=>{const o=n.headers.location,a=n.statusCode;if(o&&i.followRedirects&&a>=300&&a<400){if(++e._redirects>i.maxRedirects)return void Vt(e,u,"Maximum redirects exceeded");u.abort();const n=new bt(o,t);Mt(e,n,s,r)}else e.emit("unexpected-response",u,n)||Vt(e,u,`Unexpected server response: ${n.statusCode}`)})),u.on("upgrade",((t,s,r)=>{if(e.emit("upgrade",t),e.readyState!==Ft.CONNECTING)return;u=e._req=null;const n=gt("sha1").update(c+xt).digest("base64");if(t.headers["sec-websocket-accept"]!==n)return void Vt(e,s,"Invalid Sec-WebSocket-Accept header");const o=t.headers["sec-websocket-protocol"];let a;if(void 0!==o?d.size?d.has(o)||(a="Server sent an invalid subprotocol"):a="Server sent a subprotocol but none was requested":d.size&&(a="Server sent no subprotocol"),a)return void Vt(e,s,a);o&&(e._protocol=o);const h=t.headers["sec-websocket-extensions"];if(void 0!==h){if(!p){return void Vt(e,s,"Server sent a Sec-WebSocket-Extensions header but no extension was requested")}let t;try{t=Pt(h)}catch(t){return void Vt(e,s,"Invalid Sec-WebSocket-Extensions header")}const r=Object.keys(t);if(1!==r.length||r[0]!==vt.extensionName){return void Vt(e,s,"Server indicated an extension that was not requested")}try{p.accept(t[vt.extensionName])}catch(t){return void Vt(e,s,"Invalid Sec-WebSocket-Extensions header")}e._extensions[vt.extensionName]=p}e.setSocket(s,r,{maxPayload:i.maxPayload,skipUTF8Validation:i.skipUTF8Validation})}))}function qt(e){return e.path=e.socketPath,mt.connect(e)}function Wt(e){return e.path=void 0,e.servername||""===e.servername||(e.servername=mt.isIP(e.host)?"":e.host),_t.connect(e)}function Vt(e,t,s){e._readyState=Ft.CLOSING;const r=new Error(s);Error.captureStackTrace(r,Vt),t.setHeader?(t.abort(),t.socket&&!t.socket.destroyed&&t.socket.destroy(),t.once("abort",e.emitClose.bind(e)),e.emit("error",r)):(t.destroy(r),t.once("error",e.emit.bind(e,"error")),t.once("close",e.emitClose.bind(e)))}function $t(e,t,s){if(t){const s=It(t).length;e._socket?e._sender._bufferedBytes+=s:e._bufferedAmount+=s}if(s){s(new Error(`WebSocket is not open: readyState ${e.readyState} (${Dt[e.readyState]})`))}}function Gt(e,t){const s=this[Rt];s._closeFrameReceived=!0,s._closeMessage=t,s._closeCode=e,void 0!==s._socket[Rt]&&(s._socket.removeListener("data",es),process.nextTick(Qt,s._socket),1005===e?s.close():s.close(e,t))}function Ht(){this[Rt]._socket.resume()}function zt(e){const t=this[Rt];void 0!==t._socket[Rt]&&(t._socket.removeListener("data",es),process.nextTick(Qt,t._socket),t.close(e[Nt])),t.emit("error",e)}function Yt(){this[Rt].emitClose()}function Kt(e,t){this[Rt].emit("message",e,t)}function Xt(e){const t=this[Rt];t.pong(e,!t._isServer,Ct),t.emit("ping",e)}function Jt(e){this[Rt].emit("pong",e)}function Qt(e){e.resume()}function Zt(){const e=this[Rt];let t;this.removeListener("close",Zt),this.removeListener("data",es),this.removeListener("end",ts),e._readyState=Ft.CLOSING,this._readableState.endEmitted||e._closeFrameReceived||e._receiver._writableState.errorEmitted||null===(t=e._socket.read())||e._receiver.write(t),e._receiver.end(),this[Rt]=void 0,clearTimeout(e._closeTimer),e._receiver._writableState.finished||e._receiver._writableState.errorEmitted?e.emitClose():(e._receiver.on("error",Yt),e._receiver.on("finish",Yt))}function es(e){this[Rt]._receiver.write(e)||this.pause()}function ts(){const e=this[Rt];e._readyState=Ft.CLOSING,e._receiver.end(),this.end()}function ss(){const e=this[Rt];this.removeListener("error",ss),this.on("error",Ct),e&&(e._readyState=Ft.CLOSING,this.destroy())}Object.defineProperty(Ft,"CONNECTING",{enumerable:!0,value:Dt.indexOf("CONNECTING")}),Object.defineProperty(Ft.prototype,"CONNECTING",{enumerable:!0,value:Dt.indexOf("CONNECTING")}),Object.defineProperty(Ft,"OPEN",{enumerable:!0,value:Dt.indexOf("OPEN")}),Object.defineProperty(Ft.prototype,"OPEN",{enumerable:!0,value:Dt.indexOf("OPEN")}),Object.defineProperty(Ft,"CLOSING",{enumerable:!0,value:Dt.indexOf("CLOSING")}),Object.defineProperty(Ft.prototype,"CLOSING",{enumerable:!0,value:Dt.indexOf("CLOSING")}),Object.defineProperty(Ft,"CLOSED",{enumerable:!0,value:Dt.indexOf("CLOSED")}),Object.defineProperty(Ft.prototype,"CLOSED",{enumerable:!0,value:Dt.indexOf("CLOSED")}),["binaryType","bufferedAmount","extensions","protocol","readyState","url"].forEach((e=>{Object.defineProperty(Ft.prototype,e,{enumerable:!0})})),["open","error","close","message"].forEach((e=>{Object.defineProperty(Ft.prototype,`on${e}`,{enumerable:!0,get(){for(const t of this.listeners(e))if(t[Ot])return t[Tt];return null},set(t){for(const t of this.listeners(e))if(t[Ot]){this.removeListener(e,t);break}"function"==typeof t&&this.addEventListener(e,t,{[Ot]:!0})}})})),Ft.prototype.addEventListener=Lt,Ft.prototype.removeEventListener=At;const rs=Ft,is=process.nextTick,ns="undefined"!=typeof navigator&&"string"==typeof navigator.product&&"reactnative"===navigator.product.toLowerCase();const os={websocket:class extends C{constructor(e){super(e),this.supportsBinary=!e.forceBase64}get name(){return"websocket"}doOpen(){if(!this.check())return;const e=this.uri(),t=this.opts.protocols,s=ns?{}:x(this.opts,"agent","perMessageDeflate","pfx","key","passphrase","cert","ca","ciphers","rejectUnauthorized","localAddress","protocolVersion","origin","maxPayload","family","checkServerIdentity");this.opts.extraHeaders&&(s.headers=this.opts.extraHeaders);try{this.ws=new rs(e,t,s)}catch(e){return this.emitReserved("error",e)}this.ws.binaryType=this.socket.binaryType||"nodebuffer",this.addEventListeners()}addEventListeners(){this.ws.onopen=()=>{this.opts.autoUnref&&this.ws._socket.unref(),this.onOpen()},this.ws.onclose=e=>this.onClose({description:"websocket connection closed",context:e}),this.ws.onmessage=e=>this.onData(e.data),this.ws.onerror=e=>this.onError("websocket error",e)}write(e){this.writable=!1;for(let t=0;t{const t={};if(s.options&&(t.compress=s.options.compress),this.opts.perMessageDeflate){("string"==typeof e?Buffer.byteLength(e):e.length){this.writable=!0,this.emitReserved("drain")}),this.setTimeoutFn)}))}}doClose(){void 0!==this.ws&&(this.ws.close(),this.ws=null)}uri(){let e=this.query||{};const t=this.opts.secure?"wss":"ws";let s="";this.opts.port&&("wss"===t&&443!==Number(this.opts.port)||"ws"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port),this.opts.timestampRequests&&(e[this.opts.timestampParam]=U()),this.supportsBinary||(e.b64=1);const r=j(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(r.length?"?"+r:"")}check(){return!!rs}},polling:class extends C{constructor(e){if(super(e),this.polling=!1,"undefined"!=typeof location){const t="https:"===location.protocol;let s=location.port;s||(s=t?"443":"80"),this.xd="undefined"!=typeof location&&e.hostname!==location.hostname||s!==e.port,this.xs=e.secure!==t}const t=e&&e.forceBase64;this.supportsBinary=H&&!t}get name(){return"polling"}doOpen(){this.poll()}pause(e){this.readyState="pausing";const t=()=>{this.readyState="paused",e()};if(this.polling||!this.writable){let e=0;this.polling&&(e++,this.once("pollComplete",(function(){--e||t()}))),this.writable||(e++,this.once("drain",(function(){--e||t()})))}else t()}poll(){this.polling=!0,this.doPoll(),this.emitReserved("poll")}onData(e){((e,t)=>{const s=e.split(w),r=[];for(let e=0;e{if("opening"===this.readyState&&"open"===e.type&&this.onOpen(),"close"===e.type)return this.onClose({description:"transport closed by the server"}),!1;this.onPacket(e)})),"closed"!==this.readyState&&(this.polling=!1,this.emitReserved("pollComplete"),"open"===this.readyState&&this.poll())}doClose(){const e=()=>{this.write([{type:"close"}])};"open"===this.readyState?e():this.once("open",e)}write(e){this.writable=!1,((e,t)=>{const s=e.length,r=new Array(s);let i=0;e.forEach(((e,n)=>{_(e,!1,(e=>{r[n]=e,++i===s&&t(r.join(w))}))}))})(e,(e=>{this.doWrite(e,(()=>{this.writable=!0,this.emitReserved("drain")}))}))}uri(){let e=this.query||{};const t=this.opts.secure?"https":"http";let s="";!1!==this.opts.timestampRequests&&(e[this.opts.timestampParam]=U()),this.supportsBinary||e.sid||(e.b64=1),this.opts.port&&("https"===t&&443!==Number(this.opts.port)||"http"===t&&80!==Number(this.opts.port))&&(s=":"+this.opts.port);const r=j(e);return t+"://"+(-1!==this.opts.hostname.indexOf(":")?"["+this.opts.hostname+"]":this.opts.hostname)+s+this.opts.path+(r.length?"?"+r:"")}request(e={}){return Object.assign(e,{xd:this.xd,xs:this.xs},this.opts),new z(this.uri(),e)}doWrite(e,t){const s=this.request({method:"POST",data:e});s.on("success",t),s.on("error",((e,t)=>{this.onError("xhr post error",e,t)}))}doPoll(){const e=this.request();e.on("data",this.onData.bind(this)),e.on("error",((e,t)=>{this.onError("xhr poll error",e,t)})),this.pollXhr=e}}},as=/^(?:(?![^:@]+:[^:@\/]*@)(http|https|ws|wss):\/\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\/?#]*)(?::(\d*))?)(((\/(?:[^?#](?![^?#\/]*\.[^?#\/.]+(?:[?#]|$)))*\/?)?([^?#\/]*))(?:\?([^#]*))?(?:#(.*))?)/,hs=["source","protocol","authority","userInfo","user","password","host","port","relative","path","directory","file","query","anchor"];function cs(e){const t=e,s=e.indexOf("["),r=e.indexOf("]");-1!=s&&-1!=r&&(e=e.substring(0,s)+e.substring(s,r).replace(/:/g,";")+e.substring(r,e.length));let i=as.exec(e||""),n={},o=14;for(;o--;)n[hs[o]]=i[o]||"";return-1!=s&&-1!=r&&(n.source=t,n.host=n.host.substring(1,n.host.length-1).replace(/;/g,":"),n.authority=n.authority.replace("[","").replace("]","").replace(/;/g,":"),n.ipv6uri=!0),n.pathNames=function(e,t){const s=/\/{2,9}/g,r=t.replace(s,"/").split("/");"/"!=t.substr(0,1)&&0!==t.length||r.splice(0,1);"/"==t.substr(t.length-1,1)&&r.splice(r.length-1,1);return r}(0,n.path),n.queryKey=function(e,t){const s={};return t.replace(/(?:^|&)([^&=]*)=?([^&]*)/g,(function(e,t,r){t&&(s[t]=r)})),s}(0,n.query),n}class ls extends S{constructor(e,t={}){super(),e&&"object"==typeof e&&(t=e,e=null),e?(e=cs(e),t.hostname=e.host,t.secure="https"===e.protocol||"wss"===e.protocol,t.port=e.port,e.query&&(t.query=e.query)):t.host&&(t.hostname=cs(t.host).host),N(this,t),this.secure=null!=t.secure?t.secure:"undefined"!=typeof location&&"https:"===location.protocol,t.hostname&&!t.port&&(t.port=this.secure?"443":"80"),this.hostname=t.hostname||("undefined"!=typeof location?location.hostname:"localhost"),this.port=t.port||("undefined"!=typeof location&&location.port?location.port:this.secure?"443":"80"),this.transports=t.transports||["polling","websocket"],this.readyState="",this.writeBuffer=[],this.prevBufferLen=0,this.opts=Object.assign({path:"/engine.io",agent:!1,withCredentials:!1,upgrade:!0,timestampParam:"t",rememberUpgrade:!1,rejectUnauthorized:!0,perMessageDeflate:{threshold:1024},transportOptions:{},closeOnBeforeunload:!0},t),this.opts.path=this.opts.path.replace(/\/$/,"")+"/","string"==typeof this.opts.query&&(this.opts.query=function(e){let t={},s=e.split("&");for(let e=0,r=s.length;e{this.transport&&(this.transport.removeAllListeners(),this.transport.close())}),!1),"localhost"!==this.hostname&&(this.offlineEventListener=()=>{this.onClose("transport close",{description:"network connection lost"})},addEventListener("offline",this.offlineEventListener,!1))),this.open()}createTransport(e){const t=Object.assign({},this.opts.query);t.EIO=4,t.transport=e,this.id&&(t.sid=this.id);const s=Object.assign({},this.opts.transportOptions[e],this.opts,{query:t,socket:this,hostname:this.hostname,secure:this.secure,port:this.port});return new os[e](s)}open(){let e;if(this.opts.rememberUpgrade&&ls.priorWebsocketSuccess&&-1!==this.transports.indexOf("websocket"))e="websocket";else{if(0===this.transports.length)return void this.setTimeoutFn((()=>{this.emitReserved("error","No transports available")}),0);e=this.transports[0]}this.readyState="opening";try{e=this.createTransport(e)}catch(e){return this.transports.shift(),void this.open()}e.open(),this.setTransport(e)}setTransport(e){this.transport&&this.transport.removeAllListeners(),this.transport=e,e.on("drain",this.onDrain.bind(this)).on("packet",this.onPacket.bind(this)).on("error",this.onError.bind(this)).on("close",(e=>this.onClose("transport close",e)))}probe(e){let t=this.createTransport(e),s=!1;ls.priorWebsocketSuccess=!1;const r=()=>{s||(t.send([{type:"ping",data:"probe"}]),t.once("packet",(e=>{if(!s)if("pong"===e.type&&"probe"===e.data){if(this.upgrading=!0,this.emitReserved("upgrading",t),!t)return;ls.priorWebsocketSuccess="websocket"===t.name,this.transport.pause((()=>{s||"closed"!==this.readyState&&(c(),this.setTransport(t),t.send([{type:"upgrade"}]),this.emitReserved("upgrade",t),t=null,this.upgrading=!1,this.flush())}))}else{const e=new Error("probe error");e.transport=t.name,this.emitReserved("upgradeError",e)}})))};function i(){s||(s=!0,c(),t.close(),t=null)}const n=e=>{const s=new Error("probe error: "+e);s.transport=t.name,i(),this.emitReserved("upgradeError",s)};function o(){n("transport closed")}function a(){n("socket closed")}function h(e){t&&e.name!==t.name&&i()}const c=()=>{t.removeListener("open",r),t.removeListener("error",n),t.removeListener("close",o),this.off("close",a),this.off("upgrading",h)};t.once("open",r),t.once("error",n),t.once("close",o),this.once("close",a),this.once("upgrading",h),t.open()}onOpen(){if(this.readyState="open",ls.priorWebsocketSuccess="websocket"===this.transport.name,this.emitReserved("open"),this.flush(),"open"===this.readyState&&this.opts.upgrade&&this.transport.pause){let e=0;const t=this.upgrades.length;for(;e{this.onClose("ping timeout")}),this.pingInterval+this.pingTimeout),this.opts.autoUnref&&this.pingTimeoutTimer.unref()}onDrain(){this.writeBuffer.splice(0,this.prevBufferLen),this.prevBufferLen=0,0===this.writeBuffer.length?this.emitReserved("drain"):this.flush()}flush(){if("closed"!==this.readyState&&this.transport.writable&&!this.upgrading&&this.writeBuffer.length){const e=this.getWritablePackets();this.transport.send(e),this.prevBufferLen=e.length,this.emitReserved("flush")}}getWritablePackets(){if(!(this.maxPayload&&"polling"===this.transport.name&&this.writeBuffer.length>1))return this.writeBuffer;let e=1;for(let s=0;s=57344?s+=3:(r++,s+=4);return s}(t):Math.ceil(1.33*(t.byteLength||t.size))),s>0&&e>this.maxPayload)return this.writeBuffer.slice(0,s);e+=2}var t;return this.writeBuffer}write(e,t,s){return this.sendPacket("message",e,t,s),this}send(e,t,s){return this.sendPacket("message",e,t,s),this}sendPacket(e,t,s,r){if("function"==typeof t&&(r=t,t=void 0),"function"==typeof s&&(r=s,s=null),"closing"===this.readyState||"closed"===this.readyState)return;(s=s||{}).compress=!1!==s.compress;const i={type:e,data:t,options:s};this.emitReserved("packetCreate",i),this.writeBuffer.push(i),r&&this.once("flush",r),this.flush()}close(){const e=()=>{this.onClose("forced close"),this.transport.close()},t=()=>{this.off("upgrade",t),this.off("upgradeError",t),e()},s=()=>{this.once("upgrade",t),this.once("upgradeError",t)};return"opening"!==this.readyState&&"open"!==this.readyState||(this.readyState="closing",this.writeBuffer.length?this.once("drain",(()=>{this.upgrading?s():e()})):this.upgrading?s():e()),this}onError(e){ls.priorWebsocketSuccess=!1,this.emitReserved("error",e),this.onClose("transport error",e)}onClose(e,t){"opening"!==this.readyState&&"open"!==this.readyState&&"closing"!==this.readyState||(this.clearTimeoutFn(this.pingTimeoutTimer),this.transport.removeAllListeners("close"),this.transport.close(),this.transport.removeAllListeners(),"function"==typeof removeEventListener&&removeEventListener("offline",this.offlineEventListener,!1),this.readyState="closed",this.id=null,this.emitReserved("close",e,t),this.writeBuffer=[],this.prevBufferLen=0)}filterUpgrades(e){const t=[];let s=0;const r=e.length;for(;s"function"==typeof ArrayBuffer.isView?ArrayBuffer.isView(e):e.buffer instanceof ArrayBuffer)(e))||us&&e instanceof Blob||fs&&e instanceof File}function _s(e,t){if(!e||"object"!=typeof e)return!1;if(Array.isArray(e)){for(let t=0,s=e.length;t=0&&e.num0;case Es.ACK:case Es.BINARY_ACK:return Array.isArray(t)}}destroy(){this.reconstructor&&this.reconstructor.finishedReconstruction()}}class Ss{constructor(e){this.packet=e,this.buffers=[],this.reconPack=e}takeBinaryData(e){if(this.buffers.push(e),this.buffers.length===this.reconPack.attachments){const e=bs(this.reconPack,this.buffers);return this.finishedReconstruction(),e}return null}finishedReconstruction(){this.reconPack=null,this.buffers=[]}}var ks=Object.freeze({__proto__:null,protocol:5,get PacketType(){return Es},Encoder:class{constructor(e){this.replacer=e}encode(e){return e.type!==Es.EVENT&&e.type!==Es.ACK||!_s(e)?[this.encodeAsString(e)]:(e.type=e.type===Es.EVENT?Es.BINARY_EVENT:Es.BINARY_ACK,this.encodeAsBinary(e))}encodeAsString(e){let t=""+e.type;return e.type!==Es.BINARY_EVENT&&e.type!==Es.BINARY_ACK||(t+=e.attachments+"-"),e.nsp&&"/"!==e.nsp&&(t+=e.nsp+","),null!=e.id&&(t+=e.id),null!=e.data&&(t+=JSON.stringify(e.data,this.replacer)),t}encodeAsBinary(e){const t=ys(e),s=this.encodeAsString(t.packet),r=t.buffers;return r.unshift(s),r}},Decoder:ws});function xs(e,t,s){return e.on(t,s),function(){e.off(t,s)}}const Os=Object.freeze({connect:1,connect_error:1,disconnect:1,disconnecting:1,newListener:1,removeListener:1});class Ts extends S{constructor(e,t,s){super(),this.connected=!1,this.receiveBuffer=[],this.sendBuffer=[],this.ids=0,this.acks={},this.flags={},this.io=e,this.nsp=t,s&&s.auth&&(this.auth=s.auth),this.io._autoConnect&&this.open()}get disconnected(){return!this.connected}subEvents(){if(this.subs)return;const e=this.io;this.subs=[xs(e,"open",this.onopen.bind(this)),xs(e,"packet",this.onpacket.bind(this)),xs(e,"error",this.onerror.bind(this)),xs(e,"close",this.onclose.bind(this))]}get active(){return!!this.subs}connect(){return this.connected||(this.subEvents(),this.io._reconnecting||this.io.open(),"open"===this.io._readyState&&this.onopen()),this}open(){return this.connect()}send(...e){return e.unshift("message"),this.emit.apply(this,e),this}emit(e,...t){if(Os.hasOwnProperty(e))throw new Error('"'+e.toString()+'" is a reserved event name');t.unshift(e);const s={type:Es.EVENT,data:t,options:{}};if(s.options.compress=!1!==this.flags.compress,"function"==typeof t[t.length-1]){const e=this.ids++,r=t.pop();this._registerAckCallback(e,r),s.id=e}const r=this.io.engine&&this.io.engine.transport&&this.io.engine.transport.writable;return this.flags.volatile&&(!r||!this.connected)||(this.connected?(this.notifyOutgoingListeners(s),this.packet(s)):this.sendBuffer.push(s)),this.flags={},this}_registerAckCallback(e,t){const s=this.flags.timeout;if(void 0===s)return void(this.acks[e]=t);const r=this.io.setTimeoutFn((()=>{delete this.acks[e];for(let t=0;t{this.io.clearTimeoutFn(r),t.apply(this,[null,...e])}}packet(e){e.nsp=this.nsp,this.io._packet(e)}onopen(){"function"==typeof this.auth?this.auth((e=>{this.packet({type:Es.CONNECT,data:e})})):this.packet({type:Es.CONNECT,data:this.auth})}onerror(e){this.connected||this.emitReserved("connect_error",e)}onclose(e,t){this.connected=!1,delete this.id,this.emitReserved("disconnect",e,t)}onpacket(e){if(e.nsp===this.nsp)switch(e.type){case Es.CONNECT:if(e.data&&e.data.sid){const t=e.data.sid;this.onconnect(t)}else this.emitReserved("connect_error",new Error("It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)"));break;case Es.EVENT:case Es.BINARY_EVENT:this.onevent(e);break;case Es.ACK:case Es.BINARY_ACK:this.onack(e);break;case Es.DISCONNECT:this.ondisconnect();break;case Es.CONNECT_ERROR:this.destroy();const t=new Error(e.data.message);t.data=e.data.data,this.emitReserved("connect_error",t)}}onevent(e){const t=e.data||[];null!=e.id&&t.push(this.ack(e.id)),this.connected?this.emitEvent(t):this.receiveBuffer.push(Object.freeze(t))}emitEvent(e){if(this._anyListeners&&this._anyListeners.length){const t=this._anyListeners.slice();for(const s of t)s.apply(this,e)}super.emit.apply(this,e)}ack(e){const t=this;let s=!1;return function(...r){s||(s=!0,t.packet({type:Es.ACK,id:e,data:r}))}}onack(e){const t=this.acks[e.id];"function"==typeof t&&(t.apply(this,e.data),delete this.acks[e.id])}onconnect(e){this.id=e,this.connected=!0,this.emitBuffered(),this.emitReserved("connect")}emitBuffered(){this.receiveBuffer.forEach((e=>this.emitEvent(e))),this.receiveBuffer=[],this.sendBuffer.forEach((e=>{this.notifyOutgoingListeners(e),this.packet(e)})),this.sendBuffer=[]}ondisconnect(){this.destroy(),this.onclose("io server disconnect")}destroy(){this.subs&&(this.subs.forEach((e=>e())),this.subs=void 0),this.io._destroy(this)}disconnect(){return this.connected&&this.packet({type:Es.DISCONNECT}),this.destroy(),this.connected&&this.onclose("io client disconnect"),this}close(){return this.disconnect()}compress(e){return this.flags.compress=e,this}get volatile(){return this.flags.volatile=!0,this}timeout(e){return this.flags.timeout=e,this}onAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.push(e),this}prependAny(e){return this._anyListeners=this._anyListeners||[],this._anyListeners.unshift(e),this}offAny(e){if(!this._anyListeners)return this;if(e){const t=this._anyListeners;for(let s=0;s0&&e.jitter<=1?e.jitter:0,this.attempts=0}Ns.prototype.duration=function(){var e=this.ms*Math.pow(this.factor,this.attempts++);if(this.jitter){var t=Math.random(),s=Math.floor(t*this.jitter*e);e=0==(1&Math.floor(10*t))?e-s:e+s}return 0|Math.min(e,this.max)},Ns.prototype.reset=function(){this.attempts=0},Ns.prototype.setMin=function(e){this.ms=e},Ns.prototype.setMax=function(e){this.max=e},Ns.prototype.setJitter=function(e){this.jitter=e};class Rs extends S{constructor(e,t){var s;super(),this.nsps={},this.subs=[],e&&"object"==typeof e&&(t=e,e=void 0),(t=t||{}).path=t.path||"/socket.io",this.opts=t,N(this,t),this.reconnection(!1!==t.reconnection),this.reconnectionAttempts(t.reconnectionAttempts||1/0),this.reconnectionDelay(t.reconnectionDelay||1e3),this.reconnectionDelayMax(t.reconnectionDelayMax||5e3),this.randomizationFactor(null!==(s=t.randomizationFactor)&&void 0!==s?s:.5),this.backoff=new Ns({min:this.reconnectionDelay(),max:this.reconnectionDelayMax(),jitter:this.randomizationFactor()}),this.timeout(null==t.timeout?2e4:t.timeout),this._readyState="closed",this.uri=e;const r=t.parser||ks;this.encoder=new r.Encoder,this.decoder=new r.Decoder,this._autoConnect=!1!==t.autoConnect,this._autoConnect&&this.open()}reconnection(e){return arguments.length?(this._reconnection=!!e,this):this._reconnection}reconnectionAttempts(e){return void 0===e?this._reconnectionAttempts:(this._reconnectionAttempts=e,this)}reconnectionDelay(e){var t;return void 0===e?this._reconnectionDelay:(this._reconnectionDelay=e,null===(t=this.backoff)||void 0===t||t.setMin(e),this)}randomizationFactor(e){var t;return void 0===e?this._randomizationFactor:(this._randomizationFactor=e,null===(t=this.backoff)||void 0===t||t.setJitter(e),this)}reconnectionDelayMax(e){var t;return void 0===e?this._reconnectionDelayMax:(this._reconnectionDelayMax=e,null===(t=this.backoff)||void 0===t||t.setMax(e),this)}timeout(e){return arguments.length?(this._timeout=e,this):this._timeout}maybeReconnectOnOpen(){!this._reconnecting&&this._reconnection&&0===this.backoff.attempts&&this.reconnect()}open(e){if(~this._readyState.indexOf("open"))return this;this.engine=new ls(this.uri,this.opts);const t=this.engine,s=this;this._readyState="opening",this.skipReconnect=!1;const r=xs(t,"open",(function(){s.onopen(),e&&e()})),i=xs(t,"error",(t=>{s.cleanup(),s._readyState="closed",this.emitReserved("error",t),e?e(t):s.maybeReconnectOnOpen()}));if(!1!==this._timeout){const e=this._timeout;0===e&&r();const s=this.setTimeoutFn((()=>{r(),t.close(),t.emit("error",new Error("timeout"))}),e);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}return this.subs.push(r),this.subs.push(i),this}connect(e){return this.open(e)}onopen(){this.cleanup(),this._readyState="open",this.emitReserved("open");const e=this.engine;this.subs.push(xs(e,"ping",this.onping.bind(this)),xs(e,"data",this.ondata.bind(this)),xs(e,"error",this.onerror.bind(this)),xs(e,"close",this.onclose.bind(this)),xs(this.decoder,"decoded",this.ondecoded.bind(this)))}onping(){this.emitReserved("ping")}ondata(e){try{this.decoder.add(e)}catch(e){this.onclose("parse error")}}ondecoded(e){this.emitReserved("packet",e)}onerror(e){this.emitReserved("error",e)}socket(e,t){let s=this.nsps[e];return s||(s=new Ts(this,e,t),this.nsps[e]=s),s}_destroy(e){const t=Object.keys(this.nsps);for(const e of t){if(this.nsps[e].active)return}this._close()}_packet(e){const t=this.encoder.encode(e);for(let s=0;se())),this.subs.length=0,this.decoder.destroy()}_close(){this.skipReconnect=!0,this._reconnecting=!1,this.onclose("forced close"),this.engine&&this.engine.close()}disconnect(){return this._close()}onclose(e,t){this.cleanup(),this.backoff.reset(),this._readyState="closed",this.emitReserved("close",e,t),this._reconnection&&!this.skipReconnect&&this.reconnect()}reconnect(){if(this._reconnecting||this.skipReconnect)return this;const e=this;if(this.backoff.attempts>=this._reconnectionAttempts)this.backoff.reset(),this.emitReserved("reconnect_failed"),this._reconnecting=!1;else{const t=this.backoff.duration();this._reconnecting=!0;const s=this.setTimeoutFn((()=>{e.skipReconnect||(this.emitReserved("reconnect_attempt",e.backoff.attempts),e.skipReconnect||e.open((t=>{t?(e._reconnecting=!1,e.reconnect(),this.emitReserved("reconnect_error",t)):e.onreconnect()})))}),t);this.opts.autoUnref&&s.unref(),this.subs.push((function(){clearTimeout(s)}))}}onreconnect(){const e=this.backoff.attempts;this._reconnecting=!1,this.backoff.reset(),this.emitReserved("reconnect",e)}}const Cs={};function Ls(e,t){"object"==typeof e&&(t=e,e=void 0);const s=function(e,t="",s){let r=e;s=s||"undefined"!=typeof location&&location,null==e&&(e=s.protocol+"//"+s.host),"string"==typeof e&&("/"===e.charAt(0)&&(e="/"===e.charAt(1)?s.protocol+e:s.host+e),/^(https?|wss?):\/\//.test(e)||(e=void 0!==s?s.protocol+"//"+e:"https://"+e),r=cs(e)),r.port||(/^(http|ws)$/.test(r.protocol)?r.port="80":/^(http|ws)s$/.test(r.protocol)&&(r.port="443")),r.path=r.path||"/";const i=-1!==r.host.indexOf(":")?"["+r.host+"]":r.host;return r.id=r.protocol+"://"+i+":"+r.port+t,r.href=r.protocol+"://"+i+(s&&s.port===r.port?"":":"+r.port),r}(e,(t=t||{}).path||"/socket.io"),r=s.source,i=s.id,n=s.path,o=Cs[i]&&n in Cs[i].nsps;let a;return t.forceNew||t["force new connection"]||!1===t.multiplex||o?a=new Rs(r,t):(Cs[i]||(Cs[i]=new Rs(r,t)),a=Cs[i]),s.query&&!t.query&&(t.query=s.queryKey),a.socket(s.path,t)}Object.assign(Ls,{Manager:Rs,Socket:Ts,io:Ls,connect:Ls});var As="e";var Bs="jr",Ps="jc";const Is=e=>()=>e;new URL(".",import.meta.url).pathname;const Ds=e.resolve("executor.config.mjs"),{default:Us}=await import(Ds),js=process.argv.slice(2),Fs=JSON.parse(Buffer.from(js[0],"base64").toString("utf8")),Ms=function(e){const t={...e};for(var s in t)"function"!=typeof t[s]&&(t[s]=Is(t[s]));return t}(Us(Fs)),qs=new class{constructor(e,t){this.url=e.url(t)??process.env.QUALITEER_EXECUTOR_URL,this.jobId=e.jobId(t)??process.env.QUALITEER_JOB_ID,this.command=e.command(t)??process.env.QUALITEER_COMMAND,this.mode=As,this.buf={},this.buf.e="",this.buf.o="",this.spawn=this.spawn.bind(this),this.report=this.report.bind(this),this.onProcClose=this.onProcClose.bind(this),this.onClose=this.onClose.bind(this)}spawn(){const e=this.command,t=e.shift();this.proc=r.spawn(t,e),this.proc.stdout.setEncoding("utf8"),this.proc.stderr.setEncoding("utf8"),this.proc.stdout.on("data",(e=>this.report(e.toString(),"o"))),this.proc.stderr.on("data",(e=>this.report(e.toString(),"e"))),this.proc.on("close",this.onProcClose)}runJob(){this.socket=Ls(this.url,{query:{mode:this.mode,jobId:this.jobId}}),this.socket.on("connect",this.spawn),this.socket.on("disconnect",this.onClose)}onClose(){console.log("Server disconnected, terminating process."),this.proc&&this.proc.kill("SIGKILL")}onProcClose(e){this.socket.emit(Ps,e,(()=>this.socket.disconnect())),console.log(`Process finished with code ${e}`)}report(e,t){this.buf[t]+=e,this.buf[t].includes("\n")&&(this.buf[t].endsWith("\n")&&(this.buf[t]=this.buf[t].slice(0,-1)),this.socket.emit(Bs,this.buf[t]),"e"===t?console.error(`err: ${this.buf[t]}`):console.log(`out: ${this.buf[t]}`),this.buf[t]="")}}(Ms,Fs);qs.runJob(); diff --git a/lib/common/executor/executor-bundler.js b/lib/common/executor/executor-bundler.js new file mode 100644 index 0000000..012ba16 --- /dev/null +++ b/lib/common/executor/executor-bundler.js @@ -0,0 +1,38 @@ +import { URL } from "node:url"; +import path from "node:path"; +import caxa from "caxa"; +import { rollup } from "rollup"; +import loadConfigFile from "rollup/loadConfigFile"; +import { executorLibraryDir, binName, scriptName } from "./executor-config.js"; +// Fix import +const { default: caxaPackage } = caxa; +// Rollup Config +const rollupConfigPath = path.resolve(executorLibraryDir, "rollup.config.js"); + +// Build functions +async function packageBin() { + console.log("Packaging bundle into binary"); + return caxaPackage({ + input: "dist/bundles/", + output: `bin/${binName}`, + command: ["{{caxa}}/node_modules/.bin/node", `{{caxa}}/${scriptName}`], + uncompressionMessage: "Unpacking, please wait...", + }); +} + +async function rollupBundle() { + console.log("Rolling up executor into bundle"); + const { options, warnings } = await loadConfigFile(rollupConfigPath); + if (warnings.count !== 0) + console.log(`Rollup has ${warnings.count} warnings`); + warnings.flush(); + + for (const optionsObj of options) { + const bundle = await rollup(optionsObj); + await Promise.all(optionsObj.output.map(bundle.write)); + } +} + +await rollupBundle(); +await packageBin(); +console.log("Done"); diff --git a/lib/common/executor/executor-config.js b/lib/common/executor/executor-config.js new file mode 100644 index 0000000..9020020 --- /dev/null +++ b/lib/common/executor/executor-config.js @@ -0,0 +1,5 @@ +export const executorLibraryDir = new URL(".", import.meta.url).pathname; +export const binName = "qltr-executor"; +export const configName = "executor.config.mjs"; +export const scriptName = "qualiteer-executor.mjs"; +export const entrypointName = "executor-entrypoint.js"; diff --git a/lib/jobs/executor/executor-configurator.js b/lib/common/executor/executor-configurator.js similarity index 100% rename from lib/jobs/executor/executor-configurator.js rename to lib/common/executor/executor-configurator.js diff --git a/lib/jobs/executor/executor-entrypoint.js b/lib/common/executor/executor-entrypoint.js similarity index 57% rename from lib/jobs/executor/executor-entrypoint.js rename to lib/common/executor/executor-entrypoint.js index a414e5b..28418d3 100644 --- a/lib/jobs/executor/executor-entrypoint.js +++ b/lib/common/executor/executor-entrypoint.js @@ -1,9 +1,9 @@ import path from "node:path"; -import Executor from "../../sockets/clients/Executor.js"; +import Executor from "../sockets/clients/Executor.js"; import { normalize } from "./executor-configurator.js"; -const { default: executorConfig } = await import( - path.resolve("executor.config.js") -); +import { configName as executorConfigName } from "./executor-config.js"; +const executorConfigPath = path.resolve(executorConfigName); +const { default: executorConfig } = await import(executorConfigPath); // Load config and args const args = process.argv.slice(2); diff --git a/lib/jobs/executor/rollup.config.js b/lib/common/executor/rollup.config.js similarity index 52% rename from lib/jobs/executor/rollup.config.js rename to lib/common/executor/rollup.config.js index d107a3d..09f068e 100644 --- a/lib/jobs/executor/rollup.config.js +++ b/lib/common/executor/rollup.config.js @@ -1,11 +1,17 @@ +import path from "node:path"; import { nodeResolve } from "@rollup/plugin-node-resolve"; import commonjs from "@rollup/plugin-commonjs"; import { terser } from "rollup-plugin-terser"; +import { + executorLibraryDir, + entrypointName, + scriptName, +} from "./executor-config.js"; export default { - input: "lib/jobs/executor/executor-entrypoint.js", + input: path.resolve(executorLibraryDir, entrypointName), output: { - file: "dist/bundles/qualiteer-executor.mjs", + file: `dist/bundles/${scriptName}`, }, plugins: [nodeResolve(), commonjs(), terser()], }; diff --git a/lib/sockets/clients/Executor.js b/lib/common/sockets/clients/Executor.js similarity index 100% rename from lib/sockets/clients/Executor.js rename to lib/common/sockets/clients/Executor.js diff --git a/lib/sockets/clients/Initiator.js b/lib/common/sockets/clients/Initiator.js similarity index 100% rename from lib/sockets/clients/Initiator.js rename to lib/common/sockets/clients/Initiator.js diff --git a/lib/sockets/clients/Viewer.js b/lib/common/sockets/clients/Viewer.js similarity index 100% rename from lib/sockets/clients/Viewer.js rename to lib/common/sockets/clients/Viewer.js diff --git a/lib/sockets/clients/index.js b/lib/common/sockets/clients/index.js similarity index 100% rename from lib/sockets/clients/index.js rename to lib/common/sockets/clients/index.js diff --git a/lib/sockets/clients/web.index.js b/lib/common/sockets/clients/web.index.js similarity index 100% rename from lib/sockets/clients/web.index.js rename to lib/common/sockets/clients/web.index.js diff --git a/lib/sockets/events.js b/lib/common/sockets/events.js similarity index 100% rename from lib/sockets/events.js rename to lib/common/sockets/events.js diff --git a/lib/sockets/modes.js b/lib/common/sockets/modes.js similarity index 100% rename from lib/sockets/modes.js rename to lib/common/sockets/modes.js diff --git a/lib/database/TABLES.md b/lib/database/TABLES.md deleted file mode 100644 index 8ae0a1d..0000000 --- a/lib/database/TABLES.md +++ /dev/null @@ -1,56 +0,0 @@ -<<<<<<< HEAD -CREATE SEQUENCE test_results_id_seq; -CREATE TABLE test_results ( -id bigint NOT NULL DEFAULT nextval('test_results_seq') PRIMARY KEY, -test_name varchar(255) DEFAULT NULL, -test_class varchar(255) DEFAULT NULL, -test_method varchar(255) DEFAULT NULL, -test_path varchar(255) DEFAULT NULL, -test_type varchar(32) DEFAULT NULL, -test_timestamp timestamptz NOT NULL DEFAULT now(), -test_retry BOOLEAN DEFAULT FALSE, -origin varchar(255) DEFAULT NULL, -failed BOOLEAN DEFAULT FALSE, -failed_message varchar(2047) DEFAULT NULL, -screenshot_url varchar(255) DEFAULT NULL, -weblog_url varchar(255) DEFAULT NULL, -); -ALTER SEQUENCE test_results_id_seq OWNED BY test_results.id; - -======= - -> > > > > > > b023d8910c89d80573499890a958c0df649849e1 - -# Tables - -PG Database Tables Mapped Out - -<<<<<<< HEAD - -## `test_results` - -| id | test_name | test_class | test_method | test_path | test_type | test_timestamp | test_retry | origin | failed | failed_message | screenshot_url | weblog_url | -| int | string | string | string | string | string | timestamp | boolean | string | boolean | string | string | string | -| 1 | My Test | My Test Class | My Failing Test Method | My Test Class Path | API | Date.now() | false | Test Suite A | true | Some Failure Messsage | screenshotUrl | weblogUrl | -======= -Table `test_results` -| id | test_name | test_class | test_method | test_path | test_type | test_timestamp | test_retry | origin | failed | failed_message | screenshot_url | weblog_url | -|-----|-----------|------------|-------------|---------------------|---------------|----------------|------------|--------------|---------|------------------------|---------------------|--------------------| -| int | string | string | string | string | string | timestamp | boolean | string | boolean | string | string | string | -| 1 | My Test | My Class | My Method | /path/to/test_class | API/UI/MOBILE | Date.now() | false | Test Suite A | true | I am a test that fails | https://example.com | http://example.com | - -> > > > > > > b023d8910c89d80573499890a958c0df649849e1 - -- id Automatically Generated -- test_name\* Name of test -- test_class\* Name of class -- test_method Name of failed method if failed else null -- test_path Path to test class -- test_type API/UI/Mobile -- test_timestamp UTC Timestamp -- test_retry Should test remedy failed tests -- origin Test Suite test belongs to -- failed Indicates if the test failed or not -- failed_message Failure Message of test or null -- screenshot_url Screenshot of failure -- weblog_url Log from the web console diff --git a/lib/database/seed.js b/lib/database/seed.js deleted file mode 100644 index 6d6b767..0000000 --- a/lib/database/seed.js +++ /dev/null @@ -1,30 +0,0 @@ -import pg from "./postgres.js"; -import { upsertTest } from "./queries/catalog.js"; -import { insertTestResult } from "./queries/results.js"; -import { upsertAlertSilence } from "./queries/alerting.js"; -import { - seed as catalogSeed, - table as catalogTable, -} from "./seeds/catalog-seed.js"; -import { - seed as resultsSeed, - table as resultsTable, -} from "./seeds/results-seed.js"; -import { - seed as alertingSeed, - table as alertingTable, -} from "./seeds/alerting-seed.js"; - -const database = process.env.POSTGRES_DATABASE ?? "qualiteer"; -await pg.connect(); - -const resetAndSeed = async (table, getSeeds, seed) => { - await pg.query(`TRUNCATE ${table} RESTART IDENTITY CASCADE;`); - for (var s of getSeeds()) await seed(s); -}; - -await resetAndSeed(catalogTable, catalogSeed, upsertTest); -await resetAndSeed(resultsTable, resultsSeed, insertTestResult); -await resetAndSeed(alertingTable, alertingSeed, upsertAlertSilence); - -process.exit(); diff --git a/lib/index.js b/lib/index.js index 960430f..671ecfe 100644 --- a/lib/index.js +++ b/lib/index.js @@ -1 +1 @@ -export { default } from "./core/Qualiteer.js"; +export { default } from "./server/core/Qualiteer.js"; diff --git a/lib/jobs/executor/executor-bundler.js b/lib/jobs/executor/executor-bundler.js deleted file mode 100644 index 66a95f0..0000000 --- a/lib/jobs/executor/executor-bundler.js +++ /dev/null @@ -1,50 +0,0 @@ -import { URL } from "url"; -import loadConfigFile from "rollup/loadConfigFile"; -import path from "path"; -import { rollup } from "rollup"; -import caxa from "caxa"; - -import { verify, normalize } from "./executor-configurator.js"; -const { default: executorConfig } = await import( - path.resolve("executor.config.js") -); - -const __dirname = new URL(".", import.meta.url).pathname; -const { default: caxaPackage } = caxa; - -function testConfig() { - console.log("Testing config"); - verify(normalize(executorConfig([]))); -} - -async function packageBin() { - console.log("Packaging bundle into binary"); - return caxaPackage({ - input: "dist/bundles/", - output: "bin/executor", - command: [ - "{{caxa}}/node_modules/.bin/node", - "{{caxa}}/qualiteer-executor.mjs", - ], - uncompressionMessage: "Unpacking, please wait...", - }); -} - -async function rollupBundle() { - console.log("Rolling up executor into bundle"); - const { options, warnings } = await loadConfigFile( - path.resolve(__dirname, "rollup.config.js") - ); - console.log(`Rollup has ${warnings.count} warnings`); - warnings.flush(); - - for (const optionsObj of options) { - const bundle = await rollup(optionsObj); - await Promise.all(optionsObj.output.map(bundle.write)); - } -} - -testConfig(); -await rollupBundle(); -await packageBin(); -console.log("Done"); diff --git a/lib/jobs/job-builder.js b/lib/jobs/job-builder.js deleted file mode 100644 index a190f73..0000000 --- a/lib/jobs/job-builder.js +++ /dev/null @@ -1,53 +0,0 @@ -const baseCommand = "node"; -const suiteEntry = "tests/assets/suite/runner.js"; - -const buildCommon = (jobRequest) => { - const { isTriage, ignore, region, testNames } = jobRequest; - const command = [baseCommand, suiteEntry]; - // Apply Common Flags - if (isTriage) command.push(`isTriage=${isTriage}`); - if (ignore && ignore.length > 0) console.log("Would ignore", ignore); - if (region) command.push(`region=${region}`); - - // Return new request - return { ...jobRequest, command }; -}; - -const buildManual = (jobReq) => { - const { command, testNames } = jobReq; - if (testNames.length > 1) - throw Error("Currently only 1 test can be selected!"); - - command.push(`test=${testNames[0]}`); - return { ...jobReq, command }; -}; - -const buildTags = (jobReq) => { - const { command, tags, testNames } = jobReq; - if (testNames && testNames.length > 0) { - return console.log("Would run tags as manual"); - } - const arg = Buffer.from(JSON.stringify(tags), "utf8").toString("base64"); - command.push(`tags=${arg}`); - return { ...jobReq, command }; -}; - -const buildPipeline = (jobReq, socketId) => { - const { command, pipeline } = jobReq; - const { __test: test } = pipeline; - if (!test) throw Error("__test is required for pipeline jobs!"); - pipeline.dashboardSocketId = socketId; - const arg = Buffer.from(JSON.stringify(pipeline), "utf8").toString("base64"); - command.push(`pipeline=${arg}`); - command.push(`test=${test}`); - return { ...jobReq, command }; -}; - -export default function jobBuilder(jobRequest, id) { - const jobReq = buildCommon(jobRequest, id); - const { pipeline, testNames, tags } = jobReq; - if (pipeline) return buildPipeline(jobReq, id); - else if (tags) return buildTags(jobReq); - else if (testNames) return buildManual(jobReq); //TODO currently does nothing - else throw Error("At least 1 'pipeline or tags or testNames' is required! "); -} diff --git a/lib/jobs/job-executor.js b/lib/jobs/job-executor.js deleted file mode 100644 index e558991..0000000 --- a/lib/jobs/job-executor.js +++ /dev/null @@ -1,9 +0,0 @@ -import Executor from "../sockets/clients/Executor.js"; - -const args = process.argv.slice(2); -const url = args[0]; -const jobId = args[1]; -const command = args.slice(2); -const job = { id: jobId, command }; -const exec = new Executor(url, job, command); -exec.runJob(); diff --git a/lib/jobs/JobManager.js b/lib/server/core/JobManager.js similarity index 65% rename from lib/jobs/JobManager.js rename to lib/server/core/JobManager.js index a1e5dd3..70abe7b 100644 --- a/lib/jobs/JobManager.js +++ b/lib/server/core/JobManager.js @@ -1,12 +1,19 @@ import { v4 } from "uuid"; -import applyJobInternally from "./k8s/k8s-internal.js"; -import applyJob from "./k8s/k8s.js"; -import buildJob from "./job-builder.js"; +import { getTest } from "../database/queries/catalog.js"; +import applyJobInternally from "../k8s/k8s-internal.js"; +import applyJob from "../k8s/k8s.js"; const maxJobs = process.env.MAX_JOBS ? parseInt(process.env.MAX_JOBS) : 3; const internalDeploy = process.env.INTERNAL_DEPLOY === "true"; const launchJob = internalDeploy ? applyJobInternally : applyJob; +async function getTests(job) { + if (job.pipeline) return [await getTest(job.pipeline.__test)]; + if (!job.testNames) return []; + const tests = await Promise.all(job.testNames.map((name) => getTest(name))); + return tests; +} + class JobManager { constructor() { this.clientMaxJobs = maxJobs; @@ -35,16 +42,21 @@ class JobManager { closeJob(jobId, exitcode) { const job = this.getJobById(jobId); + if (!job) return; job.exitcode = exitcode; } - newJob(jobRequest, id) { + async newJob(jobRequest, id) { if (!jobRequest) throw Error("Request Must Be Object!"); if (!this.clients[id]) this.clients[id] = { jobs: [] }; - const job = buildJob(jobRequest, id); + const job = { ...jobRequest }; + job.image = "registry.dunemask.net/garden/dev/reed:latest"; job.id = v4(); job.log = []; this.clients[id].jobs.push(job); + job.dashboardSocketId = id; + job.tests = await getTests(job); + for (var t of job.tests) if (!t) throw Error("1 or more tests not found!"); launchJob(job); return { ...job }; } diff --git a/lib/core/Qualiteer.js b/lib/server/core/Qualiteer.js similarity index 93% rename from lib/core/Qualiteer.js rename to lib/server/core/Qualiteer.js index 07678b8..79d4bb5 100644 --- a/lib/core/Qualiteer.js +++ b/lib/server/core/Qualiteer.js @@ -7,8 +7,8 @@ import { INFO, OK, logInfo } from "../util/logging.js"; // Import Core Modules import buildRoutes from "../routes/router.js"; import pg from "../database/postgres.js"; -import injectSockets from "../sockets/socket-server.js"; -import JobManager from "../jobs/JobManager.js"; +import injectSockets from "./socket-server.js"; +import JobManager from "./JobManager.js"; import buildRabbiteer from "../rabbit/rabbit-workers.js"; // Constants diff --git a/lib/sockets/client-listeners.js b/lib/server/core/client-listeners.js similarity index 85% rename from lib/sockets/client-listeners.js rename to lib/server/core/client-listeners.js index c2f9fe2..8a1fd87 100644 --- a/lib/sockets/client-listeners.js +++ b/lib/server/core/client-listeners.js @@ -1,13 +1,13 @@ -import evt from "./events.js"; +import evt from "../../common/sockets/events.js"; -export const initiator = (socket, jobs) => { +export const initiator = async (socket, jobs) => { const jobStr = socket.handshake.query.job; const jobReq = JSON.parse(jobStr); if (!jobReq || !(jobReq instanceof Object)) throw Error("No 'job' was included in the handshake query"); - const job = jobs.newJob(jobReq, socket.id); + const job = await jobs.newJob(jobReq, socket.id); socket.join(job.id); socket.emit(evt.JOB_CRT, job.id); }; diff --git a/lib/core/crons.js b/lib/server/core/crons.js similarity index 100% rename from lib/core/crons.js rename to lib/server/core/crons.js diff --git a/lib/sockets/socket-server.js b/lib/server/core/socket-server.js similarity index 58% rename from lib/sockets/socket-server.js rename to lib/server/core/socket-server.js index 703be2e..eebcf37 100644 --- a/lib/sockets/socket-server.js +++ b/lib/server/core/socket-server.js @@ -1,6 +1,6 @@ import { Server as Skio } from "socket.io"; -import evt from "./events.js"; -import modes from "./modes.js"; +import evt from "../../common/sockets/events.js"; +import modes from "../../common/sockets/modes.js"; import { initiator, executor, viewer } from "./client-listeners.js"; @@ -13,22 +13,28 @@ const socketDrop = (io, room, id) => { s.disconnect(); }; -const socketConnect = (io, socket, jobs) => { +const socketConnect = async (io, socket, jobs) => { const { mode } = socket.handshake.query; - switch (mode) { - case modes.INIT: - initiator(socket, jobs); - break; - case modes.EXEC: - executor(io, socket, jobs); - break; - case modes.VIEW: - viewer(socket); - break; - default: - socket.send(evt.ERR, "Invalid Mode!"); - socket.disconnect(); - break; + try { + switch (mode) { + case modes.INIT: + await initiator(socket, jobs); + break; + case modes.EXEC: + executor(io, socket, jobs); + break; + case modes.VIEW: + viewer(socket); + break; + default: + socket.send(evt.ERR, "Invalid Mode!"); + socket.disconnect(); + break; + } + } catch (err) { + console.log(err); + socket.send(evt.ERR, err); + socket.disconnect(); } }; diff --git a/lib/database/delays.js b/lib/server/database/delays.js similarity index 100% rename from lib/database/delays.js rename to lib/server/database/delays.js diff --git a/lib/database/migrations/1_create_catalog_table.sql b/lib/server/database/migrations/1_create_catalog_table.sql similarity index 100% rename from lib/database/migrations/1_create_catalog_table.sql rename to lib/server/database/migrations/1_create_catalog_table.sql diff --git a/lib/database/migrations/2_create_results_table.sql b/lib/server/database/migrations/2_create_results_table.sql similarity index 100% rename from lib/database/migrations/2_create_results_table.sql rename to lib/server/database/migrations/2_create_results_table.sql diff --git a/lib/database/migrations/3_create_alerting_table.sql b/lib/server/database/migrations/3_create_alerting_table.sql similarity index 100% rename from lib/database/migrations/3_create_alerting_table.sql rename to lib/server/database/migrations/3_create_alerting_table.sql diff --git a/lib/database/mocks/alerting-mock.js b/lib/server/database/mocks/alerting-mock.js similarity index 100% rename from lib/database/mocks/alerting-mock.js rename to lib/server/database/mocks/alerting-mock.js diff --git a/lib/database/mocks/catalog-mock.js b/lib/server/database/mocks/catalog-mock.js similarity index 100% rename from lib/database/mocks/catalog-mock.js rename to lib/server/database/mocks/catalog-mock.js diff --git a/lib/database/mocks/results-mock.js b/lib/server/database/mocks/results-mock.js similarity index 100% rename from lib/database/mocks/results-mock.js rename to lib/server/database/mocks/results-mock.js diff --git a/lib/database/pg-query.js b/lib/server/database/pg-query.js similarity index 100% rename from lib/database/pg-query.js rename to lib/server/database/pg-query.js diff --git a/lib/database/postgres.js b/lib/server/database/postgres.js similarity index 89% rename from lib/database/postgres.js rename to lib/server/database/postgres.js index 8f200fe..58ff79a 100644 --- a/lib/database/postgres.js +++ b/lib/server/database/postgres.js @@ -1,4 +1,6 @@ // Imports +import path from "node:path"; +import { URL } from "node:url"; import { migrate } from "postgres-migrations"; import createPgp from "pg-promise"; import moment from "moment"; @@ -29,7 +31,8 @@ const dbConfig = { ensureDatabaseExists: true, }; -const migrationsDir = "lib/database/migrations"; +const databaseDir = new URL(".", import.meta.url).pathname; +const migrationsDir = path.resolve(databaseDir, "migrations/"); const queryMock = (str) => INFO("POSTGRES MOCK", str); diff --git a/lib/database/queries/alerting.js b/lib/server/database/queries/alerting.js similarity index 100% rename from lib/database/queries/alerting.js rename to lib/server/database/queries/alerting.js diff --git a/lib/database/queries/catalog.js b/lib/server/database/queries/catalog.js similarity index 76% rename from lib/database/queries/catalog.js rename to lib/server/database/queries/catalog.js index ae01f5a..f545188 100644 --- a/lib/database/queries/catalog.js +++ b/lib/server/database/queries/catalog.js @@ -5,6 +5,7 @@ import { selectWhereAnyQuery, onConflictUpdate, } from "../pg-query.js"; +import { WARN } from "../../util/logging.js"; import getFilteredTags from "../tags.js"; import getDelay from "../delays.js"; @@ -14,6 +15,21 @@ const PG_DISABLED = process.env.POSTGRES_DISABLED; import { testsMock, mappingsMock } from "../mocks/catalog-mock.js"; // Queries +export const removeDroppedTests = async (testNames) => { + // BUG: After dropping a test, the id jumps ridiculously high + const pgNames = testNames.map((tn) => `'${tn}'`).join(","); + const query = `DELETE FROM catalog as x where x.name not in (${pgNames});`; + return pg.query(query); +}; + +export const getTest = async (name) => { + const query = selectWhereAnyQuery(table, { name }); + const results = await pg.query(query); + if (results.length > 1) + WARN("CATALOG", `More than 1 test found for '${name}'`); + return results[0]; +}; + export const getTests = async () => { if (PG_DISABLED) return testsMock(); const query = `SELECT * from ${table}`; @@ -54,6 +70,12 @@ export const getProjects = async () => { } }; +export const truncateTests = async () => { + if (PG_DISABLED) return console.log(`Would truncate table ${table}`); + const query = `TRUNCATE ${table} RESTART IDENTITY CASCADE;`; + return await pg.query(query); +}; + export const upsertTest = async (test) => { if (PG_DISABLED) return console.log("Would insert test", test); const { diff --git a/lib/database/queries/results.js b/lib/server/database/queries/results.js similarity index 100% rename from lib/database/queries/results.js rename to lib/server/database/queries/results.js diff --git a/lib/database/seeds/alerting-seed.js b/lib/server/database/seeds/alerting-seed.js similarity index 100% rename from lib/database/seeds/alerting-seed.js rename to lib/server/database/seeds/alerting-seed.js diff --git a/lib/database/seeds/catalog-seed.js b/lib/server/database/seeds/catalog-seed.js similarity index 100% rename from lib/database/seeds/catalog-seed.js rename to lib/server/database/seeds/catalog-seed.js diff --git a/lib/database/seeds/results-seed.js b/lib/server/database/seeds/results-seed.js similarity index 100% rename from lib/database/seeds/results-seed.js rename to lib/server/database/seeds/results-seed.js diff --git a/lib/database/tags.js b/lib/server/database/tags.js similarity index 100% rename from lib/database/tags.js rename to lib/server/database/tags.js diff --git a/lib/jobs/k8s/k8s-common.js b/lib/server/k8s/k8s-common.js similarity index 61% rename from lib/jobs/k8s/k8s-common.js rename to lib/server/k8s/k8s-common.js index 587e76b..a5f7664 100644 --- a/lib/jobs/k8s/k8s-common.js +++ b/lib/server/k8s/k8s-common.js @@ -1,4 +1,5 @@ import fs from "node:fs"; +import { URL } from "node:url"; import path from "node:path"; const { QUALITEER_EXECUTOR_URL, @@ -14,34 +15,32 @@ const executorBinFetchUrl = QUALITEER_EXECUTOR_BIN_URL; const jobsDir = "jobs/"; const jobsPath = path.resolve(jobsDir); -const defaultsFile = path.resolve("./lib/jobs/k8s/k8s-job.json"); -const defaults = JSON.parse(fs.readFileSync(defaultsFile)); +const k8sFolder = new URL(".", import.meta.url).pathname; +const defaultsFilePath = path.resolve(k8sFolder, "k8s-job.json"); +const defaults = JSON.parse(fs.readFileSync(defaultsFilePath)); -function wrapCommand(jobId, command) { - const bin = executorAsScript - ? `node ${executorBin}` - : `chmod +x ${executorBin} && ./${executorBin}`; - const executorPayload = JSON.stringify({ jobId, command, url: executorUrl }); +function commandBuilder(jobId, jobRequest) { + const executorPayload = JSON.stringify({ + jobId, + jobRequest, + url: executorUrl, + }); const payload = Buffer.from(executorPayload, "utf8").toString("base64"); - const curlCmd = `if ! [ -f qltr-executor ]; then curl -o qltr-executor ${executorBinFetchUrl}; fi || true && ${bin} ${payload}`; - return curlCmd; + return [`./${executorBin}`, payload]; } export function jobBuilder(jobRequest) { - const { resources, name, image, command, id: jobId } = jobRequest; + const { resources, name, image, id: jobId } = jobRequest; // Safety Checks if (!jobId) throw Error("'jobId' required!"); - if (!name) throw Error("'name' required!"); - if (!command) throw Error("'command' required!"); if (!image) throw Error("'image' required!"); - if (!Array.isArray(command)) throw Error("'command' must be an array!"); // Apply configuration const job = { ...defaults }; - job.metadata.name = `qltr-${name}-${jobId}`; + job.metadata.name = `qltr-${jobId}`; const container = job.spec.template.spec.containers[0]; container.name = job.metadata.name; - container.command = wrapCommand(jobId, command); + container.command = commandBuilder(jobId, jobRequest); container.image = JSON.stringify(image); // Apply resources job.resources = { ...job.resources, ...resources }; diff --git a/lib/jobs/k8s/k8s-internal-engine.js b/lib/server/k8s/k8s-internal-engine.js similarity index 100% rename from lib/jobs/k8s/k8s-internal-engine.js rename to lib/server/k8s/k8s-internal-engine.js diff --git a/lib/jobs/k8s/k8s-internal.js b/lib/server/k8s/k8s-internal.js similarity index 100% rename from lib/jobs/k8s/k8s-internal.js rename to lib/server/k8s/k8s-internal.js diff --git a/lib/jobs/k8s/k8s-job.json b/lib/server/k8s/k8s-job.json similarity index 57% rename from lib/jobs/k8s/k8s-job.json rename to lib/server/k8s/k8s-job.json index 03dbed1..e42eb34 100644 --- a/lib/jobs/k8s/k8s-job.json +++ b/lib/server/k8s/k8s-job.json @@ -13,9 +13,19 @@ "name": "qltr-job-test-suite-1", "image": "node:latest", "imagePullPolicy": "Always", - "command": ["node", "--version"] + "command": ["node", "--version"], + "envFrom": [ + { + "configMapRef": { + "name": "qualiteer-job-environment" + } + } + ] } ], + "imagePullSecrets": [ + { "name": "usw-registry-secret", "namespace": "default" } + ], "restartPolicy": "Never" } }, diff --git a/lib/jobs/k8s/k8s.js b/lib/server/k8s/k8s.js similarity index 57% rename from lib/jobs/k8s/k8s.js rename to lib/server/k8s/k8s.js index b9e8e5d..a273f1f 100644 --- a/lib/jobs/k8s/k8s.js +++ b/lib/server/k8s/k8s.js @@ -1,13 +1,12 @@ import k8s from "@kubernetes/client-node"; -import { INFO, ERR } from "../../util/logging.js"; +import { INFO, ERR } from "../util/logging.js"; import { jobBuilder, createFile, deleteFile } from "./k8s-common.js"; export default async function createJob(jobRequest) { + //console.log(await jobRequest.tests); const job = jobBuilder(jobRequest); - job.spec.template.spec.containers[0].image = "node:latest"; - job.spec.template.spec.containers[0].command = ["node", "--version"]; - // job.spec.template.spec.containers[0].image = "reed"; - // job.spec.template.spec.containers[0].command = "python3 -m pytest -v --tb=no -p no:warnings".split(" "); + job.spec.template.spec.containers[0].image = + "registry.dunemask.net/garden/dev/reed:latest"; const kc = new k8s.KubeConfig(); kc.loadFromCluster(); const batchV1Api = kc.makeApiClient(k8s.BatchV1Api); @@ -17,8 +16,4 @@ export default async function createJob(jobRequest) { .createNamespacedJob("dunestorm-dunemask", job) .then((res) => INFO("K8S", `Job ${jobName} created!`)) .catch((err) => ERR("K8S", err)); - - /*const filePath = createFile(job); - applyFile(filePath); - deleteFile(filePath);*/ } diff --git a/lib/rabbit/rabbit-workers.js b/lib/server/rabbit/rabbit-workers.js similarity index 100% rename from lib/rabbit/rabbit-workers.js rename to lib/server/rabbit/rabbit-workers.js diff --git a/lib/rabbit/workers/TestResultsWorker.js b/lib/server/rabbit/workers/TestResultsWorker.js similarity index 95% rename from lib/rabbit/workers/TestResultsWorker.js rename to lib/server/rabbit/workers/TestResultsWorker.js index 37eee8e..b26805d 100644 --- a/lib/rabbit/workers/TestResultsWorker.js +++ b/lib/server/rabbit/workers/TestResultsWorker.js @@ -2,7 +2,7 @@ import { Worker } from "rabbiteer"; import { VERB } from "../../util/logging.js"; import { insertTestResult } from "../../database/queries/results.js"; -import evt from "../../sockets/events.js"; +import evt from "../../../common/sockets/events.js"; // Class export default class TestResultsWorker extends Worker { constructor(skio) { diff --git a/lib/rabbit/workers/index.js b/lib/server/rabbit/workers/index.js similarity index 100% rename from lib/rabbit/workers/index.js rename to lib/server/rabbit/workers/index.js diff --git a/lib/routes/alerting-route.js b/lib/server/routes/alerting-route.js similarity index 100% rename from lib/routes/alerting-route.js rename to lib/server/routes/alerting-route.js diff --git a/lib/routes/catalog-route.js b/lib/server/routes/catalog-route.js similarity index 50% rename from lib/routes/catalog-route.js rename to lib/server/routes/catalog-route.js index 251a642..3f6147d 100644 --- a/lib/routes/catalog-route.js +++ b/lib/server/routes/catalog-route.js @@ -3,6 +3,8 @@ import { getTests, getPipelineMappings, upsertTest, + truncateTests, + removeDroppedTests, } from "../database/queries/catalog.js"; const router = Router(); @@ -23,9 +25,20 @@ router.get("/pipeline-mappings", (req, res) => { // Post Routes router.post("/update", (req, res) => { if (!req.body) return res.status(400).send("Body required!"); - if(!Array.isArray(req.body)) return res.status(400).send("Body must be an array!"); - const upserts = Promise.all(req.body.map((catalogItem)=>upsertTest(catalogItem))); - upserts.then(()=>res.sendStatus(200)).catch((e)=>res.status(500).send(e)); + if (!Array.isArray(req.body)) + return res.status(400).send("Body must be an array!"); + const wrongImage = req.body.find(({ image }) => image !== req.body[0].image); + if (wrongImage) + return res.status(400).send("Tests cannot have unique images!"); + const testNames = req.body.map(({ name }) => name); + + // Upsert new tests + const upserts = Promise.all( + req.body.map((catalogItem) => upsertTest(catalogItem)) + ); + const dropRm = upserts.then(() => removeDroppedTests(testNames)); + + dropRm.then(() => res.sendStatus(200)).catch((e) => res.status(500).send(e)); }); export default router; diff --git a/lib/routes/dev-route.js b/lib/server/routes/dev-route.js similarity index 100% rename from lib/routes/dev-route.js rename to lib/server/routes/dev-route.js diff --git a/lib/routes/react-route.js b/lib/server/routes/react-route.js similarity index 100% rename from lib/routes/react-route.js rename to lib/server/routes/react-route.js diff --git a/lib/routes/results-route.js b/lib/server/routes/results-route.js similarity index 100% rename from lib/routes/results-route.js rename to lib/server/routes/results-route.js diff --git a/lib/routes/router.js b/lib/server/routes/router.js similarity index 100% rename from lib/routes/router.js rename to lib/server/routes/router.js diff --git a/lib/routes/vitals-route.js b/lib/server/routes/vitals-route.js similarity index 100% rename from lib/routes/vitals-route.js rename to lib/server/routes/vitals-route.js diff --git a/lib/util/logging.js b/lib/server/util/logging.js similarity index 100% rename from lib/util/logging.js rename to lib/server/util/logging.js diff --git a/package-lock.json b/package-lock.json index 689623d..8f8b0a8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -40,6 +40,7 @@ "axios": "^0.27.2", "caxa": "^2.1.0", "concurrently": "^7.3.0", + "lodash.merge": "^4.6.2", "nodemon": "^2.0.19", "prettier": "^2.7.1", "react": "^18.2.0", @@ -4078,6 +4079,12 @@ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", "dev": true }, + "node_modules/lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "node_modules/lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", @@ -9260,6 +9267,12 @@ "integrity": "sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs=", "dev": true }, + "lodash.merge": { + "version": "4.6.2", + "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", + "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", + "dev": true + }, "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", diff --git a/package.json b/package.json index 30c81ac..778f18d 100644 --- a/package.json +++ b/package.json @@ -7,25 +7,21 @@ "type": "module", "exports": { ".": "./lib/index.js", - "./clients": "./lib/sockets/clients/index.js", - "./web-clients": "./lib/sockets/clients/web.index.js" + "./clients": "./lib/common/sockets/clients/index.js", + "./web-clients": "./lib/common/sockets/clients/web.index.js" }, "bin": { "qualiteer": "./dist/app.js" }, "scripts": { "build:all": "concurrently \"npm run build:react\" \"npm run build:executor\" -n v,s -p -c yellow,green", - "build:executor": "node lib/jobs/executor/executor-bundler.js", + "build:executor": "node lib/common/executor/executor-bundler.js", "build:react": "vite build", "start": "node dist/app.js", "dev:server": "nodemon dist/app.js", "dev:react": "vite", "start:dev": "concurrently -k \"QUALITEER_DEV_PORT=52025 npm run dev:server\" \" QUALITEER_VITE_DEV_PORT=52000 QUALITEER_VITE_BACKEND_URL=http://localhost:52025 npm run dev:react\" -n s,v -p -c green,yellow", - "test": "node tests/index.js", - "test:api": "node tests/api.js", - "test:dev": "nodemon tests/index.js", - "lint": "prettier -w lib/ src/", - "seed": "node lib/database/seed.js" + "lint": "prettier -w lib/ src/" }, "browserslist": { "production": [ @@ -46,7 +42,6 @@ "dotenv": "^16.0.2", "express": "^4.18.1", "figlet": "^1.5.2", - "lodash": "^4.17.21", "moment": "^2.29.4", "path": "^0.12.7", "pg-promise": "^10.12.0", @@ -68,6 +63,7 @@ "axios": "^0.27.2", "caxa": "^2.1.0", "concurrently": "^7.3.0", + "lodash.merge": "^4.6.2", "nodemon": "^2.0.19", "prettier": "^2.7.1", "react": "^18.2.0", diff --git a/src/Dashboard.jsx b/src/Dashboard.jsx index f1d82e1..70d1632 100644 --- a/src/Dashboard.jsx +++ b/src/Dashboard.jsx @@ -1,6 +1,6 @@ // Import Contexts import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; -import { JobProvider } from "@qltr/jobs"; +import { JobProvider } from "@qltr/jobctx"; import { StoreProvider } from "@qltr/store"; import { BrowserRouter } from "react-router-dom"; // Import Views diff --git a/src/ctx/JobContext.jsx b/src/ctx/JobContext.jsx index d6c8e9b..c7e64ea 100644 --- a/src/ctx/JobContext.jsx +++ b/src/ctx/JobContext.jsx @@ -1,16 +1,6 @@ import React, { useReducer, createContext, useMemo } from "react"; import Initiator from "@qltr/initiator"; const JobContext = createContext(); - -export const jobStatus = { - OK: "o", - QUEUED: "q", - PENDING: "p", - CANCELED: "c", - ACTIVE: "a", - ERROR: "e", -}; - const ACTIONS = { CREATE: "c", UPDATE: "u", @@ -18,12 +8,7 @@ const ACTIONS = { PIPELINE: "p", }; -const url = "/"; - -const initialState = { - jobs: [], - pipelines: [], -}; +const initialState = { jobs: [], pipelines: [] }; const reducer = (state, action) => { // Current Jobs @@ -56,220 +41,21 @@ const reducer = (state, action) => { export const JobProvider = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); - const jobUpdate = (job, jobId) => dispatch({ type: ACTIONS.UPDATE, jobId, job }); const jobCreate = (job) => dispatch({ type: ACTIONS.CREATE, job: { ...job, log: [] } }); const jobDelete = (jobId) => dispatch({ type: ACTIONS.DELETE, jobId }); - const updatePipelines = (pipelines) => dispatch({ type: ACTIONS.pipeline, pipelines }); - function retryAll(failing) { - // Query Full Locator - console.log("Would retry all failing tests!"); - return jobFactory({ testNames: ["single"] }); - } - - function pipelineComponentJob(jobPipeline, pipelineReq) { - const i = new Initiator(url); - const jobId = `j${Date.now()}`; - const job = { - name: jobId, - status: jobStatus.PENDING, - jobId, - isPipeline: true, - initiator: i, - pipelineId: jobPipeline.id, - branchId: pipelineReq.pipeline.__test, - }; - const request = { - image: "node", - name: jobId, - ...pipelineReq, - }; - - jobCreate(job); - const onLog = (d) => { - const job = state.jobs.find((j) => j.jobId === jobId); - - job.log.push(d); - job.status = jobStatus.ACTIVE; - jobUpdate({ ...job }, jobId); - }; - - const onClose = (c) => { - const job = state.jobs.find((j) => j.jobId === jobId); - job.exitcode = c; - job.status = c === 0 ? jobStatus.OK : jobStatus.ERROR; - jobUpdate({ ...job }, jobId); - }; - - const onPipelineTrigger = (p) => { - const { triggers } = p; - for (var t in triggers) { - if (t === "__testDelay") continue; - const delay = triggers[t].__testDelay ?? 0; - delete triggers[t].__testDelay; - const jobReq = { - ...request, - pipeline: { - ...p, - triggers: triggers[t], - __test: t, - }, - }; - jobPipeline.pendingTriggers.push({ - testName: t, - timer: setTimeout( - () => pipelineComponentJob(jobPipeline, jobReq), - delay - ), - triggerAt: Date.now() + delay, - }); - } - }; - const started = i.newPipelineJob( - request, - onLog, - onClose, - () => {}, - onPipelineTrigger - ); - started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId)); - } - - function pipelineFactory(builderCache) { - const { tree, branches, selectedBranches } = builderCache; - const __test = Object.keys(tree)[0]; - const pipelineReq = { - image: "node", - pipeline: { __test, triggers: { ...tree[__test] } }, - isTriage: builderCache.triageFailing, - }; - const id = `pij${Date.now()}`; - const pipeline = { id, branches, pendingTriggers: [], selectedBranches }; - const { pipelines } = state; - pipelines.push(pipeline); - updatePipelines([...pipelines]); - pipelineComponentJob(pipeline, pipelineReq); - return pipeline; - } - - function pipelineCancel(pipelineId) { - const pipeline = state.pipelines.find((p) => p.id === pipelineId); - pipeline.isCanceled = true; - pipeline.pendingTriggers.forEach((t) => clearTimeout(t)); - const jobs = state.jobs.filter( - (j) => j.isPipeline && j.pipelineId === pipelineId - ); - for (var j of jobs) { - if (j.initiator.sk) j.initiator.sk.close(); - j.status = jobStatus.CANCELED; - jobUpdate({ ...j }, j.jobId); - } - } - - function pipelineDestroy(pipelineId) { - const pipelineIndex = state.pipelines.findIndex((p) => p.id === pipelineId); - const pipeline = state.pipelines[pipelineIndex]; - pipeline.pendingTriggers.forEach((t) => clearTimeout(t)); - const jobs = state.jobs.filter( - (j) => j.isPipeline && j.pipelineId === pipelineId - ); - for (var j of jobs) { - if ( - j.initiator.sk && - j.status !== jobStatus.OK && - j.status !== jobStatus.ERROR && - j.status !== jobStatus.CANCELED - ) { - j.initiator.sk.close(); - } - jobDelete(j.jobId); - } - state.pipelines.splice(pipelineIndex, 1); - } - - function jobCancel(jobId) { - const job = state.jobs.find((j) => j.jobId === jobId); - - if (job.initiator.sk) job.initiator.sk.close(); - job.status = jobStatus.CANCELED; - jobUpdate({ ...job }, jobId); - } - - function jobDestroy(jobId) { - const job = state.jobs.find((j) => j.jobId === jobId); - - if ( - job.initiator.sk && - job.status !== jobStatus.OK && - job.status !== jobStatus.ERROR && - job.status !== jobStatus.CANCELED - ) { - job.initiator.sk.close(); - } - jobDelete(jobId); - } - - function jobFactory(builderCache) { - if (builderCache.tree) return pipelineFactory(builderCache); - // Find test - const i = new Initiator(url); - const jobId = `j${Date.now()}`; - const job = { - name: jobId, - status: jobStatus.PENDING, - jobId, - isPipeline: false, - builderCache, - initiator: i, - }; - - const request = { - testNames: builderCache.testNames, - image: "node", - type: "single", - name: jobId, - isTriage: builderCache.isTriage, - }; - - jobCreate(job); - - const onLog = (d) => { - const job = state.jobs.find((j) => j.jobId === jobId); - job.log.push(d); - job.status = jobStatus.ACTIVE; - jobUpdate({ ...job }, jobId); - }; - - const onClose = (c) => { - const job = state.jobs.find((j) => j.jobId === jobId); - job.exitcode = c; - job.status = c === 0 ? jobStatus.OK : jobStatus.ERROR; - jobUpdate({ ...job }, jobId); - }; - - const started = i.newJob(request, onLog, onClose, () => {}); - started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId)); - - return jobId; - } - const context = { state, dispatch, jobUpdate, jobCreate, jobDelete, - retryAll, - jobFactory, - jobCancel, - jobDestroy, - pipelineCancel, - pipelineDestroy, + updatePipelines, }; const contextValue = useMemo(() => context, [state, dispatch]); @@ -277,5 +63,4 @@ export const JobProvider = ({ children }) => { {children} ); }; - export default JobContext; diff --git a/src/job-core/JobCore.jsx b/src/job-core/JobCore.jsx new file mode 100644 index 0000000..88d0026 --- /dev/null +++ b/src/job-core/JobCore.jsx @@ -0,0 +1,44 @@ +import { useContext } from "react"; +import { v4 as uuidv4 } from "uuid"; +import JobContext from "@qltr/jobctx"; +import Initiator from "@qltr/initiator"; +import { useOneshotCore } from "./OneshotCore.jsx"; +import { usePipelineCore } from "./PipelineCore.jsx"; +import { useJobExtra } from "./JobExtra.jsx"; +import { jobStatus, socketUrl } from "./job-config.js"; + +export function useJobCore() { + const { state, jobUpdate, jobCreate, jobDelete } = useContext(JobContext); + const { pipelineStart, pipelineCancel, pipelineDestroy } = usePipelineCore(); + const { oneshotStart, oneshotCancel, oneshotDestroy } = useOneshotCore(); + const jobExtra = useJobExtra(); + + function retryAll(failing) { + console.log("Would retry all failing tests!"); + } + + function jobCompose(builderCache) { + if (builderCache.tree) return pipelineStart(builderCache); + return oneshotStart(builderCache); + } + + return { + // Job Context + state, + // Job Core + jobCompose, + retryAll, + // Oneshot + oneshotStart, + oneshotCancel, + oneshotDestroy, + // Pipeline + pipelineCancel, + pipelineDestroy, + pipelineStart, + // Job Extra + ...jobExtra, + }; +} + +export { jobStatus } from "./job-config.js"; diff --git a/src/job-core/JobExtra.jsx b/src/job-core/JobExtra.jsx new file mode 100644 index 0000000..716a95a --- /dev/null +++ b/src/job-core/JobExtra.jsx @@ -0,0 +1,81 @@ +import { useContext } from "react"; +import { useNavigate } from "react-router-dom"; +import JobContext from "@qltr/jobctx"; +import { jobStatus } from "./job-config.js"; +// Icons +import CheckIcon from "@mui/icons-material/Check"; +import ClearIcon from "@mui/icons-material/Clear"; +import ViewColumnIcon from "@mui/icons-material/ViewColumn"; +import PendingIcon from "@mui/icons-material/Pending"; +import VisibilityIcon from "@mui/icons-material/Visibility"; +import DoNotDisturbIcon from "@mui/icons-material/DoNotDisturb"; +import ReplayIcon from "@mui/icons-material/Replay"; + +function statusIcon(status) { + switch (status) { + case jobStatus.OK: + return ; + case jobStatus.ERROR: + return ; + case jobStatus.PENDING: + return ; + case jobStatus.ACTIVE: + return ; + case jobStatus.CANCELED: + return ; + case jobStatus.QUEUED: + return ; + default: + return ; + } +} + +export function useJobExtra() { + const { state, jobUpdate, jobCreate, jobDelete } = useContext(JobContext); + const navigate = useNavigate(); + + function pipelineJobs(pl) { + return state.jobs.filter((j) => j.isPipeline && j.pipelineId === pl.id); + } + + const jobIcon = ({ status }) => statusIcon(status); + + function pipelineIcon(pl) { + const jobStatuses = pipelineJobs(pl).map(({ status }) => status); + if (jobStatuses.includes(jobStatus.ERROR)) + return statusIcon(jobStatus.ERROR); + if (jobStatuses.includes(jobStatus.ACTIVE)) + return statusIcon(jobStatus.ACTIVE); + if (jobStatuses.includes(jobStatus.PENDING)) + return statusIcon(jobStatus.PENDING); + if (pl.isCanceled) return statusIcon(jobStatus.CANCELED); + if (jobStatuses.includes(jobStatus.OK)) return statusIcon(jobStatus.OK); + return statusIcon(jobStatus.QUEUED); + } + + function selectedPipelineBranches(pl) { + return pl.branches.map((b) => + b.filter((t) => pl.selectedBranches.find((b) => b.name == t.name)) + ); + } + + function findPipelineJobByTestName(pl, testName) { + return pipelineJobs(pl).find((j) => j.branchId === testName); + } + + // Nav + const toJob = (jobId) => navigate(`/qualiteer/jobs#job-${jobId}`); + const toPipeline = (plId) => navigate(`/qualiteer/jobs#pipeline-${plId}`); + const toJobs = () => navigate(`/qualiteer/jobs`); + + return { + pipelineJobs, + jobIcon, + pipelineIcon, + selectedPipelineBranches, + findPipelineJobByTestName, + toJob, + toPipeline, + toJobs, + }; +} diff --git a/src/job-core/OneshotCore.jsx b/src/job-core/OneshotCore.jsx new file mode 100644 index 0000000..7724fe1 --- /dev/null +++ b/src/job-core/OneshotCore.jsx @@ -0,0 +1,63 @@ +import { useContext } from "react"; +import { v4 as uuidv4 } from "uuid"; +import JobContext from "@qltr/jobctx"; +import Initiator from "@qltr/initiator"; +import { jobStatus, socketUrl } from "./job-config.js"; + +export function useOneshotCore() { + const { state, jobUpdate, jobCreate, jobDelete } = useContext(JobContext); + + function oneshotCancel(jobId) { + const job = state.jobs.find((j) => j.jobId === jobId); + + if (job.initiator.sk) job.initiator.sk.close(); + job.status = jobStatus.CANCELED; + jobUpdate({ ...job }, jobId); + } + + function oneshotDestroy(jobId) { + const job = state.jobs.find((j) => j.jobId === jobId); + const { status } = job; + const isTriggered = !!job.initiator.sk; + const isCompleted = status === jobStatus.OK || status === jobStatus.ERROR; + const isCanceled = status === jobStatus.CANCELED; + if (isTriggered && !isCompleted && !isCanceled) job.initiator.sk.close(); + jobDelete(jobId); + } + + function oneshotStart(builderCache) { + const { testNames, isTriage } = builderCache; + const initiator = new Initiator(socketUrl); + const jobId = uuidv4(); + const job = { + name: jobId, + status: jobStatus.PENDING, + jobId, + isPipeline: false, + builderCache, + initiator, + }; + const request = { testNames, type: "single", isTriage }; + + jobCreate(job); + + const onLog = (d) => { + const job = state.jobs.find((j) => j.jobId === jobId); + job.log.push(d); + job.status = jobStatus.ACTIVE; + jobUpdate({ ...job }, jobId); + }; + + const onClose = (c) => { + const job = state.jobs.find((j) => j.jobId === jobId); + job.exitcode = c; + job.status = c === 0 ? jobStatus.OK : jobStatus.ERROR; + jobUpdate({ ...job }, jobId); + }; + + const started = initiator.newJob(request, onLog, onClose, () => {}); + started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId)); + return jobId; + } + return { oneshotStart, oneshotCancel, oneshotDestroy }; +} diff --git a/src/job-core/PipelineCore.jsx b/src/job-core/PipelineCore.jsx new file mode 100644 index 0000000..0bb1339 --- /dev/null +++ b/src/job-core/PipelineCore.jsx @@ -0,0 +1,110 @@ +import { useContext } from "react"; +import { v4 as uuidv4 } from "uuid"; +import JobContext from "@qltr/jobctx"; +import Initiator from "@qltr/initiator"; +import { jobStatus, socketUrl } from "./job-config.js"; + +export function usePipelineCore() { + const { state, jobUpdate, jobCreate, jobDelete, updatePipelines } = + useContext(JobContext); + + function pipelineJob(pl, plReq) { + const initiator = new Initiator(socketUrl); + const jobId = uuidv4(); + const job = { + status: jobStatus.PENDING, + jobId, + isPipeline: true, + initiator, + pipelineId: pl.id, + branchId: plReq.pipeline.__test, + }; + + jobCreate(job); + const onLog = (d) => { + const job = state.jobs.find((j) => j.jobId === jobId); + job.log.push(d); + job.status = jobStatus.ACTIVE; + jobUpdate({ ...job }, jobId); + }; + + const onClose = (c) => { + const job = state.jobs.find((j) => j.jobId === jobId); + job.exitcode = c; + job.status = c === 0 ? jobStatus.OK : jobStatus.ERROR; + jobUpdate({ ...job }, jobId); + }; + + const onPipelineTrigger = (p) => { + const { triggers } = p; + for (var t in triggers) { + if (t === "__testDelay") continue; + const delay = triggers[t].__testDelay ?? 0; + delete triggers[t].__testDelay; + const plTrigger = { ...p, triggers: triggers[t], __test: t }; + const jobReq = { ...plReq, pipeline: plTrigger }; + const timer = setTimeout(() => pipelineJob(pl, jobReq), delay); + const triggerAt = Date.now() + delay; + pl.pendingTriggers.push({ testName: t, timer, triggerAt }); + } + }; + const started = initiator.newPipelineJob( + plReq, + onLog, + onClose, + () => {}, + onPipelineTrigger + ); + started.then(() => jobUpdate({ status: jobStatus.ACTIVE }, jobId)); + } + + function pipelineStart(builderCache) { + const { tree, branches, selectedBranches, triageFailing } = builderCache; + const __test = Object.keys(tree)[0]; + const plReq = { + pipeline: { __test, triggers: { ...tree[__test] } }, + isTriage: triageFailing, + }; + const id = uuidv4(); + const pipeline = { id, branches, pendingTriggers: [], selectedBranches }; + const { pipelines } = state; + pipelines.push(pipeline); + updatePipelines([...pipelines]); + pipelineJob(pipeline, plReq); + return pipeline; + } + + function pipelineCancel(pipelineId) { + const pipeline = state.pipelines.find((p) => p.id === pipelineId); + pipeline.isCanceled = true; + pipeline.pendingTriggers.forEach((t) => clearTimeout(t)); + const jobs = state.jobs.filter( + (j) => j.isPipeline && j.pipelineId === pipelineId + ); + for (var j of jobs) { + if (j.initiator.sk) j.initiator.sk.close(); + j.status = jobStatus.CANCELED; + jobUpdate({ ...j }, j.jobId); + } + } + + function pipelineDestroy(pipelineId) { + const pipelineIndex = state.pipelines.findIndex((p) => p.id === pipelineId); + const pipeline = state.pipelines[pipelineIndex]; + pipeline.pendingTriggers.forEach((t) => clearTimeout(t)); + const jobs = state.jobs.filter( + (j) => j.isPipeline && j.pipelineId === pipelineId + ); + var isTriggered, isCompleted, isCanceled; + for (var j of jobs) { + isTriggered = !!j.initiator.sk; + isCompleted = status === jobStatus.OK || status === jobStatus.ERROR; + isCanceled = status === jobStatus.CANCELED; + if (isTriggered && !isCompleted && !isCanceled) j.initiator.sk.close(); + jobDelete(j.jobId); + } + state.pipelines.splice(pipelineIndex, 1); + } + + return { pipelineStart, pipelineCancel, pipelineDestroy }; +} diff --git a/src/job-core/job-config.js b/src/job-core/job-config.js new file mode 100644 index 0000000..9bca43c --- /dev/null +++ b/src/job-core/job-config.js @@ -0,0 +1,9 @@ +export const jobStatus = { + OK: "o", + QUEUED: "q", + PENDING: "p", + CANCELED: "c", + ACTIVE: "a", + ERROR: "e", +}; +export const socketUrl = "/"; diff --git a/src/util/JobTools.jsx b/src/util/JobTools.jsx index e9697b2..aae4cf8 100644 --- a/src/util/JobTools.jsx +++ b/src/util/JobTools.jsx @@ -1,5 +1,6 @@ import { useContext } from "react"; -import JobContext, { jobStatus } from "@qltr/jobs"; +import JobContext from "@qltr/jobctx"; +import { jobStatus } from "../job-core/job-config.js"; import { useNavigate } from "react-router-dom"; import CheckIcon from "@mui/icons-material/Check"; diff --git a/src/util/pipelines.js b/src/util/pipelines.js index 1088e62..eb4c6d8 100644 --- a/src/util/pipelines.js +++ b/src/util/pipelines.js @@ -1,4 +1,4 @@ -import _ from "lodash"; +import merge from "lodash.merge"; const nest = (arr) => { const obj = {}; @@ -8,7 +8,7 @@ const nest = (arr) => { export const asTree = (branches) => { const nests = branches.map((b) => nest(b)); - return _.merge(...nests); + return merge(...nests); }; export const asBranches = (array) => { diff --git a/src/views/Navbar.jsx b/src/views/Navbar.jsx index 1c1f4ed..179d8dc 100644 --- a/src/views/Navbar.jsx +++ b/src/views/Navbar.jsx @@ -1,6 +1,6 @@ import { useContext, useState } from "react"; import { useCurrentlyFailing } from "@qltr/queries"; -import JobContext from "@qltr/jobs"; +import JobContext from "@qltr/jobctx"; import StoreContext from "@qltr/store"; import { Link, useLocation } from "react-router-dom"; diff --git a/src/views/catalog/Catalog.jsx b/src/views/catalog/Catalog.jsx index d2e256f..e73de49 100644 --- a/src/views/catalog/Catalog.jsx +++ b/src/views/catalog/Catalog.jsx @@ -1,6 +1,6 @@ import { useEffect, useContext } from "react"; import StoreContext from "@qltr/store"; -import JobContext from "@qltr/jobs"; +import JobContext from "@qltr/jobctx"; import CatalogBox from "./CatalogBox.jsx"; import CatalogSearch from "./CatalogSearch.jsx"; import { useCatalogTests } from "@qltr/queries"; @@ -40,6 +40,7 @@ export default function Catalog() { test.job = pipelineJob; continue; } + const job = jobState.jobs.find( (j) => !j.isPipeline && j.builderCache.testNames.includes(test.name) ); diff --git a/src/views/catalog/CatalogBox.jsx b/src/views/catalog/CatalogBox.jsx index 7568281..33b785b 100644 --- a/src/views/catalog/CatalogBox.jsx +++ b/src/views/catalog/CatalogBox.jsx @@ -2,12 +2,7 @@ import React, { useState, useContext } from "react"; import { usePipelineMappings } from "@qltr/queries"; import StoreContext from "@qltr/store"; -import JobContext, { jobStatus } from "@qltr/jobs"; -import { - useJobIconState, - usePipelineIconState, - useJobNav, -} from "@qltr/util/JobTools"; +import { useJobCore, jobStatus } from "@qltr/jobcore"; import useMediaQuery from "@mui/material/useMediaQuery"; import { useTheme } from "@mui/material/styles"; @@ -39,25 +34,24 @@ export default function CatalogBox(props) { const { data: pipelineMappings, isLoading } = usePipelineMappings(); const { state: store } = useContext(StoreContext); - const { jobFactory } = useContext(JobContext); - const jobNav = useJobNav(); + const { jobCompose, toPipeline, toJob, jobIcon, pipelineIcon } = useJobCore(); const [open, setOpen] = useState(false); const toggleOpen = () => setOpen(!open); const theme = useTheme(); const minifyActions = useMediaQuery(theme.breakpoints.down("sm")); const navigateToJob = () => { - if (pipeline) return jobNav.toPipeline(pipeline.id); - jobNav.toJob(job.jobId); + if (pipeline) return toPipeline(pipeline.id); + toJob(job.jobId); }; const runTest = () => { if (isPipeline) return runPipelineTest(); - const jobId = jobFactory({ + const jobId = jobCompose({ testNames: [testName], isTriage: store.triageFailing, }); - if (store.focusJob) jobNav.toJob(jobId); + if (store.focusJob) toJob(jobId); }; const runPipelineTest = () => { @@ -74,8 +68,8 @@ export default function CatalogBox(props) { selectedBranches: as1d(primaries), isTriage: true, }; - const pipeline = jobFactory(builderCache); - if (store.focusJob) jobNav.toPipeline(pipeline.id); + const pipeline = jobCompose(builderCache); + if (store.focusJob) toPipeline(pipeline.id); }; const jobOnClick = (e) => { @@ -86,10 +80,10 @@ export default function CatalogBox(props) { navigateToJob(); }; - function jobIcon() { - if (pipeline) return usePipelineIconState(pipeline); + function boxIcon() { + if (pipeline) return pipelineIcon(pipeline); if (!job) return ; - return useJobIconState(job); + return jobIcon(job); } return ( @@ -130,7 +124,7 @@ export default function CatalogBox(props) { component="span" onClick={jobOnClick} > - {jobIcon()} + {boxIcon()} diff --git a/src/views/failing/Failing.jsx b/src/views/failing/Failing.jsx index 3664140..f6e86ed 100644 --- a/src/views/failing/Failing.jsx +++ b/src/views/failing/Failing.jsx @@ -1,7 +1,6 @@ import { useState, useContext } from "react"; import { useCurrentlyFailing, useSilencedAlerts } from "@qltr/queries"; -import JobContext from "@qltr/jobs"; -import { useJobNav } from "@qltr/util/JobTools"; +import JobContext from "@qltr/jobctx"; import SilenceDialog, { useSilenceDialog } from "../alerting/SilenceDialog.jsx"; import FailingBox from "./FailingBox.jsx"; import QuickSilence, { useQuickSilence } from "./QuickSilence.jsx"; diff --git a/src/views/failing/FailingBox.jsx b/src/views/failing/FailingBox.jsx index 3452943..c5dec1f 100644 --- a/src/views/failing/FailingBox.jsx +++ b/src/views/failing/FailingBox.jsx @@ -1,12 +1,8 @@ import React, { useState, useContext } from "react"; import { usePipelineMappings, useIgnoreResult } from "@qltr/queries"; import StoreContext from "@qltr/store"; -import JobContext, { jobStatus } from "@qltr/jobs"; -import { - useJobIconState, - usePipelineIconState, - useJobNav, -} from "@qltr/util/JobTools"; + +import { useJobCore, jobStatus } from "@qltr/jobcore"; import useMediaQuery from "@mui/material/useMediaQuery"; import { useTheme } from "@mui/material/styles"; import Accordion from "@mui/material/Accordion"; @@ -63,9 +59,8 @@ export default function FailingBox(props) { const runHistory = recentResults ? [...recentResults].reverse() : null; const { data: pipelineMappings, isLoading } = usePipelineMappings(); - const { jobFactory } = useContext(JobContext); + const { jobCompose, jobIcon, pipelineIcon } = useJobCore(); const { state: store, updateStore, removeFailure } = useContext(StoreContext); - const jobNav = useJobNav(); const theme = useTheme(); const minifyActions = useMediaQuery(theme.breakpoints.down("sm")); const [open, setOpen] = useState(false); @@ -97,22 +92,22 @@ export default function FailingBox(props) { selectedBranches: as1d(primaries), isTriage: store.triageFailing, }; - const pipeline = jobFactory(builderCache); - if (store.focusJob) jobNav.toPipeline(pipeline.id); + const pipeline = jobCompose(builderCache); + if (store.focusJob) toPipeline(pipeline.id); }; const retryTest = () => { if (isPipeline) return retryPipelineTest(); - const jobId = jobFactory({ + const jobId = jobCompose({ testNames: [testName], isTriage: store.triageFailing, }); - if (store.focusJob) jobNav.toJob(jobId); + if (store.focusJob) toJob(jobId); }; const navigateToJob = () => { - if (pipeline) return jobNav.toPipeline(pipeline.id); - jobNav.toJob(job.jobId); + if (pipeline) return toPipeline(pipeline.id); + toJob(job.jobId); }; const jobOnClick = () => { @@ -121,10 +116,10 @@ export default function FailingBox(props) { navigateToJob(); }; - function jobIcon() { - if (pipeline) return usePipelineIconState(pipeline); + function boxIcon() { + if (pipeline) return pipelineIcon(pipeline); if (!job) return ; - return useJobIconState(job); + return jobIcon(job); } return ( @@ -220,7 +215,7 @@ export default function FailingBox(props) { - {jobIcon()} + {boxIcon()} setOpen(!open); const dialogClose = (confirmed) => () => { toggleOpen(); if (!confirmed) return; const jobId = retryAll(failing); if (!store.focusJob) return; - jobNav.toJob(jobId); + toJob(jobId); }; if (!failing || failing.length === 0) return; return ( diff --git a/src/views/jobs/JobBox.jsx b/src/views/jobs/JobBox.jsx index 26bcc64..253dd0a 100644 --- a/src/views/jobs/JobBox.jsx +++ b/src/views/jobs/JobBox.jsx @@ -1,6 +1,6 @@ import React from "react"; -import { useJobIconState } from "@qltr/util/JobTools"; +import { useJobCore } from "@qltr/jobcore"; import Accordion from "@mui/material/Accordion"; import AccordionSummary from "@mui/material/AccordionSummary"; import Typography from "@mui/material/Typography"; @@ -10,8 +10,8 @@ import IconButton from "@mui/material/IconButton"; import Stack from "@mui/material/Stack"; export default function JobBox(props) { + const { jobIcon } = useJobCore(); const { job } = props; - const { name, status } = job; return ( @@ -30,7 +30,7 @@ export default function JobBox(props) { - {useJobIconState(job)} + {jobIcon(job)} diff --git a/src/views/jobs/JobLogView.jsx b/src/views/jobs/JobLogView.jsx index 2bfd06a..6945b6c 100644 --- a/src/views/jobs/JobLogView.jsx +++ b/src/views/jobs/JobLogView.jsx @@ -1,5 +1,5 @@ import React from "react"; -import { jobStatus } from "@qltr/jobs"; +import { jobStatus } from "@qltr/jobcore"; import Box from "@mui/material/Box"; import Typography from "@mui/material/Typography"; diff --git a/src/views/jobs/JobPipelineBox.jsx b/src/views/jobs/JobPipelineBox.jsx index bf98a7b..a51fd6d 100644 --- a/src/views/jobs/JobPipelineBox.jsx +++ b/src/views/jobs/JobPipelineBox.jsx @@ -1,7 +1,6 @@ import React, { useState, useContext } from "react"; import StoreContext from "@qltr/store"; -import JobContext, { jobStatus } from "@qltr/jobs"; -import { usePipelineIconState } from "@qltr/util/JobTools"; +import { useJobCore, jobStatus } from "@qltr/jobcore"; import Accordion from "@mui/material/Accordion"; import AccordionDetails from "@mui/material/AccordionDetails"; @@ -21,7 +20,8 @@ import Stack from "@mui/material/Stack"; export default function JobPipelineBox(props) { const { pipeline } = props; - const pipelineIcon = usePipelineIconState(pipeline); + const { pipelineIcon } = useJobCore(); + const boxIcon = pipelineIcon(pipeline); return ( @@ -39,7 +39,7 @@ export default function JobPipelineBox(props) { - {pipelineIcon} + {boxIcon} diff --git a/src/views/jobs/JobPipelineDisplay.jsx b/src/views/jobs/JobPipelineDisplay.jsx index 8665315..7456d60 100644 --- a/src/views/jobs/JobPipelineDisplay.jsx +++ b/src/views/jobs/JobPipelineDisplay.jsx @@ -1,13 +1,6 @@ import React, { useContext } from "react"; import { useNavigate } from "react-router-dom"; -import JobContext, { jobStatus } from "@qltr/jobs"; -import { - selectedPipelineBranches, - pipelineJobs, - findPipelineJobByTestName, - useJobIconState, - useJobNav, -} from "@qltr/util/JobTools"; +import { useJobCore, jobStatus } from "@qltr/jobcore"; import Box from "@mui/material/Box"; import AppBar from "@mui/material/AppBar"; @@ -31,28 +24,29 @@ import DeleteIcon from "@mui/icons-material/Delete"; function JobPipelineDisplay(props) { const { pipeline } = props; + const { state: jobState, pipelineCancel, pipelineDestroy, - } = useContext(JobContext); + selectedPipelineBranches, + pipelineJobs, + findPipelineJobByTestName, + toJob, + jobIcon, + } = useJobCore(); - const jobNav = useJobNav(); const nav = useNavigate(); const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; + const handleClick = (event) => setAnchorEl(event.currentTarget); + const handleClose = () => setAnchorEl(null); const selectJob = (testName) => () => { - const job = findPipelineJobByTestName(pipeline, jobState.jobs, testName); + const job = findPipelineJobByTestName(pipeline, testName); if (!job) return; - jobNav.toJob(job.jobId); + toJob(job.jobId); }; function cancelPipeline() { @@ -69,16 +63,16 @@ function JobPipelineDisplay(props) { }; function pipelineActive() { - return pipelineJobs(pipeline, jobState.jobs).find( + return pipelineJobs(pipeline).find( (j) => j.status === jobStatus.ACTIVE || j.status === jobStatus.PENDING ); } - function jobIcon(name) { + function boxIcon(name) { if (pipeline.isCanceled) return ; - const job = findPipelineJobByTestName(pipeline, jobState.jobs, name); + const job = findPipelineJobByTestName(pipeline, name); if (!job) return ; - return useJobIconState(job); + return jobIcon(job); } return ( @@ -133,7 +127,7 @@ function JobPipelineDisplay(props) { - {jobIcon(test.name)} + {boxIcon(test.name)} diff --git a/src/views/jobs/JobPiplinePendingView.jsx b/src/views/jobs/JobPiplinePendingView.jsx index 9fed45f..d0534a4 100644 --- a/src/views/jobs/JobPiplinePendingView.jsx +++ b/src/views/jobs/JobPiplinePendingView.jsx @@ -1,6 +1,6 @@ import React, { useContext, useState, useEffect } from "react"; import { useNavigate } from "react-router-dom"; -import JobContext, { jobStatus } from "@qltr/jobs"; +import { useJobCore, jobStatus } from "@qltr/jobcore"; import StoreContext from "@qltr/store"; import Box from "@mui/material/Box"; import AppBar from "@mui/material/AppBar"; @@ -25,16 +25,12 @@ import ViewColumnIcon from "@mui/icons-material/ViewColumn"; export default function JobPipelinePendingView(props) { const navigate = useNavigate(); const { job } = props; - const { jobFactory, jobCancel, jobDestroy } = useContext(JobContext); + const { jobCompose, jobCancel, jobDestroy } = useJobCore(); const { state: store } = useContext(StoreContext); const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; + const handleClick = (event) => setAnchorEl(event.currentTarget); + const handleClose = () => setAnchorEl(null); function download(filename, text) { var element = document.createElement("a"); @@ -50,7 +46,7 @@ export default function JobPipelinePendingView(props) { } function retryJob() { - const jobId = jobFactory(job.builderCache); + const jobId = jobCompose(job.builderCache); if (store.focusJob) navigate(`/qualiteer/jobs#${jobId}`); } diff --git a/src/views/jobs/JobView.jsx b/src/views/jobs/JobView.jsx index 01b5c97..db4694a 100644 --- a/src/views/jobs/JobView.jsx +++ b/src/views/jobs/JobView.jsx @@ -1,7 +1,6 @@ import React, { useContext, useState, useEffect } from "react"; -import { useJobNav } from "@qltr/util/JobTools"; import { useNavigate } from "react-router-dom"; -import JobContext, { jobStatus } from "@qltr/jobs"; +import { useJobCore, jobStatus } from "@qltr/jobcore"; import StoreContext from "@qltr/store"; import Box from "@mui/material/Box"; import AppBar from "@mui/material/AppBar"; @@ -25,18 +24,14 @@ import ViewColumnIcon from "@mui/icons-material/ViewColumn"; export default function JobView(props) { const { job } = props; - const { jobFactory, jobCancel, jobDestroy } = useContext(JobContext); + const { jobCompose, jobCancel, jobDestroy, toPipeline, toJob } = useJobCore(); const { state: store } = useContext(StoreContext); - const jobNav = useJobNav(); + const nav = useNavigate(); const [anchorEl, setAnchorEl] = React.useState(null); const open = Boolean(anchorEl); - const handleClick = (event) => { - setAnchorEl(event.currentTarget); - }; - const handleClose = () => { - setAnchorEl(null); - }; + const handleClick = (event) => setAnchorEl(event.currentTarget); + const handleClose = () => setAnchorEl(null); function download(filename, text) { var element = document.createElement("a"); @@ -52,8 +47,8 @@ export default function JobView(props) { } function retryJob() { - const jobId = jobFactory(job.builderCache); - if (store.focusJob) jobNav.toJob(jobId); + const jobId = jobCompose(job.builderCache); + if (store.focusJob) toJob(jobId); } function downloadLog() { @@ -76,8 +71,8 @@ export default function JobView(props) { }; function navigateToJobs() { - if (job.isPipeline) return jobNav.toPipeline(job.pipelineId); - jobNav.toJobs(); + if (job.isPipeline) return toPipeline(job.pipelineId); + toJobs(); } return ( diff --git a/src/views/jobs/Jobs.jsx b/src/views/jobs/Jobs.jsx index be42d18..1846d5c 100644 --- a/src/views/jobs/Jobs.jsx +++ b/src/views/jobs/Jobs.jsx @@ -1,7 +1,7 @@ import React, { useContext, useEffect } from "react"; import { useLocation, useNavigate } from "react-router-dom"; -import JobContext from "@qltr/jobs"; +import JobContext from "@qltr/jobctx"; import JobBox from "./JobBox.jsx"; import JobPipelineBox from "./JobPipelineBox.jsx"; import JobView from "./JobView.jsx"; diff --git a/src/views/jobs/builder/JobBuilder.jsx b/src/views/jobs/builder/JobBuilder.jsx index d8520bc..9faa740 100644 --- a/src/views/jobs/builder/JobBuilder.jsx +++ b/src/views/jobs/builder/JobBuilder.jsx @@ -1,7 +1,6 @@ import React, { useContext, useState } from "react"; import StoreContext from "@qltr/store"; -import JobContext from "@qltr/jobs"; -import { useJobNav } from "@qltr/util/JobTools"; +import { useJobCore } from "@qltr/jobcore"; import Dialog from "@mui/material/Dialog"; import Toolbar from "@mui/material/Toolbar"; @@ -28,8 +27,7 @@ import PipelineConfirm from "./PipelineConfirm.jsx"; export default function JobBuilder() { const { state: store } = useContext(StoreContext); - const { jobFactory } = useContext(JobContext); - const jobNav = useJobNav(); + const { jobCompose } = useJobCore(); const [quickOpen, setQuickOpen] = useState(false); const [jobDialogOpen, setJobDialogOpen] = useState(false); @@ -52,8 +50,8 @@ export default function JobBuilder() { const handleClose = (confirmed) => () => { setJobDialogOpen(false); if (!confirmed) return; - const jobId = jobFactory({ ...cache, isTriage: store.triageFailing }); - if (store.focusJob) jobNav.toJob(jobId); + const jobId = jobCompose({ ...cache, isTriage: store.triageFailing }); + if (store.focusJob) toJob(jobId); }; // Pull info from url if possible? diff --git a/executor.config.js b/template.executor.config.mjs similarity index 71% rename from executor.config.js rename to template.executor.config.mjs index c99e9f8..e46787e 100644 --- a/executor.config.js +++ b/template.executor.config.mjs @@ -1,6 +1,6 @@ export default function executorConfig(payload) { return { - command: ({ command }) => command, + command: ({jobRequest}) => ["echo", "hello"], url: ({ url }) => url, jobId: ({ jobId }) => jobId, }; diff --git a/vite.config.js b/vite.config.js index 56b14f1..4e99591 100644 --- a/vite.config.js +++ b/vite.config.js @@ -27,11 +27,13 @@ export default () => { alias: { "@qltr/util": path.resolve("./src/util/"), "@qltr/queries": path.resolve("./src/util/queries"), - "@qltr/joins": path.resolve(`./src/util/Joins.jsx`), - "@qltr/jobs": path.resolve("./src/ctx/JobContext.jsx"), + "@qltr/jobcore": path.resolve("./src/job-core/JobCore.jsx"), + "@qltr/jobctx": path.resolve("./src/ctx/JobContext.jsx"), "@qltr/store": path.resolve("./src/ctx/StoreContext.jsx"), - "@qltr/initiator": path.resolve("./lib/sockets/clients/Initiator.js"), - "@qltr/mocks": path.resolve("./lib/database/mocks/"), + "@qltr/initiator": path.resolve( + "./lib/common/sockets/clients/Initiator.js" + ), + "@qltr/mocks": path.resolve("./lib/server/database/mocks/"), }, }, });