Page 1 of 1

Web Cluster Builder Source Code

Posted: Fri Sep 19, 2025 1:58 am
by webcbuilder
Before you can make this into a docker container you need to go into the root folder and write npm i , and then also into the public folder and write npm i
After that, you can build it.

Other than that, just create all these files in their respective folder.

Create a root folder, and in it these folders:

Code: Select all

admin, contract, public
Files to add into admin folder (2):

hotpocket-js-client.min.js

Code: Select all

var BSON=function(e){"use strict";var t="undefined"!=typeof globalThis?globalThis:"undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{};function r(e){return e&&e.__esModule&&Object.prototype.hasOwnProperty.call(e,"default")?e.default:e}function n(e,t){return e(t={exports:{}},t.exports),t.exports}for(var i=function(e){var t=p(e),r=t[0],n=t[1];return 3*(r+n)/4-n},o=function(e){var t,r,n=p(e),i=n[0],o=n[1],u=new a(function(e,t,r){return 3*(t+r)/4-r}(0,i,o)),s=0,l=o>0?i-4:i;for(r=0;r<l;r+=4)t=f[e.charCodeAt(r)]<<18|f[e.charCodeAt(r+1)]<<12|f[e.charCodeAt(r+2)]<<6|f[e.charCodeAt(r+3)],u[s++]=t>>16&255,u[s++]=t>>8&255,u[s++]=255&t;2===o&&(t=f[e.charCodeAt(r)]<<2|f[e.charCodeAt(r+1)]>>4,u[s++]=255&t);1===o&&(t=f[e.charCodeAt(r)]<<10|f[e.charCodeAt(r+1)]<<4|f[e.charCodeAt(r+2)]>>2,u[s++]=t>>8&255,u[s++]=255&t);return u},u=function(e){for(var t,r=e.length,n=r%3,i=[],o=16383,u=0,f=r-n;u<f;u+=o)i.push(d(e,u,u+o>f?f:u+o));1===n?(t=e[r-1],i.push(s[t>>2]+s[t<<4&63]+"==")):2===n&&(t=(e[r-2]<<8)+e[r-1],i.push(s[t>>10]+s[t>>4&63]+s[t<<2&63]+"="));return i.join("")},s=[],f=[],a="undefined"!=typeof Uint8Array?Uint8Array:Array,l="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",c=0,h=l.length;c<h;++c)s[c]=l[c],f[l.charCodeAt(c)]=c;function p(e){var t=e.length;if(t%4>0)throw new Error("Invalid string. Length must be a multiple of 4");var r=e.indexOf("=");return-1===r&&(r=t),[r,r===t?0:4-r%4]}function d(e,t,r){for(var n,i,o=[],u=t;u<r;u+=3)n=(e[u]<<16&16711680)+(e[u+1]<<8&65280)+(255&e[u+2]),o.push(s[(i=n)>>18&63]+s[i>>12&63]+s[i>>6&63]+s[63&i]);return o.join("")}f["-".charCodeAt(0)]=62,f["_".charCodeAt(0)]=63;var y={byteLength:i,toByteArray:o,fromByteArray:u},g=function(e,t,r,n,i){var o,u,s=8*i-n-1,f=(1<<s)-1,a=f>>1,l=-7,c=r?i-1:0,h=r?-1:1,p=e[t+c];for(c+=h,o=p&(1<<-l)-1,p>>=-l,l+=s;l>0;o=256*o+e[t+c],c+=h,l-=8);for(u=o&(1<<-l)-1,o>>=-l,l+=n;l>0;u=256*u+e[t+c],c+=h,l-=8);if(0===o)o=1-a;else{if(o===f)return u?NaN:1/0*(p?-1:1);u+=Math.pow(2,n),o-=a}return(p?-1:1)*u*Math.pow(2,o-n)},_=function(e,t,r,n,i,o){var u,s,f,a=8*o-i-1,l=(1<<a)-1,c=l>>1,h=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,p=n?0:o-1,d=n?1:-1,y=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,u=l):(u=Math.floor(Math.log(t)/Math.LN2),t*(f=Math.pow(2,-u))<1&&(u--,f*=2),(t+=u+c>=1?h/f:h*Math.pow(2,1-c))*f>=2&&(u++,f/=2),u+c>=l?(s=0,u=l):u+c>=1?(s=(t*f-1)*Math.pow(2,i),u+=c):(s=t*Math.pow(2,c-1)*Math.pow(2,i),u=0));i>=8;e[r+p]=255&s,p+=d,s/=256,i-=8);for(u=u<<i|s,a+=i;a>0;e[r+p]=255&u,p+=d,u/=256,a-=8);e[r+p-d]|=128*y},b=n((function(e,t){var r="function"==typeof Symbol&&"function"==typeof Symbol.for?Symbol.for("nodejs.util.inspect.custom"):null;t.Buffer=Buffer,t.SlowBuffer=function(e){+e!=e&&(e=0);return Buffer.alloc(+e)},t.INSPECT_MAX_BYTES=50;var n=2147483647;function i(e){if(e>n)throw new RangeError('The value "'+e+'" is invalid for option "size"');var t=new Uint8Array(e);return Object.setPrototypeOf(t,Buffer.prototype),t}function Buffer(e,t,r){if("number"==typeof e){if("string"==typeof t)throw new TypeError('The "string" argument must be of type string. Received type number');return s(e)}return o(e,t,r)}function o(e,t,r){if("string"==typeof e)return function(e,t){"string"==typeof t&&""!==t||(t="utf8");if(!Buffer.isEncoding(t))throw new TypeError("Unknown encoding: "+t);var r=0|c(e,t),n=i(r),o=n.write(e,t);o!==r&&(n=n.slice(0,o));return n}(e,t);if(ArrayBuffer.isView(e))return function(e){if(z(e,Uint8Array)){var t=new Uint8Array(e);return a(t.buffer,t.byteOffset,t.byteLength)}return f(e)}(e);if(null==e)throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+babelHelpers.typeof(e));if(z(e,ArrayBuffer)||e&&z(e.buffer,ArrayBuffer))return a(e,t,r);if("undefined"!=typeof SharedArrayBuffer&&(z(e,SharedArrayBuffer)||e&&z(e.buffer,SharedArrayBuffer)))return a(e,t,r);if("number"==typeof e)throw new TypeError('The "value" argument must not be of type number. Received type number');var n=e.valueOf&&e.valueOf();if(null!=n&&n!==e)return Buffer.from(n,t,r);var o=function(e){if(Buffer.isBuffer(e)){var t=0|l(e.length),r=i(t);return 0===r.length||e.copy(r,0,0,t),r}if(void 0!==e.length)return"number"!=typeof e.length||$(e.length)?i(0):f(e);if("Buffer"===e.type&&Array.isArray(e.data))return f(e.data)}(e);if(o)return o;if("undefined"!=typeof Symbol&&null!=Symbol.toPrimitive&&"function"==typeof e[Symbol.toPrimitive])return Buffer.from(e[Symbol.toPrimitive]("string"),t,r);throw new TypeError("The first argument must be one of type string, Buffer, ArrayBuffer, Array, or Array-like Object. Received type "+babelHelpers.typeof(e))}function u(e){if("number"!=typeof e)throw new TypeError('"size" argument must be of type number');if(e<0)throw new RangeError('The value "'+e+'" is invalid for option "size"')}function s(e){return u(e),i(e<0?0:0|l(e))}function f(e){for(var t=e.length<0?0:0|l(e.length),r=i(t),n=0;n<t;n+=1)r[n]=255&e[n];return r}function a(e,t,r){if(t<0||e.byteLength<t)throw new RangeError('"offset" is outside of buffer bounds');if(e.byteLength<t+(r||0))throw new RangeError('"length" is outside of buffer bounds');var n;return n=void 0===t&&void 0===r?new Uint8Array(e):void 0===r?new Uint8Array(e,t):new Uint8Array(e,t,r),Object.setPrototypeOf(n,Buffer.prototype),n}function l(e){if(e>=n)throw new RangeError("Attempt to allocate Buffer larger than maximum size: 0x"+n.toString(16)+" bytes");return 0|e}function c(e,t){if(Buffer.isBuffer(e))return e.length;if(ArrayBuffer.isView(e)||z(e,ArrayBuffer))return e.byteLength;if("string"!=typeof e)throw new TypeError('The "string" argument must be one of type string, Buffer, or ArrayBuffer. Received type '+babelHelpers.typeof(e));var r=e.length,n=arguments.length>2&&!0===arguments[2];if(!n&&0===r)return 0;for(var i=!1;;)switch(t){case"ascii"ase"latin1"ase"binary":return r;case"utf8"ase"utf-8":return P(e).length;case"ucs2"ase"ucs-2"ase"utf16le"ase"utf-16le":return 2*r;case"hex":return r>>>1;case"base64":return Y(e).length;default:if(i)return n?-1:P(e).length;t=(""+t).toLowerCase(),i=!0}}function h(e,t,r){var n=!1;if((void 0===t||t<0)&&(t=0),t>this.length)return"";if((void 0===r||r>this.length)&&(r=this.length),r<=0)return"";if((r>>>=0)<=(t>>>=0))return"";for(e||(e="utf8");;)switch(e){case"hex":return D(this,t,r);case"utf8"ase"utf-8":return A(this,t,r);case"ascii":return E(this,t,r);case"latin1"ase"binary":return T(this,t,r);case"base64":return v(this,t,r);case"ucs2"ase"ucs-2"ase"utf16le"ase"utf-16le":return I(this,t,r);default:if(n)throw new TypeError("Unknown encoding: "+e);e=(e+"").toLowerCase(),n=!0}}function p(e,t,r){var n=e[t];e[t]=e[r],e[r]=n}function d(e,t,r,n,i){if(0===e.length)return-1;if("string"==typeof r?(n=r,r=0):r>2147483647?r=2147483647:r<-2147483648&&(r=-2147483648),$(r=+r)&&(r=i?0:e.length-1),r<0&&(r=e.length+r),r>=e.length){if(i)return-1;r=e.length-1}else if(r<0){if(!i)return-1;r=0}if("string"==typeof t&&(t=Buffer.from(t,n)),Buffer.isBuffer(t))return 0===t.length?-1:b(e,t,r,n,i);if("number"==typeof t)return t&=255,"function"==typeof Uint8Array.prototype.indexOf?i?Uint8Array.prototype.indexOf.call(e,t,r):Uint8Array.prototype.lastIndexOf.call(e,t,r):b(e,[t],r,n,i);throw new TypeError("val must be string, number or Buffer")}function b(e,t,r,n,i){var o,u=1,s=e.length,f=t.length;if(void 0!==n&&("ucs2"===(n=String(n).toLowerCase())||"ucs-2"===n||"utf16le"===n||"utf-16le"===n)){if(e.length<2||t.length<2)return-1;u=2,s/=2,f/=2,r/=2}function a(e,t){return 1===u?e[t]:e.readUInt16BE(t*u)}if(i){var l=-1;for(o=r;o<s;o++)if(a(e,o)===a(t,-1===l?0:o-l)){if(-1===l&&(l=o),o-l+1===f)return l*u}else-1!==l&&(o-=o-l),l=-1}else for(r+f>s&&(r=s-f),o=r;o>=0;o--){for(var c=!0,h=0;h<f;h++)if(a(e,o+h)!==a(t,h)){c=!1;break}if(c)return o}return-1}function B(e,t,r,n){r=Number(r)||0;var i=e.length-r;n?(n=Number(n))>i&&(n=i):n=i;var o=t.length;n>o/2&&(n=o/2);for(var u=0;u<n;++u){var s=parseInt(t.substr(2*u,2),16);if($(s))return u;e[r+u]=s}return u}function O(e,t,r,n){return C(P(t,e.length-r),e,r,n)}function m(e,t,r,n){return C(function(e){for(var t=[],r=0;r<e.length;++r)t.push(255&e.charCodeAt(r));return t}(t),e,r,n)}function S(e,t,r,n){return C(Y(t),e,r,n)}function N(e,t,r,n){return C(function(e,t){for(var r,n,i,o=[],u=0;u<e.length&&!((t-=2)<0);++u)n=(r=e.charCodeAt(u))>>8,i=r%256,o.push(i),o.push(n);return o}(t,e.length-r),e,r,n)}function v(e,t,r){return 0===t&&r===e.length?y.fromByteArray(e):y.fromByteArray(e.slice(t,r))}function A(e,t,r){r=Math.min(e.length,r);for(var n=[],i=t;i<r;){var o,u,s,f,a=e[i],l=null,c=a>239?4:a>223?3:a>191?2:1;if(i+c<=r)switch(c){case 1:a<128&&(l=a);break;case 2:128==(192&(o=e[i+1]))&&(f=(31&a)<<6|63&o)>127&&(l=f);break;case 3:o=e[i+1],u=e[i+2],128==(192&o)&&128==(192&u)&&(f=(15&a)<<12|(63&o)<<6|63&u)>2047&&(f<55296||f>57343)&&(l=f);break;case 4:o=e[i+1],u=e[i+2],s=e[i+3],128==(192&o)&&128==(192&u)&&128==(192&s)&&(f=(15&a)<<18|(63&o)<<12|(63&u)<<6|63&s)>65535&&f<1114112&&(l=f)}null===l?(l=65533,c=1):l>65535&&(l-=65536,n.push(l>>>10&1023|55296),l=56320|1023&l),n.push(l),i+=c}return function(e){var t=e.length;if(t<=w)return String.fromCharCode.apply(String,e);var r="",n=0;for(;n<t;)r+=String.fromCharCode.apply(String,e.slice(n,n+=w));return r}(n)}t.kMaxLength=n,Buffer.TYPED_ARRAY_SUPPORT=function(){try{var e=new Uint8Array(1),t={foo:function(){return 42}};return Object.setPrototypeOf(t,Uint8Array.prototype),Object.setPrototypeOf(e,t),42===e.foo()}catch(e){return!1}}(),Buffer.TYPED_ARRAY_SUPPORT||"undefined"==typeof console||"function"!=typeof console.error||console.error("This browser lacks typed array (Uint8Array) support which is required by `buffer` v5.x. Use `buffer` v4.x if you require old browser support."),Object.defineProperty(Buffer.prototype,"parent",{enumerable:!0,get:function(){if(Buffer.isBuffer(this))return this.buffer}}),Object.defineProperty(Buffer.prototype,"offset",{enumerable:!0,get:function(){if(Buffer.isBuffer(this))return this.byteOffset}}),Buffer.poolSize=8192,Buffer.from=function(e,t,r){return o(e,t,r)},Object.setPrototypeOf(Buffer.prototype,Uint8Array.prototype),Object.setPrototypeOf(Buffer,Uint8Array),Buffer.alloc=function(e,t,r){return function(e,t,r){return u(e),e<=0?i(e):void 0!==t?"string"==typeof r?i(e).fill(t,r):i(e).fill(t):i(e)}(e,t,r)},Buffer.allocUnsafe=function(e){return s(e)},Buffer.allocUnsafeSlow=function(e){return s(e)},Buffer.isBuffer=function(e){return null!=e&&!0===e._isBuffer&&e!==Buffer.prototype},Buffer.compare=function(e,t){if(z(e,Uint8Array)&&(e=Buffer.from(e,e.offset,e.byteLength)),z(t,Uint8Array)&&(t=Buffer.from(t,t.offset,t.byteLength)),!Buffer.isBuffer(e)||!Buffer.isBuffer(t))throw new TypeError('The "buf1", "buf2" arguments must be one of type Buffer or Uint8Array');if(e===t)return 0;for(var r=e.length,n=t.length,i=0,o=Math.min(r,n);i<o;++i)if(e[i]!==t[i]){r=e[i],n=t[i];break}return r<n?-1:n<r?1:0},Buffer.isEncoding=function(e){switch(String(e).toLowerCase()){case"hex"ase"utf8"ase"utf-8"ase"ascii"ase"latin1"ase"binary"ase"base64"ase"ucs2"ase"ucs-2"ase"utf16le"ase"utf-16le":return!0;default:return!1}},Buffer.concat=function(e,t){if(!Array.isArray(e))throw new TypeError('"list" argument must be an Array of Buffers');if(0===e.length)return Buffer.alloc(0);var r;if(void 0===t)for(t=0,r=0;r<e.length;++r)t+=e[r].length;var n=Buffer.allocUnsafe(t),i=0;for(r=0;r<e.length;++r){var o=e[r];if(z(o,Uint8Array))i+o.length>n.length?Buffer.from(o).copy(n,i):Uint8Array.prototype.set.call(n,o,i);else{if(!Buffer.isBuffer(o))throw new TypeError('"list" argument must be an Array of Buffers');o.copy(n,i)}i+=o.length}return n},Buffer.byteLength=c,Buffer.prototype._isBuffer=!0,Buffer.prototype.swap16=function(){var e=this.length;if(e%2!=0)throw new RangeError("Buffer size must be a multiple of 16-bits");for(var t=0;t<e;t+=2)p(this,t,t+1);return this},Buffer.prototype.swap32=function(){var e=this.length;if(e%4!=0)throw new RangeError("Buffer size must be a multiple of 32-bits");for(var t=0;t<e;t+=4)p(this,t,t+3),p(this,t+1,t+2);return this},Buffer.prototype.swap64=function(){var e=this.length;if(e%8!=0)throw new RangeError("Buffer size must be a multiple of 64-bits");for(var t=0;t<e;t+=8)p(this,t,t+7),p(this,t+1,t+6),p(this,t+2,t+5),p(this,t+3,t+4);return this},Buffer.prototype.toString=function(){var e=this.length;return 0===e?"":0===arguments.length?A(this,0,e):h.apply(this,arguments)},Buffer.prototype.toLocaleString=Buffer.prototype.toString,Buffer.prototype.equals=function(e){if(!Buffer.isBuffer(e))throw new TypeError("Argument must be a Buffer");return this===e||0===Buffer.compare(this,e)},Buffer.prototype.inspect=function(){var e="",r=t.INSPECT_MAX_BYTES;return e=this.toString("hex",0,r).replace(/(.{2})/g,"$1 ").trim(),this.length>r&&(e+=" ... "),"<Buffer "+e+">"},r&&(Buffer.prototype[r]=Buffer.prototype.inspect),Buffer.prototype.compare=function(e,t,r,n,i){if(z(e,Uint8Array)&&(e=Buffer.from(e,e.offset,e.byteLength)),!Buffer.isBuffer(e))throw new TypeError('The "target" argument must be one of type Buffer or Uint8Array. Received type '+babelHelpers.typeof(e));if(void 0===t&&(t=0),void 0===r&&(r=e?e.length:0),void 0===n&&(n=0),void 0===i&&(i=this.length),t<0||r>e.length||n<0||i>this.length)throw new RangeError("out of range index");if(n>=i&&t>=r)return 0;if(n>=i)return-1;if(t>=r)return 1;if(this===e)return 0;for(var o=(i>>>=0)-(n>>>=0),u=(r>>>=0)-(t>>>=0),s=Math.min(o,u),f=this.slice(n,i),a=e.slice(t,r),l=0;l<s;++l)if(f[l]!==a[l]){o=f[l],u=a[l];break}return o<u?-1:u<o?1:0},Buffer.prototype.includes=function(e,t,r){return-1!==this.indexOf(e,t,r)},Buffer.prototype.indexOf=function(e,t,r){return d(this,e,t,r,!0)},Buffer.prototype.lastIndexOf=function(e,t,r){return d(this,e,t,r,!1)},Buffer.prototype.write=function(e,t,r,n){if(void 0===t)n="utf8",r=this.length,t=0;else if(void 0===r&&"string"==typeof t)n=t,r=this.length,t=0;else{if(!isFinite(t))throw new Error("Buffer.write(string, encoding, offset[, length]) is no longer supported");t>>>=0,isFinite(r)?(r>>>=0,void 0===n&&(n="utf8")):(n=r,r=void 0)}var i=this.length-t;if((void 0===r||r>i)&&(r=i),e.length>0&&(r<0||t<0)||t>this.length)throw new RangeError("Attempt to write outside buffer bounds");n||(n="utf8");for(var o=!1;;)switch(n){case"hex":return B(this,e,t,r);case"utf8"ase"utf-8":return O(this,e,t,r);case"ascii"ase"latin1"ase"binary":return m(this,e,t,r);case"base64":return S(this,e,t,r);case"ucs2"ase"ucs-2"ase"utf16le"ase"utf-16le":return N(this,e,t,r);default:if(o)throw new TypeError("Unknown encoding: "+n);n=(""+n).toLowerCase(),o=!0}},Buffer.prototype.toJSON=function(){return{type:"Buffer",data:Array.prototype.slice.call(this._arr||this,0)}};var w=4096;function E(e,t,r){var n="";r=Math.min(e.length,r);for(var i=t;i<r;++i)n+=String.fromCharCode(127&e[i]);return n}function T(e,t,r){var n="";r=Math.min(e.length,r);for(var i=t;i<r;++i)n+=String.fromCharCode(e[i]);return n}function D(e,t,r){var n=e.length;(!t||t<0)&&(t=0),(!r||r<0||r>n)&&(r=n);for(var i="",o=t;o<r;++o)i+=k[e[o]];return i}function I(e,t,r){for(var n=e.slice(t,r),i="",o=0;o<n.length-1;o+=2)i+=String.fromCharCode(n[o]+256*n[o+1]);return i}function x(e,t,r){if(e%1!=0||e<0)throw new RangeError("offset is not uint");if(e+t>r)throw new RangeError("Trying to access beyond buffer length")}function U(e,t,r,n,i,o){if(!Buffer.isBuffer(e))throw new TypeError('"buffer" argument must be a Buffer instance');if(t>i||t<o)throw new RangeError('"value" argument is out of bounds');if(r+n>e.length)throw new RangeError("Index out of range")}function R(e,t,r,n,i,o){if(r+n>e.length)throw new RangeError("Index out of range");if(r<0)throw new RangeError("Index out of range")}function L(e,t,r,n,i){return t=+t,r>>>=0,i||R(e,0,r,4),_(e,t,r,n,23,4),r+4}function j(e,t,r,n,i){return t=+t,r>>>=0,i||R(e,0,r,8),_(e,t,r,n,52,8),r+8}Buffer.prototype.slice=function(e,t){var r=this.length;(e=~~e)<0?(e+=r)<0&&(e=0):e>r&&(e=r),(t=void 0===t?r:~~t)<0?(t+=r)<0&&(t=0):t>r&&(t=r),t<e&&(t=e);var n=this.subarray(e,t);return Object.setPrototypeOf(n,Buffer.prototype),n},Buffer.prototype.readUintLE=Buffer.prototype.readUIntLE=function(e,t,r){e>>>=0,t>>>=0,r||x(e,t,this.length);for(var n=this[e],i=1,o=0;++o<t&&(i*=256);)n+=this[e+o]*i;return n},Buffer.prototype.readUintBE=Buffer.prototype.readUIntBE=function(e,t,r){e>>>=0,t>>>=0,r||x(e,t,this.length);for(var n=this[e+--t],i=1;t>0&&(i*=256);)n+=this[e+--t]*i;return n},Buffer.prototype.readUint8=Buffer.prototype.readUInt8=function(e,t){return e>>>=0,t||x(e,1,this.length),this[e]},Buffer.prototype.readUint16LE=Buffer.prototype.readUInt16LE=function(e,t){return e>>>=0,t||x(e,2,this.length),this[e]|this[e+1]<<8},Buffer.prototype.readUint16BE=Buffer.prototype.readUInt16BE=function(e,t){return e>>>=0,t||x(e,2,this.length),this[e]<<8|this[e+1]},Buffer.prototype.readUint32LE=Buffer.prototype.readUInt32LE=function(e,t){return e>>>=0,t||x(e,4,this.length),(this[e]|this[e+1]<<8|this[e+2]<<16)+16777216*this[e+3]},Buffer.prototype.readUint32BE=Buffer.prototype.readUInt32BE=function(e,t){return e>>>=0,t||x(e,4,this.length),16777216*this[e]+(this[e+1]<<16|this[e+2]<<8|this[e+3])},Buffer.prototype.readIntLE=function(e,t,r){e>>>=0,t>>>=0,r||x(e,t,this.length);for(var n=this[e],i=1,o=0;++o<t&&(i*=256);)n+=this[e+o]*i;return n>=(i*=128)&&(n-=Math.pow(2,8*t)),n},Buffer.prototype.readIntBE=function(e,t,r){e>>>=0,t>>>=0,r||x(e,t,this.length);for(var n=t,i=1,o=this[e+--n];n>0&&(i*=256);)o+=this[e+--n]*i;return o>=(i*=128)&&(o-=Math.pow(2,8*t)),o},Buffer.prototype.readInt8=function(e,t){return e>>>=0,t||x(e,1,this.length),128&this[e]?-1*(255-this[e]+1):this[e]},Buffer.prototype.readInt16LE=function(e,t){e>>>=0,t||x(e,2,this.length);var r=this[e]|this[e+1]<<8;return 32768&r?4294901760|r:r},Buffer.prototype.readInt16BE=function(e,t){e>>>=0,t||x(e,2,this.length);var r=this[e+1]|this[e]<<8;return 32768&r?4294901760|r:r},Buffer.prototype.readInt32LE=function(e,t){return e>>>=0,t||x(e,4,this.length),this[e]|this[e+1]<<8|this[e+2]<<16|this[e+3]<<24},Buffer.prototype.readInt32BE=function(e,t){return e>>>=0,t||x(e,4,this.length),this[e]<<24|this[e+1]<<16|this[e+2]<<8|this[e+3]},Buffer.prototype.readFloatLE=function(e,t){return e>>>=0,t||x(e,4,this.length),g(this,e,!0,23,4)},Buffer.prototype.readFloatBE=function(e,t){return e>>>=0,t||x(e,4,this.length),g(this,e,!1,23,4)},Buffer.prototype.readDoubleLE=function(e,t){return e>>>=0,t||x(e,8,this.length),g(this,e,!0,52,8)},Buffer.prototype.readDoubleBE=function(e,t){return e>>>=0,t||x(e,8,this.length),g(this,e,!1,52,8)},Buffer.prototype.writeUintLE=Buffer.prototype.writeUIntLE=function(e,t,r,n){(e=+e,t>>>=0,r>>>=0,n)||U(this,e,t,r,Math.pow(2,8*r)-1,0);var i=1,o=0;for(this[t]=255&e;++o<r&&(i*=256);)this[t+o]=e/i&255;return t+r},Buffer.prototype.writeUintBE=Buffer.prototype.writeUIntBE=function(e,t,r,n){(e=+e,t>>>=0,r>>>=0,n)||U(this,e,t,r,Math.pow(2,8*r)-1,0);var i=r-1,o=1;for(this[t+i]=255&e;--i>=0&&(o*=256);)this[t+i]=e/o&255;return t+r},Buffer.prototype.writeUint8=Buffer.prototype.writeUInt8=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,1,255,0),this[t]=255&e,t+1},Buffer.prototype.writeUint16LE=Buffer.prototype.writeUInt16LE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,2,65535,0),this[t]=255&e,this[t+1]=e>>>8,t+2},Buffer.prototype.writeUint16BE=Buffer.prototype.writeUInt16BE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,2,65535,0),this[t]=e>>>8,this[t+1]=255&e,t+2},Buffer.prototype.writeUint32LE=Buffer.prototype.writeUInt32LE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,4,4294967295,0),this[t+3]=e>>>24,this[t+2]=e>>>16,this[t+1]=e>>>8,this[t]=255&e,t+4},Buffer.prototype.writeUint32BE=Buffer.prototype.writeUInt32BE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,4,4294967295,0),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},Buffer.prototype.writeIntLE=function(e,t,r,n){if(e=+e,t>>>=0,!n){var i=Math.pow(2,8*r-1);U(this,e,t,r,i-1,-i)}var o=0,u=1,s=0;for(this[t]=255&e;++o<r&&(u*=256);)e<0&&0===s&&0!==this[t+o-1]&&(s=1),this[t+o]=(e/u>>0)-s&255;return t+r},Buffer.prototype.writeIntBE=function(e,t,r,n){if(e=+e,t>>>=0,!n){var i=Math.pow(2,8*r-1);U(this,e,t,r,i-1,-i)}var o=r-1,u=1,s=0;for(this[t+o]=255&e;--o>=0&&(u*=256);)e<0&&0===s&&0!==this[t+o+1]&&(s=1),this[t+o]=(e/u>>0)-s&255;return t+r},Buffer.prototype.writeInt8=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,1,127,-128),e<0&&(e=255+e+1),this[t]=255&e,t+1},Buffer.prototype.writeInt16LE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,2,32767,-32768),this[t]=255&e,this[t+1]=e>>>8,t+2},Buffer.prototype.writeInt16BE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,2,32767,-32768),this[t]=e>>>8,this[t+1]=255&e,t+2},Buffer.prototype.writeInt32LE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,4,2147483647,-2147483648),this[t]=255&e,this[t+1]=e>>>8,this[t+2]=e>>>16,this[t+3]=e>>>24,t+4},Buffer.prototype.writeInt32BE=function(e,t,r){return e=+e,t>>>=0,r||U(this,e,t,4,2147483647,-2147483648),e<0&&(e=4294967295+e+1),this[t]=e>>>24,this[t+1]=e>>>16,this[t+2]=e>>>8,this[t+3]=255&e,t+4},Buffer.prototype.writeFloatLE=function(e,t,r){return L(this,e,t,!0,r)},Buffer.prototype.writeFloatBE=function(e,t,r){return L(this,e,t,!1,r)},Buffer.prototype.writeDoubleLE=function(e,t,r){return j(this,e,t,!0,r)},Buffer.prototype.writeDoubleBE=function(e,t,r){return j(this,e,t,!1,r)},Buffer.prototype.copy=function(e,t,r,n){if(!Buffer.isBuffer(e))throw new TypeError("argument should be a Buffer");if(r||(r=0),n||0===n||(n=this.length),t>=e.length&&(t=e.length),t||(t=0),n>0&&n<r&&(n=r),n===r)return 0;if(0===e.length||0===this.length)return 0;if(t<0)throw new RangeError("targetStart out of bounds");if(r<0||r>=this.length)throw new RangeError("Index out of range");if(n<0)throw new RangeError("sourceEnd out of bounds");n>this.length&&(n=this.length),e.length-t<n-r&&(n=e.length-t+r);var i=n-r;return this===e&&"function"==typeof Uint8Array.prototype.copyWithin?this.copyWithin(t,r,n):Uint8Array.prototype.set.call(e,this.subarray(r,n),t),i},Buffer.prototype.fill=function(e,t,r,n){if("string"==typeof e){if("string"==typeof t?(n=t,t=0,r=this.length):"string"==typeof r&&(n=r,r=this.length),void 0!==n&&"string"!=typeof n)throw new TypeError("encoding must be a string");if("string"==typeof n&&!Buffer.isEncoding(n))throw new TypeError("Unknown encoding: "+n);if(1===e.length){var i=e.charCodeAt(0);("utf8"===n&&i<128||"latin1"===n)&&(e=i)}}else"number"==typeof e?e&=255:"boolean"==typeof e&&(e=Number(e));if(t<0||this.length<t||this.length<r)throw new RangeError("Out of range index");if(r<=t)return this;var o;if(t>>>=0,r=void 0===r?this.length:r>>>0,e||(e=0),"number"==typeof e)for(o=t;o<r;++o)this[o]=e;else{var u=Buffer.isBuffer(e)?e:Buffer.from(e,n),s=u.length;if(0===s)throw new TypeError('The value "'+e+'" is invalid for argument "value"');for(o=0;o<r-t;++o)this[o+t]=u[o%s]}return this};var M=/[^+/0-9A-Za-z-_]/g;function P(e,t){var r;t=t||1/0;for(var n=e.length,i=null,o=[],u=0;u<n;++u){if((r=e.charCodeAt(u))>55295&&r<57344){if(!i){if(r>56319){(t-=3)>-1&&o.push(239,191,189);continue}if(u+1===n){(t-=3)>-1&&o.push(239,191,189);continue}i=r;continue}if(r<56320){(t-=3)>-1&&o.push(239,191,189),i=r;continue}r=65536+(i-55296<<10|r-56320)}else i&&(t-=3)>-1&&o.push(239,191,189);if(i=null,r<128){if((t-=1)<0)break;o.push(r)}else if(r<2048){if((t-=2)<0)break;o.push(r>>6|192,63&r|128)}else if(r<65536){if((t-=3)<0)break;o.push(r>>12|224,r>>6&63|128,63&r|128)}else{if(!(r<1114112))throw new Error("Invalid code point");if((t-=4)<0)break;o.push(r>>18|240,r>>12&63|128,r>>6&63|128,63&r|128)}}return o}function Y(e){return y.toByteArray(function(e){if((e=(e=e.split("=")[0]).trim().replace(M,"")).length<2)return"";for(;e.length%4!=0;)e+="=";return e}(e))}function C(e,t,r,n){for(var i=0;i<n&&!(i+r>=t.length||i>=e.length);++i)t[i+r]=e[i];return i}function z(e,t){return e instanceof t||null!=e&&null!=e.constructor&&null!=e.constructor.name&&e.constructor.name===t.name}function $(e){return e!=e}var k=function(){for(var e="0123456789abcdef",t=new Array(256),r=0;r<16;++r)for(var n=16*r,i=0;i<16;++i)t[n+i]=e[r]+e[i];return t}()}));

/*! ieee754. BSD-3-Clause License. Feross Aboukhadijeh <https://feross.org/opensource> */b.Buffer,b.SlowBuffer,b.INSPECT_MAX_BYTES,b.kMaxLength;var B={},O="function"==typeof Object.create?function(e,t){e.super_=t,e.prototype=Object.create(t.prototype,{constructor:{value:e,enumerable:!1,writable:!0,configurable:!0}})}:function(e,t){e.super_=t;var r=function(){};r.prototype=t.prototype,e.prototype=new r,e.prototype.constructor=e},m=/%[sdj%]/g;function S(e){if(!j(e)){for(var t=[],r=0;r<arguments.length;r++)t.push(A(arguments[r]));return t.join(" ")}r=1;for(var n=arguments,i=n.length,o=String(e).replace(m,(function(e){if("%%"===e)return"%";if(r>=i)return e;switch(e){case"%s":return String(n[r++]);case"%d":return Number(n[r++]);case"%j":try{return JSON.stringify(n[r++])}catch(e){return"[Circular]"}default:return e}})),u=n[r];r<i;u=n[++r])R(u)||!Y(u)?o+=" "+u:o+=" "+A(u);return o}var N,v={};function A(e,t){var r={seen:[],stylize:E};return arguments.length>=3&&(r.depth=arguments[2]),arguments.length>=4&&(r.colors=arguments[3]),U(t)?r.showHidden=t:t&&V(r,t),M(r.showHidden)&&(r.showHidden=!1),M(r.depth)&&(r.depth=2),M(r.colors)&&(r.colors=!1),M(r.customInspect)&&(r.customInspect=!0),r.colors&&(r.stylize=w),T(r,e,r.depth)}function w(e,t){var r=A.styles[t];return r?"["+A.colors[r][0]+"m"+e+"["+A.colors[r][1]+"m":e}function E(e,t){return e}function T(e,t,r){if(e.customInspect&&t&&$(t.inspect)&&t.inspect!==A&&(!t.constructor||t.constructor.prototype!==t)){var n=t.inspect(r,e);return j(n)||(n=T(e,n,r)),n}var i=function(e,t){if(M(t))return e.stylize("undefined","undefined");if(j(t)){var r="'"+JSON.stringify(t).replace(/^"|"$/g,"").replace(/'/g,"\\'").replace(/\\"/g,'"')+"'";return e.stylize(r,"string")}if(L(t))return e.stylize(""+t,"number");if(U(t))return e.stylize(""+t,"boolean");if(R(t))return e.stylize("null","null")}(e,t);if(i)return i;var o=Object.keys(t),u=function(e){var t={};return e.forEach((function(e,r){t[e]=!0})),t}(o);if(e.showHidden&&(o=Object.getOwnPropertyNames(t)),z(t)&&(o.indexOf("message")>=0||o.indexOf("description")>=0))return D(t);if(0===o.length){if($(t)){var s=t.name?": "+t.name:"";return e.stylize("[Function"+s+"]","special")}if(P(t))return e.stylize(RegExp.prototype.toString.call(t),"regexp");if(C(t))return e.stylize(Date.prototype.toString.call(t),"date");if(z(t))return D(t)}var f,a="",l=!1,c=["{","}"];(x(t)&&(l=!0,c=["[","]"]),$(t))&&(a=" [Function"+(t.name?": "+t.name:"")+"]");return P(t)&&(a=" "+RegExp.prototype.toString.call(t)),C(t)&&(a=" "+Date.prototype.toUTCString.call(t)),z(t)&&(a=" "+D(t)),0!==o.length||l&&0!=t.length?r<0?P(t)?e.stylize(RegExp.prototype.toString.call(t),"regexp"):e.stylize("[Object]","special"):(e.seen.push(t),f=l?function(e,t,r,n,i){for(var o=[],u=0,s=t.length;u<s;++u)K(t,String(u))?o.push(I(e,t,r,n,String(u),!0)):o.push("");return i.forEach((function(i){i.match(/^\d+$/)||o.push(I(e,t,r,n,i,!0))})),o}(e,t,r,u,o):o.map((function(n){return I(e,t,r,u,n,l)})),e.seen.pop(),function(e,t,r){if(e.reduce((function(e,t){return t.indexOf("\n"),e+t.replace(/\u001b\[\d\d?m/g,"").length+1}),0)>60)return r[0]+(""===t?"":t+"\n ")+" "+e.join(",\n  ")+" "+r[1];return r[0]+t+" "+e.join(", ")+" "+r[1]}(f,a,c))[0]+a+c[1]}function D(e){return"["+Error.prototype.toString.call(e)+"]"}function I(e,t,r,n,i,o){var u,s,f;if((f=Object.getOwnPropertyDescriptor(t,i)||{value:t[i]}).get?s=f.set?e.stylize("[Getter/Setter]","special"):e.stylize("[Getter]","special"):f.set&&(s=e.stylize("[Setter]","special")),K(n,i)||(u="["+i+"]"),s||(e.seen.indexOf(f.value)<0?(s=R(r)?T(e,f.value,null):T(e,f.value,r-1)).indexOf("\n")>-1&&(s=o?s.split("\n").map((function(e){return"  "+e})).join("\n").substr(2):"\n"+s.split("\n").map((function(e){return"   "+e})).join("\n")):s=e.stylize("[Circular]","special")),M(u)){if(o&&i.match(/^\d+$/))return s;(u=JSON.stringify(""+i)).match(/^"([a-zA-Z_][a-zA-Z_0-9]*)"$/)?(u=u.substr(1,u.length-2),u=e.stylize(u,"name")):(u=u.replace(/'/g,"\\'").replace(/\\"/g,'"').replace(/(^"|"$)/g,"'"),u=e.stylize(u,"string"))}return u+": "+s}function x(e){return Array.isArray(e)}function U(e){return"boolean"==typeof e}function R(e){return null===e}function L(e){return"number"==typeof e}function j(e){return"string"==typeof e}function M(e){return void 0===e}function P(e){return Y(e)&&"[object RegExp]"===k(e)}function Y(e){return"object"===babelHelpers.typeof(e)&&null!==e}function C(e){return Y(e)&&"[object Date]"===k(e)}function z(e){return Y(e)&&("[object Error]"===k(e)||e instanceof Error)}function $(e){return"function"==typeof e}function k(e){return Object.prototype.toString.call(e)}function J(e){return e<10?"0"+e.toString(10):e.toString(10)}A.colors={bold:[1,22],italic:[3,23],underline:[4,24],inverse:[7,27],white:[37,39],grey:[90,39],black:[30,39],blue:[34,39],cyan:[36,39],green:[32,39],magenta:[35,39],red:[31,39],yellow:[33,39]},A.styles={special:"cyan",number:"yellow",boolean:"yellow",undefined:"grey",null:"bold",string:"green",date:"magenta",regexp:"red"};var q=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function F(){var e=new Date,t=[J(e.getHours()),J(e.getMinutes()),J(e.getSeconds())].join(":");return[e.getDate(),q[e.getMonth()],t].join(" ")}function V(e,t){if(!t||!Y(t))return e;for(var r=Object.keys(t),n=r.length;n--;)e[r[n]]=t[r[n]];return e}function K(e,t){return Object.prototype.hasOwnProperty.call(e,t)}var H={inherits:O,_extend:V,log:function(){console.log("%s - %s",F(),S.apply(null,arguments))},isBuffer:function(e){return Buffer.isBuffer(e)},isPrimitive:function(e){return null===e||"boolean"==typeof e||"number"==typeof e||"string"==typeof e||"symbol"===babelHelpers.typeof(e)||void 0===e},isFunction:$,isError:z,isDate:C,isObject:Y,isRegExp:P,isUndefined:M,isSymbol:function(e){return"symbol"===babelHelpers.typeof(e)},isString:j,isNumber:L,isNullOrUndefined:function(e){return null==e},isNull:R,isBoolean:U,isArray:x,inspect:A,deprecate:function e(t,r){if(M(global.process))return function(){return e(t,r).apply(this,arguments)};var n=!1;return function(){return n||(console.error(r),n=!0),t.apply(this,arguments)}},format:S,debuglog:function(e){if(M(N)&&(N=""),e=e.toUpperCase(),!v[e])if(new RegExp("\\b"+e+"\\b","i").test(N)){v[e]=function(){var t=S.apply(null,arguments);console.error("%s %d: %s",e,0,t)}}else v[e]=function(){};return v[e]}},X=n((function(e,r){Object.defineProperty(r,"__esModule",{value:!0}),r.deprecate=r.isObjectLike=r.isDate=r.isBuffer=r.haveBuffer=r.isBigUInt64Array=r.isBigInt64Array=r.isUint8Array=r.randomBytes=r.normalizedFunctionString=void 0,r.normalizedFunctionString=function(e){return e.toString().replace("function(","function (")};var n="object"==typeof t.navigator&&"ReactNative"===t.navigator.product?"BSON: For React Native please polyfill crypto.getRandomValues, e.g. using: https://www.npmjs.com/package/react-native-get-random-values.":"BSON: No cryptographic implementation for random bytes present, falling back to a less secure implementation.",i=function(e){console.warn(n);for(var t=b.Buffer.alloc(e),r=0;r<e;++r)t[r]=Math.floor(256*Math.random());return t};function o(e){return"object"==typeof e&&null!==e}r.randomBytes=function(){if("undefined"!=typeof window){var e=window.crypto||window.msCrypto;if(e&&e.getRandomValues)return function(t){return e.getRandomValues(b.Buffer.alloc(t))}}if(void 0!==t&&t.crypto&&t.crypto.getRandomValues)return function(e){return t.crypto.getRandomValues(b.Buffer.alloc(e))};var r;try{r=B.randomBytes}catch(e){}return r||i}(),r.isUint8Array=function(e){return"[object Uint8Array]"===Object.prototype.toString.call(e)},r.isBigInt64Array=function(e){return"[object BigInt64Array]"===Object.prototype.toString.call(e)},r.isBigUInt64Array=function(e){return"[object BigUint64Array]"===Object.prototype.toString.call(e)},r.haveBuffer=function(){return void 0!==t&&void 0!==t.Buffer},r.isBuffer=function(e){var t;return"object"==typeof e&&"Buffer"===(null===(t=null==e?void 0:e.constructor)||void 0===t?void 0:t.name)},r.isDate=function(e){return o(e)&&"[object Date]"===Object.prototype.toString.call(e)},r.isObjectLike=o,r.deprecate=function(e,t){if("undefined"==typeof window&&"undefined"==typeof self)return H.deprecate(e,t);var r=!1;return function(){for(var n=[],i=0;i<arguments.length;i++)n[i]=arguments[i];return r||(console.warn(t),r=!0),e.apply(this,n)}}}));r(X),X.deprecate,X.isObjectLike,X.isDate,X.isBuffer,X.haveBuffer,X.isBigUInt64Array,X.isBigInt64Array,X.isUint8Array,X.randomBytes,X.normalizedFunctionString;var W=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.ensureBuffer=void 0,t.ensureBuffer=function(e){if(X.isBuffer(e))return e;if(ArrayBuffer.isView(e))return b.Buffer.from(e.buffer);if(e instanceof ArrayBuffer)return b.Buffer.from(e);throw new TypeError("Must use either Buffer or TypedArray")}}));r(W),W.ensureBuffer;var G=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.bufferToUuidHexString=t.uuidHexStringToBuffer=t.uuidValidateString=void 0;var r=/^(?:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}|[0-9a-f]{12}4[0-9a-f]{3}[89ab][0-9a-f]{15})$/i;t.uuidValidateString=function(e){return"string"==typeof e&&r.test(e)};t.uuidHexStringToBuffer=function(e){if(!t.uuidValidateString(e))throw new TypeError('UUID string representations must be a 32 or 36 character hex string (dashes excluded/included). Format: "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" or "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx".');var r=e.replace(/-/g,"");return b.Buffer.from(r,"hex")};t.bufferToUuidHexString=function(e,t){return void 0===t&&(t=!0),t?e.toString("hex",0,4)+"-"+e.toString("hex",4,6)+"-"+e.toString("hex",6,8)+"-"+e.toString("hex",8,10)+"-"+e.toString("hex",10,16):e.toString("hex")}}));r(G),G.bufferToUuidHexString,G.uuidHexStringToBuffer,G.uuidValidateString;var Z=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.UUID=void 0;var r=Symbol("id"),n=function(){function e(t){if(void 0===t)this.id=e.generate();else if(t instanceof e)this[r]=b.Buffer.from(t.id),this.__id=t.__id;else if((b.Buffer.isBuffer(t)||ArrayBuffer.isView(t))&&16===t.byteLength)this.id=W.ensureBuffer(t);else{if("string"!=typeof t)throw new TypeError("Argument passed in UUID constructor must be a UUID, a 16 byte Buffer or a 32/36 character hex string (dashes excluded/included, format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).");this.id=G.uuidHexStringToBuffer(t)}}return Object.defineProperty(e.prototype,"id",{get:function(){return this[r]},set:function(t){this[r]=t,e.cacheHexString&&(this.__id=G.bufferToUuidHexString(t))},enumerable:!1,configurable:!0}),e.prototype.toHexString=function(t){if(void 0===t&&(t=!0),e.cacheHexString&&this.__id)return this.__id;var r=G.bufferToUuidHexString(this.id,t);return e.cacheHexString&&(this.__id=r),r},e.prototype.toString=function(e){return e?this.id.toString(e):this.toHexString()},e.prototype.toJSON=function(){return this.toHexString()},e.prototype.equals=function(t){if(!t)return!1;if(t instanceof e)return t.id.equals(this.id);try{return new e(t).id.equals(this.id)}catch(e){return!1}},e.prototype.toBinary=function(){return new Q.Binary(this.id,Q.Binary.SUBTYPE_UUID)},e.generate=function(){var e=X.randomBytes(16);return e[6]=15&e[6]|64,e[8]=63&e[8]|128,b.Buffer.from(e)},e.isValid=function(t){if(!t)return!1;if(t instanceof e)return!0;if("string"==typeof t)return G.uuidValidateString(t);if(b.Buffer.isBuffer(t)){if(16!==t.length)return!1;try{return parseInt(t[6].toString(16)[0],10)===Q.Binary.SUBTYPE_UUID}catch(e){return!1}}return!1},e.createFromHexString=function(t){return new e(G.uuidHexStringToBuffer(t))},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return'new UUID("'+this.toHexString()+'")'},e}();t.UUID=n,Object.defineProperty(n.prototype,"_bsontype",{value:"UUID"})}));r(Z),Z.UUID;var Q=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.Binary=void 0;var Binary=function(){function Binary(e,t){if(!(this instanceof Binary))return new Binary(e,t);if(!(null==e||"string"==typeof e||ArrayBuffer.isView(e)||e instanceof ArrayBuffer||Array.isArray(e)))throw new TypeError("Binary can only be constructed from string, Buffer, TypedArray, or Array<number>");this.sub_type=null!=t?t:Binary.BSON_BINARY_SUBTYPE_DEFAULT,null==e?(this.buffer=b.Buffer.alloc(Binary.BUFFER_SIZE),this.position=0):("string"==typeof e?this.buffer=b.Buffer.from(e,"binary"):Array.isArray(e)?this.buffer=b.Buffer.from(e):this.buffer=W.ensureBuffer(e),this.position=this.buffer.byteLength)}return Binary.prototype.put=function(e){if("string"==typeof e&&1!==e.length)throw new TypeError("only accepts single character String");if("number"!=typeof e&&1!==e.length)throw new TypeError("only accepts single character Uint8Array or Array");var t;if((t="string"==typeof e?e.charCodeAt(0):"number"==typeof e?e:e[0])<0||t>255)throw new TypeError("only accepts number in a valid unsigned byte range 0-255");if(this.buffer.length>this.position)this.buffer[this.position++]=t;else{var r=b.Buffer.alloc(Binary.BUFFER_SIZE+this.buffer.length);this.buffer.copy(r,0,0,this.buffer.length),this.buffer=r,this.buffer[this.position++]=t}},Binary.prototype.write=function(e,t){if(t="number"==typeof t?t:this.position,this.buffer.length<t+e.length){var r=b.Buffer.alloc(this.buffer.length+e.length);this.buffer.copy(r,0,0,this.buffer.length),this.buffer=r}ArrayBuffer.isView(e)?(this.buffer.set(W.ensureBuffer(e),t),this.position=t+e.byteLength>this.position?t+e.length:this.position):"string"==typeof e&&(this.buffer.write(e,t,e.length,"binary"),this.position=t+e.length>this.position?t+e.length:this.position)},Binary.prototype.read=function(e,t){return t=t&&t>0?t:this.position,this.buffer.slice(e,e+t)},Binary.prototype.value=function(e){return(e=!!e)&&this.buffer.length===this.position?this.buffer:e?this.buffer.slice(0,this.position):this.buffer.toString("binary",0,this.position)},Binary.prototype.length=function(){return this.position},Binary.prototype.toJSON=function(){return this.buffer.toString("base64")},Binary.prototype.toString=function(e){return this.buffer.toString(e)},Binary.prototype.toExtendedJSON=function(e){e=e||{};var t=this.buffer.toString("base64"),r=Number(this.sub_type).toString(16);return e.legacy?{$binary:t,$type:1===r.length?"0"+r:r}:{$binary:{base64:t,subType:1===r.length?"0"+r:r}}},Binary.prototype.toUUID=function(){if(this.sub_type===Binary.SUBTYPE_UUID)return new Z.UUID(this.buffer.slice(0,this.position));throw new Error('Binary sub_type "'+this.sub_type+'" is not supported for converting to UUID. Only "'+Binary.SUBTYPE_UUID+'" is currently supported.')},Binary.fromExtendedJSON=function(e,t){var r,n;if(t=t||{},"$binary"in e?t.legacy&&"string"==typeof e.$binary&&"$type"in e?(n=e.$type?parseInt(e.$type,16):0,r=b.Buffer.from(e.$binary,"base64")):"string"!=typeof e.$binary&&(n=e.$binary.subType?parseInt(e.$binary.subType,16):0,r=b.Buffer.from(e.$binary.base64,"base64")):"$uuid"in e&&(n=4,r=G.uuidHexStringToBuffer(e.$uuid)),!r)throw new TypeError("Unexpected Binary Extended JSON format "+JSON.stringify(e));return new Binary(r,n)},Binary.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},Binary.prototype.inspect=function(){return'new Binary(Buffer.from("'+this.value(!0).toString("hex")+'", "hex"), '+this.sub_type+")"},Binary.BSON_BINARY_SUBTYPE_DEFAULT=0,Binary.BUFFER_SIZE=256,Binary.SUBTYPE_DEFAULT=0,Binary.SUBTYPE_FUNCTION=1,Binary.SUBTYPE_BYTE_ARRAY=2,Binary.SUBTYPE_UUID_OLD=3,Binary.SUBTYPE_UUID=4,Binary.SUBTYPE_MD5=5,Binary.SUBTYPE_USER_DEFINED=128,Binary}();t.Binary=Binary,Object.defineProperty(Binary.prototype,"_bsontype",{value:"Binary"})}));r(Q),Q.Binary;var ee=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.Code=void 0;var r=function(){function e(t,r){if(!(this instanceof e))return new e(t,r);this.code=t,this.scope=r}return e.prototype.toJSON=function(){return{code:this.code,scope:this.scope}},e.prototype.toExtendedJSON=function(){return this.scope?{$code:this.code,$scope:this.scope}:{$code:this.code}},e.fromExtendedJSON=function(t){return new e(t.$code,t.$scope)},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){var e=this.toJSON();return'new Code("'+e.code+'"'+(e.scope?", "+JSON.stringify(e.scope):"")+")"},e}();t.Code=r,Object.defineProperty(r.prototype,"_bsontype",{value:"Code"})}));r(ee),ee.Code;var te=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.DBRef=t.isDBRefLike=void 0,t.isDBRefLike=function(e){return X.isObjectLike(e)&&null!=e.$id&&null!=e.$ref};var r=function(){function e(t,r,n,i){if(!(this instanceof e))return new e(t,r,n,i);var o=t.split(".");2===o.length&&(n=o.shift(),t=o.shift()),this.collection=t,this.oid=r,this.db=n,this.fields=i||{}}return Object.defineProperty(e.prototype,"namespace",{get:function(){return this.collection},set:function(e){this.collection=e},enumerable:!1,configurable:!0}),e.prototype.toJSON=function(){var e=Object.assign({$ref:this.collection,$id:this.oid},this.fields);return null!=this.db&&(e.$db=this.db),e},e.prototype.toExtendedJSON=function(e){e=e||{};var t={$ref:this.collection,$id:this.oid};return e.legacy?t:(this.db&&(t.$db=this.db),t=Object.assign(t,this.fields))},e.fromExtendedJSON=function(t){var r=Object.assign({},t);return delete r.$ref,delete r.$id,delete r.$db,new e(t.$ref,t.$id,t.$db,r)},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){var e=void 0===this.oid||void 0===this.oid.toString?this.oid:this.oid.toString();return'new DBRef("'+this.namespace+'", new ObjectId("'+e+'")'+(this.db?', "'+this.db+'"':"")+")"},e}();t.DBRef=r,Object.defineProperty(r.prototype,"_bsontype",{value:"DBRef"})}));r(te),te.DBRef,te.isDBRefLike;var re=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.Long=void 0;var r=void 0;try{r=new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([0,97,115,109,1,0,0,0,1,13,2,96,0,1,127,96,4,127,127,127,127,1,127,3,7,6,0,1,1,1,1,1,6,6,1,127,1,65,0,11,7,50,6,3,109,117,108,0,1,5,100,105,118,95,115,0,2,5,100,105,118,95,117,0,3,5,114,101,109,95,115,0,4,5,114,101,109,95,117,0,5,8,103,101,116,95,104,105,103,104,0,0,10,191,1,6,4,0,35,0,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,126,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,127,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,128,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,129,34,4,66,32,135,167,36,0,32,4,167,11,36,1,1,126,32,0,173,32,1,173,66,32,134,132,32,2,173,32,3,173,66,32,134,132,130,34,4,66,32,135,167,36,0,32,4,167,11])),{}).exports}catch(e){}var n=4294967296,i=0x10000000000000000,o=i/2,u={},s={},f=function(){function e(t,r,n){if(void 0===t&&(t=0),void 0===r&&(r=0),!(this instanceof e))return new e(t,r,n);this.low=0|t,this.high=0|r,this.unsigned=!!n,Object.defineProperty(this,"__isLong__",{value:!0,configurable:!1,writable:!1,enumerable:!1})}return e.fromBits=function(t,r,n){return new e(t,r,n)},e.fromInt=function(t,r){var n,i,o;return r?(o=0<=(t>>>=0)&&t<256)&&(i=s[t])?i:(n=e.fromBits(t,(0|t)<0?-1:0,!0),o&&(s[t]=n),n):(o=-128<=(t|=0)&&t<128)&&(i=u[t])?i:(n=e.fromBits(t,t<0?-1:0,!1),o&&(u[t]=n),n)},e.fromNumber=function(t,r){if(isNaN(t))return r?e.UZERO:e.ZERO;if(r){if(t<0)return e.UZERO;if(t>=i)return e.MAX_UNSIGNED_VALUE}else{if(t<=-o)return e.MIN_VALUE;if(t+1>=o)return e.MAX_VALUE}return t<0?e.fromNumber(-t,r).neg():e.fromBits(t%n|0,t/n|0,r)},e.fromBigInt=function(t,r){return e.fromString(t.toString(),r)},e.fromString=function(t,r,n){if(0===t.length)throw Error("empty string");if("NaN"===t||"Infinity"===t||"+Infinity"===t||"-Infinity"===t)return e.ZERO;if("number"==typeof r?(n=r,r=!1):r=!!r,(n=n||10)<2||36<n)throw RangeError("radix");var i;if((i=t.indexOf("-"))>0)throw Error("interior hyphen");if(0===i)return e.fromString(t.substring(1),r,n).neg();for(var o=e.fromNumber(Math.pow(n,8)),u=e.ZERO,s=0;s<t.length;s+=8){var f=Math.min(8,t.length-s),a=parseInt(t.substring(s,s+f),n);if(f<8){var l=e.fromNumber(Math.pow(n,f));u=u.mul(l).add(e.fromNumber(a))}else u=(u=u.mul(o)).add(e.fromNumber(a))}return u.unsigned=r,u},e.fromBytes=function(t,r,n){return n?e.fromBytesLE(t,r):e.fromBytesBE(t,r)},e.fromBytesLE=function(t,r){return new e(t[0]|t[1]<<8|t[2]<<16|t[3]<<24,t[4]|t[5]<<8|t[6]<<16|t[7]<<24,r)},e.fromBytesBE=function(t,r){return new e(t[4]<<24|t[5]<<16|t[6]<<8|t[7],t[0]<<24|t[1]<<16|t[2]<<8|t[3],r)},e.isLong=function(e){return X.isObjectLike(e)&&!0===e.__isLong__},e.fromValue=function(t,r){return"number"==typeof t?e.fromNumber(t,r):"string"==typeof t?e.fromString(t,r):e.fromBits(t.low,t.high,"boolean"==typeof r?r:t.unsigned)},e.prototype.add=function(t){e.isLong(t)||(t=e.fromValue(t));var r=this.high>>>16,n=65535&this.high,i=this.low>>>16,o=65535&this.low,u=t.high>>>16,s=65535&t.high,f=t.low>>>16,a=0,l=0,c=0,h=0;return c+=(h+=o+(65535&t.low))>>>16,h&=65535,l+=(c+=i+f)>>>16,c&=65535,a+=(l+=n+s)>>>16,l&=65535,a+=r+u,a&=65535,e.fromBits(c<<16|h,a<<16|l,this.unsigned)},e.prototype.and=function(t){return e.isLong(t)||(t=e.fromValue(t)),e.fromBits(this.low&t.low,this.high&t.high,this.unsigned)},e.prototype.compare=function(t){if(e.isLong(t)||(t=e.fromValue(t)),this.eq(t))return 0;var r=this.isNegative(),n=t.isNegative();return r&&!n?-1:!r&&n?1:this.unsigned?t.high>>>0>this.high>>>0||t.high===this.high&&t.low>>>0>this.low>>>0?-1:1:this.sub(t).isNegative()?-1:1},e.prototype.comp=function(e){return this.compare(e)},e.prototype.divide=function(t){if(e.isLong(t)||(t=e.fromValue(t)),t.isZero())throw Error("division by zero");if(r){if(!this.unsigned&&-2147483648===this.high&&-1===t.low&&-1===t.high)return this;var n=(this.unsigned?r.div_u:r.div_s)(this.low,this.high,t.low,t.high);return e.fromBits(n,r.get_high(),this.unsigned)}if(this.isZero())return this.unsigned?e.UZERO:e.ZERO;var i,o,u;if(this.unsigned){if(t.unsigned||(t=t.toUnsigned()),t.gt(this))return e.UZERO;if(t.gt(this.shru(1)))return e.UONE;u=e.UZERO}else{if(this.eq(e.MIN_VALUE))return t.eq(e.ONE)||t.eq(e.NEG_ONE)?e.MIN_VALUE:t.eq(e.MIN_VALUE)?e.ONE:(i=this.shr(1).div(t).shl(1)).eq(e.ZERO)?t.isNegative()?e.ONE:e.NEG_ONE:(o=this.sub(t.mul(i)),u=i.add(o.div(t)));if(t.eq(e.MIN_VALUE))return this.unsigned?e.UZERO:e.ZERO;if(this.isNegative())return t.isNegative()?this.neg().div(t.neg()):this.neg().div(t).neg();if(t.isNegative())return this.div(t.neg()).neg();u=e.ZERO}for(o=this;o.gte(t);){i=Math.max(1,Math.floor(o.toNumber()/t.toNumber()));for(var s=Math.ceil(Math.log(i)/Math.LN2),f=s<=48?1:Math.pow(2,s-48),a=e.fromNumber(i),l=a.mul(t);l.isNegative()||l.gt(o);)i-=f,l=(a=e.fromNumber(i,this.unsigned)).mul(t);a.isZero()&&(a=e.ONE),u=u.add(a),o=o.sub(l)}return u},e.prototype.div=function(e){return this.divide(e)},e.prototype.equals=function(t){return e.isLong(t)||(t=e.fromValue(t)),(this.unsigned===t.unsigned||this.high>>>31!=1||t.high>>>31!=1)&&(this.high===t.high&&this.low===t.low)},e.prototype.eq=function(e){return this.equals(e)},e.prototype.getHighBits=function(){return this.high},e.prototype.getHighBitsUnsigned=function(){return this.high>>>0},e.prototype.getLowBits=function(){return this.low},e.prototype.getLowBitsUnsigned=function(){return this.low>>>0},e.prototype.getNumBitsAbs=function(){if(this.isNegative())return this.eq(e.MIN_VALUE)?64:this.neg().getNumBitsAbs();var t,r=0!==this.high?this.high:this.low;for(t=31;t>0&&0==(r&1<<t);t--);return 0!==this.high?t+33:t+1},e.prototype.greaterThan=function(e){return this.comp(e)>0},e.prototype.gt=function(e){return this.greaterThan(e)},e.prototype.greaterThanOrEqual=function(e){return this.comp(e)>=0},e.prototype.gte=function(e){return this.greaterThanOrEqual(e)},e.prototype.ge=function(e){return this.greaterThanOrEqual(e)},e.prototype.isEven=function(){return 0==(1&this.low)},e.prototype.isNegative=function(){return!this.unsigned&&this.high<0},e.prototype.isOdd=function(){return 1==(1&this.low)},e.prototype.isPositive=function(){return this.unsigned||this.high>=0},e.prototype.isZero=function(){return 0===this.high&&0===this.low},e.prototype.lessThan=function(e){return this.comp(e)<0},e.prototype.lt=function(e){return this.lessThan(e)},e.prototype.lessThanOrEqual=function(e){return this.comp(e)<=0},e.prototype.lte=function(e){return this.lessThanOrEqual(e)},e.prototype.modulo=function(t){if(e.isLong(t)||(t=e.fromValue(t)),r){var n=(this.unsigned?r.rem_u:r.rem_s)(this.low,this.high,t.low,t.high);return e.fromBits(n,r.get_high(),this.unsigned)}return this.sub(this.div(t).mul(t))},e.prototype.mod=function(e){return this.modulo(e)},e.prototype.rem=function(e){return this.modulo(e)},e.prototype.multiply=function(t){if(this.isZero())return e.ZERO;if(e.isLong(t)||(t=e.fromValue(t)),r){var n=r.mul(this.low,this.high,t.low,t.high);return e.fromBits(n,r.get_high(),this.unsigned)}if(t.isZero())return e.ZERO;if(this.eq(e.MIN_VALUE))return t.isOdd()?e.MIN_VALUE:e.ZERO;if(t.eq(e.MIN_VALUE))return this.isOdd()?e.MIN_VALUE:e.ZERO;if(this.isNegative())return t.isNegative()?this.neg().mul(t.neg()):this.neg().mul(t).neg();if(t.isNegative())return this.mul(t.neg()).neg();if(this.lt(e.TWO_PWR_24)&&t.lt(e.TWO_PWR_24))return e.fromNumber(this.toNumber()*t.toNumber(),this.unsigned);var i=this.high>>>16,o=65535&this.high,u=this.low>>>16,s=65535&this.low,f=t.high>>>16,a=65535&t.high,l=t.low>>>16,c=65535&t.low,h=0,p=0,d=0,y=0;return d+=(y+=s*c)>>>16,y&=65535,p+=(d+=u*c)>>>16,d&=65535,p+=(d+=s*l)>>>16,d&=65535,h+=(p+=o*c)>>>16,p&=65535,h+=(p+=u*l)>>>16,p&=65535,h+=(p+=s*a)>>>16,p&=65535,h+=i*c+o*l+u*a+s*f,h&=65535,e.fromBits(d<<16|y,h<<16|p,this.unsigned)},e.prototype.mul=function(e){return this.multiply(e)},e.prototype.negate=function(){return!this.unsigned&&this.eq(e.MIN_VALUE)?e.MIN_VALUE:this.not().add(e.ONE)},e.prototype.neg=function(){return this.negate()},e.prototype.not=function(){return e.fromBits(~this.low,~this.high,this.unsigned)},e.prototype.notEquals=function(e){return!this.equals(e)},e.prototype.neq=function(e){return this.notEquals(e)},e.prototype.ne=function(e){return this.notEquals(e)},e.prototype.or=function(t){return e.isLong(t)||(t=e.fromValue(t)),e.fromBits(this.low|t.low,this.high|t.high,this.unsigned)},e.prototype.shiftLeft=function(t){return e.isLong(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?e.fromBits(this.low<<t,this.high<<t|this.low>>>32-t,this.unsigned):e.fromBits(0,this.low<<t-32,this.unsigned)},e.prototype.shl=function(e){return this.shiftLeft(e)},e.prototype.shiftRight=function(t){return e.isLong(t)&&(t=t.toInt()),0==(t&=63)?this:t<32?e.fromBits(this.low>>>t|this.high<<32-t,this.high>>t,this.unsigned):e.fromBits(this.high>>t-32,this.high>=0?0:-1,this.unsigned)},e.prototype.shr=function(e){return this.shiftRight(e)},e.prototype.shiftRightUnsigned=function(t){if(e.isLong(t)&&(t=t.toInt()),0===(t&=63))return this;var r=this.high;if(t<32){var n=this.low;return e.fromBits(n>>>t|r<<32-t,r>>>t,this.unsigned)}return 32===t?e.fromBits(r,0,this.unsigned):e.fromBits(r>>>t-32,0,this.unsigned)},e.prototype.shr_u=function(e){return this.shiftRightUnsigned(e)},e.prototype.shru=function(e){return this.shiftRightUnsigned(e)},e.prototype.subtract=function(t){return e.isLong(t)||(t=e.fromValue(t)),this.add(t.neg())},e.prototype.sub=function(e){return this.subtract(e)},e.prototype.toInt=function(){return this.unsigned?this.low>>>0:this.low},e.prototype.toNumber=function(){return this.unsigned?(this.high>>>0)*n+(this.low>>>0):this.high*n+(this.low>>>0)},e.prototype.toBigInt=function(){return BigInt(this.toString())},e.prototype.toBytes=function(e){return e?this.toBytesLE():this.toBytesBE()},e.prototype.toBytesLE=function(){var e=this.high,t=this.low;return[255&t,t>>>8&255,t>>>16&255,t>>>24,255&e,e>>>8&255,e>>>16&255,e>>>24]},e.prototype.toBytesBE=function(){var e=this.high,t=this.low;return[e>>>24,e>>>16&255,e>>>8&255,255&e,t>>>24,t>>>16&255,t>>>8&255,255&t]},e.prototype.toSigned=function(){return this.unsigned?e.fromBits(this.low,this.high,!1):this},e.prototype.toString=function(t){if((t=t||10)<2||36<t)throw RangeError("radix");if(this.isZero())return"0";if(this.isNegative()){if(this.eq(e.MIN_VALUE)){var r=e.fromNumber(t),n=this.div(r),i=n.mul(r).sub(this);return n.toString(t)+i.toInt().toString(t)}return"-"+this.neg().toString(t)}for(var o=e.fromNumber(Math.pow(t,6),this.unsigned),u=this,s="";;){var f=u.div(o),a=(u.sub(f.mul(o)).toInt()>>>0).toString(t);if((u=f).isZero())return a+s;for(;a.length<6;)a="0"+a;s=""+a+s}},e.prototype.toUnsigned=function(){return this.unsigned?this:e.fromBits(this.low,this.high,!0)},e.prototype.xor=function(t){return e.isLong(t)||(t=e.fromValue(t)),e.fromBits(this.low^t.low,this.high^t.high,this.unsigned)},e.prototype.eqz=function(){return this.isZero()},e.prototype.le=function(e){return this.lessThanOrEqual(e)},e.prototype.toExtendedJSON=function(e){return e&&e.relaxed?this.toNumber():{$numberLong:this.toString()}},e.fromExtendedJSON=function(t,r){var n=e.fromString(t.$numberLong);return r&&r.relaxed?n.toNumber():n},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return'new Long("'+this.toString()+'")'},e.TWO_PWR_24=e.fromInt(16777216),e.MAX_UNSIGNED_VALUE=e.fromBits(-1,-1,!0),e.ZERO=e.fromInt(0),e.UZERO=e.fromInt(0,!0),e.ONE=e.fromInt(1),e.UONE=e.fromInt(1,!0),e.NEG_ONE=e.fromInt(-1),e.MAX_VALUE=e.fromBits(-1,2147483647,!1),e.MIN_VALUE=e.fromBits(0,-2147483648,!1),e}();t.Long=f,Object.defineProperty(f.prototype,"__isLong__",{value:!0}),Object.defineProperty(f.prototype,"_bsontype",{value:"Long"})}));r(re),re.Long;var ne=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.Decimal128=void 0;var r=/^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/,n=/^(\+|-)?(Infinity|inf)$/i,i=/^(\+|-)?NaN$/i,o=6111,u=-6176,s=[124,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].reverse(),f=[248,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].reverse(),a=[120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0].reverse(),l=/^([-+])?(\d+)?$/;function c(e){return!isNaN(parseInt(e,10))}function h(e){var t=re.Long.fromNumber(1e9),r=re.Long.fromNumber(0);if(!(e.parts[0]||e.parts[1]||e.parts[2]||e.parts[3]))return{quotient:e,rem:r};for(var n=0;n<=3;n++)r=(r=r.shiftLeft(32)).add(new re.Long(e.parts[n],0)),e.parts[n]=r.div(t).low,r=r.modulo(t);return{quotient:e,rem:r}}function p(e,t){throw new TypeError('"'+e+'" is not a valid Decimal128 string - '+t)}var d=function(){function e(t){if(!(this instanceof e))return new e(t);this.bytes=t}return e.fromString=function(t){var h,d=!1,y=!1,g=!1,_=0,B=0,O=0,m=0,S=0,N=[0],v=0,A=0,w=0,E=0,T=0,D=0,I=new re.Long(0,0),x=new re.Long(0,0),U=0;if(t.length>=7e3)throw new TypeError(t+" not a valid Decimal128 string");var R=t.match(r),L=t.match(n),j=t.match(i);if(!R&&!L&&!j||0===t.length)throw new TypeError(t+" not a valid Decimal128 string");if(R){var M=R[2],P=R[4],Y=R[5],C=R[6];P&&void 0===C&&p(t,"missing exponent power"),P&&void 0===M&&p(t,"missing exponent base"),void 0===P&&(Y||C)&&p(t,"missing e before exponent")}if("+"!==t[U]&&"-"!==t[U]||(d="-"===t[U++]),!c(t[U])&&"."!==t[U]){if("i"===t[U]||"I"===t[U])return new e(b.Buffer.from(d?f:a));if("N"===t[U])return new e(b.Buffer.from(s))}for(;c(t[U])||"."===t[U];)"."!==t[U]?(v<34&&("0"!==t[U]||g)&&(g||(S=B),g=!0,N[A++]=parseInt(t[U],10),v+=1),g&&(O+=1),y&&(m+=1),B+=1,U+=1):(y&&p(t,"contains multiple periods"),y=!0,U+=1);if(y&&!B)throw new TypeError(t+" not a valid Decimal128 string");if("e"===t[U]||"E"===t[U]){var z=t.substr(++U).match(l);if(!z||!z[2])return new e(b.Buffer.from(s));T=parseInt(z[0],10),U+=z[0].length}if(t[U])return new e(b.Buffer.from(s));if(w=0,v){if(E=v-1,1!==(_=O))for(;"0"===t[S+_-1];)_-=1}else w=0,E=0,N[0]=0,O=1,v=1,_=0;for(T<=m&&m-T>16384?T=u:T-=m;T>o;){if((E+=1)-w>34){if(N.join("").match(/^0+$/)){T=o;break}p(t,"overflow")}T-=1}for(;T<u||v<O;){if(0===E&&_<v){T=u,_=0;break}if(v<O?O-=1:E-=1,T<o)T+=1;else{if(N.join("").match(/^0+$/)){T=o;break}p(t,"overflow")}}if(E-w+1<_){var $=B;y&&(S+=1,$+=1),d&&(S+=1,$+=1);var k=parseInt(t[S+E+1],10),J=0;if(k>=5&&(J=1,5===k))for(J=N[E]%2==1?1:0,D=S+E+2;D<$;D++)if(parseInt(t[D],10)){J=1;break}if(J)for(var q=E;q>=0;q--)if(++N[q]>9&&(N[q]=0,0===q)){if(!(T<o))return new e(b.Buffer.from(d?f:a));T+=1,N[q]=1}}if(I=re.Long.fromNumber(0),x=re.Long.fromNumber(0),0===_)I=re.Long.fromNumber(0),x=re.Long.fromNumber(0);else if(E-w<17){q=w;for(x=re.Long.fromNumber(N[q++]),I=new re.Long(0,0);q<=E;q++)x=(x=x.multiply(re.Long.fromNumber(10))).add(re.Long.fromNumber(N[q]))}else{q=w;for(I=re.Long.fromNumber(N[q++]);q<=E-17;q++)I=(I=I.multiply(re.Long.fromNumber(10))).add(re.Long.fromNumber(N[q]));for(x=re.Long.fromNumber(N[q++]);q<=E;q++)x=(x=x.multiply(re.Long.fromNumber(10))).add(re.Long.fromNumber(N[q]))}var F,V,K,H,X=function(e,t){if(!e&&!t)return{high:re.Long.fromNumber(0),low:re.Long.fromNumber(0)};var r=e.shiftRightUnsigned(32),n=new re.Long(e.getLowBits(),0),i=t.shiftRightUnsigned(32),o=new re.Long(t.getLowBits(),0),u=r.multiply(i),s=r.multiply(o),f=n.multiply(i),a=n.multiply(o);return u=u.add(s.shiftRightUnsigned(32)),s=new re.Long(s.getLowBits(),0).add(f).add(a.shiftRightUnsigned(32)),{high:u=u.add(s.shiftRightUnsigned(32)),low:a=s.shiftLeft(32).add(new re.Long(a.getLowBits(),0))}}(I,re.Long.fromString("100000000000000000"));X.low=X.low.add(x),F=X.low,V=x,K=F.high>>>0,H=V.high>>>0,(K<H||K===H&&F.low>>>0<V.low>>>0)&&(X.high=X.high.add(re.Long.fromNumber(1))),h=T+6176;var W={low:re.Long.fromNumber(0),high:re.Long.fromNumber(0)};X.high.shiftRightUnsigned(49).and(re.Long.fromNumber(1)).equals(re.Long.fromNumber(1))?(W.high=W.high.or(re.Long.fromNumber(3).shiftLeft(61)),W.high=W.high.or(re.Long.fromNumber(h).and(re.Long.fromNumber(16383).shiftLeft(47))),W.high=W.high.or(X.high.and(re.Long.fromNumber(0x7fffffffffff)))):(W.high=W.high.or(re.Long.fromNumber(16383&h).shiftLeft(49)),W.high=W.high.or(X.high.and(re.Long.fromNumber(562949953421311)))),W.low=X.low,d&&(W.high=W.high.or(re.Long.fromString("9223372036854775808")));var G=b.Buffer.alloc(16);return U=0,G[U++]=255&W.low.low,G[U++]=W.low.low>>8&255,G[U++]=W.low.low>>16&255,G[U++]=W.low.low>>24&255,G[U++]=255&W.low.high,G[U++]=W.low.high>>8&255,G[U++]=W.low.high>>16&255,G[U++]=W.low.high>>24&255,G[U++]=255&W.high.low,G[U++]=W.high.low>>8&255,G[U++]=W.high.low>>16&255,G[U++]=W.high.low>>24&255,G[U++]=255&W.high.high,G[U++]=W.high.high>>8&255,G[U++]=W.high.high>>16&255,G[U++]=W.high.high>>24&255,new e(G)},e.prototype.toString=function(){for(var e,t=0,r=new Array(36),n=0;n<r.length;n++)r[n]=0;var i,o,u,s=0,f=!1,a={parts:[0,0,0,0]},l=[];s=0;var c=this.bytes,p=c[s++]|c[s++]<<8|c[s++]<<16|c[s++]<<24,d=c[s++]|c[s++]<<8|c[s++]<<16|c[s++]<<24,y=c[s++]|c[s++]<<8|c[s++]<<16|c[s++]<<24,g=c[s++]|c[s++]<<8|c[s++]<<16|c[s++]<<24;s=0,{low:new re.Long(p,d),high:new re.Long(y,g)}.high.lessThan(re.Long.ZERO)&&l.push("-");var _=g>>26&31;if(_>>3==3){if(30===_)return l.join("")+"Infinity";if(31===_)return"NaN";e=g>>15&16383,i=8+(g>>14&1)}else i=g>>14&7,e=g>>17&16383;var b=e-6176;if(a.parts[0]=(16383&g)+((15&i)<<14),a.parts[1]=y,a.parts[2]=d,a.parts[3]=p,0===a.parts[0]&&0===a.parts[1]&&0===a.parts[2]&&0===a.parts[3])f=!0;else for(u=3;u>=0;u--){var B=0,O=h(a);if(a=O.quotient,B=O.rem.low)for(o=8;o>=0;o--)r[9*u+o]=B%10,B=Math.floor(B/10)}if(f)t=1,r[s]=0;else for(t=36;!r[s];)t-=1,s+=1;var m=t-1+b;if(m>=34||m<=-7||b>0){if(t>34)return l.push("0"),b>0?l.push("E+"+b):b<0&&l.push("E"+b),l.join("");l.push(""+r[s++]),(t-=1)&&l.push(".");for(n=0;n<t;n++)l.push(""+r[s++]);l.push("E"),m>0?l.push("+"+m):l.push(""+m)}else if(b>=0)for(n=0;n<t;n++)l.push(""+r[s++]);else{var S=t+b;if(S>0)for(n=0;n<S;n++)l.push(""+r[s++]);else l.push("0");for(l.push(".");S++<0;)l.push("0");for(n=0;n<t-Math.max(S-1,0);n++)l.push(""+r[s++])}return l.join("")},e.prototype.toJSON=function(){return{$numberDecimal:this.toString()}},e.prototype.toExtendedJSON=function(){return{$numberDecimal:this.toString()}},e.fromExtendedJSON=function(t){return e.fromString(t.$numberDecimal)},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return'Decimal128.fromString("'+this.toString()+'")'},e}();t.Decimal128=d,Object.defineProperty(d.prototype,"_bsontype",{value:"Decimal128"})}));r(ne),ne.Decimal128;var ie=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.Double=void 0;var r=function(){function e(t){if(!(this instanceof e))return new e(t);t instanceof Number&&(t=t.valueOf()),this.value=+t}return e.prototype.valueOf=function(){return this.value},e.prototype.toJSON=function(){return this.value},e.prototype.toExtendedJSON=function(e){return e&&(e.legacy||e.relaxed&&isFinite(this.value))?this.value:Object.is(Math.sign(this.value),-0)?{$numberDouble:"-"+this.value.toFixed(1)}:(Number.isInteger(this.value)?(t=this.value.toFixed(1)).length>=13&&(t=this.value.toExponential(13).toUpperCase()):t=this.value.toString(),{$numberDouble:t});var t},e.fromExtendedJSON=function(t,r){var n=parseFloat(t.$numberDouble);return r&&r.relaxed?n:new e(n)},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return"new Double("+this.toExtendedJSON().$numberDouble+")"},e}();t.Double=r,Object.defineProperty(r.prototype,"_bsontype",{value:"Double"})}));r(ie),ie.Double;var oe=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.Int32=void 0;var r=function(){function e(t){if(!(this instanceof e))return new e(t);t instanceof Number&&(t=t.valueOf()),this.value=+t}return e.prototype.valueOf=function(){return this.value},e.prototype.toJSON=function(){return this.value},e.prototype.toExtendedJSON=function(e){return e&&(e.relaxed||e.legacy)?this.value:{$numberInt:this.value.toString()}},e.fromExtendedJSON=function(t,r){return r&&r.relaxed?parseInt(t.$numberInt,10):new e(t.$numberInt)},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return"new Int32("+this.valueOf()+")"},e}();t.Int32=r,Object.defineProperty(r.prototype,"_bsontype",{value:"Int32"})}));r(oe),oe.Int32;var ue=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.MaxKey=void 0;var r=function(){function e(){if(!(this instanceof e))return new e}return e.prototype.toExtendedJSON=function(){return{$maxKey:1}},e.fromExtendedJSON=function(){return new e},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return"new MaxKey()"},e}();t.MaxKey=r,Object.defineProperty(r.prototype,"_bsontype",{value:"MaxKey"})}));r(ue),ue.MaxKey;var se=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.MinKey=void 0;var r=function(){function e(){if(!(this instanceof e))return new e}return e.prototype.toExtendedJSON=function(){return{$minKey:1}},e.fromExtendedJSON=function(){return new e},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return"new MinKey()"},e}();t.MinKey=r,Object.defineProperty(r.prototype,"_bsontype",{value:"MinKey"})}));r(se),se.MinKey;var fe=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.ObjectId=void 0;for(var r=X.randomBytes(5),n=new RegExp("^[0-9a-fA-F]{24}$"),i=[],o=0;o<256;o++)i[o]=(o<=15?"0":"")+o.toString(16);for(var u=[],s=0;s<10;)u[48+s]=s++;for(;s<16;)u[55+s]=u[87+s]=s++;var f=Symbol("id"),a=function(){function e(t){if(!(this instanceof e))return new e(t);if(t instanceof e&&(this[f]=t.id,this.__id=t.__id),"object"==typeof t&&t&&"id"in t&&("toHexString"in t&&"function"==typeof t.toHexString?this[f]=b.Buffer.from(t.toHexString(),"hex"):this[f]="string"==typeof t.id?b.Buffer.from(t.id):t.id),null!=t&&"number"!=typeof t||(this[f]=e.generate("number"==typeof t?t:void 0),e.cacheHexString&&(this.__id=this.id.toString("hex"))),ArrayBuffer.isView(t)&&12===t.byteLength&&(this[f]=W.ensureBuffer(t)),"string"==typeof t)if(12===t.length){var r=b.Buffer.from(t);12===r.byteLength&&(this[f]=r)}else{if(24!==t.length||!n.test(t))throw new TypeError("Argument passed in must be a Buffer or string of 12 bytes or a string of 24 hex characters");this[f]=b.Buffer.from(t,"hex")}e.cacheHexString&&(this.__id=this.id.toString("hex"))}return Object.defineProperty(e.prototype,"id",{get:function(){return this[f]},set:function(t){this[f]=t,e.cacheHexString&&(this.__id=t.toString("hex"))},enumerable:!1,configurable:!0}),Object.defineProperty(e.prototype,"generationTime",{get:function(){return this.id.readInt32BE(0)},set:function(e){this.id.writeUInt32BE(e,0)},enumerable:!1,configurable:!0}),e.prototype.toHexString=function(){if(e.cacheHexString&&this.__id)return this.__id;var t=this.id.toString("hex");return e.cacheHexString&&!this.__id&&(this.__id=t),t},e.getInc=function(){return e.index=(e.index+1)%16777215},e.generate=function(t){"number"!=typeof t&&(t=~~(Date.now()/1e3));var n=e.getInc(),i=b.Buffer.alloc(12);return i.writeUInt32BE(t,0),i[4]=r[0],i[5]=r[1],i[6]=r[2],i[7]=r[3],i[8]=r[4],i[11]=255&n,i[10]=n>>8&255,i[9]=n>>16&255,i},e.prototype.toString=function(e){return e?this.id.toString(e):this.toHexString()},e.prototype.toJSON=function(){return this.toHexString()},e.prototype.equals=function(t){return null!=t&&(t instanceof e?this.toString()===t.toString():"string"==typeof t&&e.isValid(t)&&12===t.length&&b.Buffer.isBuffer(this.id)?t===this.id.toString("binary"):"string"==typeof t&&e.isValid(t)&&24===t.length?t.toLowerCase()===this.toHexString():"string"==typeof t&&e.isValid(t)&&12===t.length?b.Buffer.from(t).equals(this.id):"object"==typeof t&&"toHexString"in t&&"function"==typeof t.toHexString&&t.toHexString()===this.toHexString())},e.prototype.getTimestamp=function(){var e=new Date,t=this.id.readUInt32BE(0);return e.setTime(1e3*Math.floor(t)),e},e.createPk=function(){return new e},e.createFromTime=function(t){var r=b.Buffer.from([0,0,0,0,0,0,0,0,0,0,0,0]);return r.writeUInt32BE(t,0),new e(r)},e.createFromHexString=function(t){if(void 0===t||null!=t&&24!==t.length)throw new TypeError("Argument passed in must be a single String of 12 bytes or a string of 24 hex characters");return new e(b.Buffer.from(t,"hex"))},e.isValid=function(t){return null!=t&&("number"==typeof t||("string"==typeof t?12===t.length||24===t.length&&n.test(t):t instanceof e||(!(!b.Buffer.isBuffer(t)||12!==t.length)||"object"==typeof t&&"toHexString"in t&&"function"==typeof t.toHexString&&("string"==typeof t.id?12===t.id.length:24===t.toHexString().length&&n.test(t.id.toString("hex"))))))},e.prototype.toExtendedJSON=function(){return this.toHexString?{$oid:this.toHexString()}:{$oid:this.toString("hex")}},e.fromExtendedJSON=function(t){return new e(t.$oid)},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e.prototype.inspect=function(){return'new ObjectId("'+this.toHexString()+'")'},e.index=~~(16777215*Math.random()),e}();t.ObjectId=a,Object.defineProperty(a.prototype,"generate",{value:X.deprecate((function(e){return a.generate(e)}),"Please use the static `ObjectId.generate(time)` instead")}),Object.defineProperty(a.prototype,"getInc",{value:X.deprecate((function(){return a.getInc()}),"Please use the static `ObjectId.getInc()` instead")}),Object.defineProperty(a.prototype,"get_inc",{value:X.deprecate((function(){return a.getInc()}),"Please use the static `ObjectId.getInc()` instead")}),Object.defineProperty(a,"get_inc",{value:X.deprecate((function(){return a.getInc()}),"Please use the static `ObjectId.getInc()` instead")}),Object.defineProperty(a.prototype,"_bsontype",{value:"ObjectID"})}));r(fe),fe.ObjectId;var ae=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.BSONRegExp=void 0;var r=function(){function e(t,r){if(!(this instanceof e))return new e(t,r);this.pattern=t,this.options=null!=r?r:"",this.options.split("").sort().join("");for(var n=0;n<this.options.length;n++)if("i"!==this.options[n]&&"m"!==this.options[n]&&"x"!==this.options[n]&&"l"!==this.options[n]&&"s"!==this.options[n]&&"u"!==this.options[n])throw new Error("The regular expression option ["+this.options[n]+"] is not supported")}return e.parseOptions=function(e){return e?e.split("").sort().join(""):""},e.prototype.toExtendedJSON=function(e){return(e=e||{}).legacy?{$regex:this.pattern,$options:this.options}:{$regularExpression:{pattern:this.pattern,options:this.options}}},e.fromExtendedJSON=function(t){if("$regex"in t){if("string"==typeof t.$regex)return new e(t.$regex,e.parseOptions(t.$options));if("BSONRegExp"===t.$regex._bsontype)return t}if("$regularExpression"in t)return new e(t.$regularExpression.pattern,e.parseOptions(t.$regularExpression.options));throw new TypeError("Unexpected BSONRegExp EJSON object form: "+JSON.stringify(t))},e}();t.BSONRegExp=r,Object.defineProperty(r.prototype,"_bsontype",{value:"BSONRegExp"})}));r(ae),ae.BSONRegExp;var le=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.BSONSymbol=void 0;var r=function(){function e(t){if(!(this instanceof e))return new e(t);this.value=t}return e.prototype.valueOf=function(){return this.value},e.prototype.toString=function(){return this.value},e.prototype.inspect=function(){return'new BSONSymbol("'+this.value+'")'},e.prototype.toJSON=function(){return this.value},e.prototype.toExtendedJSON=function(){return{$symbol:this.value}},e.fromExtendedJSON=function(t){return new e(t.$symbol)},e.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},e}();t.BSONSymbol=r,Object.defineProperty(r.prototype,"_bsontype",{value:"Symbol"})}));r(le),le.BSONSymbol;

/*! *****************************************************************************

    Copyright (c) Microsoft Corporation.


    Permission to use, copy, modify, and/or distribute this software for any

    purpose with or without fee is hereby granted.


    THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH

    REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY

    AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,

    INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM

    LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR

    OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR

    PERFORMANCE OF THIS SOFTWARE.

    ***************************************************************************** */

var ce=function(e,t){return ce=Object.setPrototypeOf||{__proto__:[]}instanceof Array&&function(e,t){e.__proto__=t}||function(e,t){for(var r in t)t.hasOwnProperty(r)&&(e[r]=t[r])},ce(e,t)};var he=function(){return he=Object.assign||function(e){for(var t,r=1,n=arguments.length;r<n;r++)for(var i in t=arguments[r])Object.prototype.hasOwnProperty.call(t,i)&&(e[i]=t[i]);return e},he.apply(this,arguments)};function pe(e){var t="function"==typeof Symbol&&Symbol.iterator,r=t&&e[t],n=0;if(r)return r.call(e);if(e&&"number"==typeof e.length)return{next:function(){return e&&n>=e.length&&(e=void 0),{value:e&&e[n++],done:!e}}};throw new TypeError(t?"Object is not iterable.":"Symbol.iterator is not defined.")}function de(e,t){var r="function"==typeof Symbol&&e[Symbol.iterator];if(!r)return e;var n,i,o=r.call(e),u=[];try{for(;(void 0===t||t-- >0)&&!(n=o.next()).done;)u.push(n.value)}catch(e){i={error:e}}finally{try{n&&!n.done&&(r=o.return)&&r.call(o)}finally{if(i)throw i.error}}return u}function ye(e){return this instanceof ye?(this.v=e,this):new ye(e)}var ge=Object.freeze({__proto__:null,__extends:function(e,t){function r(){this.constructor=e}ce(e,t),e.prototype=null===t?Object.create(t):(r.prototype=t.prototype,new r)},get __assign(){return he},__rest:function(e,t){var r={};for(var n in e)Object.prototype.hasOwnProperty.call(e,n)&&t.indexOf(n)<0&&(r[n]=e[n]);if(null!=e&&"function"==typeof Object.getOwnPropertySymbols){var i=0;for(n=Object.getOwnPropertySymbols(e);i<n.length;i++)t.indexOf(n[i])<0&&Object.prototype.propertyIsEnumerable.call(e,n[i])&&(r[n[i]]=e[n[i]])}return r},__decorate:function(e,t,r,n){var i,o=arguments.length,u=o<3?t:null===n?n=Object.getOwnPropertyDescriptor(t,r):n;if("object"===("undefined"==typeof Reflect?"undefined":babelHelpers.typeof(Reflect))&&"function"==typeof Reflect.decorate)u=Reflect.decorate(e,t,r,n);else for(var s=e.length-1;s>=0;s--)(i=e[s])&&(u=(o<3?i(u):o>3?i(t,r,u):i(t,r))||u);return o>3&&u&&Object.defineProperty(t,r,u),u},__param:function(e,t){return function(r,n){t(r,n,e)}},__metadata:function(e,t){if("object"===("undefined"==typeof Reflect?"undefined":babelHelpers.typeof(Reflect))&&"function"==typeof Reflect.metadata)return Reflect.metadata(e,t)},__awaiter:function(e,t,r,n){return new(r||(r=Promise))((function(i,o){function u(e){try{f(n.next(e))}catch(e){o(e)}}function s(e){try{f(n.throw(e))}catch(e){o(e)}}function f(e){var t;e.done?i(e.value):(t=e.value,t instanceof r?t:new r((function(e){e(t)}))).then(u,s)}f((n=n.apply(e,t||[])).next())}))},__generator:function(e,t){var r,n,i,o,u={label:0,sent:function(){if(1&i[0])throw i[1];return i[1]},trys:[],ops:[]};return o={next:s(0),throw:s(1),return:s(2)},"function"==typeof Symbol&&(o[Symbol.iterator]=function(){return this}),o;function s(o){return function(s){return function(o){if(r)throw new TypeError("Generator is already executing.");for(;u;)try{if(r=1,n&&(i=2&o[0]?n.return:o[0]?n.throw||((i=n.return)&&i.call(n),0):n.next)&&!(i=i.call(n,o[1])).done)return i;switch(n=0,i&&(o=[2&o[0],i.value]),o[0]){case 0ase 1:i=o;break;case 4:return u.label++,{value:o[1],done:!1};case 5:u.label++,n=o[1],o=[0];continue;case 7:o=u.ops.pop(),u.trys.pop();continue;default:if(!(i=u.trys,(i=i.length>0&&i[i.length-1])||6!==o[0]&&2!==o[0])){u=0;continue}if(3===o[0]&&(!i||o[1]>i[0]&&o[1]<i[3])){u.label=o[1];break}if(6===o[0]&&u.label<i[1]){u.label=i[1],i=o;break}if(i&&u.label<i[2]){u.label=i[2],u.ops.push(o);break}i[2]&&u.ops.pop(),u.trys.pop();continue}o=t.call(e,u)}catch(e){o=[6,e],n=0}finally{r=i=0}if(5&o[0])throw o[1];return{value:o[0]?o[1]:void 0,done:!0}}([o,s])}}},__createBinding:function(e,t,r,n){void 0===n&&(n=r),e[n]=t[r]},__exportStar:function(e,t){for(var r in e)"default"===r||t.hasOwnProperty(r)||(t[r]=e[r])},__values:pe,__read:de,__spread:function(){for(var e=[],t=0;t<arguments.length;t++)e=e.concat(de(arguments[t]));return e},__spreadArrays:function(){for(var e=0,t=0,r=arguments.length;t<r;t++)e+=arguments[t].length;var n=Array(e),i=0;for(t=0;t<r;t++)for(var o=arguments[t],u=0,s=o.length;u<s;u++,i++)n[i]=o[u];return n},__await:ye,__asyncGenerator:function(e,t,r){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var n,i=r.apply(e,t||[]),o=[];return n={},u("next"),u("throw"),u("return"),n[Symbol.asyncIterator]=function(){return this},n;function u(e){i[e]&&(n[e]=function(t){return new Promise((function(r,n){o.push([e,t,r,n])>1||s(e,t)}))})}function s(e,t){try{(r=i[e](t)).value instanceof ye?Promise.resolve(r.value.v).then(f,a):l(o[0][2],r)}catch(e){l(o[0][3],e)}var r}function f(e){s("next",e)}function a(e){s("throw",e)}function l(e,t){e(t),o.shift(),o.length&&s(o[0][0],o[0][1])}},__asyncDelegator:function(e){var t,r;return t={},n("next"),n("throw",(function(e){throw e})),n("return"),t[Symbol.iterator]=function(){return this},t;function n(n,i){t[n]=e[n]?function(t){return(r=!r)?{value:ye(e[n](t)),done:"return"===n}:i?i(t):t}:i}},__asyncValues:function(e){if(!Symbol.asyncIterator)throw new TypeError("Symbol.asyncIterator is not defined.");var t,r=e[Symbol.asyncIterator];return r?r.call(e):(e=pe(e),t={},n("next"),n("throw"),n("return"),t[Symbol.asyncIterator]=function(){return this},t);function n(r){t[r]=e[r]&&function(t){return new Promise((function(n,i){(function(e,t,r,n){Promise.resolve(n).then((function(t){e({value:t,done:r})}),t)})(n,i,(t=e[r](t)).done,t.value)}))}}},__makeTemplateObject:function(e,t){return Object.defineProperty?Object.defineProperty(e,"raw",{value:t}):e.raw=t,e},__importStar:function(e){if(e&&e.__esModule)return e;var t={};if(null!=e)for(var r in e)Object.hasOwnProperty.call(e,r)&&(t[r]=e[r]);return t.default=e,t},__importDefault:function(e){return e&&e.__esModule?e:{default:e}},__classPrivateFieldGet:function(e,t){if(!t.has(e))throw new TypeError("attempted to get private field on non-instance");return t.get(e)},__classPrivateFieldSet:function(e,t,r){if(!t.has(e))throw new TypeError("attempted to set private field on non-instance");return t.set(e,r),r}}),_e=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.Timestamp=t.LongWithoutOverridesClass=void 0,t.LongWithoutOverridesClass=re.Long;var r=function(e){function t(r,n){var i=this;return i instanceof t?(i=re.Long.isLong(r)?e.call(this,r.low,r.high,!0)||this:e.call(this,r,n,!0)||this,Object.defineProperty(i,"_bsontype",{value:"Timestamp",writable:!1,configurable:!1,enumerable:!1}),i):new t(r,n)}return ge.__extends(t,e),t.prototype.toJSON=function(){return{$timestamp:this.toString()}},t.fromInt=function(e){return new t(re.Long.fromInt(e,!0))},t.fromNumber=function(e){return new t(re.Long.fromNumber(e,!0))},t.fromBits=function(e,r){return new t(e,r)},t.fromString=function(e,r){return new t(re.Long.fromString(e,!0,r))},t.prototype.toExtendedJSON=function(){return{$timestamp:{t:this.high>>>0,i:this.low>>>0}}},t.fromExtendedJSON=function(e){return new t(e.$timestamp.i,e.$timestamp.t)},t.prototype[Symbol.for("nodejs.util.inspect.custom")]=function(){return this.inspect()},t.prototype.inspect=function(){return"new Timestamp("+this.getLowBits().toString()+", "+this.getHighBits().toString()+")"},t.MAX_VALUE=re.Long.MAX_UNSIGNED_VALUE,t}(t.LongWithoutOverridesClass);t.Timestamp=r}));r(_e),_e.Timestamp,_e.LongWithoutOverridesClass;var be=n((function(e,t){function r(e){return X.isObjectLike(e)&&Reflect.has(e,"_bsontype")&&"string"==typeof e._bsontype}Object.defineProperty(t,"__esModule",{value:!0}),t.EJSON=t.isBSONType=void 0,t.isBSONType=r;var n=2147483647,i=-2147483648,o=0x8000000000000000,u=-0x8000000000000000,s={$oid:fe.ObjectId,$binary:Q.Binary,$uuid:Q.Binary,$symbol:le.BSONSymbol,$numberInt:oe.Int32,$numberDecimal:ne.Decimal128,$numberDouble:ie.Double,$numberLong:re.Long,$minKey:se.MinKey,$maxKey:ue.MaxKey,$regex:ae.BSONRegExp,$regularExpression:ae.BSONRegExp,$timestamp:_e.Timestamp};function f(e,t){if(void 0===t&&(t={}),"number"==typeof e){if(t.relaxed||t.legacy)return e;if(Math.floor(e)===e){if(e>=i&&e<=n)return new oe.Int32(e);if(e>=u&&e<=o)return re.Long.fromNumber(e)}return new ie.Double(e)}if(null==e||"object"!=typeof e)return e;if(e.$undefined)return null;for(var r=Object.keys(e).filter((function(t){return t.startsWith("$")&&null!=e[t]})),a=0;a<r.length;a++){var l=s[r[a]];if(l)return l.fromExtendedJSON(e,t)}if(null!=e.$date){var c=e.$date,h=new Date;return t.legacy?"number"==typeof c?h.setTime(c):"string"==typeof c&&h.setTime(Date.parse(c)):"string"==typeof c?h.setTime(Date.parse(c)):re.Long.isLong(c)?h.setTime(c.toNumber()):"number"==typeof c&&t.relaxed&&h.setTime(c),h}if(null!=e.$code){var p=Object.assign({},e);return e.$scope&&(p.$scope=f(e.$scope)),ee.Code.fromExtendedJSON(e)}if(null!=e.$ref||null!=e.$dbPointer){var d=e.$ref?e:e.$dbPointer;if(d instanceof te.DBRef)return d;var y=Object.keys(d).filter((function(e){return e.startsWith("$")})),g=!0;if(y.forEach((function(e){-1===["$ref","$id","$db"].indexOf(e)&&(g=!1)})),g)return te.DBRef.fromExtendedJSON(d)}return e}function a(e){var t=e.toISOString();return 0!==e.getUTCMilliseconds()?t:t.slice(0,-5)+"Z"}function l(e,t){if(Array.isArray(e))return function(e,t){return e.map((function(e){return l(e,t)}))}(e,t);if(void 0===e)return null;if(e instanceof Date){var s=e.getTime(),f=s>-1&&s<2534023188e5;return t.legacy?t.relaxed&&f?{$date:e.getTime()}:{$date:a(e)}:t.relaxed&&f?{$date:a(e)}:{$date:{$numberLong:e.getTime().toString()}}}if("number"==typeof e&&!t.relaxed){if(Math.floor(e)===e){var h=e>=u&&e<=o;if(e>=i&&e<=n)return{$numberInt:e.toString()};if(h)return{$numberLong:e.toString()}}return{$numberDouble:e.toString()}}if(e instanceof RegExp){var p=e.flags;if(void 0===p){var d=e.toString().match(/[gimuy]*$/);d&&(p=d[0])}return new ae.BSONRegExp(e.source,p).toExtendedJSON(t)}return null!=e&&"object"==typeof e?function(e,t){if(null==e||"object"!=typeof e)throw new Error("not an object instance");var n=e._bsontype;if(void 0===n){var i={};for(var o in e)i[o]=l(e[o],t);return i}if(r(e)){var u=e;if("function"!=typeof u.toExtendedJSON){var s=c[e._bsontype];if(!s)throw new TypeError("Unrecognized or invalid _bsontype: "+e._bsontype);u=s(u)}return"Code"===n&&u.scope?u=new ee.Code(u.code,l(u.scope,t)):"DBRef"===n&&u.oid&&(u=new te.DBRef(u.collection,l(u.oid,t),u.db,u.fields)),u.toExtendedJSON(t)}throw new Error("_bsontype must be a string, but was: "+typeof n)}(e,t):e}var c={Binary:function(e){return new Q.Binary(e.value(),e.sub_type)},Code:function(e){return new ee.Code(e.code,e.scope)},DBRef:function(e){return new te.DBRef(e.collection||e.namespace,e.oid,e.db,e.fields)},Decimal128:function(e){return new ne.Decimal128(e.bytes)},Double:function(e){return new ie.Double(e.value)},Int32:function(e){return new oe.Int32(e.value)},Long:function(e){return re.Long.fromBits(null!=e.low?e.low:e.low_,null!=e.low?e.high:e.high_,null!=e.low?e.unsigned:e.unsigned_)},MaxKey:function(){return new ue.MaxKey},MinKey:function(){return new se.MinKey},ObjectID:function(e){return new fe.ObjectId(e)},ObjectId:function(e){return new fe.ObjectId(e)},BSONRegExp:function(e){return new ae.BSONRegExp(e.pattern,e.options)},Symbol:function(e){return new le.BSONSymbol(e.value)},Timestamp:function(e){return _e.Timestamp.fromBits(e.low,e.high)}};!function(e){function t(e,t){var r=Object.assign({},{relaxed:!0,legacy:!1},t);return"boolean"==typeof r.relaxed&&(r.strict=!r.relaxed),"boolean"==typeof r.strict&&(r.relaxed=!r.strict),JSON.parse(e,(function(e,t){return f(t,r)}))}function r(e,t,r,n){null!=r&&"object"==typeof r&&(n=r,r=0),null==t||"object"!=typeof t||Array.isArray(t)||(n=t,t=void 0,r=0);var i=l(e,n=Object.assign({},{relaxed:!0,legacy:!1},n));return JSON.stringify(i,t,r)}e.parse=t,e.stringify=r,e.serialize=function(e,t){return t=t||{},JSON.parse(r(e,t))},e.deserialize=function(e,r){return r=r||{},t(JSON.stringify(e),r)}}(t.EJSON||(t.EJSON={}))}));r(be),be.EJSON,be.isBSONType;var Be=n((function(e,r){var n;Object.defineProperty(r,"__esModule",{value:!0}),r.Map=void 0,r.Map=n;var i=function(e){return e&&e.Math==Math&&e};var o=i("object"==typeof globalThis&&globalThis)||i("object"==typeof window&&window)||i("object"==typeof self&&self)||i("object"==typeof t&&t)||Function("return this")();Object.prototype.hasOwnProperty.call(o,"Map")?r.Map=n=o.Map:r.Map=n=function(){function e(e){void 0===e&&(e=[]),this._keys=[],this._values={};for(var t=0;t<e.length;t++)if(null!=e[t]){var r=e[t],n=r[0],i=r[1];this._keys.push(n),this._values[n]={v:i,i:this._keys.length-1}}}return e.prototype.clear=function(){this._keys=[],this._values={}},e.prototype.delete=function(e){var t=this._values[e];return null!=t&&(delete this._values[e],this._keys.splice(t.i,1),!0)},e.prototype.entries=function(){var e=this,t=0;return{next:function(){var r=e._keys[t++];return{value:void 0!==r?[r,e._values[r].v]:void 0,done:void 0===r}}}},e.prototype.forEach=function(e,t){t=t||this;for(var r=0;r<this._keys.length;r++){var n=this._keys[r];e.call(t,this._values[n].v,n,t)}},e.prototype.get=function(e){return this._values[e]?this._values[e].v:void 0},e.prototype.has=function(e){return null!=this._values[e]},e.prototype.keys=function(){var e=this,t=0;return{next:function(){var r=e._keys[t++];return{value:void 0!==r?r:void 0,done:void 0===r}}}},e.prototype.set=function(e,t){return this._values[e]?(this._values[e].v=t,this):(this._keys.push(e),this._values[e]={v:t,i:this._keys.length-1},this)},e.prototype.values=function(){var e=this,t=0;return{next:function(){var r=e._keys[t++];return{value:void 0!==r?e._values[r].v:void 0,done:void 0===r}}}},Object.defineProperty(e.prototype,"size",{get:function(){return this._keys.length},enumerable:!1,configurable:!0}),e}()}));r(Be),Be.Map;var Oe=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.BSON_BINARY_SUBTYPE_USER_DEFINED=t.BSON_BINARY_SUBTYPE_MD5=t.BSON_BINARY_SUBTYPE_UUID_NEW=t.BSON_BINARY_SUBTYPE_UUID=t.BSON_BINARY_SUBTYPE_BYTE_ARRAY=t.BSON_BINARY_SUBTYPE_FUNCTION=t.BSON_BINARY_SUBTYPE_DEFAULT=t.BSON_DATA_MAX_KEY=t.BSON_DATA_MIN_KEY=t.BSON_DATA_DECIMAL128=t.BSON_DATA_LONG=t.BSON_DATA_TIMESTAMP=t.BSON_DATA_INT=t.BSON_DATA_CODE_W_SCOPE=t.BSON_DATA_SYMBOL=t.BSON_DATA_CODE=t.BSON_DATA_DBPOINTER=t.BSON_DATA_REGEXP=t.BSON_DATA_NULL=t.BSON_DATA_DATE=t.BSON_DATA_BOOLEAN=t.BSON_DATA_OID=t.BSON_DATA_UNDEFINED=t.BSON_DATA_BINARY=t.BSON_DATA_ARRAY=t.BSON_DATA_OBJECT=t.BSON_DATA_STRING=t.BSON_DATA_NUMBER=t.JS_INT_MIN=t.JS_INT_MAX=t.BSON_INT64_MIN=t.BSON_INT64_MAX=t.BSON_INT32_MIN=t.BSON_INT32_MAX=void 0,t.BSON_INT32_MAX=2147483647,t.BSON_INT32_MIN=-2147483648,t.BSON_INT64_MAX=Math.pow(2,63)-1,t.BSON_INT64_MIN=-Math.pow(2,63),t.JS_INT_MAX=Math.pow(2,53),t.JS_INT_MIN=-Math.pow(2,53),t.BSON_DATA_NUMBER=1,t.BSON_DATA_STRING=2,t.BSON_DATA_OBJECT=3,t.BSON_DATA_ARRAY=4,t.BSON_DATA_BINARY=5,t.BSON_DATA_UNDEFINED=6,t.BSON_DATA_OID=7,t.BSON_DATA_BOOLEAN=8,t.BSON_DATA_DATE=9,t.BSON_DATA_NULL=10,t.BSON_DATA_REGEXP=11,t.BSON_DATA_DBPOINTER=12,t.BSON_DATA_CODE=13,t.BSON_DATA_SYMBOL=14,t.BSON_DATA_CODE_W_SCOPE=15,t.BSON_DATA_INT=16,t.BSON_DATA_TIMESTAMP=17,t.BSON_DATA_LONG=18,t.BSON_DATA_DECIMAL128=19,t.BSON_DATA_MIN_KEY=255,t.BSON_DATA_MAX_KEY=127,t.BSON_BINARY_SUBTYPE_DEFAULT=0,t.BSON_BINARY_SUBTYPE_FUNCTION=1,t.BSON_BINARY_SUBTYPE_BYTE_ARRAY=2,t.BSON_BINARY_SUBTYPE_UUID=3,t.BSON_BINARY_SUBTYPE_UUID_NEW=4,t.BSON_BINARY_SUBTYPE_MD5=5,t.BSON_BINARY_SUBTYPE_USER_DEFINED=128}));r(Oe),Oe.BSON_BINARY_SUBTYPE_USER_DEFINED,Oe.BSON_BINARY_SUBTYPE_MD5,Oe.BSON_BINARY_SUBTYPE_UUID_NEW,Oe.BSON_BINARY_SUBTYPE_UUID,Oe.BSON_BINARY_SUBTYPE_BYTE_ARRAY,Oe.BSON_BINARY_SUBTYPE_FUNCTION,Oe.BSON_BINARY_SUBTYPE_DEFAULT,Oe.BSON_DATA_MAX_KEY,Oe.BSON_DATA_MIN_KEY,Oe.BSON_DATA_DECIMAL128,Oe.BSON_DATA_LONG,Oe.BSON_DATA_TIMESTAMP,Oe.BSON_DATA_INT,Oe.BSON_DATA_CODE_W_SCOPE,Oe.BSON_DATA_SYMBOL,Oe.BSON_DATA_CODE,Oe.BSON_DATA_DBPOINTER,Oe.BSON_DATA_REGEXP,Oe.BSON_DATA_NULL,Oe.BSON_DATA_DATE,Oe.BSON_DATA_BOOLEAN,Oe.BSON_DATA_OID,Oe.BSON_DATA_UNDEFINED,Oe.BSON_DATA_BINARY,Oe.BSON_DATA_ARRAY,Oe.BSON_DATA_OBJECT,Oe.BSON_DATA_STRING,Oe.BSON_DATA_NUMBER,Oe.JS_INT_MIN,Oe.JS_INT_MAX,Oe.BSON_INT64_MIN,Oe.BSON_INT64_MAX,Oe.BSON_INT32_MIN,Oe.BSON_INT32_MAX;var me=n((function(e,t){function r(e,t,r){var i=5;if(Array.isArray(e))for(var o=0;o<e.length;o++)i+=n(o.toString(),e[o],t,!0,r);else for(var u in e.toBSON&&(e=e.toBSON()),e)i+=n(u,e[u],t,!1,r);return i}function n(e,t,n,i,o){switch(void 0===n&&(n=!1),void 0===i&&(i=!1),void 0===o&&(o=!1),t&&t.toBSON&&(t=t.toBSON()),typeof t){case"string":return 1+b.Buffer.byteLength(e,"utf8")+1+4+b.Buffer.byteLength(t,"utf8")+1;case"number":return Math.floor(t)===t&&t>=Oe.JS_INT_MIN&&t<=Oe.JS_INT_MAX&&t>=Oe.BSON_INT32_MIN&&t<=Oe.BSON_INT32_MAX?(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+5:(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+9;case"undefined":return i||!o?(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1:0;case"boolean":return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+2;case"object":if(null==t||"MinKey"===t._bsontype||"MaxKey"===t._bsontype)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1;if("ObjectId"===t._bsontype||"ObjectID"===t._bsontype)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+13;if(t instanceof Date||X.isDate(t))return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+9;if(ArrayBuffer.isView(t)||t instanceof ArrayBuffer)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+6+t.byteLength;if("Long"===t._bsontype||"Double"===t._bsontype||"Timestamp"===t._bsontype)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+9;if("Decimal128"===t._bsontype)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+17;if("Code"===t._bsontype)return null!=t.scope&&Object.keys(t.scope).length>0?(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+4+4+b.Buffer.byteLength(t.code.toString(),"utf8")+1+r(t.scope,n,o):(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+4+b.Buffer.byteLength(t.code.toString(),"utf8")+1;if("Binary"===t._bsontype)return t.sub_type===Q.Binary.SUBTYPE_BYTE_ARRAY?(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+(t.position+1+4+1+4):(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+(t.position+1+4+1);if("Symbol"===t._bsontype)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+b.Buffer.byteLength(t.value,"utf8")+4+1+1;if("DBRef"===t._bsontype){var u=Object.assign({$ref:t.collection,$id:t.oid},t.fields);return null!=t.db&&(u.$db=t.db),(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+r(u,n,o)}return t instanceof RegExp||"[object RegExp]"===Object.prototype.toString.call(t)?(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+b.Buffer.byteLength(t.source,"utf8")+1+(t.global?1:0)+(t.ignoreCase?1:0)+(t.multiline?1:0)+1:"BSONRegExp"===t._bsontype?(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+b.Buffer.byteLength(t.pattern,"utf8")+1+b.Buffer.byteLength(t.options,"utf8")+1:(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+r(t,n,o)+1;case"function":if(t instanceof RegExp||"[object RegExp]"===Object.prototype.toString.call(t)||"[object RegExp]"===String.call(t))return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+b.Buffer.byteLength(t.source,"utf8")+1+(t.global?1:0)+(t.ignoreCase?1:0)+(t.multiline?1:0)+1;if(n&&null!=t.scope&&Object.keys(t.scope).length>0)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+4+4+b.Buffer.byteLength(X.normalizedFunctionString(t),"utf8")+1+r(t.scope,n,o);if(n)return(null!=e?b.Buffer.byteLength(e,"utf8")+1:0)+1+4+b.Buffer.byteLength(X.normalizedFunctionString(t),"utf8")+1}return 0}Object.defineProperty(t,"__esModule",{value:!0}),t.calculateObjectSize=void 0,t.calculateObjectSize=r}));r(me),me.calculateObjectSize;var Se=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.validateUtf8=void 0;t.validateUtf8=function(e,t,r){for(var n=0,i=t;i<r;i+=1){var o=e[i];if(n){if(128!=(192&o))return!1;n-=1}else if(128&o)if(192==(224&o))n=1;else if(224==(240&o))n=2;else{if(240!=(248&o))return!1;n=3}}return!n}}));r(Se),Se.validateUtf8;var Ne=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.deserialize=void 0;var r=re.Long.fromNumber(Oe.JS_INT_MAX),n=re.Long.fromNumber(Oe.JS_INT_MIN),i={};function o(e,t,s,f){void 0===f&&(f=!1);var a=null!=s.evalFunctions&&s.evalFunctions,l=null!=s.cacheFunctions&&s.cacheFunctions,c=null==s.fieldsAsRaw?null:s.fieldsAsRaw,h=null!=s.raw&&s.raw,p="boolean"==typeof s.bsonRegExp&&s.bsonRegExp,d=null!=s.promoteBuffers&&s.promoteBuffers,y=null==s.promoteLongs||s.promoteLongs,g=null==s.promoteValues||s.promoteValues,_=t;if(e.length<5)throw new Error("corrupt bson message < 5 bytes long");var B=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24;if(B<5||B>e.length)throw new Error("corrupt bson message");for(var O=f?[]:{},m=0;;){var S=e[t++];if(0===S)break;for(var N=t;0!==e[N]&&N<e.length;)N++;if(N>=e.byteLength)throw new Error("Bad BSON Document: illegal CString");var v=f?m++:e.toString("utf8",t,N);if(t=N+1,S===Oe.BSON_DATA_STRING){if((H=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24)<=0||H>e.length-t||0!==e[t+H-1])throw new Error("bad string length in bson");if(!Se.validateUtf8(e,t,t+H-1))throw new Error("Invalid UTF-8 string in BSON document");var A=e.toString("utf8",t,t+H-1);O[v]=A,t+=H}else if(S===Oe.BSON_DATA_OID){var w=b.Buffer.alloc(12);e.copy(w,0,t,t+12),O[v]=new fe.ObjectId(w),t+=12}else if(S===Oe.BSON_DATA_INT&&!1===g)O[v]=new oe.Int32(e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24);else if(S===Oe.BSON_DATA_INT)O[v]=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24;else if(S===Oe.BSON_DATA_NUMBER&&!1===g)O[v]=new ie.Double(e.readDoubleLE(t)),t+=8;else if(S===Oe.BSON_DATA_NUMBER)O[v]=e.readDoubleLE(t),t+=8;else if(S===Oe.BSON_DATA_DATE){var E=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24,T=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24;O[v]=new Date(new re.Long(E,T).toNumber())}else if(S===Oe.BSON_DATA_BOOLEAN){if(0!==e[t]&&1!==e[t])throw new Error("illegal boolean type value");O[v]=1===e[t++]}else if(S===Oe.BSON_DATA_OBJECT){var D=t;if((V=e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24)<=0||V>e.length-t)throw new Error("bad embedded document length in bson");O[v]=h?e.slice(t,t+V):o(e,D,s,!1),t+=V}else if(S===Oe.BSON_DATA_ARRAY){D=t;var I=s,x=t+(V=e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24);if(c&&c[v]){for(var U in I={},s)I[U]=s[U];I.raw=!0}if(O[v]=o(e,D,I,!0),0!==e[(t+=V)-1])throw new Error("invalid array terminator byte");if(t!==x)throw new Error("corrupted array bson")}else if(S===Oe.BSON_DATA_UNDEFINED)O[v]=void 0;else if(S===Oe.BSON_DATA_NULL)O[v]=null;else if(S===Oe.BSON_DATA_LONG){E=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24,T=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24;var R=new re.Long(E,T);O[v]=y&&!0===g&&R.lessThanOrEqual(r)&&R.greaterThanOrEqual(n)?R.toNumber():R}else if(S===Oe.BSON_DATA_DECIMAL128){var L=b.Buffer.alloc(16);e.copy(L,0,t,t+16),t+=16;var j=new ne.Decimal128(L);"toObject"in j&&"function"==typeof j.toObject?O[v]=j.toObject():O[v]=j}else if(S===Oe.BSON_DATA_BINARY){var M=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24,P=M,Y=e[t++];if(M<0)throw new Error("Negative binary type element size found");if(M>e.byteLength)throw new Error("Binary type size larger than document size");if(null!=e.slice){if(Y===Q.Binary.SUBTYPE_BYTE_ARRAY){if((M=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24)<0)throw new Error("Negative binary type element size found for subtype 0x02");if(M>P-4)throw new Error("Binary type with subtype 0x02 contains too long binary size");if(M<P-4)throw new Error("Binary type with subtype 0x02 contains too short binary size")}O[v]=d&&g?e.slice(t,t+M):new Q.Binary(e.slice(t,t+M),Y)}else{var C=b.Buffer.alloc(M);if(Y===Q.Binary.SUBTYPE_BYTE_ARRAY){if((M=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24)<0)throw new Error("Negative binary type element size found for subtype 0x02");if(M>P-4)throw new Error("Binary type with subtype 0x02 contains too long binary size");if(M<P-4)throw new Error("Binary type with subtype 0x02 contains too short binary size")}for(N=0;N<M;N++)C[N]=e[t+N];O[v]=d&&g?C:new Q.Binary(C,Y)}t+=M}else if(S===Oe.BSON_DATA_REGEXP&&!1===p){for(N=t;0!==e[N]&&N<e.length;)N++;if(N>=e.length)throw new Error("Bad BSON Document: illegal CString");var z=e.toString("utf8",t,N);for(N=t=N+1;0!==e[N]&&N<e.length;)N++;if(N>=e.length)throw new Error("Bad BSON Document: illegal CString");var $=e.toString("utf8",t,N);t=N+1;var k=new Array($.length);for(N=0;N<$.length;N++)switch($[N]){case"m":k[N]="m";break;case"s":k[N]="g";break;case"i":k[N]="i"}O[v]=new RegExp(z,k.join(""))}else if(S===Oe.BSON_DATA_REGEXP&&!0===p){for(N=t;0!==e[N]&&N<e.length;)N++;if(N>=e.length)throw new Error("Bad BSON Document: illegal CString");z=e.toString("utf8",t,N);for(N=t=N+1;0!==e[N]&&N<e.length;)N++;if(N>=e.length)throw new Error("Bad BSON Document: illegal CString");$=e.toString("utf8",t,N);t=N+1,O[v]=new ae.BSONRegExp(z,$)}else if(S===Oe.BSON_DATA_SYMBOL){if((H=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24)<=0||H>e.length-t||0!==e[t+H-1])throw new Error("bad string length in bson");var J=e.toString("utf8",t,t+H-1);O[v]=g?J:new le.BSONSymbol(J),t+=H}else if(S===Oe.BSON_DATA_TIMESTAMP){E=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24,T=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24;O[v]=new _e.Timestamp(E,T)}else if(S===Oe.BSON_DATA_MIN_KEY)O[v]=new se.MinKey;else if(S===Oe.BSON_DATA_MAX_KEY)O[v]=new ue.MaxKey;else if(S===Oe.BSON_DATA_CODE){if((H=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24)<=0||H>e.length-t||0!==e[t+H-1])throw new Error("bad string length in bson");var q=e.toString("utf8",t,t+H-1);O[v]=a?l?u(q,i,O):u(q):new ee.Code(q),t+=H}else if(S===Oe.BSON_DATA_CODE_W_SCOPE){var F=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24;if(F<13)throw new Error("code_w_scope total size shorter minimum expected length");if((H=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24)<=0||H>e.length-t||0!==e[t+H-1])throw new Error("bad string length in bson");q=e.toString("utf8",t,t+H-1),D=t+=H;var V=e[t]|e[t+1]<<8|e[t+2]<<16|e[t+3]<<24,K=o(e,D,s,!1);if(t+=V,F<8+V+H)throw new Error("code_w_scope total size is too short, truncating scope");if(F>8+V+H)throw new Error("code_w_scope total size is too long, clips outer document");a?(O[v]=l?u(q,i,O):u(q),O[v].scope=K):O[v]=new ee.Code(q,K)}else{if(S!==Oe.BSON_DATA_DBPOINTER)throw new Error("Detected unknown BSON type "+S.toString(16)+' for fieldname "'+v+'"');var H;if((H=e[t++]|e[t++]<<8|e[t++]<<16|e[t++]<<24)<=0||H>e.length-t||0!==e[t+H-1])throw new Error("bad string length in bson");if(!Se.validateUtf8(e,t,t+H-1))throw new Error("Invalid UTF-8 string in BSON document");var X=e.toString("utf8",t,t+H-1);t+=H;var W=b.Buffer.alloc(12);e.copy(W,0,t,t+12);w=new fe.ObjectId(W);t+=12,O[v]=new te.DBRef(X,w)}}if(B!==t-_){if(f)throw new Error("corrupt array bson");throw new Error("corrupt object bson")}var G=Object.keys(O).filter((function(e){return e.startsWith("$")})),Z=!0;if(G.forEach((function(e){-1===["$ref","$id","$db"].indexOf(e)&&(Z=!1)})),!Z)return O;if(te.isDBRefLike(O)){var ce=Object.assign({},O);return delete ce.$ref,delete ce.$id,delete ce.$db,new te.DBRef(O.$ref,O.$id,O.$db,ce)}return O}function u(e,t,r){return t?(null==t[e]&&(t[e]=new Function(e)),t[e].bind(r)):new Function(e)}t.deserialize=function(e,t,r){var n=(t=null==t?{}:t)&&t.index?t.index:0,i=e[n]|e[n+1]<<8|e[n+2]<<16|e[n+3]<<24;if(i<5)throw new Error("bson size must be >= 5, is "+i);if(t.allowObjectSmallerThanBufferSize&&e.length<i)throw new Error("buffer length "+e.length+" must be >= bson size "+i);if(!t.allowObjectSmallerThanBufferSize&&e.length!==i)throw new Error("buffer length "+e.length+" must === bson size "+i);if(i+n>e.byteLength)throw new Error("(bson size "+i+" + options.index "+n+" must be <= buffer length "+e.byteLength+")");if(0!==e[n+i-1])throw new Error("One object, sized correctly, with a spot for an EOO, but the EOO isn't 0x00");return o(e,n,t,r)}}));r(Ne),Ne.deserialize;var ve=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.writeIEEE754=t.readIEEE754=void 0,t.readIEEE754=function(e,t,r,n,i){var o,u,s="big"===r,f=8*i-n-1,a=(1<<f)-1,l=a>>1,c=-7,h=s?0:i-1,p=s?1:-1,d=e[t+h];for(h+=p,o=d&(1<<-c)-1,d>>=-c,c+=f;c>0;o=256*o+e[t+h],h+=p,c-=8);for(u=o&(1<<-c)-1,o>>=-c,c+=n;c>0;u=256*u+e[t+h],h+=p,c-=8);if(0===o)o=1-l;else{if(o===a)return u?NaN:1/0*(d?-1:1);u+=Math.pow(2,n),o-=l}return(d?-1:1)*u*Math.pow(2,o-n)},t.writeIEEE754=function(e,t,r,n,i,o){var u,s,f,a="big"===n,l=8*o-i-1,c=(1<<l)-1,h=c>>1,p=23===i?Math.pow(2,-24)-Math.pow(2,-77):0,d=a?o-1:0,y=a?-1:1,g=t<0||0===t&&1/t<0?1:0;for(t=Math.abs(t),isNaN(t)||t===1/0?(s=isNaN(t)?1:0,u=c):(u=Math.floor(Math.log(t)/Math.LN2),t*(f=Math.pow(2,-u))<1&&(u--,f*=2),(t+=u+h>=1?p/f:p*Math.pow(2,1-h))*f>=2&&(u++,f/=2),u+h>=c?(s=0,u=c):u+h>=1?(s=(t*f-1)*Math.pow(2,i),u+=h):(s=t*Math.pow(2,h-1)*Math.pow(2,i),u=0)),isNaN(t)&&(s=0);i>=8;)e[r+d]=255&s,d+=y,s/=256,i-=8;for(u=u<<i|s,isNaN(t)&&(u+=8),l+=i;l>0;)e[r+d]=255&u,d+=y,u/=256,l-=8;e[r+d-y]|=128*g}}));r(ve),ve.writeIEEE754,ve.readIEEE754;var Ae=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.serializeInto=void 0;var r=/\x00/,n=new Set(["$db","$ref","$id","$clusterTime"]);function i(e){return"[object RegExp]"===Object.prototype.toString.call(e)}function o(e,t,r,n,i){e[n++]=Oe.BSON_DATA_STRING;var o=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8");e[(n=n+o+1)-1]=0;var u=e.write(r,n+4,void 0,"utf8");return e[n+3]=u+1>>24&255,e[n+2]=u+1>>16&255,e[n+1]=u+1>>8&255,e[n]=u+1&255,n=n+4+u,e[n++]=0,n}function u(e,t,r,n,i){Number.isInteger(r)&&r>=Oe.BSON_INT32_MIN&&r<=Oe.BSON_INT32_MAX?(e[n++]=Oe.BSON_DATA_INT,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,e[n++]=255&r,e[n++]=r>>8&255,e[n++]=r>>16&255,e[n++]=r>>24&255):(e[n++]=Oe.BSON_DATA_NUMBER,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,ve.writeIEEE754(e,r,n,"little",52,8),n+=8);return n}function s(e,t,r,n,i){return e[n++]=Oe.BSON_DATA_NULL,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,n}function f(e,t,r,n,i){return e[n++]=Oe.BSON_DATA_BOOLEAN,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,e[n++]=r?1:0,n}function a(e,t,r,n,i){e[n++]=Oe.BSON_DATA_DATE,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var o=re.Long.fromNumber(r.getTime()),u=o.getLowBits(),s=o.getHighBits();return e[n++]=255&u,e[n++]=u>>8&255,e[n++]=u>>16&255,e[n++]=u>>24&255,e[n++]=255&s,e[n++]=s>>8&255,e[n++]=s>>16&255,e[n++]=s>>24&255,n}function l(e,t,n,i,o){if(e[i++]=Oe.BSON_DATA_REGEXP,i+=o?e.write(t,i,void 0,"ascii"):e.write(t,i,void 0,"utf8"),e[i++]=0,n.source&&null!=n.source.match(r))throw Error("value "+n.source+" must not contain null bytes");return i+=e.write(n.source,i,void 0,"utf8"),e[i++]=0,n.ignoreCase&&(e[i++]=105),n.global&&(e[i++]=115),n.multiline&&(e[i++]=109),e[i++]=0,i}function c(e,t,n,i,o){if(e[i++]=Oe.BSON_DATA_REGEXP,i+=o?e.write(t,i,void 0,"ascii"):e.write(t,i,void 0,"utf8"),e[i++]=0,null!=n.pattern.match(r))throw Error("pattern "+n.pattern+" must not contain null bytes");return i+=e.write(n.pattern,i,void 0,"utf8"),e[i++]=0,i+=e.write(n.options.split("").sort().join(""),i,void 0,"utf8"),e[i++]=0,i}function h(e,t,r,n,i){return null===r?e[n++]=Oe.BSON_DATA_NULL:"MinKey"===r._bsontype?e[n++]=Oe.BSON_DATA_MIN_KEY:e[n++]=Oe.BSON_DATA_MAX_KEY,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,n}function p(e,t,r,n,i){if(e[n++]=Oe.BSON_DATA_OID,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,"string"==typeof r.id)e.write(r.id,n,void 0,"binary");else{if(!r.id||!r.id.copy)throw new TypeError("object ["+JSON.stringify(r)+"] is not a valid ObjectId");r.id.copy(e,n,0,12)}return n+12}function d(e,t,r,n,i){e[n++]=Oe.BSON_DATA_BINARY,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var o=r.length;return e[n++]=255&o,e[n++]=o>>8&255,e[n++]=o>>16&255,e[n++]=o>>24&255,e[n++]=Oe.BSON_BINARY_SUBTYPE_DEFAULT,e.set(W.ensureBuffer(r),n),n+=o}function y(e,t,r,n,i,o,u,s,f,a){void 0===i&&(i=!1),void 0===o&&(o=0),void 0===u&&(u=!1),void 0===s&&(s=!0),void 0===f&&(f=!1),void 0===a&&(a=[]);for(var l=0;l<a.length;l++)if(a[l]===r)throw new Error("cyclic dependency detected");a.push(r),e[n++]=Array.isArray(r)?Oe.BSON_DATA_ARRAY:Oe.BSON_DATA_OBJECT,n+=f?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var c=A(e,r,i,n,o+1,u,s,a);return a.pop(),c}function g(e,t,r,n,i){return e[n++]=Oe.BSON_DATA_DECIMAL128,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,r.bytes.copy(e,n,0,16),n+16}function _(e,t,r,n,i){e[n++]="Long"===r._bsontype?Oe.BSON_DATA_LONG:Oe.BSON_DATA_TIMESTAMP,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var o=r.getLowBits(),u=r.getHighBits();return e[n++]=255&o,e[n++]=o>>8&255,e[n++]=o>>16&255,e[n++]=o>>24&255,e[n++]=255&u,e[n++]=u>>8&255,e[n++]=u>>16&255,e[n++]=u>>24&255,n}function b(e,t,r,n,i){return r=r.valueOf(),e[n++]=Oe.BSON_DATA_INT,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,e[n++]=255&r,e[n++]=r>>8&255,e[n++]=r>>16&255,e[n++]=r>>24&255,n}function B(e,t,r,n,i){return e[n++]=Oe.BSON_DATA_NUMBER,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0,ve.writeIEEE754(e,r.value,n,"little",52,8),n+=8}function O(e,t,r,n,i,o,u){e[n++]=Oe.BSON_DATA_CODE,n+=u?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var s=X.normalizedFunctionString(r),f=e.write(s,n+4,void 0,"utf8")+1;return e[n]=255&f,e[n+1]=f>>8&255,e[n+2]=f>>16&255,e[n+3]=f>>24&255,n=n+4+f-1,e[n++]=0,n}function m(e,t,r,n,i,o,u,s,f){if(void 0===i&&(i=!1),void 0===o&&(o=0),void 0===u&&(u=!1),void 0===s&&(s=!0),void 0===f&&(f=!1),r.scope&&"object"==typeof r.scope){e[n++]=Oe.BSON_DATA_CODE_W_SCOPE,n+=f?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var a=n,l="string"==typeof r.code?r.code:r.code.toString();n+=4;var c=e.write(l,n+4,void 0,"utf8")+1;e[n]=255&c,e[n+1]=c>>8&255,e[n+2]=c>>16&255,e[n+3]=c>>24&255,e[n+4+c-1]=0,n=n+c+4;var h=A(e,r.scope,i,n,o+1,u,s);n=h-1;var p=h-a;e[a++]=255&p,e[a++]=p>>8&255,e[a++]=p>>16&255,e[a++]=p>>24&255,e[n++]=0}else{e[n++]=Oe.BSON_DATA_CODE,n+=f?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;l=r.code.toString();var d=e.write(l,n+4,void 0,"utf8")+1;e[n]=255&d,e[n+1]=d>>8&255,e[n+2]=d>>16&255,e[n+3]=d>>24&255,n=n+4+d-1,e[n++]=0}return n}function S(e,t,r,n,i){e[n++]=Oe.BSON_DATA_BINARY,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var o=r.value(!0),u=r.position;return r.sub_type===Q.Binary.SUBTYPE_BYTE_ARRAY&&(u+=4),e[n++]=255&u,e[n++]=u>>8&255,e[n++]=u>>16&255,e[n++]=u>>24&255,e[n++]=r.sub_type,r.sub_type===Q.Binary.SUBTYPE_BYTE_ARRAY&&(u-=4,e[n++]=255&u,e[n++]=u>>8&255,e[n++]=u>>16&255,e[n++]=u>>24&255),e.set(o,n),n+=r.position}function N(e,t,r,n,i){e[n++]=Oe.BSON_DATA_SYMBOL,n+=i?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var o=e.write(r.value,n+4,void 0,"utf8")+1;return e[n]=255&o,e[n+1]=o>>8&255,e[n+2]=o>>16&255,e[n+3]=o>>24&255,n=n+4+o-1,e[n++]=0,n}function v(e,t,r,n,i,o,u){e[n++]=Oe.BSON_DATA_OBJECT,n+=u?e.write(t,n,void 0,"ascii"):e.write(t,n,void 0,"utf8"),e[n++]=0;var s=n,f={$ref:r.collection||r.namespace,$id:r.oid};null!=r.db&&(f.$db=r.db);var a=A(e,f=Object.assign(f,r.fields),!1,n,i+1,o),l=a-s;return e[s++]=255&l,e[s++]=l>>8&255,e[s++]=l>>16&255,e[s++]=l>>24&255,a}function A(e,t,A,w,E,T,D,I){void 0===A&&(A=!1),void 0===w&&(w=0),void 0===E&&(E=0),void 0===T&&(T=!1),void 0===D&&(D=!0),void 0===I&&(I=[]),w=w||0,(I=I||[]).push(t);var x=w+4;if(Array.isArray(t))for(var U=0;U<t.length;U++){var R=""+U;if((Y=t[U])&&Y.toBSON){if("function"!=typeof Y.toBSON)throw new TypeError("toBSON is not a function");Y=Y.toBSON()}if("string"==typeof Y)x=o(e,R,Y,x,!0);else if("number"==typeof Y)x=u(e,R,Y,x,!0);else{if("bigint"==typeof Y)throw new TypeError("Unsupported type BigInt, please use Decimal128");if("boolean"==typeof Y)x=f(e,R,Y,x,!0);else if(Y instanceof Date||X.isDate(Y))x=a(e,R,Y,x,!0);else if(void 0===Y)x=s(e,R,0,x,!0);else if(null===Y)x=s(e,R,0,x,!0);else if("ObjectId"===Y._bsontype||"ObjectID"===Y._bsontype)x=p(e,R,Y,x,!0);else if(X.isBuffer(Y)||X.isUint8Array(Y))x=d(e,R,Y,x,!0);else if(Y instanceof RegExp||i(Y))x=l(e,R,Y,x,!0);else if("object"==typeof Y&&null==Y._bsontype)x=y(e,R,Y,x,A,E,T,D,!0,I);else if("object"==typeof Y&&be.isBSONType(Y)&&"Decimal128"===Y._bsontype)x=g(e,R,Y,x,!0);else if("Long"===Y._bsontype||"Timestamp"===Y._bsontype)x=_(e,R,Y,x,!0);else if("Double"===Y._bsontype)x=B(e,R,Y,x,!0);else if("function"==typeof Y&&T)x=O(e,R,Y,x,0,0,!0);else if("Code"===Y._bsontype)x=m(e,R,Y,x,A,E,T,D,!0);else if("Binary"===Y._bsontype)x=S(e,R,Y,x,!0);else if("Symbol"===Y._bsontype)x=N(e,R,Y,x,!0);else if("DBRef"===Y._bsontype)x=v(e,R,Y,x,E,T,!0);else if("BSONRegExp"===Y._bsontype)x=c(e,R,Y,x,!0);else if("Int32"===Y._bsontype)x=b(e,R,Y,x,!0);else if("MinKey"===Y._bsontype||"MaxKey"===Y._bsontype)x=h(e,R,Y,x,!0);else if(void 0!==Y._bsontype)throw new TypeError("Unrecognized or invalid _bsontype: "+Y._bsontype)}}else if(t instanceof Be.Map)for(var L=t.entries(),j=!1;!j;){var M=L.next();if(!(j=!!M.done)){R=M.value[0];var P=typeof(Y=M.value[1]);if("string"==typeof R&&!n.has(R)){if(null!=R.match(r))throw Error("key "+R+" must not contain null bytes");if(A){if("$"===R[0])throw Error("key "+R+" must not start with '$'");if(~R.indexOf("."))throw Error("key "+R+" must not contain '.'")}}if("string"===P)x=o(e,R,Y,x);else if("number"===P)x=u(e,R,Y,x);else{if("bigint"===P||X.isBigInt64Array(Y)||X.isBigUInt64Array(Y))throw new TypeError("Unsupported type BigInt, please use Decimal128");if("boolean"===P)x=f(e,R,Y,x);else if(Y instanceof Date||X.isDate(Y))x=a(e,R,Y,x);else if(null===Y||void 0===Y&&!1===D)x=s(e,R,0,x);else if("ObjectId"===Y._bsontype||"ObjectID"===Y._bsontype)x=p(e,R,Y,x);else if(X.isBuffer(Y)||X.isUint8Array(Y))x=d(e,R,Y,x);else if(Y instanceof RegExp||i(Y))x=l(e,R,Y,x);else if("object"===P&&null==Y._bsontype)x=y(e,R,Y,x,A,E,T,D,!1,I);else if("object"===P&&"Decimal128"===Y._bsontype)x=g(e,R,Y,x);else if("Long"===Y._bsontype||"Timestamp"===Y._bsontype)x=_(e,R,Y,x);else if("Double"===Y._bsontype)x=B(e,R,Y,x);else if("Code"===Y._bsontype)x=m(e,R,Y,x,A,E,T,D);else if("function"==typeof Y&&T)x=O(e,R,Y,x,0,0,T);else if("Binary"===Y._bsontype)x=S(e,R,Y,x);else if("Symbol"===Y._bsontype)x=N(e,R,Y,x);else if("DBRef"===Y._bsontype)x=v(e,R,Y,x,E,T);else if("BSONRegExp"===Y._bsontype)x=c(e,R,Y,x);else if("Int32"===Y._bsontype)x=b(e,R,Y,x);else if("MinKey"===Y._bsontype||"MaxKey"===Y._bsontype)x=h(e,R,Y,x);else if(void 0!==Y._bsontype)throw new TypeError("Unrecognized or invalid _bsontype: "+Y._bsontype)}}}else{if(t.toBSON){if("function"!=typeof t.toBSON)throw new TypeError("toBSON is not a function");if(null!=(t=t.toBSON())&&"object"!=typeof t)throw new TypeError("toBSON function did not return an object")}for(var R in t){var Y;if((Y=t[R])&&Y.toBSON){if("function"!=typeof Y.toBSON)throw new TypeError("toBSON is not a function");Y=Y.toBSON()}P=typeof Y;if("string"==typeof R&&!n.has(R)){if(null!=R.match(r))throw Error("key "+R+" must not contain null bytes");if(A){if("$"===R[0])throw Error("key "+R+" must not start with '$'");if(~R.indexOf("."))throw Error("key "+R+" must not contain '.'")}}if("string"===P)x=o(e,R,Y,x);else if("number"===P)x=u(e,R,Y,x);else{if("bigint"===P)throw new TypeError("Unsupported type BigInt, please use Decimal128");if("boolean"===P)x=f(e,R,Y,x);else if(Y instanceof Date||X.isDate(Y))x=a(e,R,Y,x);else if(void 0===Y)!1===D&&(x=s(e,R,0,x));else if(null===Y)x=s(e,R,0,x);else if("ObjectId"===Y._bsontype||"ObjectID"===Y._bsontype)x=p(e,R,Y,x);else if(X.isBuffer(Y)||X.isUint8Array(Y))x=d(e,R,Y,x);else if(Y instanceof RegExp||i(Y))x=l(e,R,Y,x);else if("object"===P&&null==Y._bsontype)x=y(e,R,Y,x,A,E,T,D,!1,I);else if("object"===P&&"Decimal128"===Y._bsontype)x=g(e,R,Y,x);else if("Long"===Y._bsontype||"Timestamp"===Y._bsontype)x=_(e,R,Y,x);else if("Double"===Y._bsontype)x=B(e,R,Y,x);else if("Code"===Y._bsontype)x=m(e,R,Y,x,A,E,T,D);else if("function"==typeof Y&&T)x=O(e,R,Y,x,0,0,T);else if("Binary"===Y._bsontype)x=S(e,R,Y,x);else if("Symbol"===Y._bsontype)x=N(e,R,Y,x);else if("DBRef"===Y._bsontype)x=v(e,R,Y,x,E,T);else if("BSONRegExp"===Y._bsontype)x=c(e,R,Y,x);else if("Int32"===Y._bsontype)x=b(e,R,Y,x);else if("MinKey"===Y._bsontype||"MaxKey"===Y._bsontype)x=h(e,R,Y,x);else if(void 0!==Y._bsontype)throw new TypeError("Unrecognized or invalid _bsontype: "+Y._bsontype)}}}I.pop(),e[x++]=0;var C=x-w;return e[w++]=255&C,e[w++]=C>>8&255,e[w++]=C>>16&255,e[w++]=C>>24&255,x}t.serializeInto=A}));r(Ae),Ae.serializeInto;var we=n((function(e,t){Object.defineProperty(t,"__esModule",{value:!0}),t.ObjectID=t.Decimal128=t.BSONRegExp=t.MaxKey=t.MinKey=t.Int32=t.Double=t.Timestamp=t.Long=t.UUID=t.ObjectId=t.Binary=t.DBRef=t.BSONSymbol=t.Map=t.Code=t.LongWithoutOverridesClass=t.EJSON=t.BSON_INT64_MIN=t.BSON_INT64_MAX=t.BSON_INT32_MIN=t.BSON_INT32_MAX=t.BSON_DATA_UNDEFINED=t.BSON_DATA_TIMESTAMP=t.BSON_DATA_SYMBOL=t.BSON_DATA_STRING=t.BSON_DATA_REGEXP=t.BSON_DATA_OID=t.BSON_DATA_OBJECT=t.BSON_DATA_NUMBER=t.BSON_DATA_NULL=t.BSON_DATA_MIN_KEY=t.BSON_DATA_MAX_KEY=t.BSON_DATA_LONG=t.BSON_DATA_INT=t.BSON_DATA_DECIMAL128=t.BSON_DATA_DBPOINTER=t.BSON_DATA_DATE=t.BSON_DATA_CODE_W_SCOPE=t.BSON_DATA_CODE=t.BSON_DATA_BOOLEAN=t.BSON_DATA_BINARY=t.BSON_DATA_ARRAY=t.BSON_BINARY_SUBTYPE_UUID_NEW=t.BSON_BINARY_SUBTYPE_UUID=t.BSON_BINARY_SUBTYPE_USER_DEFINED=t.BSON_BINARY_SUBTYPE_MD5=t.BSON_BINARY_SUBTYPE_FUNCTION=t.BSON_BINARY_SUBTYPE_DEFAULT=t.BSON_BINARY_SUBTYPE_BYTE_ARRAY=void 0,t.deserializeStream=t.calculateObjectSize=t.deserialize=t.serializeWithBufferAndIndex=t.serialize=t.setInternalBufferSize=void 0,Object.defineProperty(t,"Binary",{enumerable:!0,get:function(){return Q.Binary}}),Object.defineProperty(t,"Code",{enumerable:!0,get:function(){return ee.Code}}),Object.defineProperty(t,"DBRef",{enumerable:!0,get:function(){return te.DBRef}}),Object.defineProperty(t,"Decimal128",{enumerable:!0,get:function(){return ne.Decimal128}}),Object.defineProperty(t,"Double",{enumerable:!0,get:function(){return ie.Double}}),Object.defineProperty(t,"Int32",{enumerable:!0,get:function(){return oe.Int32}}),Object.defineProperty(t,"Long",{enumerable:!0,get:function(){return re.Long}}),Object.defineProperty(t,"Map",{enumerable:!0,get:function(){return Be.Map}}),Object.defineProperty(t,"MaxKey",{enumerable:!0,get:function(){return ue.MaxKey}}),Object.defineProperty(t,"MinKey",{enumerable:!0,get:function(){return se.MinKey}}),Object.defineProperty(t,"ObjectId",{enumerable:!0,get:function(){return fe.ObjectId}}),Object.defineProperty(t,"ObjectID",{enumerable:!0,get:function(){return fe.ObjectId}}),Object.defineProperty(t,"BSONRegExp",{enumerable:!0,get:function(){return ae.BSONRegExp}}),Object.defineProperty(t,"BSONSymbol",{enumerable:!0,get:function(){return le.BSONSymbol}}),Object.defineProperty(t,"Timestamp",{enumerable:!0,get:function(){return _e.Timestamp}}),Object.defineProperty(t,"UUID",{enumerable:!0,get:function(){return Z.UUID}}),Object.defineProperty(t,"BSON_BINARY_SUBTYPE_BYTE_ARRAY",{enumerable:!0,get:function(){return Oe.BSON_BINARY_SUBTYPE_BYTE_ARRAY}}),Object.defineProperty(t,"BSON_BINARY_SUBTYPE_DEFAULT",{enumerable:!0,get:function(){return Oe.BSON_BINARY_SUBTYPE_DEFAULT}}),Object.defineProperty(t,"BSON_BINARY_SUBTYPE_FUNCTION",{enumerable:!0,get:function(){return Oe.BSON_BINARY_SUBTYPE_FUNCTION}}),Object.defineProperty(t,"BSON_BINARY_SUBTYPE_MD5",{enumerable:!0,get:function(){return Oe.BSON_BINARY_SUBTYPE_MD5}}),Object.defineProperty(t,"BSON_BINARY_SUBTYPE_USER_DEFINED",{enumerable:!0,get:function(){return Oe.BSON_BINARY_SUBTYPE_USER_DEFINED}}),Object.defineProperty(t,"BSON_BINARY_SUBTYPE_UUID",{enumerable:!0,get:function(){return Oe.BSON_BINARY_SUBTYPE_UUID}}),Object.defineProperty(t,"BSON_BINARY_SUBTYPE_UUID_NEW",{enumerable:!0,get:function(){return Oe.BSON_BINARY_SUBTYPE_UUID_NEW}}),Object.defineProperty(t,"BSON_DATA_ARRAY",{enumerable:!0,get:function(){return Oe.BSON_DATA_ARRAY}}),Object.defineProperty(t,"BSON_DATA_BINARY",{enumerable:!0,get:function(){return Oe.BSON_DATA_BINARY}}),Object.defineProperty(t,"BSON_DATA_BOOLEAN",{enumerable:!0,get:function(){return Oe.BSON_DATA_BOOLEAN}}),Object.defineProperty(t,"BSON_DATA_CODE",{enumerable:!0,get:function(){return Oe.BSON_DATA_CODE}}),Object.defineProperty(t,"BSON_DATA_CODE_W_SCOPE",{enumerable:!0,get:function(){return Oe.BSON_DATA_CODE_W_SCOPE}}),Object.defineProperty(t,"BSON_DATA_DATE",{enumerable:!0,get:function(){return Oe.BSON_DATA_DATE}}),Object.defineProperty(t,"BSON_DATA_DBPOINTER",{enumerable:!0,get:function(){return Oe.BSON_DATA_DBPOINTER}}),Object.defineProperty(t,"BSON_DATA_DECIMAL128",{enumerable:!0,get:function(){return Oe.BSON_DATA_DECIMAL128}}),Object.defineProperty(t,"BSON_DATA_INT",{enumerable:!0,get:function(){return Oe.BSON_DATA_INT}}),Object.defineProperty(t,"BSON_DATA_LONG",{enumerable:!0,get:function(){return Oe.BSON_DATA_LONG}}),Object.defineProperty(t,"BSON_DATA_MAX_KEY",{enumerable:!0,get:function(){return Oe.BSON_DATA_MAX_KEY}}),Object.defineProperty(t,"BSON_DATA_MIN_KEY",{enumerable:!0,get:function(){return Oe.BSON_DATA_MIN_KEY}}),Object.defineProperty(t,"BSON_DATA_NULL",{enumerable:!0,get:function(){return Oe.BSON_DATA_NULL}}),Object.defineProperty(t,"BSON_DATA_NUMBER",{enumerable:!0,get:function(){return Oe.BSON_DATA_NUMBER}}),Object.defineProperty(t,"BSON_DATA_OBJECT",{enumerable:!0,get:function(){return Oe.BSON_DATA_OBJECT}}),Object.defineProperty(t,"BSON_DATA_OID",{enumerable:!0,get:function(){return Oe.BSON_DATA_OID}}),Object.defineProperty(t,"BSON_DATA_REGEXP",{enumerable:!0,get:function(){return Oe.BSON_DATA_REGEXP}}),Object.defineProperty(t,"BSON_DATA_STRING",{enumerable:!0,get:function(){return Oe.BSON_DATA_STRING}}),Object.defineProperty(t,"BSON_DATA_SYMBOL",{enumerable:!0,get:function(){return Oe.BSON_DATA_SYMBOL}}),Object.defineProperty(t,"BSON_DATA_TIMESTAMP",{enumerable:!0,get:function(){return Oe.BSON_DATA_TIMESTAMP}}),Object.defineProperty(t,"BSON_DATA_UNDEFINED",{enumerable:!0,get:function(){return Oe.BSON_DATA_UNDEFINED}}),Object.defineProperty(t,"BSON_INT32_MAX",{enumerable:!0,get:function(){return Oe.BSON_INT32_MAX}}),Object.defineProperty(t,"BSON_INT32_MIN",{enumerable:!0,get:function(){return Oe.BSON_INT32_MIN}}),Object.defineProperty(t,"BSON_INT64_MAX",{enumerable:!0,get:function(){return Oe.BSON_INT64_MAX}}),Object.defineProperty(t,"BSON_INT64_MIN",{enumerable:!0,get:function(){return Oe.BSON_INT64_MIN}});var r=be;Object.defineProperty(t,"EJSON",{enumerable:!0,get:function(){return r.EJSON}});var n=_e;Object.defineProperty(t,"LongWithoutOverridesClass",{enumerable:!0,get:function(){return n.LongWithoutOverridesClass}});var i=17825792,o=b.Buffer.alloc(i);function u(e){o.length<e&&(o=b.Buffer.alloc(e))}function s(e,t){void 0===t&&(t={});var r="boolean"==typeof t.checkKeys&&t.checkKeys,n="boolean"==typeof t.serializeFunctions&&t.serializeFunctions,u="boolean"!=typeof t.ignoreUndefined||t.ignoreUndefined,s="number"==typeof t.minInternalBufferSize?t.minInternalBufferSize:i;o.length<s&&(o=b.Buffer.alloc(s));var f=Ae.serializeInto(o,e,r,0,0,n,u,[]),a=b.Buffer.alloc(f);return o.copy(a,0,0,a.length),a}function f(e,t,r){void 0===r&&(r={});var n="boolean"==typeof r.checkKeys&&r.checkKeys,i="boolean"==typeof r.serializeFunctions&&r.serializeFunctions,u="boolean"!=typeof r.ignoreUndefined||r.ignoreUndefined,s="number"==typeof r.index?r.index:0,f=Ae.serializeInto(o,e,n,0,0,i,u);return o.copy(t,s,0,f),s+f-1}function a(e,t){return void 0===t&&(t={}),Ne.deserialize(W.ensureBuffer(e),t)}function l(e,t){void 0===t&&(t={});var r="boolean"==typeof(t=t||{}).serializeFunctions&&t.serializeFunctions,n="boolean"!=typeof t.ignoreUndefined||t.ignoreUndefined;return me.calculateObjectSize(e,r,n)}function c(e,t,r,n,i,o){for(var u=Object.assign({allowObjectSmallerThanBufferSize:!0,index:0},o),s=W.ensureBuffer(e),f=t,a=0;a<r;a++){var l=s[f]|s[f+1]<<8|s[f+2]<<16|s[f+3]<<24;u.index=f,n[i+a]=Ne.deserialize(s,u),f+=l}return f}t.setInternalBufferSize=u,t.serialize=s,t.serializeWithBufferAndIndex=f,t.deserialize=a,t.calculateObjectSize=l,t.deserializeStream=c;var h={Binary:Q.Binary,Code:ee.Code,DBRef:te.DBRef,Decimal128:ne.Decimal128,Double:ie.Double,Int32:oe.Int32,Long:re.Long,UUID:Z.UUID,Map:Be.Map,MaxKey:ue.MaxKey,MinKey:se.MinKey,ObjectId:fe.ObjectId,ObjectID:fe.ObjectId,BSONRegExp:ae.BSONRegExp,BSONSymbol:le.BSONSymbol,Timestamp:_e.Timestamp,EJSON:be.EJSON,setInternalBufferSize:u,serialize:s,serializeWithBufferAndIndex:f,deserialize:a,calculateObjectSize:l,deserializeStream};t.default=h})),Ee=r(we),Te=we.ObjectID,De=we.Decimal128,Ie=we.BSONRegExp,xe=we.MaxKey,Ue=we.MinKey,Re=we.Int32,Le=we.Double,je=we.Timestamp,Me=we.Long,Pe=we.UUID,Ye=we.ObjectId,Ce=we.Binary,ze=we.DBRef,$e=we.BSONSymbol,ke=we.Map,Je=we.Code,qe=we.LongWithoutOverridesClass,Fe=we.EJSON,Ve=we.BSON_INT64_MIN,Ke=we.BSON_INT64_MAX,He=we.BSON_INT32_MIN,Xe=we.BSON_INT32_MAX,We=we.BSON_DATA_UNDEFINED,Ge=we.BSON_DATA_TIMESTAMP,Ze=we.BSON_DATA_SYMBOL,Qe=we.BSON_DATA_STRING,et=we.BSON_DATA_REGEXP,tt=we.BSON_DATA_OID,rt=we.BSON_DATA_OBJECT,nt=we.BSON_DATA_NUMBER,it=we.BSON_DATA_NULL,ot=we.BSON_DATA_MIN_KEY,ut=we.BSON_DATA_MAX_KEY,st=we.BSON_DATA_LONG,ft=we.BSON_DATA_INT,at=we.BSON_DATA_DECIMAL128,lt=we.BSON_DATA_DBPOINTER,ct=we.BSON_DATA_DATE,ht=we.BSON_DATA_CODE_W_SCOPE,pt=we.BSON_DATA_CODE,dt=we.BSON_DATA_BOOLEAN,yt=we.BSON_DATA_BINARY,gt=we.BSON_DATA_ARRAY,_t=we.BSON_BINARY_SUBTYPE_UUID_NEW,bt=we.BSON_BINARY_SUBTYPE_UUID,Bt=we.BSON_BINARY_SUBTYPE_USER_DEFINED,Ot=we.BSON_BINARY_SUBTYPE_MD5,mt=we.BSON_BINARY_SUBTYPE_FUNCTION,St=we.BSON_BINARY_SUBTYPE_DEFAULT,Nt=we.BSON_BINARY_SUBTYPE_BYTE_ARRAY,vt=we.deserializeStream,At=we.calculateObjectSize,wt=we.deserialize,Et=we.serializeWithBufferAndIndex,Tt=we.serialize,Dt=we.setInternalBufferSize;return e.BSONRegExp=Ie,e.BSONSymbol=$e,e.BSON_BINARY_SUBTYPE_BYTE_ARRAY=Nt,e.BSON_BINARY_SUBTYPE_DEFAULT=St,e.BSON_BINARY_SUBTYPE_FUNCTION=mt,e.BSON_BINARY_SUBTYPE_MD5=Ot,e.BSON_BINARY_SUBTYPE_USER_DEFINED=Bt,e.BSON_BINARY_SUBTYPE_UUID=bt,e.BSON_BINARY_SUBTYPE_UUID_NEW=_t,e.BSON_DATA_ARRAY=gt,e.BSON_DATA_BINARY=yt,e.BSON_DATA_BOOLEAN=dt,e.BSON_DATA_CODE=pt,e.BSON_DATA_CODE_W_SCOPE=ht,e.BSON_DATA_DATE=ct,e.BSON_DATA_DBPOINTER=lt,e.BSON_DATA_DECIMAL128=at,e.BSON_DATA_INT=ft,e.BSON_DATA_LONG=st,e.BSON_DATA_MAX_KEY=ut,e.BSON_DATA_MIN_KEY=ot,e.BSON_DATA_NULL=it,e.BSON_DATA_NUMBER=nt,e.BSON_DATA_OBJECT=rt,e.BSON_DATA_OID=tt,e.BSON_DATA_REGEXP=et,e.BSON_DATA_STRING=Qe,e.BSON_DATA_SYMBOL=Ze,e.BSON_DATA_TIMESTAMP=Ge,e.BSON_DATA_UNDEFINED=We,e.BSON_INT32_MAX=Xe,e.BSON_INT32_MIN=He,e.BSON_INT64_MAX=Ke,e.BSON_INT64_MIN=Ve,e.Binary=Ce,e.Code=Je,e.DBRef=ze,e.Decimal128=De,e.Double=Le,e.EJSON=Fe,e.Int32=Re,e.Long=Me,e.LongWithoutOverridesClass=qe,e.Map=ke,e.MaxKey=xe,e.MinKey=Ue,e.ObjectID=Te,e.ObjectId=Ye,e.Timestamp=je,e.UUID=Pe,e.calculateObjectSize=At,e.default=Ee,e.deserialize=wt,e.deserializeStream=vt,e.serialize=Tt,e.serializeWithBufferAndIndex=Et,e.setInternalBufferSize=Dt,e.BufferPolyfill=b.Buffer,Object.defineProperty(e,"__esModule",{value:!0}),e}({});(()=>{const e=!("undefined"==typeof window);if(e&&window.HotPocket)return;if(!e){const e=require("util");TextEncoder=e.TextEncoder,TextDecoder=e.TextDecoder}const t="0.6.",r=237,n=new TextEncoder,i=new TextDecoder;let o=null,u=null,s=null,f=null,a=0;const l={json:"json",bson:"bson"};Object.freeze(l);const c={disconnect:"disconnect",contractOutput:"contract_output",connectionChange:"connection_change",unlChange:"unl_change",ledgerEvent:"ledger_event",healthEvent:"health_event"};Object.freeze(c);const h={unlChange:"unl_change",ledgerEvent:"ledger_event",healthEvent:"health_event"};Object.freeze(h);const p=async(e=null)=>{if(await S(),e){const t=b(e);if(t[0]!=r)throw"Invaid key type. 'ed' expected.";const n=new Uint8Array(33);return n[0]=r,n.set(t.slice(33),1),{privateKey:t,publicKey:n}}{const e=u.crypto_sign_keypair(),t=new Uint8Array(65);t[0]=r,t.set(e.privateKey,1);const n=new Uint8Array(33);return n[0]=r,n.set(e.publicKey,1),{privateKey:t,publicKey:n}}},d=async(t,r,n)=>{const i={contractId:null,contractVersion:null,trustedServerKeys:null,protocol:l.json,requiredConnectionCount:1,connectionTimeoutMs:5e3},u=n?{...i,...n}:i;if(!r)throw"clientKeys not specified.";if(""==u.contractId)throw"contractId not specified. Specify null to bypass contract id validation.";if(""==u.contractVersion)throw"contractVersion not specified. Specify null to bypass contract version validation.";if(!u.protocol||u.protocol!=l.json&&u.protocol!=l.bson)throw"Valid protocol not specified.";if(!u.requiredConnectionCount||0==u.requiredConnectionCount)throw"requiredConnectionCount must be greater than 0.";if(!u.connectionTimeoutMs||0==u.connectionTimeoutMs)throw"Connection timeout must be greater than 0.";await S(),await async function(){if(f)return;if(e&&window.blake3)f=window.blake3;else{if(e&&!window.blake3)return v||(v=new Promise((e=>{"undefined"==typeof BigUint64Array&&(BigUint64Array=function(e){}),"undefined"==typeof BigInt&&(BigInt=function(e){});import("https://cdn.jsdelivr.net/npm/blake3@2.1.4/browser-async.js").then((t=>{t.default().then((t=>{f=t,e()})).catch((t=>{console.error(t),e()}))})).catch((t=>{console.error(t),e()}))}))),void await v;e||(f=require("blake3"))}if(!f)throw"Blake3 reference not found."}(),function(){if(o)return;e&&window.WebSocket?o=window.WebSocket:e||(o=require("ws"));if(!o)throw"WebSocket reference not found."}(),u.protocol==l.bson&&await async function(){if(s)return;e?(N||(N=new Promise((e=>{setTimeout((()=>{s=window.BSON,window.Buffer=window.BSON.BufferPolyfill,e()}),0)}))),await N):e||(s=require("bson"));if(!s)throw"BSON reference not found."}();const a={};if(t&&t.forEach((e=>{const t=e.trim();t.length>0&&(a[t]=!0)})),0==Object.keys(a).length)throw"servers not specified.";if(u.requiredConnectionCount>Object.keys(a).length)throw"requiredConnectionCount is higher than no. of servers.";let c={};return u.trustedServerKeys&&u.trustedServerKeys.sort().forEach((e=>{const t=e.trim();t.length>0&&(c[t]=!0)})),0==Object.keys(c).length&&(c=null),new y(u.contractId,u.contractVersion,r,a,c,u.protocol,u.requiredConnectionCount,u.connectionTimeoutMs)};function y(e,t,r,n,i,o,u,s){let f=new m;const a=()=>i,l=e=>i=e,p=Object.keys(n).map((e=>({server:e,connection:null,lastActivity:0}))),d={};d[h.unlChange]=!!i,d[h.ledgerEvent]=!1,d[h.healthEvent]=!1;let y=0,_=null,b=0,B=null;const O=()=>{if(2==y)return;if(B=setTimeout((()=>{O()}),1e3),p.filter((e=>e.connection&&e.connection.isConnected())).length==u)return b=0,_&&_(!0),_=null,void(y=1);if(0==b)b=(new Date).getTime();else if((new Date).getTime()-b>s)return w(1,"Missing-connections timeout reached."),void this.close().then((()=>{_?(_(!1),_=null):f&&f.emit(c.disconnect)}));let n=p.filter((e=>e.connection)).length;if(n==u)return;const i=p.filter((e=>!e.connection&&(new Date).getTime()-e.lastActivity>3e3));for(i.sort(((e,t)=>e.lastActivity-t.lastActivity));n<u&&i.length>0;){const u=i.shift();u.connection=new g(e,t,r,u.server,a,l,o,s,f),u.lastActivity=(new Date).getTime(),u.connection.connect().then((e=>{if(e){f&&f.emit(c.connectionChange,u.server,"add");for(const[e,t]of Object.entries(d))t&&u.connection.subscribe(e)}else u.connection=null})),u.connection.onClose=()=>{u.connection=null,f&&f.emit(c.connectionChange,u.server,"remove")},n++}},S=async(e,t)=>{if(2==y)return await Promise.resolve();null==t&&(t=u);const r=p.filter((e=>e.connection&&e.connection.isConnected())).map((e=>e.connection)).slice(0,t),n=await Promise.all(r.map((t=>e(t))));return 1==t&&n.length<=1?0==n.length?null:n[0]:n},N=(e,t)=>{if(2==y)return Promise.resolve();null==t&&(t=u);const r=p.filter((e=>e.connection&&e.connection.isConnected())).map((e=>e.connection)).slice(0,t);return Promise.all(r.map((t=>e(t))))};this.connect=()=>{if(!(y>0))return b=(new Date).getTime(),O(),new Promise((e=>{_=e}))},this.close=async()=>{2!=y&&(y=2,B&&(clearTimeout(B),B=null),f.clear(c.connectionChange),f.clear(c.contractOutput),await Promise.all(p.filter((e=>e.connection)).map((e=>e.connection.close()))),p.forEach((e=>e.connection=null)))},this.on=(e,t)=>{f.on(e,t)},this.clear=e=>{f.clear(e)},this.submitContractInput=(e,t=null,r=null,n=!0)=>S((i=>i.submitContractInput(e,t,r,n)),1),this.submitContractReadRequest=(e,t=null,r=15e3)=>(t=t?t.toString():(new Date).getTime().toString(),S((n=>n.submitContractReadRequest(e,t,r)))),this.getStatus=()=>S((e=>e.getStatus())),this.getLcl=()=>S((e=>e.getLcl())),this.subscribe=e=>(d[e]=!0,N((t=>t.subscribe(e)))),this.unsubscribe=e=>(d[e]=!1,N((t=>t.unsubscribe(e)))),this.getLedgerBySeqNo=(e,t,r)=>S((n=>n.getLedgerBySeqNo(e,t,r)))}function g(r,s,a,h,p,d,y,g,m){const S=new _(a,l.json);let N=0,v=null,A=null,E=null,T=!1,D=null,I=null,x=null,U=null,R=[],L=[],j={},M={},P={};const Y=e=>{const t=f.createHash();return e.forEach((e=>t.update(e))),new Uint8Array(t.digest())},C=(e,t)=>{const r=[];let n=!1;for(let i of e)if(Array.isArray(i)){const e=C(i,n?null:t);1==e[0]&&(n=!0),r.push(e[1])}else{if(!t&&!i)return w(1,"Self hash encountered more than once in output hash tree."),[!1,null];i||(n=!0);const e=i?S.binaryDecode(i):t;r.push(e)}return[n,Y(r)]},z=(e,t)=>{const r=Y([a.publicKey,...S.spreadArrayField(e.outputs)]);if(!function(e,t){if(e.length!=t.length)return!1;for(let r=0;r<e.length;r++)if(e[r]!==t[r])return!1;return!0}(r,S.binaryDecode(e.output_hash)))return w(1,"Contract output hash mismatch."),!1;const n=C(e.hash_tree,r);if(1==n[0]){return((e,t,r)=>{const n=Object.keys(r).length;if(0==n)return w(1,"Cannot validate outputs with empty unl."),!1;const i={};for(const n of t){const t=S.stringifyValue(n[0]),o=S.binaryDecode(n[0]),s=S.binaryDecode(n[1]);!i[t]&&r[t]&&u.crypto_sign_verify_detached(s,e,o.slice(1))&&(i[t]=!0)}return Object.keys(i).length/n>=.8})(n[1],e.unl_sig,t)}return!1},$=(e,t)=>{e=e.map((e=>S.deserializeValue(e))).sort();let r=p();if(r&&r[T]){r={};e.map((e=>S.stringifyValue(e))).forEach((e=>r[e]=!0)),d(r),w(0,"Updated trusted keys.")}t||m&&m.emit(c.unlChange,e)},k=async n=>{const o=e&&(N<2||y==l.json)?i.decode(n.data):n.data;let f;try{f=S.deserializeMessage(o)}catch(e){return w(1,e),w(0,"Exception deserializing: "),w(0,o||n),void(N<2&&this.close())}let a=!1;N<2?a=(e=>{if(0==N&&"user_challenge"==e.type&&e.hp_version&&e.contract_id){if(!e.hp_version.startsWith(t))return w(1,`Incompatible HotPocket server version. Expected:0.6.* Got:${e.hp_version}`),!1;if(!e.contract_id)return w(1,"Server did not specify contract id."),!1;if(r&&e.contract_id!=r)return w(1,`Contract id mismatch. Expected:${r} Got:${e.contract_id}`),!1;if(!e.contract_version)return w(1,"Server did not specify contract version."),!1;if(s&&e.contract_version!=s)return w(1,`Contract version mismatch. Expected:${s} Got:${e.contract_version}`),!1;A=e.contract_id,E=e.contract_version,v=B(u.randombytes_buf(16));const n=S.createUserChallengeResponse(e.challenge,v,y);return V(S.serializeObject(n)),N=1,!0}if(1==N&&v&&"server_challenge_response"==e.type&&e.sig&&e.pubkey){const t=p();if(t&&!t[e.pubkey])return w(1,`${h} is not among the trusted servers.`),!1;const r=v+A+E,n=e.pubkey.substring(2);return u.crypto_sign_verify_detached(b(e.sig),r,b(n))?(clearTimeout(I),I=null,v=null,T=e.pubkey,N=2,$(e.unl,!0),S.useProtocol(y),x&&x(!0),w(0,`Connected to ${h}`),!0):(w(1,`${h} challenge response verification failed.`),!1)}return w(1,`${h} invalid message during handshake. Connection status:${N}`),w(0,e),!1})(f):2==N&&(a=(e=>{if("contract_read_response"==e.type){const t=e.reply_for,r=M[t];r&&(clearTimeout(r.timer),r.resolver(S.deserializeValue(e.content)),delete M[t])}else if("contract_input_status"==e.type){const t=S.stringifyValue(e.input_hash),r=j[t];if(r){const n={status:e.status};"accepted"==e.status?(n.ledgerSeqNo=e.ledger_seq_no,n.ledgerHash=S.deserializeValue(e.ledger_hash)):n.reason=e.reason,r(n),delete j[t]}}else if("contract_output"==e.type){if(m){const t=p();!t||z(e,t)?m.emit(c.contractOutput,{ledgerSeqNo:e.ledger_seq_no,ledgerHash:S.deserializeValue(e.ledger_hash),outputHash:S.deserializeValue(e.output_hash),outputs:e.outputs.map((e=>S.deserializeValue(e)))}):w(1,"Output validation failed.")}}else if("stat_response"==e.type)R.forEach((t=>{t({hpVersion:e.hp_version,ledgerSeqNo:e.ledger_seq_no,ledgerHash:S.deserializeValue(e.ledger_hash),voteStatus:e.vote_status,roundTime:e.round_time,contractExecutionEnabled:e.contract_execution_enabled,readRequestsEnabled:e.read_requests_enabled,isFullHistoryNode:e.is_full_history_node,weaklyConnected:e.weakly_connected,currentUnl:e.current_unl.map((e=>S.deserializeValue(e))),peers:e.peers})})),R=[];else if("lcl_response"==e.type)L.forEach((t=>{t({ledgerSeqNo:e.ledger_seq_no,ledgerHash:S.deserializeValue(e.ledger_hash)})})),L=[];else if("unl_change"==e.type)$(e.unl,!1);else if("ledger_event"==e.type){const t={event:e.event};"ledger_created"==t.event?t.ledger=S.deserializeLedger(e.ledger):"vote_status"==t.event&&(t.voteStatus=e.vote_status),m.emit(c.ledgerEvent,t)}else if("health_event"==e.type){const t=S.deserializeHealthEvent(e);m.emit(c.healthEvent,t)}else{if("ledger_query_result"!=e.type)return w(1,"Received unrecognized contract message: type:"+e.type),!1;{const t=P[e.reply_for];if(t){const r=e.results.map((e=>{const t=S.deserializeLedger(e);return e.inputs&&(t.inputs=e.inputs.map((e=>({pubkey:S.deserializeValue(e.pubkey),hash:S.deserializeValue(e.hash),nonce:e.nonce,blob:S.deserializeValue(e.blob)})))),e.outputs&&(t.outputs=e.outputs.map((e=>({pubkey:S.deserializeValue(e.pubkey),hash:S.deserializeValue(e.hash),blobs:e.blobs.map((e=>S.deserializeValue(e)))})))),t}));"seq_no"==t.type&&t.resolver(r.length>0?r[0]:null),delete P[e.reply_for]}}}return!0})(f)),a||N<2&&this.close()},J=()=>{D.addEventListener("message",k),D.addEventListener("close",q),I=setTimeout((()=>{this.close(),I=null}),g)},q=()=>{w(0,U?"Closing connection to "+h:"Disconnected from "+h),m=null,I&&(clearTimeout(I),I=null),x&&x(!1),x=null,R.forEach((e=>e(null))),R=[],Object.values(j).forEach((e=>e({status:"failed",reason:"connection_error"}))),j={},Object.values(M).forEach((e=>{clearTimeout(e.timer),e.resolver(null)})),M={},this.onClose&&this.onClose(),U&&U()},F=e=>{x&&x(!1)},V=e=>{O(e)?D.send(n.encode(e)):D.send(e)};this.isConnected=()=>2==N,this.connect=()=>(w(0,"Connecting to "+h),new Promise((t=>{D=e?new o(h):new o(h,{rejectUnauthorized:!1}),e&&(D.binaryType="arraybuffer"),x=t,D.addEventListener("error",F),D.addEventListener("open",J)}))),this.close=()=>D.readyState==o.OPEN?new Promise((e=>{U=e,D.close()})):(D.close(),Promise.resolve()),this.getStatus=()=>{if(2!=N)return Promise.resolve(null);const e=new Promise((e=>{R.push(e)}));if(1==R.length){const e=S.createStatusRequest();V(S.serializeObject(e))}return e},this.getLcl=()=>{if(2!=N)return Promise.resolve(null);const e=new Promise((e=>{L.push(e)}));if(1==L.length){const e=S.createLclRequest();V(S.serializeObject(e))}return e},this.submitContractInput=async(e,t,r,n)=>{if(2!=N)throw"Connection error.";if(0==r)throw"Max ledger seq no. or offset cannot be 0.";if(!n&&!r)throw"Max ledger seq. no not specified.";if(t&&(!Number.isInteger(t)||t<=0))throw"Input nonce must be a positive integer.";if(t||(t=(new Date).getTime()),n){r||(r=10);const e=await this.getLcl();if(!e)throw"Error retrieving last closed ledger.";r+=e.ledgerSeqNo}const i=S.createContractInputComponents(e,t,r),o=S.stringifyValue(i.hash),u=new Promise((e=>{j[o]=e})),s=S.createContractInputMessage(i.container,i.sig);return V(S.serializeObject(s)),{hash:S.binaryEncode(i.hash),submissionStatus:u}},this.submitContractReadRequest=(e,t,r)=>2!=N?Promise.resolve():new Promise((n=>{const i=setTimeout((()=>{n(null),delete M[t]}),r);M[t]={resolver:n,timer:i};const o=S.createReadRequest(e,t);V(S.serializeObject(o))})),this.subscribe=e=>{if(2!=N)return Promise.resolve();const t=S.createSubscriptionRequest(e,!0);return V(S.serializeObject(t)),Promise.resolve()},this.unsubscribe=e=>{if(2!=N)return Promise.resolve();const t=S.createSubscriptionRequest(e,!1);return V(S.serializeObject(t)),Promise.resolve()},this.getLedgerBySeqNo=(e,t,r)=>{if(2!=N)return Promise.resolve(null);const n={seq_no:S.serializeNumber(e)},i=S.createLedgerQuery("seq_no",n,t,r),o=new Promise((e=>{P[i.id]={type:"seq_no",resolver:e}}));return V(S.serializeObject(i)),o}}function _(e,t){this.useProtocol=e=>{t=e},this.binaryEncode=e=>t==l.json?B(e):Buffer.isBuffer(e)?e:Buffer.from(e),this.binaryDecode=e=>t==l.json?b(e):new Uint8Array(e.buffer),this.serializeObject=e=>t==l.json?JSON.stringify(e):s.serialize(e),this.deserializeMessage=e=>t==l.json?JSON.parse(e):s.deserialize(e),this.serializeInput=e=>t==l.json?O(e)?e:e.toString():Buffer.isBuffer(e)?e:Buffer.from(e),this.deserializeValue=e=>t==l.json?e:e.buffer,this.serializeNumber=e=>t==l.json?e:s.Long.fromNumber(e),this.stringifyValue=e=>{if(O(e))return e;if(e instanceof Uint8Array)return B(e);if(e.buffer)return B(new Uint8Array(e.buffer));throw"Cannot stringify signature."},this.spreadArrayField=e=>t==l.json?e:e.map((e=>e.buffer)),this.createUserChallengeResponse=(t,r,n)=>{const i=u.crypto_sign_detached(t,e.privateKey.slice(1));return{type:"user_challenge_response",sig:this.binaryEncode(i),pubkey:this.binaryEncode(e.publicKey),server_challenge:r,protocol:n}},this.createContractInputComponents=(t,r,n)=>{if(0==t.length)return null;const i={input:this.serializeInput(t),nonce:this.serializeNumber(r),max_ledger_seq_no:this.serializeNumber(n)},o=this.serializeObject(i),s=u.crypto_sign_detached(o,e.privateKey.slice(1));return{hash:new Uint8Array(f.hash(s)),container:o,sig:s}},this.createContractInputMessage=(e,t)=>({type:"contract_input",input_container:e,sig:this.binaryEncode(t)}),this.createReadRequest=(e,t)=>0==e.length?null:{type:"contract_read_request",id:t,content:this.serializeInput(e)},this.createStatusRequest=()=>({type:"stat"}),this.createLclRequest=()=>({type:"lcl"}),this.createSubscriptionRequest=(e,t)=>({type:"subscription",channel:e,enabled:t}),this.createLedgerQuery=(e,t,r,n)=>{const i=[];return r&&i.push("inputs"),n&&i.push("outputs"),{type:"ledger_query",id:"query_"+e+"_"+(new Date).getTime().toString(),filter_by:e,params:t,include:i}},this.deserializeLedger=e=>({seqNo:e.seq_no,timestamp:e.timestamp,hash:this.deserializeValue(e.hash),prevHash:this.deserializeValue(e.prev_hash),stateHash:this.deserializeValue(e.state_hash),configHash:this.deserializeValue(e.config_hash),userHash:this.deserializeValue(e.user_hash),inputHash:this.deserializeValue(e.input_hash),outputHash:this.deserializeValue(e.output_hash)}),this.deserializeHealthEvent=e=>"proposal"===e.event?{event:e.event,commLatency:e.comm_latency,readLatency:e.read_latency,batchSize:e.batch_size}:"connectivity"===e.event?{event:e.event,peerCount:e.peer_count,weaklyConnected:e.weakly_connected}:void 0}function b(e){return new Uint8Array(e.match(/.{1,2}/g).map((e=>parseInt(e,16))))}function B(e){return e.reduce(((e,t)=>e+t.toString(16).padStart(2,"0")),"")}function O(e){return"string"==typeof e||e instanceof String}function m(){const e={};this.on=(t,r)=>{e[t]||(e[t]=[]),e[t].push(r)},this.emit=(t,...r)=>{e[t]&&e[t].forEach((e=>e(...r)))},this.clear=t=>{t?delete e[t]:Object.keys(e).forEach((t=>delete e[t]))}}async function S(){if(e?u||(u=window.sodium||await new Promise((e=>{window.sodium={onload:async t=>e(t)}}))):(u||(u=require("libsodium-wrappers")),await u.ready),!u)throw"Sodium reference not found. Please include sodium js lib in browser scripts."}let N=null;let v=null;function A(e){a=e}function w(e,t){e>=a&&console.log(t)}e?window.HotPocket={generateKeys:p,createClient:d,events,notificationChannels:h,protocols:l,setLogLevel:A}:module.exports={generateKeys:p,createClient:d,events,notificationChannels:h,protocols:l,setLogLevel:A}})();
index.html

Code: Select all

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
  <title>Evernode WebCluster Builder</title>
  <style>
   :root {
  --primary: #4f46e5;
  --primary-light: #6366f1;
  --gray-light: #f3f4f6;
  --gray: #e5e7eb;
  --gray-dark: #6b7280;
  --text: #111827;
  --bg: #f9fafb;
  --danger: #dc2626;
}

body {
  font-family: 'Segoe UI', sans-serif;
  background: var(--bg);
  margin: 0;
  padding: 2rem;
  color: var(--text);
}
body.dark {
  --primary: #6366f1;
  --primary-light: #818cf8;
  --gray-light: #1f2937;
  --gray: #374151;
  --gray-dark: #d1d5db;
  --text: #f9fafb;
  --bg: #111827;
  --danger: #f87171;
  background-color: var(--bg);
  color: var(--text);
}
body.dark #hpinfoForm input[type="text"],
body.dark #hpinfoForm input[type="number"] {
  background-color: #1f2937 !important;
  color: #f9fafb !important;
  border-color: var(--gray);
}
body.dark #infoPopup,
body.dark #usersTab,
body.dark #managerInfoPopup,
body.dark #clusterInfoPopup,
body.dark #clusterSettingsInfoPopup,
body.dark #sidedishInfoPopup,
body.dark #apiInfoPopup,
body.dark #privatevariablesInfoPopup,
body.dark #nodejsInfoPopup,
body.dark #clusterOpsInfoPopup,
body.dark #morefunctionsInfoPopup {color:#000!important;}
body.dark #morefunctionsTab h1 {color:#000!important;}
body.dark .nodejsTab
body.dark #uploadfuncsdiv h2 {color:#000!important;}
body.dark #funcversions h2 {color:#000!important;}
body.dark .rendered-html p, body.dark .rendered-html {color:#000;}
body.dark #clusterTree li span {color:#FFF!important;}
body.dark pre#hpLog,
body.dark pre#fileContent,
body.dark pre#response,
body.dark pre#nodeStdout,
body.dark pre#nodeStderr,
body.dark pre#stdoutLog,
body.dark pre#stderrLog {
  color: black !important;
  background: #f3f4f6 !important; /* Optional: light background for readability */
}
body.dark .tab-nav button.active {
  background-color: black !important;
  color: white !important;
}

body.dark .card,
body.dark .section,
body.dark .popup-content,
body.dark .dropzone,
body.dark input,
body.dark textarea,
body.dark select,
body.dark .file-item,
body.dark .tab-nav button {
  background: #1f2937;
  color: var(--text);
  border-color: var(--gray);
}

body.dark input:read-only,
body.dark textarea:fullscreen {
  background: #374151;
}

body.dark .highlight-layer {
  background: #1f2937;
  color: var(--text);
}

body.dark .slider {
  background-color: #4b5563;
}

body.dark .popup-content button {
  background-color: #4f46e5;
}

body.dark .popup-content button:hover {
  background-color: #6366f1;
}

.wrapper {
  max-width: 1000px;
  margin: 0 auto;
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

.card {
  background: #fff;
  padding: 2rem;
  border-radius: 1rem;
  box-shadow: 0 10px 25px rgba(0, 0, 0, 0.05);
}

h1, h2, h3 {
  margin-bottom: 1rem;
}

input, button, textarea, select {
  font-size: 1rem;
  padding: 0.6rem 0.8rem;
  border-radius: 0.5rem;
  border: 1px solid var(--gray);
  width: 100%;
  box-sizing: border-box;
  margin-bottom: 1rem;
}

input:read-only {
  background: #f0f0f0;
}
textarea:fullscreen {
  width: 100vw !important;
  height: 100vh !important;
  font-size: 16px;
  border: none;
  margin: 0;
  padding: 10px;
  box-sizing: border-box;
}

button {
  background: var(--primary);
  color: white;
  border: none;
  cursor: pointer;
  transition: background 0.3s;
}

button:hover {
  background: var(--primary-light);
}
.confirm-delete {
  background-color: green;
  color: white;
}

.tab-nav {
  display: flex;
  gap: 0.5rem;
  margin-bottom: 1rem;
  flex-wrap: wrap;
  flex-direction: row; 
}

@media (max-width: 768px) {
  .tab-nav {
    flex-direction: column;
    align-items: stretch;
  }
}

.tab-nav button {
  background: var(--gray);
  color: var(--text);
  padding: 0.6rem 1rem;
  border-radius: 0.5rem;
  font-weight: 500;
  width: auto;
}

@media (max-width: 768px) {
  .tab-nav button {
    width: 100%;
  }
}

.tab-nav button.active {
  background: var(--primary);
  color: white;
}

.grid {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 2rem;
  align-items: start;
}

.section {
  background: var(--gray-light);
  border: 1px solid var(--gray);
  padding: 1rem;
  border-radius: 0.75rem;
}

.tree ul {
  list-style: none;
  padding-left: 1rem;
  margin-left: 0.5rem;
  border-left: 1px solid #ddd;
}

.tree li {
  margin: 0.5rem 0;
}

.tree span {
  cursor: pointer;
  padding: 0.3rem 0.6rem;
  display: inline-block;
}

.file-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background: white;
  border: 1px solid var(--gray);
  padding: 0.5rem;
  margin-bottom: 0.5rem;
  border-radius: 0.5rem;
}

.file-item button {
  background: var(--danger);
  margin-left: 0.5rem;
}

.dropzone {
  border: 2px dashed var(--primary);
  color: var(--primary);
  padding: 2rem;
  text-align: center;
  border-radius: 0.5rem;
  cursor: pointer;
  transition: background 0.3s;
}

.dropzone:hover {
  background: #eef2ff;
}

.switch-block {
  margin-top: 1.5rem;
}

.switch {
  position: relative;
  display: inline-block;
  width: 50px;
  height: 28px;
}

.switch input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  inset: 0;
  background-color: #ccc;
  border-radius: 28px;
  transition: 0.3s;
}

.slider:before {
  position: absolute;
  content: "";
  height: 20px;
  width: 20px;
  background: white;
  left: 4px;
  top: 4px;
  border-radius: 50%;
  transition: 0.3s;
}

.switch inputhecked + .slider {
  background: var(--primary);
}

.switch inputhecked + .slider:before {
  transform: translateX(22px);
}

.message {
  margin-top: 1rem;
  font-weight: 500;
  color: green;
  max-height: 300px;
  overflow-y: auto;
  background: #ecfdf5;
  padding: 1rem;
  border-radius: 0.5rem;
  font-size: 0.95rem;
}
.filemessage {
  margin-top: 1rem;
  font-weight: 500;
  color: green;
  max-height: 300px;
  overflow-y: auto;
  background: #ecfdf5;
  padding: 1rem;
  border-radius: 0.5rem;
  font-size: 0.95rem;
}
pre {
  background: #f3f4f6;
  padding: 1rem;
  border-radius: 0.5rem;
  font-size: 0.85rem;
  overflow-x: auto;
}

.popup-overlay {
 position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.7);
display: flex;
justify-content: center;
align-items: center;
z-index: 1000;
opacity: 0;
visibility: hidden;
transition: opacity 0.3s ease, visibility 0.3s ease;
}

.popup-overlay.active {
opacity: 1;
visibility: visible;
}

.popup-content {
background: #fff;

max-width: 800px;
width: 90%;
padding: 20px;
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.3);
transform: scale(0.7);
transition: transform 0.3s ease;
max-height: 80vh;
overflow-y: auto;
}

.popup-overlay.active .popup-content {
transform: scale(1);
}

.popup-content h2 {
margin: 0 0 15px;
font-size: 24px;
color: #333;
text-align: center;
font-weight: bold;
}

.popup-content .terms {
font-size: 14px;
color: #555;
line-height: 1.6;
margin-bottom: 20px;
}
.popup-content button {
display: block;
width: 100%;
padding: 12px;
background: #000;
color: #fff;
border: none;
border-radius: 8px; 
font-size: 16px;
cursor: pointer;
transition: background 0.3s ease, transform 0.2s ease;
}
.card-content:focus {
  outline: 2px dashed #007bff;
  background-color: #f9f9f9;
}

.popup-content button:hover {
background: #333; 
transform: translateY(-2px);
}

.popup-content button:active {
transform: translateY(0);
}

.popup-content::-webkit-scrollbar {
width: 8px;
}

.popup-content::-webkit-scrollbar-track {
background: #f1f1f1;
}

.popup-content::-webkit-scrollbar-thumb {
background: #000;
border-radius: 4px;
}

#morefunctionsTab .card {
  background: #fff;
  border: 1px solid #ddd;
  padding: 1rem;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.05);
  margin-bottom: 1rem;
}

.card-content:focus {
  outline: 2px dashed #007bff;
  background-color: #f9f9f9;
}
#morefunctionsTab .card button {
  margin-top: 8px;
}

.editor-wrapper {
  position: relative;
  width: 100%;
  height: 300px;
  font-family: monospace;
  font-size: 14px;
  line-height: 1.5;
}

.highlight-layer,
#editor {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  padding: 8px;
  margin: 0;
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
  white-space: pre-wrap;
  word-wrap: break-word;
  box-sizing: border-box;
  overflow: auto;
}

.highlight-layer {
  color: black;
  background: white;
  z-index: 0;
  pointer-events: none;
}

#editor {
  background: transparent;
  color: transparent;
  caret-color: black;
  z-index: 1;
  border: none; /* important */
  resize: none;
}


  .tag { color: #d73a49; }
  .attr { color: #6f42c1; }
  .string { color: #032f62; }
  </style>
   <script src="hotpocket-js-client.min.js"></script>
</head>
<body>


    <div class="popup-overlay" id="termsPopup">
        <div class="popup-content">
            <h2>Terms and Conditions</h2>
            <div class="terms">
Terms and Conditions and Legal Disclaimer for Everwebhost Software
Effective Date: May 13, 2025
Welcome to Everwebhost, a software project built with Evernode Hotpocket Technology. We’re excited to share this open-source software with you! By using, copying, modifying, or sharing Everwebhost ("Software"), you agree to these Terms and Conditions and the Legal Disclaimer below. They’re designed to keep things simple, protect everyone involved, and make sure you feel confident using the Software.
1. You’re Free to Use the Software
Open-Source Freedom: Everwebhost is released under a permissive open-source license (see the COPYING file included with the Software). This means you can use, copy, modify, share, or even sell the Software for any purpose—personal, commercial, or otherwise.

Fork and Create: Feel free to fork the project, build something new, or integrate it into your own work. There are no restrictions on what you can do, as long as you follow the license terms in the COPYING file.

Safe to Use: We’ve made the Software open and accessible so you can trust it’s yours to explore and build with. The community is welcome to contribute and make it even better!

2. We’re Not Responsible for How You Use It
Your Responsibility: You’re in charge of how you use the Software. Whether you’re running it on a server, building a business, or just experimenting, you’re responsible for the results. This includes making sure it works for your needs and complies with any laws or regulations.

No Liability: The author(s) of Everwebhost (that’s us) aren’t responsible for anything that happens when you use the Software. This includes any issues, losses, or problems—technical, legal, or otherwise. You’re using it at your own risk, but we’ve made it open so you can verify and customize it to feel secure.

Feel Confident: We’re not liable because we trust you to use the Software responsibly. You have full control to test, modify, and secure it to meet your needs, which makes it a safe choice for your projects.

3. No Promises, Just Possibilities
As-Is Software: The Software comes "AS IS," with no guarantees. We don’t promise it’s perfect, bug-free, or ideal for every situation. But that’s the beauty of open-source—you can check it out, fix what you need, and make it your own.

No Support Required: We don’t have to provide updates, fixes, or support, but we hope the community (including you!) will keep the project alive and thriving.

Why This Matters: By not making promises, we keep the Software free and open for everyone. You’re empowered to make it work for you, which is why so many developers love open-source projects like this.

4. You’re in Control
Check It Yourself: You’re responsible for testing the Software to ensure it’s safe and suitable for your needs. Open-source means you can see every line of code, so you can trust what you’re using.

Third-Party Components: Everwebhost uses Evernode Hotpocket Technology and may include other third-party tools, which have their own licenses. You’ll need to check those terms (usually included with the Software) to make sure you’re compliant. We’ve made sure everything is standard and easy to follow.

Stay Legal: Make sure your use of the Software follows all relevant laws (like data privacy or intellectual property rules). This is on you, but it’s standard for any software project and nothing to worry about if you’re diligent.

5. Our Rights
We Own the Original: The Software is licensed, not sold. We (the authors) keep the intellectual property rights, but you get to use it freely under the COPYING license.

Give Credit: If you share or modify the Software, please include the copyright notice and license terms from the COPYING file. It’s a small way to keep the project open and fair for everyone.

6. If Something Goes Wrong
Stopping Use: If you don’t follow these terms or the COPYING license, your right to use the Software ends automatically. But we’re all about keeping things open, so just stick to the rules, and you’re good!

Disputes: If there’s ever a disagreement about these terms, we’ll handle it through arbitration in San Francisco, California, under the rules of the American Arbitration Association. This is a fair and standard way to resolve issues.

Laws: These terms follow the laws of California, USA. It’s a common choice for software projects and keeps things clear.

7. Keeping Things Flexible
Updates to Terms: We might update these terms in the future. If we do, we’ll make the new version available with the Software. Keep using it, and you’re agreeing to the updates.

If Something’s Off: If any part of these terms isn’t valid under the law, the rest still applies. This keeps everything fair and functional.

No Surprises: These terms, along with the COPYING file, are the full deal. No hidden rules or surprises.

Legal Disclaimer
We’re Not Liable: We, the authors, aren’t responsible for any problems, damages, or losses from using Everwebhost. Whether it’s a technical glitch, a legal issue, or something else, you’re responsible for your use of the Software. This is standard for open-source projects and helps keep the Software free for everyone.
No Guarantees: The Software has no warranties—none at all. We don’t promise it’s flawless or fits every need. But you can inspect and tweak it, which makes it a trustworthy choice for developers.
You’re Covered: You’re responsible for making sure the Software is secure and legal for your use. This includes checking Evernode Hotpocket Technology or other components. It’s easy to do, and we’ve kept everything transparent to help you feel safe.
Protecting Us: If someone makes a claim against us because of how you used the Software, you agree to cover us (this is called indemnification). It’s a standard way to ensure we’re not dragged into issues we didn’t cause.
Why You’re Safe: Open-source software like Everwebhost is used by millions because it’s transparent and flexible. You can verify the code, customize it, and join a community of users. We’ve designed these terms to protect you by giving you control and clarity.


            </div>
            <button onclick="acceptTerms()">Accept</button>
        </div>
    </div>


  <div class="wrapper">
  <div class="tab-nav">
    <button class="active" data-tab="loginTab" onclick="showTab('loginTab')">Login</button>
    <button data-tab="usersTab" onclick="showTab('usersTab');">User Management</button>
    <button data-tab="managerTab" onclick="showTab('managerTab'); loadFolders();">File Manager</button>
    <button data-tab="clusterTab" onclick="showTab('clusterTab')">Cluster Files</button>
    <button data-tab="clustersettingsTab" onclick="showTab('clustersettingsTab'); loadHPInfo();">Cluster Settings</button>
    <button data-tab="sidedishTab" onclick="showTab('sidedishTab');">Side Dish</button>
    <button data-tab="apiTab" onclick="showTab('apiTab'); refreshNodeEndpoints(); loadApiModeAndRefresh();">API Endpoints</button>
    <button data-tab="privatevariablesTab" onclick="showTab('privatevariablesTab');">Instance Variables</button>
    <button data-tab="nodejsTab" onclick="showTab('nodejsTab'); setTimeout(() => { fetchAvailableModules(); fetchExtraModules(); }, 100);">Node JS</button>    
    <button data-tab="clusteroperationsTab" onclick="showTab('clusteroperationsTab');">Cluster Logs</button>
    <button data-tab="morefunctionsTab" onclick="showTab('morefunctionsTab'); loadMoreFunctions(); ">More Functions</button>
<div id="darkModeToggleWrapper" style="position: fixed; top: 1rem; right: 1rem; z-index: 2000; display: flex; align-items: center; gap: 0.5rem;">
  <span id="themeIcon" style="font-size: 1.25rem;">🌞</span>
  <label class="switch">
    <input type="checkbox" id="darkModeToggle" onchange="toggleDarkMode()" />
    <span class="slider"></span>
  </label>

</div>

  </div>

<div id="morefunctionsTab" style="display: none; position: relative;">
<div class="card" style="position: relative;">
  <h1 style="margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center;">
    <span>More Functions</span>
    <span style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem;">
      <span>Use Cluster File</span>
      <label class="switch">
        <input type="checkbox" id="toggleClusterMoreFunctions" onchange="toggleClusterMoreFunctions()" />
        <span class="slider"></span>
      </label>
    </span>
  </h1>

  <div class="message" id="clusterMoreStatus" style="font-size: 0.85rem; color: #777;"></div>
</div>


  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('morefunctionsInfoPopup').style.display = 
       (document.getElementById('morefunctionsInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>


  <div id="morefunctionsInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>More Functions:</strong><br>
    - These are custom HTML cards rendered into the page.<br>
    - Use them to add admin UI, buttons, logs, or anything you want.<br>
    - HTML is live — forms, inputs, and scripts work instantly.<br>
    - Edit and save content to make permanent changes.
  </div>


  <div class="card" style="padding-top:24px;">
    <div id="cards-area" style="padding-top:24px;"></div>
    <button onclick="addCard()" style="margin-top: 1rem;" id="addMoreCardBtn">Add Card</button>
  </div>
<div id="uploadfuncsdiv" class="card" style="margin-top: 24px;">
  <h2>Upload functions to Cluster</h2>
  <button onclick="uploadMoreFunctionsJson()">Upload functions to cluster</button>
  <div class="message" id="moreuploadlog"></div>
</div>
<div id="funcversions" class="card" style="margin-top: 24px;">
  <h2>Function Versions</h2>
  <button onclick="checkMoreFunctionVersions()">Check Versions</button>
  <div id="moreVersionList" style="margin-top: 1rem;"></div>
</div>

</div>


<div id="apiTab" style="display: none; position: relative;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('apiInfoPopup').style.display = (document.getElementById('apiInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>

  <div id="apiInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>Node.js Tab Help:</strong><br>
    - This tab allows you to dynamically register API endpoints in your Node.js backend.<br>
    - Use the refresh button to list all currently active endpoints.<br>
    - To add a new route, enter the path (e.g. <code>/hello</code>) and the handler code.<br>
    - The code should define a function <code>handler(req, res)</code>.<br>
    - The endpoint is immediately live once added.<br>
    - To create an endpoint that requires a password, just check the require auth checkbox.
  </div>

  <div class="card" style="position: relative;">
  <h1 style="margin-bottom: 1rem; display: flex; justify-content: space-between; align-items: center;">
    <span>Node.js Endpoints</span>
    <span style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem;">
      <span>Use Cluster APIs</span>
      <label class="switch">
        <input type="checkbox" id="toggleClusterApi" onchange="toggleClusterApi()" />
        <span class="slider"></span>
      </label>
    </span>
  </h1>

  <button onclick="refreshNodeEndpoints()">Refresh Endpoints</button>
  <div id="nodejsRoutes" style="margin-top: 1rem;"></div>
</div>

<div class="card" id="editApiCard" style="display: none;margin-top:24px;">
  <h2>Edit API Endpoint</h2>

  <input type="text" id="editApiPath" placeholder="/your-endpoint" readonly />

  <select id="editApiMethod" style="display:none;max-width: 120px; margin: 8px 0;">
    <option value="get">GET</option>
    <option value="post">POST</option>
    <option value="put">PUT</option>
    <option value="delete">DELETE</option>
  </select>

<div style="margin-bottom: 1rem;">
  <label for="editApiAuthRequired" style="display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem; margin: 0;">
    <input type="checkbox" id="editApiAuthRequired" style="width: auto; margin: 0;" />
    Require auth?
  </label>
</div>


  <textarea id="editApiCode" placeholder="function handler(req, res) {res.json({ status: 'ok', message: 'Your dynamic API is working!' });}" style="height: 150px;"></textarea>

  <button id="apieditbtn" onclick="saveEditedEndpoint()">Save Changes</button>
  <button id="apicancelbtn" onclick="cancelApiEdit()">Cancel</button>
  <div class="message" id="editApiLog"></div>

  
</div>



 <div class="card" style="margin-top:24px;">
  <h2>Add New API Endpoint</h2>

 <div style="display: flex; gap: 1rem; align-items: center; margin-bottom: 0.5rem; flex-wrap: wrap;">
  <select id="apiMethod" style="max-width: 120px;">
    <option value="get">GET</option>
    <option value="post">POST</option>
    <option value="put">PUT</option>
    <option value="delete">DELETE</option>
  </select>

  <input type="text" id="apiPath" placeholder="/your-endpoint" style="flex: 1;" />
</div>

<div style="margin-bottom: 1rem; display: flex; align-items: center; gap: 0.5rem; font-size: 0.95rem;">
  <input type="checkbox" id="apiAuthRequired" style="width: auto; margin: 0;" />
  <label for="apiAuthRequired" style="margin: 0;">Require auth?</label>
</div>


  <textarea id="apiCode" placeholder="res.end('hello world');" style="height: 150px;"></textarea>

  <button id="apiSubmitBtn" onclick="addNodeEndpoint()">Add Endpoint</button>

  <div class="message" id="nodejsLog"></div>
</div>



<div class="card" style="margin-top:24px;">
  <h2>Push Endpoints</h2>
  <button onclick="pushDynamicApiVersion()">Push endpoints to cluster</button>
  <div class="message" id="dynamicApiPushLog"></div>
</div>

<div class="card" style="margin-top:24px;">
  <h2>API Version History</h2>
  <button onclick="checkApiVersions()">Check API Versions</button>
  <div id="apiVersionList" style="margin-top: 1rem;"></div>

<div class="message" id="apivmessage" style="max-height:350px;overflow-y:scroll;"></div>
</div>
</div>


<div class="card" id="privatevariablesTab" style="display: none; position: relative;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('privatevariablesInfoPopup').style.display =
         (document.getElementById('privatevariablesInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>

  <div id="privatevariablesInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>Instance Variables Help:</strong><br>
    - Add variables with a unique key.<br>
    - The prefix <code>extravar.</code> is automatically enforced.
  </div>

  <h2 style="margin-bottom: 16px;">Instance Variables</h2>

  <div style="display: flex; gap: 8px; margin-bottom: 16px; flex-wrap: wrap;">
    <div style="display: flex; align-items: center; gap: 4px;">
      <span style="font-weight: bold;">extravar.</span>
      <input id="newVarName" placeholder="yourKey" style="padding: 8px 10px; border: 1px solid #ccc; border-radius: 4px; min-width: 120px;" />
    </div>
    <input id="newVarValue" placeholder="Variable value" style="padding: 8px 10px; border: 1px solid #ccc; border-radius: 4px; min-width: 180px;" />
    <button onclick="addPrivateVariable()" style="padding: 8px 16px; background-color: #007bff; color: white; border: none; border-radius: 4px; cursor: pointer;">
      Add Variable
    </button>
  </div>

  <div id="privateVarList" style="margin-top: 1rem;"></div>
  <div class="message" id="privateVarMessage" style="max-height: 300px; overflow-y: auto; margin-top: 10px;"></div>

</div>

<div id="clusterTab" style="display:none;">
  <div class="card" id="card" style="padding: 16px; border-radius: 8px; position: relative;">
    <h1 style="margin-top: 0;">Browse Cluster Files</h1>
    <button onclick="loadClusterFiles()" style="width: 100%;">Load Cluster Files</button>
    <div id="clusterTree" style="margin-top: 1rem;">Not loaded yet.</div>
    <div id="clusterFileView" class="section" style="margin-top: 1rem;"></div>


    <div style="position: absolute; top: 8px; right: 8px; cursor: pointer; font-size: 18px;" onclick="document.getElementById('clusterInfoPopup').style.display = (document.getElementById('clusterInfoPopup').style.display === 'block') ? 'none' : 'block';">❓</div>


    <div id="clusterInfoPopup" style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9;padding: 10px; border-radius: 6px; font-size: 12px; width: 240px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
      <strong>Cluster Info:</strong><br>
      Use this section to view and interact with files stored across the cluster.<br><br>
      Click "Load Cluster Files" to fetch the directory structure. You can then preview, edit, or manage files.
    </div>
  </div>
</div>

  <div id="sidedishTab" style="display: none; position: relative;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('sidedishInfoPopup').style.display = (document.getElementById('sidedishInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>
  <div id="sidedishInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>Side Dish Info:</strong><br>
    - This is a side dish to the hotpocket contract, it allows you to write your own smart contract code, and then submit it for voting. If all instances in your cluster vote yes on it (based on the quorum), then the code will go live and be a part of the consensus. You can always vote for an other version of the code, but if it breaks the system then that is obviously a problem.
    - Fetch the current `sidedish.js` from the cluster to view or edit.<br>
    - Use "Edit Local" to make changes, then "Save" to keep them locally.<br>
    - Push your version to the cluster when ready.<br>
    - Use version control below to activate or delete cluster versions.
  </div>
  <div class="card" id="card">
    <h1>Side Dish</h1>

    <div style="margin-bottom: 1rem;">
     <!-- <button onclick="fetchSidedishFromCluster()">Fetch sidedish.js from Cluster</button> -->
      <button onclick="loadLocalSidedish()">Edit Local sidedish.js</button>
    </div>

    <div id="sidedishEditor" style="display:none;">
      <textarea id="sidedishTextArea" style="width:100%; height:300px;"></textarea>
      <button onclick="saveLocalSidedish()">Save sidedish.js Locally</button>
      <button onclick="disableButtonTemporarily(this, () => uploadSidedishJS())">Push sidedish.js to cluster</button>
      <div class="message" id="sidedishUploadLog"></div>
    </div>

    <div class="card">
      <h2>Side Dish Versions</h2>
      <button onclick="listSidedishVersions()">Check Versions</button>
      <div id="sidedishVersionList" style="margin-top: 1rem;"></div>
    </div>
  </div>
</div>

  <div id="usersTab" style="display: none; position: relative;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('userTabInfoPopup').style.display = (document.getElementById('userTabInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>
  <div id="userTabInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>User Management Info:</strong><br>
    - Use this tab to add, remove, or update users.<br>
    - The default user is <code>admin</code>; it cannot be removed.<br>
    - Changing a password requires the current one.<br>
    - Be sure passwords match before submitting.
  </div>

  <div class="card" id="userAdminCard">
    <h2>User Management</h2>

    <h3>Add User</h3>
    <input type="text" id="addUsername" placeholder="Username" />
    <input type="password" id="addPassword" placeholder="Password" />
    <button onclick="addUser()">Add User</button>

    <h3>Remove User</h3>
    <input type="text" id="removeUsername" placeholder="Username (cannot be 'admin')" />
    <button onclick="removeUser()">Remove User</button>

    <h3>Change Password</h3>
    <input type="text" id="changeUser" placeholder="Username" />
    <input type="password" id="oldPassword" placeholder="Old Password" />
    <input type="password" id="newPassword" placeholder="New Password" />
    <input type="password" id="newPasswordConfirm" placeholder="Confirm New Password" />
    <button onclick="changePassword()">Change Password</button>
  </div>
</div>

<div id="clusteroperationsTab" style="display: none; position: relative;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('clusterOpsInfoPopup').style.display = (document.getElementById('clusterOpsInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>
  <div id="clusterOpsInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>Cluster Operations Info:</strong><br>
    - Use this panel to monitor live logs from the node.<br>
    - Toggle "Trace Logs Live" to stream logs in real time.<br>
    - <code>hp.log</code>: Contract events and internal logic<br>
    - <code>stdout.log</code> & <code>stderr.log</code>: Output from your contract<br>
    - For performance, avoid keeping live trace on unless needed.
  </div>

  <div class="card" id="card">
    <h1>Operate Cluster</h1>
    
    <h2>Live Logs</h2>
    <div class="switch-block">
      <label class="switch">
        <input type="checkbox" id="logTraceToggle" onchange="toggleLogTrace()" />
        <span class="slider"></span>
      </label>
      <div class="switch-label">Trace Logs Live</div>
    </div>
    
    <div style="display: flex; gap: 1rem; flex-wrap: wrap;">
      <div class="section" style="flex: 1; max-width: 900px;">
        <h3>hp.log</h3>
        <pre id="hpLog" style="max-height: 300px; overflow-y: scroll; background: #eee;"></pre>
      </div>
      <div class="section" style="flex: 1; max-width: 900px;">
        <h3>stdout.log</h3>
        <pre id="stdoutLog" style="max-height: 300px; overflow-y: scroll; background: #eee;"></pre>
      </div>
      <div class="section" style="flex: 1; max-width: 900px;">
        <h3>stderr.log</h3>
        <pre id="stderrLog" style="max-height: 300px; overflow-y: scroll; background: #eee;"></pre>
      </div>
    </div>

</div>
</div>


<div id="nodejsTab" class="card" style="display: none; position: relative; padding: 16px;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('nodejsInfoPopup').style.display = (document.getElementById('nodejsInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>
  <div id="nodejsInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>Node JS Info:</strong><br>
    - Install new packages with npm<br>
    - Add packages to nodejs (require)<br>
    - Check nodejs server logs
  </div>

  <h2>Install NPM Package (Locally)</h2>
  <form id="installForm" style="margin-bottom: 32px;">
    <label for="package">Package Name:</label>
    <input type="text" id="package" name="package" placeholder="e.g. express" required />
    <br><br>
    <label for="version">Version (optional):</label>
    <input type="text" id="version" name="version" placeholder="e.g. 4.18.2" />
    <br><br>
    <button type="submit" style="background:#000;">Install</button>
  </form>
  <pre id="response" style="margin-top: 20px;"></pre>

  <h2>Uninstall NPM Package (Locally)</h2>
  <form id="uninstallForm" style="margin-bottom: 32px;">
    <label for="unpkg">Package Name:</label>
    <input type="text" id="unpkg" name="unpkg" placeholder="e.g. express" required />
    <br><br>
    <button type="submit" style="background:#000;">Uninstall</button>
  </form>
  <pre id="unresponse" style="margin-top: 20px;"></pre>

  <h2>Node.js Logs</h2>
  <div class="switch-block" style="margin-bottom: 16px;">
    <label class="switch">
      <input type="checkbox" id="nodeLogToggle" onchange="toggleNodeLogs()" />
      <span class="slider"></span>
    </label>
    <div class="switch-label">Show Node.js Logs</div>
  </div>

  <div class="section" style="flex: 1; max-width: 900px; margin-bottom: 24px;">
    <h3>webadmin.out.log</h3>
    <pre id="nodeStdout" style="max-height: 300px; overflow-y: scroll; background: #eee;"></pre>
  </div>

  <div class="section" style="flex: 1; max-width: 900px;">
    <h3>webadmin.err.log</h3>
    <pre id="nodeStderr" style="max-height: 300px; overflow-y: scroll; background: #eee;"></pre>
  </div>

  <h2>Manage Extra Node Modules</h2>
  <div style="display: flex; gap: 40px; flex-wrap: wrap; margin-bottom: 32px;">
    <div style="flex: 1; min-width: 300px;">
      <h3>Available Modules</h3>
      <ul id="availableModules" style="list-style: none; padding-left: 0; margin: 0;max-height:350px;overflow-y:scroll;"></ul>
    </div>

    <div style="flex: 1; min-width: 300px;">
      <h3>Extra Modules (Loaded)</h3>
      <ul id="extraModules" style="list-style: none; padding-left: 0; margin: 0;"></ul>
    </div>
  </div>

</div>




<div id="clustersettingsTab" style="display: none; position: relative;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('clusterSettingsInfoPopup').style.display = (document.getElementById('clusterSettingsInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>

  <div id="clusterSettingsInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>Cluster Settings Help:</strong><br>
    - Use the red/green buttons to stop or restart HotPocket.<br>
    - Import or generate a keypair for this node.<br>
    - Update UNL (validators), known peers, and round settings.<br>
    - Public keys and ports are auto-filled; no need to edit them manually.<br>
    - The most reliable way to create a cluster is to configure one instance as the primary node (just a temporare thing) by adding the public keys of all other instances to its UNL. On the remaining instances, set only the primary node’s public key in their UNL, and include its contract ID, mesh address, and port as a known peer.<br>
    <b>Note:</b> You must restart each instance after adding or removing entries from the UNL, and if you want to import a keypair you need to stop Evernode first.

  </div>

  <div class="card" id="card">
    <h1>Cluster Settings</h1>
    <button onclick="stopHotPocket()" style="background: #dc2626; max-width:300px; padding:6px; font-size:12px;">Stop Evernode (HP)</button>
    <button style="background: #0e8532; max-width:300px; padding:6px; font-size:12px;" onclick="restartHotPocket()">Restart Evernode (HP)</button>

    <form id="hpinfoForm">
      <label>Public Key: <input type="text" style="background:#f0f0f0" name="public_key" readonly /></label><br />
      <button style="background:#000!important;max-width:300px;padding:6px;font-size:12px;" onclick="importPrivateKey()">Import A Keypair</button>
      <button style="background:#000!important;max-width:300px;padding:6px;font-size:12px;" type="button" onclick="showCurrentKeypair()">Show Node Keypair</button>
      <button style="background:#000!important;max-width:300px;padding:6px;font-size:12px;" type="button" onclick="generateAndShowKeypair()">Generate Keypair</button>
      <br>
      <label>Contract ID: <input type="text" name="contract_id" /></label><br />
      <div>
        <label>UNL:</label>
        <div id="unlList"></div>
        <button type="button" onclick="addUnlField()">Add UNL</button>
      </div>
      <div style="margin-top:1rem;">
        <label>Known Peers:</label>
        <div id="knownPeersList"></div>
        <button type="button" onclick="addKnownPeerField()">Add Known Peer</button>
      </div>
      <label>Your User Port: <input type="number" style="background:#f0f0f0" name="user_port" readonly /></label><br />
      <div style="margin-bottom: 1rem;">
        <label for="mesh_port_label">Your Mesh Address and Port (used in peer list):</label>
        <input type="text" id="mesh_port_label" readonly style="background: #f0f0f0; border: 1px solid var(--gray); padding: 0.6rem 0.8rem; width: 100%; border-radius: 0.5rem; margin-top: 0.3rem;" />
      </div>
      <input type="hidden" name="mesh_port" />
      <label>Roundtime (ms): <input type="number" name="roundtime" /></label><br />
      <label>Stage Slice: <input type="number" name="stage_slice" /></label><br />
      <label>Threshold (%): <input type="number" name="threshold" /></label><br />
      <button type="submit">Update</button>
    </form>

    <p id="hpinfoStatus"></p>
  </div>
</div>
  <div id="loginTab">
  <div class="card" id="card" style="padding: 16px; border-radius: 8px; position: relative;">
    <h1 style="margin-top: 0;">Admin File Manager</h1>
    <span style="font-size:12px;">Default: admin</span><br>
    <input type="text" id="username" placeholder="Username" style="width: 100%; margin-bottom: 8px;" />
    <span style="font-size:12px;">Default: password</span>
    <input type="password" id="password" placeholder="Password" style="width: 100%; margin-bottom: 8px;" />
    <button onclick="loadFolders();" style="width: 100%;">Login & Load Folders</button>

    <!-- Info box -->
    <div style="position: absolute; top: 8px; right: 8px; cursor: pointer; font-size: 18px;" onclick="document.getElementById('infoPopup').style.display = (document.getElementById('infoPopup').style.display === 'block') ? 'none' : 'block';">❓</div>
    <div id="infoPopup" style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 200px; box-shadow: 0 2px 8px rgba(0,0,0,0.1);">
      <strong>Login Info:</strong><br>
      Default username is <code>admin</code><br>
      Default password is <code>password</code><br>
      You can change your credentials in the User Management Section.<br><br>
      The password is local, so you can't use it to log in on other peoples instances even thought they are in the same cluster.
    </div>
    <!-- Info box END -->

  </div>
</div>

<div id="managerTab" style="display: none; position: relative;">

  <div style="position: absolute; top: 8px; right: 8px; font-size: 18px; cursor: pointer; z-index: 10;"
       onclick="document.getElementById('managerInfoPopup').style.display = (document.getElementById('managerInfoPopup').style.display === 'block') ? 'none' : 'block';">
    ❓
  </div>


  <div id="managerInfoPopup"
       style="display: none; position: absolute; top: 32px; right: 8px; background: #f9f9f9; border: 1px solid #ccc; padding: 10px; border-radius: 6px; font-size: 12px; width: 260px; box-shadow: 0 2px 8px rgba(0,0,0,0.1); z-index: 10;">
    <strong>Manager Tab Info:</strong><br>
    - Left side: Browse folders or create new ones.<br>
    - Right side: Upload or manage files in the selected folder.<br>
    - Use the switch below to toggle between local files and cluster files (on the frontend).<br>
    - Use the push local files to cluster button to push your local files to the cluster (and everyone in it).<br>
    - Check version history and manage active versions.
  </div>

  <div id="fileandedit" class="grid">
    <div class="section tree">
      <h3>Folders</h3>
      <div id="folderTree">Click 'Login & Load Folders'</div>
      <input type="text" id="newFolderName" placeholder="New subfolder or file name" />
      <button onclick="disableButtonTemporarily(this, () => Promise.resolve(createSubfolder()))">Create Folder in Selected</button>
      <button onclick="disableButtonTemporarily(this, () => Promise.resolve(createEmptyFile()))">Create File in Selected</button>
    </div>

    <div class="section editorsection">
      <h3>Files in <span id="selectedPathDisplay">(none)</span></h3>
      <div id="fileList">No folder selected.</div>
      <div class="dropzone" id="dropzone">Drag & Drop Files Here</div>
      <input type="file" id="fileInput" multiple />
      <button onclick="disableButtonTemporarily(this, () => uploadFiles())">Upload Files</button>
      <div class="filemessage" id="filemessage" style="max-height:350px;overflow-y:scroll;"></div>
    </div>
  </div>

  <div class="switch-block">
    <label class="switch">
      <input type="checkbox" id="clusterToggle" onchange="saveClusterSetting()" />
      <span class="slider"></span>
    </label>
    <div class="switch-label">Serve files from cluster</div>
    <div class="switch-description" style="padding-bottom:6px;">
      This means you serve the website from the Evernode State folder instead of the instance's locally held files. The files you see on this page are held locally though.
    </div>
    <button onclick="disableButtonTemporarily(this, () => pushLocalFilesToCluster())">Push Local Files to Cluster</button>
    <h3>Manage Versions</h3>
    <button onclick="disableButtonTemporarily(this, () => checkVersions())">Check Versions</button>
    <div id="versionList" style="margin-top:1rem;"></div>
  </div>

  <div class="message" id="message" style="max-height:350px;overflow-y:scroll;"></div>
</div>

</div></div>
  <script>
let useClusterApis = false;
let clusterMore = false;
let nodeLogInterval;





function toggleClusterApi() {
  const useCluster = document.getElementById('toggleClusterApi').checked;
  useClusterApis = useCluster;

  fetch('/admin/set-api-mode', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ useCluster })
  }).then(() => {
    updateApiUiMode(useCluster);
    refreshNodeEndpoints(); 
  });
}


async function fetchAvailableModules() {
  const res = await fetch('/admin/modules/available', {
    headers: {
      'Authorization': getAuthHeader()
    }
  });
  const data = await res.json();
  const list = document.getElementById('availableModules');
  list.innerHTML = '';
  data.available.forEach(mod => {
    const li = document.createElement('li');
    li.style.marginBottom = '6px';
    li.innerHTML = `${mod} <a href="#" onclick="loadModule('${mod}'); return false;" style="margin-left: 8px; font-size: 12px; color: #007bff; text-decoration: underline;">[Load]</a>`;
    list.appendChild(li);
  });
}


  async function fetchExtraModules() {
   const res = await fetch('/admin/modules/extra', {
    headers: {
      'Authorization': getAuthHeader()
    }
  });
    const data = await res.json();
    const list = document.getElementById('extraModules');
    list.innerHTML = '';
    data.extra.forEach(mod => {
      const li = document.createElement('li');
      li.textContent = mod;
      list.appendChild(li);
    });
  }

async function loadModule(name) {
  try {
    const res = await fetch('/admin/modules/extra', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': getAuthHeader()
      },
      body: JSON.stringify({ name })
    });

    const data = await res.json();
    alert(data.message || data.error);

    if (res.ok) {

      await fetchAvailableModules();
      await fetchExtraModules();
    }

  } catch (err) {
    alert("Failed to load module: " + err.message);
  }
}


document.getElementById('uninstallForm').addEventListener('submit', async function (e) {
    e.preventDefault();

    const pkg = document.getElementById('unpkg').value.trim();
    const responseEl = document.getElementById('unresponse');
    responseEl.textContent = 'Uninstalling...';

    try {
      const response = await fetch('/admin/uninstall', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': getAuthHeader()
        },
        body: JSON.stringify({ package: pkg })
      });

      const text = await response.text();
      let data;

      try {
        data = JSON.parse(text);
        fetchAvailableModules();
        fetchExtraModules();
      } catch {
        data = { error: text };
      }

      responseEl.textContent = JSON.stringify(data, null, 2);
    } catch (err) {
      responseEl.textContent = 'Error: ' + err.message;
    }
  });
  
 document.getElementById('installForm').addEventListener('submit', async function (e) {
    e.preventDefault();
    const pkg = document.getElementById('package').value.trim();
    const version = document.getElementById('version').value.trim();

    const responseEl = document.getElementById('response');
    responseEl.textContent = 'Installing...';

    try {
      const response = await fetch('/admin/install', {
        method: 'POST',
         headers: {
          'Content-Type': 'application/json',
          'Authorization': getAuthHeader()
        },
        body: JSON.stringify({
          package: pkg,
          version: version || undefined
        })
      });

const text = await response.text();
let data;

try {
  data = JSON.parse(text);
  fetchAvailableModules();
  fetchExtraModules();
} catch {
  data = { error: text };
}

responseEl.textContent = JSON.stringify(data, null, 2);

      responseEl.textContent = JSON.stringify(data, null, 2);
    } catch (err) {
      responseEl.textContent = 'Error: ' + err.message;
    }
  });


  function toggleNodeLogs() {
    const enabled = document.getElementById('nodeLogToggle').checked;
    const stdoutEl = document.getElementById('nodeStdout');
    const stderrEl = document.getElementById('nodeStderr');

    if (enabled) {
      nodeLogInterval = setInterval(async () => {
        try {
          const [stdoutRes, stderrRes] = await Promise.all([
            fetch('/admin/nodestdout', {headers: { 'Authorization': getAuthHeader() }}),
            fetch('/admin/nodestderr', {headers: { 'Authorization': getAuthHeader() }})
          ]);
          const stdout = await stdoutRes.text();
          const stderr = await stderrRes.text();

          stdoutEl.textContent = stdout;
          stderrEl.textContent = stderr;

          stdoutEl.scrollTop = stdoutEl.scrollHeight;
          stderrEl.scrollTop = stderrEl.scrollHeight;
        } catch (err) {
          stdoutEl.textContent = 'Error loading stdout';
          stderrEl.textContent = 'Error loading stderr';
        }
      }, 2000);
    } else {
      clearInterval(nodeLogInterval);
    }
  }

function updateApiUiMode(useCluster) {

  document.getElementById('apiSubmitBtn').style.display = useCluster ? 'none' : '';
  document.getElementById('apieditbtn').style.display = useCluster ? 'none' : '';
  document.getElementById('apicancelbtn').style.display = useCluster ? 'none' : '';
  document.getElementById('apiPath').disabled = useCluster;
  document.getElementById('apiMethod').disabled = useCluster;
  document.getElementById('apiAuthRequired').disabled = useCluster;
  document.getElementById('apiCode').readOnly = useCluster;

const msgMain = document.getElementById('nodejsLog');
msgMain.textContent = useCluster ? 'Cluster mode is enabled. Endpoints are read-only.' : '';
const msgEdit = document.getElementById('editApiLog');
msgEdit.textContent = useCluster ? 'Cluster mode is enabled. Endpoints are read-only.' : '';
}

function loadApiModeAndRefresh() {
  const authHeader = getAuthHeader();
  if (!authHeader) return;

  fetch('/admin/get-api-mode', {
    headers: { 'Authorization': authHeader }
  })
    .then(res => res.text())
    .then(text => {
      try {
        const data = JSON.parse(text);
        const toggle = document.getElementById('toggleClusterApi');
        toggle.checked = data.useCluster;
        useClusterApis = data.useCluster;
        updateApiUiMode(data.useCluster);
        refreshNodeEndpoints();
      } catch (e) {
        console.error('Failed to parse API mode response:', text);
      }
    })
    .catch(err => {
      console.error('Failed to get API mode:', err);
    });
}



let editingEndpointPath = null;


async function pushDynamicApiVersion() {
  const log = document.getElementById('dynamicApiPushLog');
  log.innerHTML = 'Pushing...';

  try {
    const res = await fetch('/admin/push-api', {
      method: 'POST',
      headers: { 'Authorization': getAuthHeader() }
    });

    const data = await res.json();
    if (data.success) {
      log.innerHTML = `<div>${escapeHtml(data.message)}</div>`;
    } else {
      log.innerHTML = `<div style="color:red;">${escapeHtml(data.error)}</div>`;
    }
  } catch (err) {
    log.innerHTML = `<div style="color:red;">Push failed: ${escapeHtml(err.message)}</div>`;
  }
}


let moreFuncCards = [];

function loadMoreFunctions() {
  fetch('/toggle-morefunctions-source', {
    headers: { 'Authorization': getAuthHeader() }
  })
    .then(res => res.json())
    .then(data => {
      clusterMore = !!data.clusterMore;
      const statusText = document.getElementById('clusterMoreStatus');
      if (statusText) {
        statusText.textContent = clusterMore
          ? 'Using cluster version of morefunctions.json (view-only)'
          : 'Using local morefunctions.json (editable)';
      }

      return fetch('/morefunctions/load', {
        headers: { 'Authorization': getAuthHeader() }
      });
    })
    .then(res => res.json())
    .then(cards => {
      moreFuncCards = cards;
      renderCards();
    });
}

function renderCards() {
  const area = document.getElementById('cards-area');
  area.innerHTML = '';

  moreFuncCards.forEach((html, idx) => {
    const wrapper = document.createElement('div');
    wrapper.className = 'card';
    wrapper.style = 'margin-bottom: 1rem; position: relative; padding-bottom: 2rem;';

    const rendered = document.createElement('div');
    rendered.className = 'rendered-html';
    rendered.style = 'border: 1px solid #ddd; padding: 1rem; border-radius: 6px; background: #fefefe;';
    rendered.innerHTML = html;
    wrapper.appendChild(rendered);

    rendered.querySelectorAll('script').forEach(oldScript => {
      const newScript = document.createElement('script');
      if (oldScript.src) {
        newScript.src = oldScript.src;
      } else {
        newScript.textContent = oldScript.textContent;
      }
      oldScript.replaceWith(newScript);
    });

    const textarea = document.createElement('textarea');
    textarea.value = html;
    textarea.style = 'width: 100%; height: 120px; margin-top: 10px; font-family: monospace;';
    textarea.hidden = true;
    wrapper.appendChild(textarea);

    if (!clusterMore) {
      const editBtn = document.createElement('button');
      editBtn.innerText = '✏️ Edit';
      editBtn.style = 'margin-right: 8px;';
      editBtn.onclick = () => {
        textarea.hidden = false;
        rendered.hidden = true;
      };

      const saveBtn = document.createElement('button');
      saveBtn.innerText = '💾 Save';
      saveBtn.style = 'margin-right: 8px;';
      saveBtn.onclick = () => {
        moreFuncCards[idx] = textarea.value;
        saveAllCards();
        renderCards();
      };

      const deleteBtn = document.createElement('button');
      deleteBtn.innerText = '🗑 Delete';
      deleteBtn.style = 'color: red;';
      deleteBtn.onclick = () => {
        if (confirm('Delete this card?')) {
          moreFuncCards.splice(idx, 1);
          saveAllCards();
          renderCards();
        }
      };

      wrapper.appendChild(editBtn);
      wrapper.appendChild(saveBtn);
      wrapper.appendChild(deleteBtn);
    }

    area.appendChild(wrapper);
  });

  const uploadDiv = document.getElementById('uploadfuncsdiv');
  if (uploadDiv) {
    uploadDiv.style.display = clusterMore ? 'none' : 'block';
  }

  const addCardBtn = document.getElementById('addMoreCardBtn');
  if (addCardBtn) {
    addCardBtn.style.display = clusterMore ? 'none' : 'inline-block';
  }
}
  function toggleDarkMode() {
    const body = document.body;
    const isDark = body.classList.toggle('dark');
    document.getElementById('themeIcon').textContent = isDark ? '🌙' : '🌞';
    localStorage.setItem('darkMode', isDark ? 'enabled' : 'disabled');
  }

  window.addEventListener('DOMContentLoaded', () => {
    const isDark = localStorage.getItem('darkMode') === 'enabled';
    if (isDark) {
      document.body.classList.add('dark');
      document.getElementById('darkModeToggle').checked = true;
      document.getElementById('themeIcon').textContent = '🌙';
    }
  });

function uploadMoreFunctionsJson() {
  const log = document.getElementById('moreuploadlog');
  log.innerText = 'Uploading...';

  fetch('/admin/morefunctions-upload', {
    method: 'POST',
    headers: { 'Authorization': getAuthHeader() }
  })
    .then(async res => {
      if (!res.ok) {
        const text = await res.text(); // Read the raw HTML
        throw new Error(`HTTP ${res.status}: ${text}`);
      }
      return res.json();
    })
    .then(data => {
      log.innerText = data.message || data.error || 'Unexpected response.';
    })
    .catch(err => {
      log.innerText = 'Upload failed: ' + err.message;
    });
}

async function loadPrivateVariables() {
    try {
      const res = await fetch('/admin/vars/extra', { headers: { 'Authorization': getAuthHeader() } });
      const data = await res.json();
      const list = document.getElementById('privateVarList');
      list.innerHTML = '';

      for (const [name, value] of Object.entries(data.extra)) {
        const card = document.createElement('div');
        card.className = 'card';
        card.style.marginBottom = '10px';
       card.innerHTML = `
  <div style="
    display: flex; 
    justify-content: space-between; 
    align-items: flex-start;
  ">
    <div style="flex: 1;">
      <div style="font-weight: 600; font-size: 14px; margin-bottom: 6px;">${name}</div>
      <code style="font-size: 13px; color: #555; word-break: break-word;">${JSON.stringify(value)}</code>
    </div>
    <button 
      onclick="deletePrivateVariable('${name}')"
      style="background: transparent;border: none;color: #000;font-size: 16px;cursor: pointer;padding: 0;max-width:50px;"
      title="Delete"
    >🗑</button>
  </div>
`;
        list.appendChild(card);
      }
    } catch (err) {
      document.getElementById('privateVarMessage').innerText = 'Failed to load variables: ' + err.message;
    }
  }

 async function addPrivateVariable() {
    const rawName = document.getElementById('newVarName').value.trim();
    const valueRaw = document.getElementById('newVarValue').value;

    if (!rawName || valueRaw === '') {
      alert('Both key and value are required.');
      return;
    }

    const name = `extravar.${rawName}`;
    let value;

    try {
      value = JSON.parse(valueRaw);
    } catch {
      value = valueRaw;
    }

    const res = await fetch('/admin/vars/extra', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': getAuthHeader()
      },
      body: JSON.stringify({ name, value })
    });

    const data = await res.json();
    document.getElementById('privateVarMessage').innerText = data.message || data.error;
    loadPrivateVariables();

    // Clear fields
    document.getElementById('newVarName').value = '';
    document.getElementById('newVarValue').value = '';
  }

  async function deletePrivateVariable(name) {
    if (!confirm(`Delete variable "${name}"?`)) return;

    const res = await fetch('/admin/vars/extra', {
      method: 'DELETE',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': getAuthHeader()
      },
      body: JSON.stringify({ name })
    });

    const data = await res.json();
    document.getElementById('privateVarMessage').innerText = data.message || data.error;
    loadPrivateVariables();
  }

  // Optional: auto-load when tab is shown
  // document.getElementById('privatevariablesTab').style.display = 'block'; // if testing
  // loadPrivateVariables();


function fetchMoreFunctionVersion(name) {
  fetch('/admin/morefunctions-fetch', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ version: name })
  })
    .then(res => res.json())
    .then(data => {
      alert(data.message || data.error || 'Done');
    })
    .catch(err => {
      alert('Fetch failed: ' + err.message);
    });
}



function addCard() {
  moreFuncCards.push('<div><p>New Custom Block</p></div>');
  renderCards();
  saveAllCards();
}

function saveAllCards() {
  fetch('/morefunctions/save', {
    method: 'POST',
    headers: { 'Authorization': getAuthHeader(), 'Content-Type': 'application/json' },
    body: JSON.stringify({ cards: moreFuncCards })
  }).then(res => res.json()).then(data => {
    if (!data.success) alert('Save failed');
  });
}


function refreshNodeEndpoints() {
  const container = document.getElementById('nodejsRoutes');
  container.innerHTML = 'Loading...';

  fetch('/admin/list-endpoints', {
    headers: { 'Authorization': getAuthHeader() }
  })
    .then(res => res.json())
    .then(data => {
      if (!Array.isArray(data)) throw new Error('Invalid format');
      
      container.innerHTML = data.map(ep => {
        const escapedMethod = escapeHtml(ep.method.toUpperCase());
        const escapedPath = escapeHtml(ep.path);

        const editLabel = useClusterApis ? 'View' : 'Edit';
        const deleteBtn = useClusterApis ? '' : `
          <button onclick="deleteNodeEndpoint('${ep.method}', '${ep.path}')">Delete</button>`;

        return `
          <div class="file-item">
            <span>${escapedMethod} ${escapedPath}</span>
            <div style="display:flex; gap:0.5rem;">
              <button onclick="editNodeEndpointPrompt('${ep.path}')">${editLabel}</button>
              ${deleteBtn}
            </div>
          </div>
        `;
      }).join('');
    })
    .catch(err => {
      container.innerHTML = 'Failed to load endpoints: ' + err.message;
    });
}

function editNodeEndpointPrompt(path) {
  const endpointUrl = useClusterApis ? '/admin/read-api-cluster' : '/admin/read-api-local';

  fetch(endpointUrl, { // ← was `url` before — fixed here
    headers: { 'Authorization': getAuthHeader() }
  })
  .then(res => res.json())
  .then(data => {
    const endpoints = JSON.parse(data.content || '[]');
    const match = endpoints.find(ep => ep.path === path);
    if (!match) {
      alert('Endpoint not found.');
      return;
    }

    document.getElementById('editApiCard').style.display = 'block';

    document.getElementById('editApiPath').value = match.path;
    document.getElementById('editApiCode').value = match.code || '';
    document.getElementById('editApiAuthRequired').checked = !!match.auth;
    document.getElementById('editApiMethod').value = (match.method || 'get').toLowerCase();

    document.getElementById('editApiCode').readOnly = useClusterApis;
    document.getElementById('editApiAuthRequired').disabled = useClusterApis;
    document.getElementById('editApiMethod').disabled = useClusterApis;

    const log = document.getElementById('editApiLog');
    log.textContent = useClusterApis ? 'Viewing from cluster. Editing is disabled.' : '';
  })
  .catch(err => {
    alert('Failed to load endpoint: ' + err.message);
  });
}



function saveEditedEndpoint() {
  const path = document.getElementById('editApiPath').value.trim();
  const code = document.getElementById('editApiCode').value.trim();
  const auth = document.getElementById('editApiAuthRequired').checked;
  const log = document.getElementById('editApiLog');
const method = document.getElementById('editApiMethod').value.trim().toLowerCase();
  if (!path || !code) {
    log.innerHTML = '<div style="color:red;">Path and code are required.</div>';
    return;
  }

  fetch('/admin/edit-endpoint', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ path, code, method, auth })
  })
  .then(res => res.json())
  .then(data => {
    log.innerHTML = `<div>${escapeHtml(data.message || data.error)}</div>`;
    document.getElementById('nodejsLog').innerHTML = `<div>${escapeHtml(data.message || data.error)}</div>`;
    refreshNodeEndpoints();
    cancelApiEdit();
  })
  .catch(err => {
    log.innerHTML = `<div style="color:red;">Save failed: ${escapeHtml(err.message)}</div>`;
  });
}


function toggleClusterMoreFunctions() {
  const useCluster = document.getElementById('toggleClusterMoreFunctions').checked;

  fetch('/toggle-morefunctions-source', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ useCluster })
  })
    .then(() => loadMoreFunctions())
    .catch(err => alert('Failed to toggle cluster mode: ' + err.message));
}
function initClusterMoreToggle() {
  fetch('/toggle-morefunctions-source', {
    headers: { 'Authorization': getAuthHeader() }
  })
    .then(res => res.json())
    .then(data => {
      const checkbox = document.getElementById('toggleClusterMoreFunctions');
      if (checkbox) checkbox.checked = !!data.clusterMore;
    });
}


function cancelApiEdit() {
  document.getElementById('editApiCard').style.display = 'none';
  document.getElementById('editApiPath').value = '';
  document.getElementById('editApiCode').value = '';
  document.getElementById('editApiLog').innerHTML = '';
}

function deleteNodeEndpoint(method, path) {
  if (!confirm(`Delete endpoint ${method.toLowerCase()} ${path}?`)) return;

  fetch('/admin/delete-endpoint', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      method: method.toLowerCase(),
      path
    })
  })
  .then(res => res.json())
  .then(data => {
    alert(data.message || data.error);
    refreshNodeEndpoints();
  });
}




function addNodeEndpoint() {
  const path = document.getElementById('apiPath').value.trim();
  const code = document.getElementById('apiCode').value.trim();
  const method = document.getElementById('apiMethod').value.trim().toLowerCase();
  const auth = document.getElementById('apiAuthRequired').checked;
  const log = document.getElementById('nodejsLog');

  if (!path || !code || !method) {
    log.innerHTML = '<div style="color:red;">Path, method, and code required.</div>';
    return;
  }

  fetch('/admin/add-endpoint', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ path, method, code, auth })
  })
  .then(res => res.json())
  .then(data => {
    log.innerHTML = `<div>${escapeHtml(data.message || data.error)}</div>`;
    refreshNodeEndpoints();
    document.getElementById('apiPath').value = '';
    document.getElementById('apiCode').value = '';
    document.getElementById('apiMethod').value = 'get'; 
    document.getElementById('apiAuthRequired').checked = false;
  })
  .catch(err => {
    log.innerHTML = `<div style="color:red;">Failed: ${escapeHtml(err.message)}</div>`;
  });
}



  document.addEventListener('DOMContentLoaded', () => {
    const popup = document.getElementById('termsPopup');
    popup.classList.add('active');
  });


 function acceptTerms() {
   const popup = document.getElementById('termsPopup');
   const mainContent = document.querySelector('.main-content');


   popup.classList.remove('active');
       


 localStorage.setItem('termsAccepted', 'true');
 }
  if (localStorage.getItem('termsAccepted') === 'true') {
     document.getElementById('termsPopup').classList.remove('active');
           
 }

async function uploadSidedishJS() {
  const log = document.getElementById('sidedishUploadLog');
  log.innerHTML = '';

  try {
    const res = await fetch('/admin/sidedish-upload', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      }
    });

    const result = await res.json();
    if (result.error) {
      log.innerHTML = `<div style="color:red;">Failed to upload: ${escapeHtml(result.error)}</div>`;
    } else {
      log.innerHTML = `<div>${escapeHtml(result.message)}</div>`;
    }
  } catch (err) {
    log.innerHTML = `<div style="color:red;">Upload error: ${escapeHtml(err.message)}</div>`;
  }
}


async function pushSidedishToCluster() {
  if (!confirm("This will push the current local sidedish.js to the cluster in a new versioned folder. Proceed?")) return;

  try {
    const res = await fetch('/admin/push-sidedish', {
      method: 'POST',
      headers: { 'Authorization': getAuthHeader() }
    });

    const data = await res.json();
    if (data.success) {
      showMessagesidedish(data.message || 'Pushed successfully.');
      listSidedishVersions();
    } else {
      showMessagesidedish(data.error || 'Unknown error.');
    }
  } catch (err) {
    showMessagesidedish("Push failed: " + err.message);
  }
}
async function toggleDeleteSidedish(version) {
  try {
    const res = await fetch('/admin/vote-delete-sd', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    showMessagesidedish(data.message || data.error);
    listSidedishVersions();
  } catch (err) {
    showMessagesidedish("Delete vote failed: " + err.message);
  }
}

async function listSidedishVersions() {
  const container = document.getElementById('sidedishVersionList');
  container.innerHTML = 'Loading...';

  try {
    const [treeRes, voteRes] = await Promise.all([
      fetch('/admin/browse-cluster', {
        headers: { 'Authorization': getAuthHeader() }
      }),
      fetch('/admin/my-sidedish-vote', {
        headers: { 'Authorization': getAuthHeader() }
      })
    ]);

    const tree = await treeRes.json();
    const voteData = await voteRes.json();
    const votedVersion = voteData.sidedish;
    const voteMap = voteData.sidedishVotes || {};
    const liveVersion = voteData.live;

    const versions = tree['sidedish_versions'] || {};
    const versionNames = Object.keys(versions).sort().reverse();

    if (versionNames.length === 0) {
      container.innerHTML = '<em>No versions found.</em>';
      return;
    }

    container.innerHTML = versionNames.map(name => {
      const vote = voteMap[name] || {};
      const voted = vote.votedByMe;
      const deleteVoted = vote.deleteVotedByMe;

      return `
        <div class="file-item" style="display:flex; justify-content:space-between; align-itemsenter;">
          <div>
            <strong>${name}</strong> ${name === liveVersion ? '<span style="color:green;">(LIVE)</span>' : ''}<br/>
            <small>Votes: ${vote.votes || 0} ${voted ? '(you voted)' : ''}</small><br/>
            <small>Delete votes: ${vote.deleteVotes || 0} ${deleteVoted ? '(you voted)' : ''}</small><br/>
            <button style="margin-left:0px!important;padding:6px!important;font-size:12px;" onclick="disableButtonTemporarily(this, () => fetchSidedishVersion('${name}'))">Fetch to local</button>
          </div>
          <div style="display:flex; flex-directionolumn; align-items:flex-end; gap:4px;">
            ${
              voted
                ? '<span style="color:green;">✅ Already voted</span>'
                : `<button onclick="disableButtonTemporarily(this, () => voteSidedishVersion('${name}'))">Go Live</button>`
            }
            ${
              name === liveVersion
                ? '<small style="color:#999;">Live version (cannot delete)</small>'
                : `<label class="switch">
                    <input type="checkbox" onchange="toggleDeleteSidedish('${name}', this.checked)" ${deleteVoted ? 'checked' : ''}>
                    <span class="slider"></span>
                   </label>`
            }
          </div>
        </div>
      `;
    }).join('');
  } catch (err) {
    container.innerHTML = 'Failed to list versions: ' + err.message;
  }
}


async function voteSidedishVersion(version) {
  if (!confirm(`Vote to activate sidedish version: ${version}?`)) return;

  try {
    const res = await fetch('/admin/promote-sidedish', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    alert(data.message || data.error || 'Unknown result');
    listSidedishVersions();
  } catch (err) {
    alert('Vote failed: ' + err.message);
  }
}

function fetchSidedishFromCluster() {
  const confirmOverwrite = confirm("This will overwrite your local sidedish.js (if any). Proceed?");
  if (!confirmOverwrite) return;

  fetch('/admin/fetch-sidedish', {
    method: 'POST',
    headers: { 'Authorization': getAuthHeader(), 'Content-Type': 'application/json' }
  })
  .then(res => res.json())
  .then(data => {
    showMessagesidedish(data.message || data.error);
  })
  .catch(err => {
    showMessagesidedish('Fetch failed: ' + err.message);
  });
}

function loadLocalSidedish() {
  fetch('/admin/read-sd-local', {
    headers: { 'Authorization': getAuthHeader() }
  })
  .then(res => {
    if (!res.ok) throw new Error('Unable to read local sidedish.js');
    return res.json();
  })
  .then(data => {
    document.getElementById('sidedishTextArea').value = data.content || '';
    document.getElementById('sidedishEditor').style.display = 'block';
  })
  .catch(err => {
    showMessagesidedish('Load failed: ' + err.message);
  });
}

function saveLocalSidedish() {
  const content = document.getElementById('sidedishTextArea').value;

  fetch('/admin/file', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({
      path: '../sidedish.js',
      content
    })
  })
  .then(res => res.json())
  .then(data => {
    showMessagesidedish(data.message || data.error);
  })
  .catch(err => {
    showMessagesidedish('Save failed: ' + err.message);
  });
}

function escapeHtml(str) {
  return str.replace(/[&<>"']/g, tag => ({
    '&': '&amp;', '<': '&lt;', '>': '&gt;',
    '"': '&quot;', "'": '&#39;'
  }[tag]));
}

async function stopHotPocket() {
  if (!confirm("Are you sure you want to stop HotPocket?")) return;

  try {
    const response = await fetch('/admin/stop-hotpocket', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      }
    });

    const result = await response.json();
    alert(result.message || 'HotPocket stopped.');

    setTimeout(() => {
      updateHotPocketButtons();
    }, 3000); // 3 seconds
  } catch (err) {
    alert('Error stopping HotPocket.');
    console.error(err);
  }
}
   
async function generateAndShowKeypair() {
  try {
    const res = await fetch('/admin/generate-keypair', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      }
    });

    const data = await res.json();
    if (data.privateKey && data.publicKey) {
      alert("Keypair generated:\n\nPublic Key:\n" + data.publicKey + "\n\nPrivate Key:\n" + data.privateKey);
    } else {
      alert("Failed: " + data.error);
    }
  } catch (err) {
    alert("Error generating keypair: " + err.message);
  }
}

async function addUser() {
  const username = document.getElementById('addUsername').value.trim();
  const password = document.getElementById('addPassword').value;
  if (!username || !password) return alert('Username and password required.');

  const res = await fetch('/admin/add-user', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, password })
  });
  const result = await res.json();
  alert(result.message || result.error);
}

async function removeUser() {
  const username = document.getElementById('removeUsername').value.trim();
  if (!username) return alert('Username required.');
  if (username === 'admin') return alert('Cannot remove admin user.');

  const res = await fetch('/admin/remove-user', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username })
  });
  const result = await res.json();
  alert(result.message || result.error);
}
async function changePassword() {
  const username = document.getElementById('changeUser').value.trim();
  const oldPassword = document.getElementById('oldPassword').value;
  const newPassword = document.getElementById('newPassword').value;
  const newPasswordConfirm = document.getElementById('newPasswordConfirm').value;

  if (!username || !oldPassword || !newPassword || !newPasswordConfirm) {
    return alert('All fields required.');
  }

  const res = await fetch('/admin/change-password', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json'
    },
    body: JSON.stringify({ username, oldPassword, newPassword, newPasswordConfirm })
  });

  const result = await res.json();
  alert(result.message || result.error);


  if (res.ok) {
    location.reload(); 
  }
}


async function showCurrentKeypair() {
  try {
    const res = await fetch('/admin/show-keypair', {
      headers: { 'Authorization': getAuthHeader() }
    });

    const data = await res.json();
    if (data.error) {
      alert("Error: " + data.error);
      return;
    }

    alert(`Public Key:\n${data.publicKey}\n\nPrivate Key:\n${data.privateKey}`);
  } catch (err) {
    alert("Failed to fetch keypair: " + err.message);
  }
}

async function importPrivateKey() {
  const pubKey = prompt("Enter your PUBLIC key (starting with 'ed'):");
  const privKey = prompt("Enter your PRIVATE key (starting with 'ed'):");

  if (
    !pubKey || !privKey ||
    !/^ed[0-9a-f]{64}$/i.test(pubKey) ||
    !/^ed[0-9a-f]{128}$/i.test(privKey)
  ) {
    alert("Invalid key format.\nPublic key must be 'ed' + 64 hex chars.\nPrivate key must be 'ed' + 128 hex chars.");
    return;
  }

  try {
    const res = await fetch('/admin/import-key', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({
        public_key: pubKey,
        private_key: privKey
      })
    });

    const result = await res.json();
    if (result.success) {
      alert("Key pair imported successfully.");
      loadHPInfo(); // reload hp.cfg into UI
    } else {
      alert("Failed: " + (result.error || "Unknown error."));
    }
  } catch (err) {
    alert("Import failed: " + err.message);
  }
}

async function restartHotPocket() {
  if (!confirm("Are you sure you want to restart HotPocket?")) return;

  try {
    const response = await fetch('/admin/restart-hotpocket', {
      method: 'POST',
      headers: { 'Authorization': getAuthHeader(), 'Content-Type': 'application/json' },
    });
    const result = await response.json();
    alert(result.message || 'Restart complete.');


    setTimeout(() => {
      updateHotPocketButtons();
    }, 3000);
  } catch (err) {
    alert('Error restarting HotPocket.');
    console.error(err);
  }
}

const editableMimeTypes = [
  'text/plain',
  'text/html',
  'text/css',
  'application/json',
  'application/javascript',
  'application/xml',
  'text/x-python',
  'text/x-c',
  'text/x-c++',
  'text/markdown',
  'text/x-sh',
  'application/x-httpd-php',
  'application/x-yaml',
  'application/x-sql',
];

const editableExtensions = [
  // Config & Shell
  '.sh', '.cfg', '.override', '.conf', '.env', '.ini', '.log', 

  // Plain text
  '.txt', '.md', '.markdown', '.csv', '.tsv', '.json', '.yaml', '.yml', '.xml',

  // Web files
  '.html', '.htm', '.css', '.js', '.ts',

  // Templates / Scripting
  '.php', '.py', '.rb', '.pl', '.lua',

  // Code / Dev files
  '.c', '.cpp', '.h', '.hpp', '.java', '.cs', '.go', '.rs', '.swift', '.kt',

  // Misc dev
  '.sql', '.bat', '.dockerfile', '.makefile', '.gitignore', '.npmrc'
];

let logPollIntervals = {};

function toggleLogTrace() {
  const enabled = document.getElementById('logTraceToggle').checked;
  const files = ['hp', 'stdout', 'stderr'];

  files.forEach(type => {
    const el = document.getElementById(type + 'Log');

    if (enabled) {
      logPollIntervals[type] = setInterval(() => {
        fetch(`/admin/trace-log?file=${type}`, {
          headers: { 'Authorization': getAuthHeader() }
        })
        .then(res => res.json())
        .then(data => {
          el.textContent = data.log;
          el.scrollTop = el.scrollHeight;
        })
        .catch(() => {
          el.textContent = '[Failed to fetch logs]';
        });
      }, 1500);
    } else {
      clearInterval(logPollIntervals[type]);
    }
  });
}

function addUnlField(value = '') {
  const container = document.getElementById('unlList');
  const div = document.createElement('div');
  div.innerHTML = `
    <input type="text" value="${value}" />
    <button class="deletebutton" type="button" onclick="this.parentElement.remove()">Delete UNL</button>
  `;
  container.appendChild(div);
}

function addKnownPeerField(value = '') {
  const container = document.getElementById('knownPeersList');
  const div = document.createElement('div');
  div.innerHTML = `
    <input type="text" value="${value}" />
    <button class="deletebutton" type="button" onclick="this.parentElement.remove()">Delete Peer</button>
  `;
  container.appendChild(div);
}
function updateHotPocketButtons() {
  fetch('/admin/hotpocket-status', {
    headers: { 'Authorization': getAuthHeader() }
  })
  .then(res => res.json())
  .then(data => {
    const isRunning = data.running;

    document.querySelectorAll('#hpinfoForm button').forEach(btn => {
      if (
        btn.textContent.includes('Import A Keypair')
      ) {
        btn.disabled = isRunning;
        btn.style.opacity = isRunning ? 0.6 : 1;
        btn.style.cursor = isRunning ? 'not-allowed' : 'pointer';
      }
    });

    console.log('[HotPocket Status] Running:', isRunning);
  })
  .catch(err => {
    console.error('Failed to check HotPocket status:', err);
  });
}

function loadHPInfo() {
   updateHotPocketButtons();
  fetch('/admin/hpinfo', {
    headers: { 'Authorization': getAuthHeader() }
  })
  .then(res => res.json())
  .then(data => {
    const form = document.getElementById('hpinfoForm');
    form.public_key.value = data.public_key;
    form.contract_id.value = data.contract_id;
    document.getElementById('unlList').innerHTML = '';
(data.unl || []).forEach(unl => addUnlField(unl));

document.getElementById('knownPeersList').innerHTML = '';
(data.known_peers || []).forEach(peer => addKnownPeerField(peer));
    form.user_port.value = data.user_port;
    const domain = window.location.hostname;
form.mesh_port.value = data.mesh_port;
document.getElementById('mesh_port_label').value = `${window.location.hostname}:${data.mesh_port}`;
    form.roundtime.value = data.roundtime;
    form.stage_slice.value = data.stage_slice;
    form.threshold.value = data.threshold;
  });
}

document.getElementById('hpinfoForm').addEventListener('submit', function(e) {
  e.preventDefault();
  const form = e.target;

  const updated = {
    public_key: form.public_key.value,
    contract_id: form.contract_id.value,
    unl: Array.from(document.querySelectorAll('#unlList input'))
          .map(input => input.value.trim())
          .filter(val => val),

known_peers: Array.from(document.querySelectorAll('#knownPeersList input'))
          .map(input => input.value.trim())
          .filter(val => val),
    user_port: parseInt(form.user_port.value),
    mesh_port: parseInt(form.mesh_port.value),
    roundtime: parseInt(form.roundtime.value),
    stage_slice: parseInt(form.stage_slice.value),
    threshold: parseInt(form.threshold.value),
  };

  fetch('/admin/hpinfo', {
    method: 'POST',
    headers: {
      'Authorization': getAuthHeader(),
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(updated)
  })
  .then(res => res.json())
  .then(response => {
    document.getElementById('hpinfoStatus').textContent = response.success
      ? 'Configuration updated successfully.'
      : 'Update failed.';
  });
 
});

function loadClusterFiles() {
  fetch('/admin/browse-cluster', {
    headers: { 'Authorization': getAuthHeader() }
  })
    .then(res => {
      if (!res.ok) throw new Error('Unauthorized');
      return res.json();
    })
    .then(async tree => {
      const filePaths = [];
      collectClusterFilePaths(tree, '', filePaths);
      const checksums = await fetch('/admin/cluster-checksums', {
        method: 'POST',
        headers: {
          'Authorization': getAuthHeader(),
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({ files: filePaths })
      })
      .then(res => res.json())
      .catch(() => ({})); 

      const container = document.getElementById('clusterTree');
      container.innerHTML = '';
      const rootEl = renderClusterTree(tree, '', checksums);
      container.appendChild(rootEl);
    })
    .catch(err => {
      document.getElementById('clusterTree').textContent = 'Failed to load cluster files. Check login.';
    });
}

function collectClusterFilePaths(tree, path, list) {
  for (const key in tree) {
    const fullPath = path ? `${path}/${key}` : key;
    if (tree[key] === null) {
      list.push(fullPath);
    } else {
      collectClusterFilePaths(tree[key], fullPath, list);
    }
  }
}

function renderClusterTree(tree, path, checksums = {}) {
  const ul = document.createElement('ul');

  for (const key in tree) {
    const fullPath = path ? `${path}/${key}` : key;
    const id = 'cluster_' + btoa(fullPath).replace(/=/g, '');
    const li = document.createElement('li');

    if (tree[key] === null) {
      // File
      const span = document.createElement('span');
      span.textContent = key;
      span.style.color = 'blue';
      span.style.cursor = 'pointer';
      span.onclick = () => viewClusterFile(fullPath);

      const checksumText = checksums[fullPath] || 'Unavailable';
      const checksumDiv = document.createElement('div');
      checksumDiv.style.fontSize = '8px';
      checksumDiv.style.color = '#666';
      checksumDiv.style.marginLeft = '1rem';
      checksumDiv.textContent = 'SHA-256: ' + checksumText;

      li.appendChild(span);
      li.appendChild(checksumDiv);
    } else {
      const toggle = document.createElement('span');
      toggle.textContent = '[+]';
      toggle.style.cursor = 'pointer';
      toggle.onclick = () => toggleFolder(id);

      const label = document.createElement('strong');
      label.textContent = key;
      label.style.marginLeft = '0.3rem';

      const nestedDiv = document.createElement('div');
      nestedDiv.id = id;
      nestedDiv.style.display = 'none';
      nestedDiv.style.marginLeft = '1rem';

      const childUl = renderClusterTree(tree[key], fullPath, checksums);
      nestedDiv.appendChild(childUl);

      li.appendChild(toggle);
      li.appendChild(label);
      li.appendChild(nestedDiv);
    }

    ul.appendChild(li);
  }

  return ul;
}

function toggleFolder(id) {
  const folder = document.getElementById(id);
  const toggleBtn = folder.previousElementSibling.previousElementSibling;

  if (folder.style.display === 'none') {
    folder.style.display = 'block';
    toggleBtn.textContent = '[-]';
  } else {
    folder.style.display = 'none';
    toggleBtn.textContent = '[+]';
  }
}

function viewhpinfo() {
  fetch('/admin/hpinfo', {
    method: 'GET',
    headers: {
      'Authorization': getAuthHeader(),
    }
  })
  .then(res => res.json())
  .then(data => {
    console.log('Config:', data);
  });
}

function updatehpinfo(updatedValues) {
  fetch('/admin/hpinfo', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': getAuthHeader(),
    },
    body: JSON.stringify(updatedValues),
  })
  .then(res => res.json())
  .then(response => {
    if (response.success) {
      alert('Config updated');
    }
  });
}

function viewClusterFile(filePath) {
  const auth = getAuthHeader();
  const fileView = document.getElementById('clusterFileView');

  fileView.innerHTML = `
    <p><strong>Selected:</strong> ${filePath}</p>
    <div style="display:flex; gap:0.5rem; margin-bottom:0.5rem;">
      <button onclick="downloadClusterFile('${filePath}')">Download</button>
    </div>
    <pre id="fileContent" style="white-space:pre-wrap;margin-bottom:0.5rem;max-height:500px;overflow-y:scroll;background:#f3f4f6;padding:1rem;border-radius:0.5rem;"></pre>
    <div id="fileChecksum" style="font-size: 0.85rem; color: #555;"></div>
  `;

  fetch('/admin/read-cluster?path=' + encodeURIComponent(filePath), {
    headers: { 'Authorization': auth }
  })
    .then(res => {
      if (res.status === 415) {
        return Promise.reject(new Error('This file cannot be previewed (non-text)'));
      }
      if (!res.ok) {
        return res.text().then(msg => {
          throw new Error(`Server returned ${res.status}: ${msg}`);
        });
      }
      return res.text();
    })
    .then(text => {
      document.getElementById('fileContent').textContent = text;
    })
    .catch(err => {
      document.getElementById('fileContent').textContent = 'Error: ' + err.message;
    });

  fetch('/admin/cluster-checksum?path=' + encodeURIComponent(filePath), {
    headers: { 'Authorization': auth }
  })
    .then(res => res.json())
    .then(data => {
      const checksum = data.checksum || 'Unavailable';
      document.getElementById('fileChecksum').innerHTML = `<strong>SHA-256:</strong> ${checksum}`;
    })
    .catch(err => {
      document.getElementById('fileChecksum').textContent = 'SHA-256: Error fetching checksum';
    });
}

function downloadClusterFile(filePath) {
  const auth = getAuthHeader();
  fetch('/admin/download-cluster?path=' + encodeURIComponent(filePath), {
    headers: { 'Authorization': auth }
  })
    .then(response => {
      if (!response.ok) throw new Error('Download failed: ' + response.status);
      return response.blob();
    })
    .then(blob => {
      const url = window.URL.createObjectURL(blob);
      const link = document.createElement('a');
      link.href = url;
      link.download = filePath.split('/').pop();
      document.body.appendChild(link);
      link.click();
      link.remove();
      window.URL.revokeObjectURL(url);
    })
    .catch(err => {
      alert("Download failed: " + err.message);
    });
}

function showTab(tabId) {
  document.getElementById('loginTab').style.display = 'none';
  document.getElementById('managerTab').style.display = 'none';
  document.getElementById('clusterTab').style.display = 'none';
  document.getElementById('clustersettingsTab').style.display = 'none';
  document.getElementById('clusteroperationsTab').style.display = 'none';
  document.getElementById('morefunctionsTab').style.display = 'none';
  document.getElementById('nodejsTab').style.display = 'none';
  document.getElementById('usersTab').style.display = 'none';
  document.getElementById('sidedishTab').style.display = 'none';
  document.getElementById('apiTab').style.display = 'none';
  document.getElementById('privatevariablesTab').style.display = 'none';
  document.getElementById(tabId).style.display = 'block';

  document.querySelectorAll('.tab-nav button').forEach(btn => btn.classList.remove('active'));
const activeButton = document.querySelector(`.tab-nav button[data-tab="${tabId}"]`);
if (activeButton) activeButton.classList.add('active');
}

function disableButtonTemporarily(button, callback) {
      button.disabled = true;
      button.style.opacity = 0.6;
      button.style.cursor = 'not-allowed';
      callback().finally(() => {
        button.disabled = false;
        button.style.opacity = 1;
        button.style.cursor = 'pointer';
      });
    }

    function getAuthHeader() {
  const user = document.getElementById('username').value;
  const pass = document.getElementById('password').value;
  return 'Basic ' + btoa(user + ':' + pass);
}

async function checkVersions() {
  const [versionsRes, voteRes] = await Promise.all([
    fetch('/admin/list-versions', {
      headers: { 'Authorization': getAuthHeader() }
    }),
    fetch('/admin/my-vote', {
      headers: { 'Authorization': getAuthHeader() }
    })
  ]);

  const versions = await versionsRes.json();
  const vote = await voteRes.json();
  const votedVersion = vote.version;

  const container = document.getElementById('versionList');

  if (!Array.isArray(versions) || versions.length === 0) {
    container.innerHTML = '<em>No versions found.</em>';
    return;
  }

container.innerHTML = versions.map(v => `
  <div class="file-item" style="display: flex; justify-content: space-between; align-items: center;">
    <div>
      <strong>${v.name}</strong><br/>
      <small>Votes: ${v.votes} ${v.votedByMe ? '(you voted)' : ''}</small><br/>
      <small>Delete votes: ${v.deleteVotes} ${v.deleteVotedByMe ? '(you voted)' : ''}</small>
      <button style="margin-left:0px!important;padding:6px!important;font-size:12px;" onclick="disableButtonTemporarily(this, () => fetchVersionFiles('${v.name}'))">Fetch to local</button>
    </div>
    <div style="display:flex; flex-directionolumn; align-items:flex-end;">
      ${
        v.votedByMe
          ? '<span style="color:green;">✅ Already live vote</span>'
          : `<button onclick="disableButtonTemporarily(this, () => promoteVersion('${v.name}'))">Go Live</button>`
      }
      ${
        v.isLive
          ? '<small style="color:#999;">Live version (cannot delete)</small>'
          : `<label class="switch">
               <input type="checkbox" onchange="toggleDeleteVote('${v.name}', this.checked)" ${v.deleteVotedByMe ? 'checked' : ''}>
               <span class="slider"></span>
             </label>`
      }
    </div>
  </div>
`).join('');

}

async function checkApiVersions() {
  const container = document.getElementById('apiVersionList');
  container.innerHTML = 'Loading...';

  try {
    const res = await fetch('/admin/list-api-versions', {
      headers: { 'Authorization': getAuthHeader() }
    });

    const versions = await res.json();

    if (!Array.isArray(versions) || versions.length === 0) {
      container.innerHTML = '<em>No API versions found.</em>';
      return;
    }

container.innerHTML = versions.map(v => `
  <div class="file-item" style="display: flex; justify-content: space-between; align-items: center;">
    <div>
      <strong>${v.name}</strong> ${v.isLive ? '<span style="color:green;">(LIVE)</span>' : ''}<br/>
      <small>Votes: ${v.votes} ${v.votedByMe ? '(you voted)' : ''}</small><br/>
      <small>Delete votes: ${v.deleteVotes} ${v.deleteVotedByMe ? '(you voted)' : ''}</small><br/>
      <button style="margin-left:0px!important;padding:6px!important;font-size:12px;" onclick="fetchApiVersion('${v.name}')">Fetch to local</button>
    </div>
    <div style="display:flex; flex-directionolumn; align-items:flex-end; gap:4px;">
      ${
        v.votedByMe
          ? '<span style="color:green;">✅ Already voted</span>'
          : `<button onclick="disableButtonTemporarily(this, () => promoteApiVersion('${v.name}'))">Go Live</button>`
      }
      ${
        v.isLive
          ? '<small style="color:#999;">Live version (cannot delete)</small>'
          : `<label class="switch">
               <input type="checkbox" onchange="deleteVoteApiVersion('${v.name}', this.checked)" ${v.deleteVotedByMe ? 'checked' : ''}>
               <span class="slider"></span>
             </label>`
      }
    </div>
  </div>
`).join('');

  } catch (err) {
    container.innerHTML = 'Failed to load API versions: ' + err.message;
  }
}

async function checkMoreFunctionVersions() {
  const container = document.getElementById('moreVersionList');
  container.innerHTML = 'Loading...';

  try {
    const res = await fetch('/admin/list-morefunctions', {
      headers: { 'Authorization': getAuthHeader() }
    });

    const versions = await res.json();

    if (!Array.isArray(versions) || versions.length === 0) {
      container.innerHTML = '<em>No MoreFunction versions found.</em>';
      return;
    }

    container.innerHTML = versions.map(v => `
      <div class="file-item" style="display: flex; justify-content: space-between; align-items: center;">
        <div>
          <strong>${v.name}</strong> ${v.isLive ? '<span style="color:green;">(LIVE)</span>' : ''}<br/>
          <small>Votes: ${v.votes} ${v.votedByMe ? '(you voted)' : ''}</small><br/>
          <small>Delete votes: ${v.deleteVotes} ${v.deleteVotedByMe ? '(you voted)' : ''}</small><br/>
          <button style="margin-left:0px!important;padding:6px!important;font-size:12px;" onclick="fetchMoreFunctionVersion('${v.name}')">Fetch to local</button>
        </div>
        <div style="display:flex; flex-directionolumn; align-items:flex-end; gap:4px;">
          ${
            v.votedByMe
              ? '<span style="color:green;">✅ Already voted</span>'
              : `<button onclick="disableButtonTemporarily(this, () => promoteMoreFunctionVersion('${v.name}'))">Go Live</button>`
          }
          ${
            v.isLive
              ? '<small style="color:#999;">Live version (cannot delete)</small>'
              : `<label class="switch">
                   <input type="checkbox" onchange="deleteVoteMoreFunctionVersion('${v.name}', this.checked)" ${v.deleteVotedByMe ? 'checked' : ''}>
                   <span class="slider"></span>
                 </label>`
          }
        </div>
      </div>
    `).join('');

  } catch (err) {
    container.innerHTML = 'Failed to load MoreFunction versions: ' + err.message;
  }
}

function promoteMoreFunctionVersion(name) {
  fetch('/admin/morefunctions-promote', {
    method: 'POST',
    headers: { 'Authorization': getAuthHeader(), 'Content-Type': 'application/json' },
    body: JSON.stringify({ version: name })
  }).then(res => res.json()).then(data => {
    alert(data.message || data.error || 'Done');
    checkMoreFunctionVersions();
  });
}

function deleteVoteMoreFunctionVersion(name, checked) {
  fetch('/admin/morefunctions-deletevote', {
    method: 'POST',
    headers: { 'Authorization': getAuthHeader(), 'Content-Type': 'application/json' },
    body: JSON.stringify({ version: name })
  }).then(res => res.json()).then(data => {
    console.log(data.message || data.error);
    checkMoreFunctionVersions();
  });
}

async function fetchVersionFiles(version) {
  if (!confirm(`This will overwrite all current local files of same name with local files from version: ${version}. Proceed?`)) return;

  try {
    const res = await fetch('/admin/fetch-version', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    if (data.error) throw new Error(data.error);

    showMessage(data.message);
  } catch (err) {
    showMessage("Fetch failed: " + err.message);
  }
}

async function fetchSidedishVersion(version) {
  if (!confirm(`This will overwrite your current local sidedish.js with the version: ${version}. Proceed?`)) return;

  try {
    const res = await fetch('/admin/fetch-sidedish', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    if (data.error) throw new Error(data.error);

    showMessage(data.message);
  } catch (err) {
    showMessage("Fetch failed: " + err.message);
  }
}

window.fetchApiVersion = async function(version) {
  if (!confirm(`This will overwrite your current local api file with the version: ${version}. Proceed?`)) return;

  try {
    const res = await fetch('/admin/fetch-api', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    if (data.error) throw new Error(data.error);

    showMessage(data.message);
  } catch (err) {
    showMessage("Fetch failed: " + err.message);
  }
};



async function toggleDeleteVote(version) {
  try {
    const res = await fetch('/admin/vote-delete', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': getAuthHeader()
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    if (data.error) throw new Error(data.error);

    showMessage(data.message);
    checkVersions(); 
  } catch (err) {
    showMessage("Failed to update delete vote: " + err.message);
  }
}

async function deleteVoteApiVersion(version) {
  try {
    const res = await fetch('/admin/vote-delete-api', {
      method: 'POST',
      headers: {
        'Content-Type': 'application/json',
        'Authorization': getAuthHeader()
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    if (data.error) throw new Error(data.error);

    apivMessage(data.message);
  } catch (err) {
    apivMessage("Failed to update delete vote: " + err.message);
  }
}

async function promoteApiVersion(version) {
  if (!confirm(`Vote to activate api version: ${version}?`)) return;

  try {
    const res = await fetch('/admin/promote-api', {
      method: 'POST',
      headers: {
        'Authorization': getAuthHeader(),
        'Content-Type': 'application/json'
      },
      body: JSON.stringify({ version })
    });

    const data = await res.json();
    alert(data.message || data.error || 'Unknown result');
    checkApiVersions(); 
  } catch (err) {
    alert('Vote failed: ' + err.message);
  }
}


async function promoteVersion(version) {
  const res = await fetch('/admin/promote-version', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': getAuthHeader()
    },
    body: JSON.stringify({ version })
  });

  const data = await res.json();
  alert(data.message || data.error);
}

let syncEventSource = null;

function startSyncProgressStream() {
  if (syncEventSource) syncEventSource.close();

  syncEventSource = new EventSource('/admin/sync-progress');
  syncEventSource.onmessage = function (e) {
    const data = JSON.parse(e.data);
    showMessage(data.message);
  };
}

async function pushLocalFilesToCluster() {
  startSyncProgressStream();
  try {
    const res = await fetch('/admin/sync-to-cluster', {
      method: 'POST',
      headers: { 'Authorization': getAuthHeader() },
      credentials: 'include'
    });

    const data = await res.json();
    showMessage(data.message || "Sync completed.");
  } catch (err) {
    showMessage("Sync failed: " + err.message);
  }
}

    let selectedFolder = '';
    const folderTree = document.getElementById('folderTree');
    const fileList = document.getElementById('fileList');
    const fileInput = document.getElementById('fileInput');
    const selectedPathDisplay = document.getElementById('selectedPathDisplay');

function showMessage(msg) {
  const log = document.getElementById('message');
  log.innerHTML += `<div>${escapeHtml(msg)}</div>`;
  log.scrollTop = log.scrollHeight;
}
function showMessagesidedish(msg) {
  const log = document.getElementById('sidedishUploadLog');
  log.innerHTML += `<div>${escapeHtml(msg)}</div>`;
  log.scrollTop = log.scrollHeight;
}
function fileMessage(msg) {
  const log = document.getElementById('filemessage');
  log.innerHTML += `<div>${escapeHtml(msg)}</div>`;
  log.scrollTop = log.scrollHeight;
}

function apivMessage(msg) {
  const log = document.getElementById('apivmessage');
  log.innerHTML += `<div>${escapeHtml(msg)}</div>`;
  log.scrollTop = log.scrollHeight;
}

function loadFolders() {
  showTab('managerTab');
  fetch('/admin/folders', {
    headers: { 'Authorization': getAuthHeader() }
  })
  .then(res => {

    console.log('Response status:', res.status);

    if (!res.ok) throw new Error('Unauthorized');
    return res.json();
  })
  .then(data => {
    document.querySelector('.grid').classList.remove('locked');
    document.querySelector('.switch-block').classList.remove('locked');
    folderTree.innerHTML = renderTree(data, '');
    selectFolder('');
    loadClusterSetting(); 
  })
  .catch(err => {
    console.error('Error loading folders:', err);
    showMessage('Login failed. Check credentials.');
  });
}

function createEmptyFile() {
  const fileName = document.getElementById('newFolderName').value.trim();
  if (!fileName) return showMessage('Enter a file name.');

  const fullPath = selectedFolder ? `${selectedFolder}/${fileName}` : fileName;

  fetch('/admin/file', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': getAuthHeader()
    },
    body: JSON.stringify({ path: fullPath, content: '' }),
    credentials: 'include'
  })
  .then(res => res.json())
  .then(data => {
    showMessage(data.message);
    loadFiles();
    document.getElementById('newFolderName').value = '';
  });
}

function renderTree(tree, path, depth = 0) {
  let html = '<ul>';
  if (depth === 0) {
    html += `<li>
      <span onclick="selectFolder('')">/ (root)</span>
    </li>`;
    html += renderTree(tree, '', 1); // Render root's children
  } else {
    for (const key in tree) {
      const fullPath = path ? `${path}/${key}` : key;
      html += `
        <li>
          <span onclick="selectFolder('${escapeHtml(fullPath)}')">${escapeHtml(key)}</span>
          <span class="delete-folder" onclick="deleteFolderFromTree('${fullPath}')">❌</span>
          ${renderTree(tree[key], fullPath, depth + 1)}
        </li>`;
    }
  }
  html += '</ul>';
  return html;
}

function loadClusterSetting() {
  fetch('/admin/settings', { headers: { 'Authorization': getAuthHeader() } })
    .then(res => res.json())
    .then(data => {
      document.getElementById('clusterToggle').checked = !!data.useCluster;
    });
}

function saveClusterSetting() {
  const useCluster = document.getElementById('clusterToggle').checked;
  fetch('/admin/settings', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': getAuthHeader()
    },
    body: JSON.stringify({ useCluster }),
    credentials: 'include'
  })
  .then(res => res.json())
  .then(data => {
    showMessage(data.message);
  });
}

function deleteFolderFromTree(folderPath) {
  if (!confirm(`Are you sure you want to delete folder: ${folderPath}?`)) return;

  fetch('/admin/delete-folder', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': getAuthHeader()
    },
    body: JSON.stringify({ folder: folderPath }),
    credentials: 'include'
  })
  .then(res => res.json())
  .then(data => {
    showMessage(data.message);
    loadFolders();
    fileList.innerHTML = '';
    selectedPathDisplay.textContent = '(none)';
  });
}

    function selectFolder(path) {
      selectedFolder = path || '';
      selectedPathDisplay.textContent = selectedFolder || '/';
      fileList.innerHTML = '';
      loadFiles();
    }

    function loadFiles() {
  fetch('/admin/list-files?folder=' + encodeURIComponent(selectedFolder), {
    headers: { 'Authorization': getAuthHeader() }
  })
  .then(res => res.json())
  .then(async data => {
    const fileHtmls = await Promise.all(data.map(async file => {
      if (file.type === 'folder') {
        return `<div style="font-weight: bold; color: #f0a500; padding-left: 20px;padding-bottom:8px; position: relative;">
  <span style="position: absolute; left: 0;">📁</span>
  <em>${file.name}</em>
</div>
`;
      }

      const fullPath = selectedFolder ? `${selectedFolder}/${file.name}` : file.name;

      try {
        const res = await fetch('/admin/file-info?path=' + encodeURIComponent(fullPath), {
          headers: { 'Authorization': getAuthHeader() }
        });

        const info = await res.json();
        const ext = (info.extension || '').toLowerCase();
        const isEditable =
          editableMimeTypes.some(type => info.mimeType && info.mimeType.startsWith(type)) ||
          editableExtensions.includes(ext);

        const editButton = isEditable
          ? `<button onclick="editFile('${file.name}')">Edit</button>`
          : '';

        const checksumRes = await fetch('/admin/checksum?path=' + encodeURIComponent(fullPath), {
          headers: { 'Authorization': getAuthHeader() }
        });

        const checksumData = await checksumRes.json();
        const checksum = checksumData.checksum || 'Error';

        return `
          <div class="file-item">
            <span>${escapeHtml(file.name)}</span>
            <div style="display: flex; flex-direction: column; align-items: flex-end; gap: 0.3rem;">
              <div style="display: flex; gap: 0.5rem;">
                ${editButton}
                <button class="delete-btn" data-filename="${file.name}">Delete</button>
              </div>
              <div style="font-size: 0.75rem; color: #555; word-break: break-all;">
                SHA-256: ${checksum}
              </div>
            </div>
          </div>`;
      } catch (err) {
        return `
          <div class="file-item">
            <span>${escapeHtml(file.name)}</span>
            <div style="display: flex; gap: 0.5rem;">
              <button class="delete-btn" data-filename="${file.name}">Delete</button>
            </div>
          </div>`;
      }
    }));

    fileList.innerHTML = fileHtmls.join('');

  });
}
document.addEventListener('click', function (e) {
  if (e.target.classList.contains('delete-btn')) {
    const btn = e.target;

    
    if (!btn.classList.contains('confirm-delete')) {
      btn.classList.add('confirm-delete');
      btn.textContent = 'Confirm';

     
      setTimeout(() => {
        if (btn.classList.contains('confirm-delete')) {
          btn.classList.remove('confirm-delete');
          btn.textContent = 'Delete';
        }
      }, 4000); 

    } else {
      
      const filename = btn.getAttribute('data-filename');
      deleteFile(filename);
    }
  }
});


function editFile(filename) {
  const fullPath = selectedFolder ? `${selectedFolder}/${filename}` : filename;
  fetch('/admin/file?path=' + encodeURIComponent(fullPath), {
    headers: { 'Authorization': getAuthHeader() }
  })
  .then(res => res.json())
  .then(data => {
fileList.innerHTML = `
  <h4>Editing: ${escapeHtml(filename)}</h4>
  <label>New File Name:</label>
  <input type="text" id="editFileName" value="${filename}" />
  <label>Content:</label>
  <div class="editor-wrapper">
  <pre class="highlight-layer" id="highlight-output"></pre>
  <textarea id="editor" spellcheck="false"></textarea>
</div>
  <div style="margin-top:10px;">
    <button onclick="saveFile('${fullPath}')">Save</button>
    <button onclick="toggleFullScreenEditor()">Open editor in full screen</button>
    <button onclick="cycleEditorSize(this)">Resize Editor</button>
  </div>
`;
document.getElementById("editor").value = data.content;
initEditor();
  });
}

let editorSizeState = 'normal';

function cycleEditorSize(buttonEl) {
  const wrapper = document.querySelector(".editor-wrapper");
  const wrappersection = document.querySelector(".editorsection");
  const fileGrid = document.getElementById("fileandedit");
  const treePanel = document.querySelector(".tree");

  if (!wrapper || !fileGrid || !treePanel) return;


  wrapper.style.height = '';
  wrappersection.style.width = '';
  fileGrid.style.display = '';
  fileGrid.style.gridTemplateColumns = '';
  treePanel.style.display = '';

  if (editorSizeState === 'normal') {

    fileGrid.style.display = 'flex';
    fileGrid.style.flexDirection = 'row';
    treePanel.style.display = 'none';
    wrapper.style.height = '500px';
    wrappersection.style.width = '100%';
    editorSizeState = 'medium';
    buttonEl.textContent = "Resize Editor (Large)";
  } else if (editorSizeState === 'medium') {

    fileGrid.style.display = 'flex';
    fileGrid.style.flexDirection = 'row';
    treePanel.style.display = 'none';
    wrapper.style.height = '700px';
    wrappersection.style.width = '100%';
    editorSizeState = 'large';
    buttonEl.textContent = "Resize Editor (Normal)";
  } else {

    fileGrid.style.display = 'grid';
    fileGrid.style.gridTemplateColumns = '1fr 2fr';
    treePanel.style.display = '';
    wrapper.style.height = '';
    wrappersection.style.width = '100%';
    editorSizeState = 'normal';
    buttonEl.textContent = "Resize Editor (Medium)";
  }
}





function toggleFullScreenEditor() {
  const wrapper = document.querySelector(".editor-wrapper");
  if (!wrapper) return;

  if (!document.fullscreenElement) {
    wrapper.requestFullscreen().catch(err => {
      alert(`Error attempting full screen: ${err.message}`);
    });
  } else {
    document.exitFullscreen();
  }
}


function saveFile(originalPath) {
  const newName = document.getElementById('editFileName').value.trim();
  const content = document.getElementById('editor').value;

  const originalDir = originalPath.substring(0, originalPath.lastIndexOf('/'));
  const newPath = originalDir ? `${originalDir}/${newName}` : newName;

  fetch('/admin/file', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': getAuthHeader()
    },
    body: JSON.stringify({ path: originalPath, newPath, content }),
    credentials: 'include'
  })
  .then(res => res.json())
  .then(data => {
    fileMessage(data.message);
    loadFiles();


    const fileGrid = document.getElementById("fileandedit");
    const treePanel = document.querySelector(".tree");
    const wrapper = document.querySelector(".editor-wrapper");

    if (fileGrid && treePanel && wrapper) {
      fileGrid.style.display = 'grid';
      fileGrid.style.gridTemplateColumns = '1fr 2fr';
      treePanel.style.display = '';
      wrapper.style.height = '';
    }

    editorSizeState = 'normal';
    
  });
}

function deleteFile(filename) {
      fetch('/admin/delete-files', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': getAuthHeader()
        },
        body: JSON.stringify({ files: [selectedFolder + '/' + filename] }),
        credentials: 'include'
      })
      .then(res => res.json())
      .then(data => {
        fileMessage(data.message);
        loadFiles();
      });
    }

function deleteSelectedFolder() {
      if (selectedFolder.trim() === '') {
        fileMessage('Deleting root folder is not allowed');
        return;
      }
      fetch('/admin/delete-folder', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': getAuthHeader()
        },
        body: JSON.stringify({ folder: selectedFolder }),
        credentials: 'include'
      })
      .then(res => res.json())
      .then(data => {
        fileMessage(data.message);
        loadFolders();
        fileList.innerHTML = '';
        selectedPathDisplay.textContent = '(none)';
      });
    }

function createSubfolder() {
      const folderName = document.getElementById('newFolderName').value;
      if (!folderName.trim()) {
        fileMessage('Enter a subfolder name.');
        return;
      }
      fetch('/admin/create-folder', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json',
          'Authorization': getAuthHeader()
        },
        body: JSON.stringify({
          base: selectedFolder,
          folder: folderName
        }),
        credentials: 'include'
      })
      .then(res => res.json())
      .then(data => {
        fileMessage(data.message);
        loadFolders();
      });
    }

async function uploadFiles() {
  const files = droppedFiles || Array.from(fileInput.files);
  if (files.length === 0) {
    fileMessage('No files selected or dropped.');
    return;
  }

  const chunkSize = 2 * 1024 * 1024; // 2MB

  for (const file of files) {
    const totalChunks = Math.ceil(file.size / chunkSize);
    for (let i = 0; i < totalChunks; i++) {
      const chunk = file.slice(i * chunkSize, (i + 1) * chunkSize);
      const formData = new FormData();
      formData.append('files', chunk);
      formData.append('filename', file.name);
      formData.append('chunkNo', i + 1);
      formData.append('totalChunks', totalChunks);

      try {
        const res = await fetch('/admin/upload', {
          method: 'POST',
          headers: {
            'Authorization': getAuthHeader(),
            'x-upload-folder': encodeURIComponent(selectedFolder)
          },
          body: formData,
          credentials: 'include'
        });

        if (!res.ok) {
          const error = await res.text();
          throw new Error(error || `Chunk ${i + 1} failed`);
        }
      } catch (err) {
        fileMessage(`Upload failed for "${file.name}" (chunk ${i + 1}): ${err.message}`);
        return;
      }
    }

    fileMessage(`Upload complete: ${file.name}`);
  }

  droppedFiles = null;
  fileInput.value = '';
  loadFiles();
}

let droppedFiles = null;
const dropzone = document.getElementById('dropzone');
dropzone.addEventListener('click', () => fileInput.click());
dropzone.addEventListener('dragover', e => {
  e.preventDefault();
  dropzone.style.backgroundColor = '#eef2ff';
});
dropzone.addEventListener('dragleave', () => {
  dropzone.style.backgroundColor = '';
});
dropzone.addEventListener('drop', e => {
  e.preventDefault();
  droppedFiles = Array.from(e.dataTransfer.files);
  dropzone.style.backgroundColor = '';
  fileMessage(`${droppedFiles.length} file(s) dropped. Ready to upload.`);
});




function initEditor() {
  const editor = document.getElementById('editor');
  const highlightOutput = document.getElementById('highlight-output');

  if (!editor || !highlightOutput) return;

  function highlightHTML(code) {
    const escape = code
      .replace(/&/g, '&amp;')
      .replace(/</g, '&lt;')
      .replace(/>/g, '&gt;');

    return escape
      .replace(/(&lt;\/?[a-z]+)(.*?)(\/?&gt;)/gi, (match, tag, attrs, close) => {
        return `<span class="tag">${tag}</span><span class="attr">${attrs}</span><span class="tag">${close}</span>`;
      })
      .replace(/(&quot;[^&]*?&quot;)/g, '<span class="string">$1</span>');
  }

  editor.addEventListener('input', () => {
    highlightOutput.innerHTML = highlightHTML(editor.value);
  });

  editor.addEventListener('scroll', () => {
    highlightOutput.scrollTop = editor.scrollTop;
    highlightOutput.scrollLeft = editor.scrollLeft;
  });

  // Optional: highlight initial content
  highlightOutput.innerHTML = highlightHTML(editor.value);
}
  </script>

</div>

<footer style="text-align: center; margin-top: 3rem; color: #07112d; font-size: 0.9rem;">
<p style="font-size:10px;">Decentralized Hosting at...</p>
<div style="margin:auto;height:18.5px;width:133px">
  <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="100%" height="100%" viewBox="0 0 3030 421" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xml:space="preserve" xmlns:serif="http://www.serif.com/" style="fill-rule:evenodd;clip-rule:evenodd;stroke-linejoin:round;stroke-miterlimit:2;"><use id="Background" xlink:href="#_Image1" x="0" y="0" width="727px" height="101px" transform="matrix(4.16667,0,0,4.16667,0,0)"/><defs><image id="_Image1" width="727px" height="101px" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAtcAAABlCAYAAACGE88KAAAgAElEQVR4nO2dB7heRdHH/zGYhQCBBQQUFZAibSkGAekEjJCPTkAkQuhSBZGPLkVBejM0KSEQ6aE3ASH0joCLdEJHpW3oWSDhe4ZMPmO8Cfe+Z845u+fM73neR1HunNk973vO7O7Mf3pBURRFqQ1j3YIA9gSwRwx+vN4JZXKMdfMAcAAWBzA/gPkAfBPAHABmAzBTFxM2FsA7AN4F8BqAlwC8AODvAP4Wg39XJ1lRyqOXsW4lAF9v2Rw/HIP/sCd/YKxbFcDXBK79Ygz+ZQE7SWKs6wtgOSHfOnoJGOvopbNE+rMlyucx+LulDBrrZgWwdBpDwwcAKOh8H8CbPf3tpo6xjub5UQAXAtiqLQG2sY6CwmULmnktBv+8kEtJYKxbEsBAACsCoPfznCX4RYH2/QDuBHBzDP6lDKdqqhjrlp3KoqMqIj+33qOFTgz+g/pnpR6EfufZMR2A6QGMAjBri8a9DIDHevg3uwHYVODa9/IDs6lsC2CYwNgokJq3w79dGcCVDZ7jrnhP+DdMAd9oQXtiGOs+AvA678g9AcDz5/EY/LgUfe4mW9Aiwlg3NAb/RRYeF2NBge/Yybzrny3Gut4AVuf3y/q8K102C/BnCCb68Cw/My+LwT+S83wyZwNYKglPJs7vOH5mvcELG1oQPk0bSPTPMfgJCbhZFhK/8+yYLgZ/q7FuBQDX8SQoXXOUUHC9orFu+Rj8A02bZ2Pd1wRfdGfE4McK2VKaxYwAFubPgMlGFo11tHi9DcBNMfiHMhz1lgA+Mtbt0pIAu7UY6yi43Q7A1hUF1NOCfkv70sdY9zQHpyNj8G+2/T4JMf1kC5pVpjD5obHuYQD3AKDTxztj8B9nO1LlS75Mc4jBPwOAAuw7dFq6Jgb/Vzo+EzK3t7R/ibA+PzyK8imAkxo6R0p5GABrAPgdgAeNdc8b6w4z1i2c2ZzvBOA0Y53WxDQQY90qxrqrATwHYP8EAuspWQTAcQBeMdadbaxbLC33GsdMfHJxIIAbKU/eWHeLsW53Y9232j45ufL/OcQx+Hc4z+vctk/KNDhayM5Gxrr5SvGwXvYSuvqIGPw/cp0EJRlooXcwgGeMddcY61bM6NbsxKdlSkMw1q1urLud85xpIyL1xZPhnfUnjHUXa5BdGTTvawH4A6W+GevuMtbtYKzr15LxN4L/KNCLwX8ag9+Wj4f0SHIKYvB03PyggCnKsdtD1LmaMdb17+K4qxPoe3dsk+ZGSYL16NiVghtjnVTBbdnsY6zTADtzjHXfN9Zdz3mnq2U4GloE/JSD7LOMdXMl4FNb6MU1RGcC+Iex7kxd5ORBl+oXMfhjAGwMQPN+/hup3evtjXWzSDpWM78SuvyoplX/K0lBwc0DxrpzWBEldSgH9lD9CuWHsW56XhxRse2gBgyJAr3tKZ2FUxYk1LOU7kNKXDuQnKKx7kaulVMSZao/jhj8Vbxiel1v3n9wFVf5FoXyrHZMY0jFYB3WzYTMSS1eFGVabMs7cQMymKVDjHX7JeCH0k2MdStzUL1vA6VuZ+aUhXsyrGdoCmsDuM9Yd4OxbvG2T0aKTHPlGYN/lDWLmyDNIwJL5hwjZO6Xxrrpah6SBLsJvUBuaYgMlJIHtCikwqF9MvD2yEz8bDX0PDfWHc7iAE1X36Kd00eNdY3YJMqUdUiC1Fh3eiYnca3hK491YvCky0gNVK5o+2RNxgWssVuUbwvu+NaCsY5k0X4hdG3dtVaqhp6BRxvrzmC94ZQhP3fWb0iacC7yraz60JaUCUpV+KOx7k/cQEypnt5cAP20sS7reKJJdOsBwJqLg2n3pO0TBi78BHCCkDkphY26GArAClybumbemucUKA2AFojnZ5BHepoG2OlhrPsBn/Cu2tIpGMJpCt9JwJe2Qou7S4x1F+kudv10+0VCDQ1i8AdwMPVZg+eku5xFepQCdvob63KsIJ/UNEZK9UR3rZW62UJw0VwmFGBvqd+WNDDWrcPyevO0fCqW5GLhZRLwpc1sTh2ojXU/bPtE1EmPd2li8OcDWBPAO42aiR4Sg/8QwClC5n5dmeOyDOLOXkV5VtOOlETYw1i3fQY3Y4SxbkgCfrQavgfXcddQZWJDHJK7XEnnolbmBUD62Fu3eA5qpaMj0Bj8XVzoKKGakTPDqFWxgP/rZlp1LZXSciwXiipKCpxqrFsy8TvxNU5j2TQBX1oJnx6c36L86u7SjwuFc1DiaTLUjOZcKrDVbq/V0/FDIQY/BsCP6EeU8fgLEYN/G8A5AqZ6CepEV4KxbmluNV2Uf/ALSlFSoQ+AkRko+dDz+wJj3foJ+NIqjHXUVGVEzYF1BPAy6R5TOgYrlDzCm15UcP95jb7NAOBaY93yNfqgTIQKbIdnULDdKAq9PGLwY411g1j1IifeEPT1eAA7C0jRDTXWHcRt6HNAatf6RC4QTY2nMmv8kOLO/8kATipoYxZ+Ts3JOa2LAFiKF/ZlHsXTzvWeAI4r8RoS0HNnlLFucAz+msR9bQTGOtIYHllxYP02B893cTD9HIBXqBZqan9grKPvxvwAvg9gCd4MWZkD3yog9ZCbjXU/isE/WdlMyUFFzjf3wFovfl5Nzx/LaTLz8WcBAK4m3XNKD+ljrNsqBj++husX5XX+7mZD4Z2ZGDytjl/KadCSxOBfMdZdyIWeRZiBg/TDUx+zsW5uLpooynsATq93NFPl0xh8a7/XQowtaw6NdX34Yfsz/pQRaB9krBseg5coXC4TellfbKxbNwZ/W+K+Zo2xjoLUSyoKkF4B8CcAlwH4W09T52Lwn3E9C32uZa30PrwwHcIysGV3CaYUEWp0snwM/l8lX0uaN6WfX9S1EwApy6zI3WIH8klZFVDB9ifGuh2mtShLlM9zex9rrpgM1FRG4su6Gz/8UkeqacypXBiqKD2CTjsokIzBUztgkv/6Lb04hGdxlozStWhxfp3muZaHsW42Ll7sV+JlvuDibhINmD8Gf2AM/jGpmhT+3dwRg6fGL3NzwPWghO1pQMV1V/BOequJwY+Lwd8bgz8uBr8egNl5c+CyilTYtgNwRNvvQxVocC0AH3lJHMmSTuXPax/QNDDWzcCC9UUZx2kDilKIGHyIwR8CYFE+Opdk14yaY0wKsFdIwJdGwbKjF3CgWBYUYC0Rg9+EF46lpnpxoHdRDJ7yotcC8FCJl1sxgxSryqHNpRj8xTH4zTi9lvKjy97h35/SQxKcjkahwbUcRwlZ+lXilb1b8Wq7KOfG4N+sdyhKk4jBv8w7flISmeC8yZwUOSjAvkkLycSh1vNrl2TbA1iFAqy6cpO5gdfynJv7dkmX+aUW304deh/G4H/PefK/Llnu+Cxj3XIl2m89GlwLEYO/X2jXjHL6fpzEoKaAg36JY3IqqDhW1DlFmfg7HB+D3124GUxuuzz9uJCsfwK+ZA8rIx1WwjgmcDpT/xj83XXPEzeKOw/AYgCuLOky53CbeGXq9+GTGDw9vxaihlFCKadT0ofrNMrOuW8tGlzLIrV7nWpTmbW58rwol8bgX6x3KErDoZ3GG4SGuDrn2+YEBdh/NtY5/aJ3DssxnldC0Rkd/Q+gdCYuPEyGGPxbMfiNaae5hDzgOajWJqXxpgqnu+3KhY9jSnBzfg7elRLQ4FqQGPyfqe2ogMWBib4UpeT3tNW5UiosN0XFO2MFrkPPyVUzvGMUyNymAXYh9mJZRkkeB/BDKixMeeAx+GGsZvGesOlNND2k+3DTvh+U1MV4C2PdhmX53mY0uJZHKnBMSqWAX9BrCZi6MQb/uIAdRZkmMfh/Ch7n5xhcgwNsysFeMAFfssJYR7rqhwr7TAH1ajH4V3OYixj87ZQPzjrDkpzMsnRK9+4DLXAGcxqRNKcZ62bW+yCLBtfyUMX3CwJWh7CedCpIBftSqTOK0h3OBhAEZmqVjGebGlncrgF2jzlCuOEKBaqDOFDKhhg8FVwOEC50pKYqe+Q0D3XDOfGHcHMbSSUZej4clOespIsG18LwcbSE5FAf1pOuHS5AGSLgx/0x+DtTGJPSDlhH/VKBwS6W+YTNwwH2dxLwJXm4iFGykJXSBTeMwX+c43zE4J/lFJEPBM2SJNysgvZaQQz+TA6wJSGVsu+1fW4l0eC6HEYA+KeA5Z0S0djdRaigR3etlTq4XOCafTlNIGfI/9HGum/rt/Ar2Y/bWUtA74J1c9uxnpIY/KPcdEZKvYKUKvaU87A9xODpRG5fwQFTg5+D2z6vkmhwXQIkzg/gJAHLswu0VS8E58XtLGDqKaFGO4rSU+5j+ceiNGFnZwHewc59oVAaxrrFuDW4BPS92zQGL52zXAsxeOpQ+TvBa++p+b6dEYOnztDDBU3+XFPH5NDgujzOEKqy3pO7g9UFdYz8hsC1j6acsRrHobQUTg2RqINoSkC6AMv0Sfyum8ivBHetD01Bw1oYKqq7R8jkLKzqo3TGrkIKZUTv1IQUckaD65LgI8DTBawvTEeKdYyBm8ZIHNtRZfxFAnYUpVMkdGIli9vqZgmW6dMAezKMdbPzhoIEjzQxFY7rirYB8ImQyd1r3kDKFj4lp3qoKDSGrTPU9E8S/UKXy8kAxglcoa7VJBWwLC5g54QY/KcCdhSlUyRa7Tetm9kSLNOnXdr+DRUxSknE7RKD/1zIVlLE4J8TKtwHp1sNaMjUVA63zJeS6OsrJF7QejS4LhHW2R0hcIXVuXq9aiR2rd9hOTRFqROJRe6MDbyDy3CrdA2wJ7KNkJ0LY/APCtlKlWOECvchOO9t5VgAzwmNfdu2T6YEGlyXz7FCxVSVVlVzUc/aAqZO5ZxXRakTI3BtqaPX1FiOA+wUlIlqgxtlSXSzpIXc/plOQ7fh5/pvhMxtYKxrUtpVpXAL/f2Errm0sW7RTIaeLBpcl0wMfgw3linKz1hvuiokgnnSdB2W4G1R2ofEUf9nDZ41CrCva3mAvamQnQti8K8I2Uqd8wG8IeAjnQoNavZUlc6VgsWNgxMeZxZocF0NEkUtfYQk8b4SYx21TN5SwNTZMXjJrl6K0ikSzVPebfjsr9HyAHsDITsSMqxZwLU0pwn5umHDp6tUWI3rWKFrbJToMLNBg+sKiME/TtJXAlfa2Vgncbz9VewksNNHhTzHl++qonSLhQSmSWKHLnUowL7EWCfRNCobjHXUAnpJAX9vicE/0ZJpm8QZQilTA1mhSumcUQD+JTB/S6uSUDE0uK6OIwWuNCd3yCoNfqlKtF2/qEVHo0rCcFtfiRfFqy25zyT9OaplAfZAITsjhexkQwyeitavF/B3TqEFTmvhkwSJNFRa5KzZ9vksggbXFRGDv5M7xRWl7MLGzQFI5HYfLWBDUSRYS8DGOCGt7LK4FcAzgrbXI8ULY13vtIZZGisJGP60xV1oJQI6CN2HtnOx0PhXbvtEFkGD62qRyL1e0li3Role7yVg49oY/N8F7CiKBJsL2PCJaxa/xoGJxAJ+EptQwVpLAuwVBWzczM3D2sh1QnKXEveh7dwrdMq2XNsnsggaXFfLtQCeFLhiKbvXHLQvJWCqcV3JlDwx1i3OecRFeSD1CeDjedqlv0HQLKWhndfkXFhj3UwAFhMwJZEakSUsyyfR5n3ZFk6fKFzYeIuATcq7ni6hoWWFBtcVwl96iXSJ9Yx1C5bguUQnyLtj8PcK2FEUCaQ6l92Uw92IwX/Mlf6XCJqljm2nNTjAXpRzTItyV73DqB2J5/6CFRXtN517BMb3dbofbZ/ITtFVSfVcCODwgtJg9CL4JX9EMNYtzIVMRWnKrnUfY918CfgxJW9oK/nuYawj3dyNBUzRcfdtVfgsAX0/jHVD2O+hQmZJQYjmdBfeJGgSiwuMZSyApxo2Lz1FIqDrzYsdKb3mtiJxigA+0Xk6gTmcLtH38Zu8ofFfaHBdMZS3aawjLco/FLzyNsa63wjm+O0hsHvjhY+k64Qe8C8m6Ncy+uL5aox18woqN1w5tQdoqsTgxxvrtuPdJymFIQqw6fj/f3Oai24gsTv3UAx+Qumeps39Qt7Np8+4YsTgnzXW0YJv1oKmUglo50n0fUynhFd19X/QauCl6v2phN/G4Icn6ts5AA4BMHsBG5QnSC/PE4o6Y6yzQjtcRzdwV0vJDGPdt1k9YzYhz8/N8TvAAfZW/I9SAfbexrrxMXipVssp8F0BH57LfxqKEYN/31hHTcPmKGgqxR3KHCF1ox8U9Fvit9FKaOd63oYOvF8CPnQJ7YIZ604WyAfdnezQS7SgnR25/WwRXhbO81SUHmOs6w/gCsGXAjUE+Uuud4ID7G0AzMzyehLsa6z7NAZ/cL2jE2MeAUPPJzKWunleILiWuB8K8IJAcP0tncfO0ILG+jgVwEcFrz5f0ZaxXA28u8AsHJu4VJnSYIx1MxjrDuOiKsndlqNyP43hHP3BrFYkxW+MdU3ZvZZoMJSyBnqVSMyD1IlT25G4F7btk9gpGlzXRAz+XQB/FLh6UVm+TQV2CugoMNUUHKXBUIteY93e/CKhnVTJroKPUqfRJszeZAG25C78kQ0JsCWCubcFbDSBtwTGoMG1DG8KWNF70SEaXNcL5Ut/VtCDlfkovFMkmsZQasonAnYUZZoY62Y21q1krNvLWEfyeK/TqQmAuUuYuT2aVKTGAfYGAEYLmqUAe2dBe3Uwk8A1i55CNoUPBcYxS9snUQiJd/L0SYwkQ1QtpEZi8K8b60jRYNuCXtDu9ZY9/SMKUgRE+z/iFBdFmZKtjXWrC8xKX64JmLvCnZRTYvCN0y3meg+S3LwRwKpCZkkDm2yfLmQvR9ramXFKJFR1dNNPBongumgtVmvR4Lp+jiFZvYIyeJsZ634dg+/pMdAeAqM/IwYfBOwozWPeTAumSa943wT8KAUOsNendt2CLY4pwP44Bn9eYsPtDhLF70WLyptCVpKVDWds2yegTnSFWDMx+GdIR7egF5Rn+oue/AHLlRVtsEEpLScWtKEoKUEvpPVz07XuKayPPxDAg4Jmh3Pzmtx4X8Df3hmOuwz6Nm9I2VJU41opgAbXaSDR1fAXrPzRXXYTeCGMpNSW4q4rShLQMepGMfhWyKpxgL0Oyw1KQO+T8411P613ZLWgecITmUHAhvZKkEHiXmgtQYdocJ0AMfiHuOlFEebp7k40yZYB2KHg9b7gQjJFaQJfqmnE4G9v091k1aIBwgH2SE47yQWJAEJ3bCcys4ANzV+XQSK4HpfCQHJEg+t0OFrAk926+e8NESgMo5bQTxe0oSgpQGkBA2PwTWnd3yNi8G9xgC3VZZBaro/KKMB+V8BG0cYpTUFiHiTuhyKj3671VB2iwXUixOBvAfBIQW9WMdYt1Y1/T6KQUWIxoCh1QwvEFWLwd7T5TkwWYL8gZJIC7EuNdQOE7JWJhEb1AukOr1K+J3AxDa5lkLgXGlx3iKqFpAUFrJcW9Ii6LW4/tf/TWLcGgCUKXmN0DF6yECpFqFjzjQT9+jQBH5oCKVvsFoOX0ObNnhj8ayydeIfQi9kAuI6k/2LwtyU8PxK/c4n5agILCoxB63hkkFjw/UPABr2zXhawkyJTLXzX4DotLuej2YUKeLWFsW4fzqXsCt217h5PxuCXzsFRpce8SIvQGPz1OnX/CQfYPwFwu0DnVnDeJwXYa8Xg75X0VRCJF3+RZ3YjMNb1E0pFaGogVjUSwfWrRQ3E4J8EMF/lo68ZqeB6KSE5I0myO1qibnDGOtK9PquAGXqZDe1KIs9YR1/wonmQj8bgbypoQ1Hq4C3Wlf8DdytUuoDUUngHWzLAvtFYR3ntDyQ452MEbPzQWNcrBt9mpYsfCtl5SchOazHWLSAkxfdi2+eyU6SC61di8CpYLsP5AH4L4JsFrO1krDupiwf9TgWb1UBzrZUMISWMM0iHWdv0dw8OsNcEcLdQkRrtat5srNswBi/Zfl2CpwRsUIH4IkK2cmUlAb8ncB2EUgyJewG9F52jBY2JwTtqJxT0amEAq03+PxjrKP9xu4J2qdhpVEEbilIlp8fgXQz+VA2sewY3uBogVPAHDrCvT7DI0QvZWVnITq6sKOD3mKY3cKoIieCauo4+m8VoE0SD6zQ5Q0Drc8cp/vmnAjtQx8Xgtc2vkhNUg6AyaR0Sg/ccYEudTE7KwR5Y+WCmQgz+fSEZwrVLcTADuHeCxOLir42frGr4scBVntD0uc7R4DpBWL3glIKebTJFULFrQXv/BDCioA1FqRrqnHeQznrncIC9tmBdzZcBtkD9hyQSueCDjHUzpTOkShkEYEaBC96X0ZiTxFhHue/zC/iWYn1ENmhwnS4nFeyO1IcLG+nHtiyA5QqO9OQYvHZrUnrCyfyQL/JZR2DGdzHWSbxsWgsXIg4UDLBJB/uwhObzbgEb0wNYV8BOjmwq5HOqijI5sbmQr7rQKYBK8SVKDP5tY905BXecdzTWnSCwa/0+p6ooSk8YG4MvWvn/krHuagAbFLBBgdzh3JlU6RAKsI11dB9uEGqtnBK3CPnycwAXN2NKuoexjlQp1hMwFQQaqbUaY910gsH1rW2fzyLoznXaHMtFBZ1ChY0bCfzYTlc1GKVGDgVQVOKMcq/7600sRgz+dt6dbVRxaAx+jJDsGKWGLCxgJyeoaVlfAX9Ha01PYai26lsCdp6JwRfWuG4zGlwnTAyexPQvKujhuXxc2SmRj/cVpRZi8I8JdC6FykjKwN0WN+Qupk3iSoGx9BJq1JUFvFO6m5CvVzV8uqpgb6Fr6L0oiAbX6VM0IOhX8O/Pi8FLtEBVlCIcVvAUh1iTuw8qBYnB3wxgcMMC7MuF7GxtrCvSpyAn6FR0XgF/6Xt0TUvmrBSMdVR0LNVVWIPrgmhwnTgx+Ce4sr4OJnBqiqLUSgyemnP8ScCHY4x1+twTIAZ/DQfYTTnKv1+o9XZfzvFvNCy/d4TQGP8cgy8qP9ta+JkmdTI3RpVCiqMvmTw4qiYvR1GnttwmS2kshwvslC6phY1ycIA9lBfiuY+FxnCekLltjHVLCdlKlV8B+K6QbyrzWoxd+NkmwYiWt/EXQYPrDIjB3yMkFdVTNEdVSQZe6A0X8OdwY12ROgRlMmLwFwDYqgkBNteoSIyDcq9PbeopibGOgur9hMy9CeBaIVutw1hHaTm/Fxr3eP4NKAXR4DofjqzY01ti8NotS0mN33ORbRG+K1iEpfw7wN4p97lg6Uip3F9qQb2nkK1kMNbRwoFkYmcW8onUqJpWHFsJxrreAEYK3ovLY/CvJT7sLNDgOh9uBOAr9LauVBRFmSox+FeENNcPYH1eRYgY/Fl8PJ07Jwj6f2QDJSBJkWItIVvUmOx0IVtthN7TqwiO+/i2T6gUGlxnAudAVRXwPsRyW4qSIkcL6CxbAAfq3ZUlBk+B0i8zH8NdAO4QMkedcq8w1s0pZK9WjHVrCp+iUn7vvzKcitox1m0tKL1H3BSDfzDT6UgODa7zgrR+i3a86w6aa60kC0tD/kHAv904d1QRJAY/DMD+mc/poYK26Dt2tbFOotFKbRjrFgNwGYDeQj58VkO6YyMw1q0P4EzhsRzc0uksBQ2uMyIG/3kF0njPCjVTUJQyOQ7AhwXtU1Hjb/UuyRODPyrnAJs7UUq1RCdWoMDUWNdH0GZlGOsWBHAzn/hIMYzTvJQeYKzbgJS8AHxdcN4u1F1rWTS4zo9zubq6LI5hSSpFSZYY/NtC+YFbGeukJKyUyeAAO+fajV8LK6AMouYcue1gG+sWAkCLjXkEzb7bBi1waYx1O1KakXBg/RGAfdMYYXPQ4DozYvCflNiO/A2uPFaUHDgJQNHGE720eLc8YvD755pmFoOnAvJThc2uA2C0sW4uYbulYKxbHsC9woE1sX8MPiQyzOShEw9jHaXC/bGEuO1AVQiRR4PrPDkNwAcleH5iDP7TNkygkj8x+LFCgfE6xroB+pUohxj8fkI58nVwEIDXha+7HICHOXBNFmPdtlzYOYewjxSsn5Xy2FPCWLcIAOp1sXsJbtG9GJbv7KSLBtcZwkGFhBzZ5LxXgk1FKZtTALwjcI1jWL9XKYc9c3y+xODfB7BjCaa/TY3BjHX7G+umK8F+x5BEpbHuAtayNsLm6eR1O+0A+NUY64yxjhSNHgOwbAmXoO/2zzUNtBw0uM6XEwFI7jKfEoMvWiCmKJXC31mJ3E3SIv6p3r1y4GBqlxx3LGPwN5S0MJiOmyLdb6xbrgT7PcZYtzmApwFsUdIl9o3BP13iELKHunoa62j+n+Rnm/QCZxLbx+BfbNfsVocG15nCcmQjhLwfV2Iet6KUDeUh/lPgGkfkquaQAxxg70zKBBm6vxeAv5Vkuz8H2Bez3F3lGOtWM9aRvvdFAMrKB7+KT5qULqBCV2PddhxU08nB90qcp6Nj8JfpfSgPDa7zhmT5JI7Xhsfg32rzRCr5wkW+EpJ632tCC++UicGPJ4WW3AJs/o5tIlBAOzV68cnJE8Y6ajqzStlpSpx2sDEH1aQGsnKJl3sBwNaaDvKfUEoQNeYx1p3Buf1nA/h+yZe9ShtolY8G1xkTg3+eRf2LMJ41gxUlZyg/9FUB/w821vXTb0J5TBZgX5uZ389zADy+xMtQQL0RgDspIDXWHWasc1KBNgdzFLifxupQl5ccVIMXJOvF4MtamGQDp3wsZqzb3lg3gmV1/wLgFwBmrWAcdwMYwr9BpUSkCim+S0UQGd2oNxqkikEyV5sV+PtLNO+qS0j6aL4E/foqJrSxMQP9no11h9ApTEFTswPYh1UilJKgl7uxbjA3w1gvl3mOwd9krNuFU5HKZn7umkeft411owHcxWkDz9Ficlo7wVwoSTZIbWJxAKtzID1jZRM2sQvj4Bj8UxVeU4o5O3gH9OXmVP34v8/B94A+ZGtpALPUNB5qErNuDP7jmq7fMcY6mtO5c/JZKrh+XMhOVSzDFbjZE4P/q7GOOmcN7HAs2uq8a/K9cPAAAAV+SURBVBYFkOOi472KdkBSZCQfdy5Q0Le9aGcvBv9GM6cpDXhBNJh3sDt9flVODP5MY93sXIxYFRSkbcqfSYwz1v2LO5V+yLUzFDjPxP/5TcF3fCdMYDWKv6R8P6dBFQuoqrgfwNo5nh4Y674D4BpemGSDpoU0g061fm+MwZdVpKMolRKD/xzAbwSuOQOAw/TulQ+fIFIaxOjM/D4ygY0J2s2bl3elSTN7NZZso53q7yQQWA+NwV9aow/KRCgwXTPTwHpZ3nHPKrCGBtfNIAY/mr+APUU70ylN4xIAEkfQ2xjrFtVvR/nwMfW6GQbY+2n6UJdQYL1VDP5PCfrWNkgFbONMU0E25dqDrNJBJqHBdXPoaaB8Xwz+zrZPmtIsuCGCRCV8b02Zqg5++W/U4SZBnX4fAWBXDigVgO7j+jH4C3QuauUTXuDsmVvxIhXvGusOAHApnyJmiQbXzeFqFv/vLrprrTSVq4RqKtYz1pWtpKAwfGw9MMMAm5Q31ue85zZDvRdWjcFf3/J5qBt69vWPwY/MzXHuM3A+9RxIwJ1CaHDdEHjH7phujuap3GSwFKW7sIKClI6rtkWvkMkC7Ccy85sCyuV6uMHRJEjF5Acx+EdaOv4UIGWW31H+fY7qLMa6bwC4jYpgE3CnMBpcN4sLuqn1e5SK+StNhltWPyAwxB9RzqJ+WaqDA+wBGQbYT3GAfX4C7lQFbeocyQVzEl1Slc64g1TQYvAH5ygzzPUtdGK1UgLuiKDBdYPgH9WJXzGiV7nFraI0nQOExvd71gxWKoI7xg7IbSc4Bv9BDH4o9x54OwGXymQMgDVi8AfE4D9r7jCThhobbRSDXz0G//ccB2Cs+wlLBebYV2KqaHDdPM4C8M40RnW8PgiVNhCDv413dIqyMIAd9EtTLRxg/5hbZ+fm+2UsidfEXezxnILotCi+NqiJ0DbUjyEGf1Wug+CGTDdw051GocF1w4jBU1HNqVMZ1TvcJlpR2oJU7vUhxrqZ9FtTLTH417izYI4B9ju8i72SUIpSClAgtEQMft8c5d0awGgunl0kBj+Ctf2zw1jX21h3CscqjYxDNbhuJsMAfNTFyIZx8K0orSAGfw+AmwTGOhd1btRvTfVwgE1Fjq9n6v+9nLu/Ucadge/gFJD/icG3tWizLl7lbqALxeAHxOCvZQGDLDHWUfv361jCsrFocN1AYvCU63f2FCP7aBo72orSZKQafexjrJtLvynVE4MfwzvYuQbYX/DxfX8AGwilK5XNFyzxuirn9N6egc9NgfKnj+NF2bwx+ANj8M/nPjZjHeVV02Jz7QTcKRUt0mkuJwCgfKav8wjP4aBbUVpFDP5hY93VHNQUYUYABzd9xyVVKLgw1q3BL+c5Mh3DBG5HfY2xjlo6bwdgCACbgHuToAXMCADDeVGjlMsELtylU7a7SY6OT2sahbGO0qOuBPCNNnyfNLhuKDH4V4x1FwIYyvqXx7d9TpRWczDnKhbVrN7RWHdSDP65tk9oHdC8G+sGsB5ulgH2JGLwlCKyu7Fub057IYWRQQBmq8Gd17j50igK8HLr6pcBFEC/yQuXl7kg8TmWm/RNz1831tECcjiAPgm4UwkUXK/RgnFOSfbHK92EKrq3Iuk9Craz8FiGu1v6vSakClweE5jDl4R8KUwM/m/GuhUA9JUwJ+ze8wJz3RqN4Ri85wC7EWkKMfjITb2uNdZ9jVNH1uJCyB+VFGy/zicA9LklVxm3abA9gDoLkCmlhvTaP+D/DG1dsHDXRVq8/SQBdypDO481HNplA3BmDP7Jts+FoijNwVi3PIBlY/CNriXhPNXFASwGYH7WA56bd+5n43SlKXmP1aHe5UD6Jf54yufVhi+KUiIA/g8QaTgi2/mrGAAAAA5lWElmTU0AKgAAAAgAAAAAAAAA0lOTAAAAAElFTkSuQmCC"/></defs></svg>
</footer>

</body>

</html>
Folder to add into contract folder:

Code: Select all

dist
Files to add into /contract/dist (4):
hp.cfg.override

Code: Select all

{
    "contract": {
        "unl": [
            "eda4a4516e425aa8bf8d8010506083885651b0d9d10de77ea665d7f5f863df3975"
        ],
        "bin_path": "/usr/bin/node",
        "bin_args": "index.js"
    }
}
index.js

Code: Select all

/******/ (() => { // webpackBootstrap
/******/ 	var __webpack_modules__ = ({

/***/ 875:
/***/ ((module, __unused_webpack_exports, __nccwpck_require__) => {

/******/ (() => { // webpackBootstrap
/******/ 	var __webpack_modules__ = ({

/***/ 782:
/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require2_) => {

"use strict";
__nccwpck_require2_.r(__webpack_exports__);
/* harmony export */ __nccwpck_require2_.d(__webpack_exports__, {
/* harmony export */   "controlMessages": () => (/* binding */ controlMessages),
/* harmony export */   "clientProtocols": () => (/* binding */ clientProtocols),
/* harmony export */   "constants": () => (/* binding */ constants),
/* harmony export */   "writeAsync": () => (/* binding */ writeAsync),
/* harmony export */   "writevAsync": () => (/* binding */ writevAsync),
/* harmony export */   "readAsync": () => (/* binding */ readAsync),
/* harmony export */   "invokeCallback": () => (/* binding */ invokeCallback),
/* harmony export */   "errHandler": () => (/* binding */ errHandler)
/* harmony export */ });
const fs = __nccwpck_require2_(147);

const controlMessages = {
    peerChangeset: "peer_changeset"
}
Object.freeze(controlMessages);

const clientProtocols = {
    json: "json",
    bson: "bson"
}
Object.freeze(clientProtocols);

const constants = {
    MAX_SEQ_PACKET_SIZE: 128 * 1024,
    PATCH_CONFIG_PATH: "../patch.cfg",
    POST_EXEC_SCRIPT_NAME: "post_exec.sh"
}
Object.freeze(constants);

function writeAsync(fd, buf) {
    return new Promise(resolve => fs.write(fd, buf, resolve));
}
function writevAsync(fd, bufList) {
    return new Promise(resolve => fs.writev(fd, bufList, resolve));
}
function readAsync(fd, buf, offset, size) {
    return new Promise(resolve => fs.read(fd, buf, 0, size, offset, resolve));
}

async function invokeCallback(callback, ...args) {
    if (!callback)
        return;

    if (callback.constructor.name === 'AsyncFunction') {
        await callback(...args).catch(errHandler);
    }
    else {
        callback(...args);
    }
}

function errHandler(err) {
    console.log(err);
}

/***/ }),

/***/ 244:
/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require2_) => {

"use strict";
// ESM COMPAT FLAG
__nccwpck_require2_.r(__webpack_exports__);

// EXPORTS
__nccwpck_require2_.d(__webpack_exports__, {
  "HotPocketContract": () => (/* binding */ HotPocketContract)
});

// EXTERNAL MODULE: ./src/common.js
var common = __nccwpck_require2_(782);
;// CONCATENATED MODULE: ./src/patch-config.js


const fs = __nccwpck_require2_(147);

// Handles patch config manipulation.
class PatchConfig {

    // Loads the config value if there's a patch config file. Otherwise throw error.
    getConfig() {
        if (!fs.existsSync(common.constants.PATCH_CONFIG_PATH))
            throw "Patch config file does not exist.";

        return new Promise((resolve, reject) => {
            fs.readFile(common.constants.PATCH_CONFIG_PATH, 'utf8', function (err, data) {
                if (err) reject(err);
                else resolve(JSON.parse(data));
            });
        });
    }

    updateConfig(config) {

        this.validateConfig(config);

        return new Promise((resolve, reject) => {
            // Format json to match with the patch.cfg json format created by HP at the startup.
            fs.writeFile(common.constants.PATCH_CONFIG_PATH, JSON.stringify(config, null, 4), (err) => {
                if (err) reject(err);
                else resolve();
            });
        });
    }

    validateConfig(config) {
        // Validate all config fields.
        if (!config.version)
            throw "Contract version is not specified.";
        if (!config.unl || !config.unl.length)
            throw "UNL list cannot be empty.";
        for (let publicKey of config.unl) {
            // Public keys are validated against length, ed prefix and hex characters.
            if (!publicKey.length)
                throw "UNL public key not specified.";
            else if (!(/^(e|E)(d|D)[0-9a-fA-F]{64}$/g.test(publicKey)))
                throw "Invalid UNL public key specified.";
        }
        if (!config.bin_path || !config.bin_path.length)
            throw "Binary path cannot be empty.";
        if (config.consensus.mode != "public" && config.consensus.mode != "private")
            throw "Invalid consensus mode configured in patch file. Valid values: public|private";
        if (config.consensus.roundtime < 1 && config.consensus.roundtime > 3600000)
            throw "Round time must be between 1 and 3600000ms inclusive.";
        if (config.consensus.stage_slice < 1 || config.consensus.stage_slice > 33)
            throw "Stage slice must be between 1 and 33 percent inclusive.";
        if (config.consensus.threshold < 1 || config.consensus.threshold > 100)
            throw "Consensus threshold must be between 1 and 100 percent inclusive.";
        if (config.npl.mode != "public" && config.npl.mode != "private")
            throw "Invalid npl mode configured in patch file. Valid values: public|private";
        if (config.round_limits.user_input_bytes < 0 || config.round_limits.user_output_bytes < 0 || config.round_limits.npl_output_bytes < 0 ||
            config.round_limits.proc_cpu_seconds < 0 || config.round_limits.proc_mem_bytes < 0 || config.round_limits.proc_ofd_count < 0)
            throw "Invalid round limits.";
        if (config.max_input_ledger_offset < 0)
            throw "Invalid max input ledger offset";
    }
}
;// CONCATENATED MODULE: ./src/contract-context.js



// HotPocket contract context which is passed into every smart contract invocation.

class ContractContext {

    #patchConfig = null;
    #controlChannel = null;

    constructor(hpargs, users, unl, controlChannel) {
        this.#patchConfig = new PatchConfig();
        this.#controlChannel = controlChannel;
        this.contractId = hpargs.contract_id;
        this.publicKey = hpargs.public_key;
        this.privateKey = hpargs.private_key;
        this.readonly = hpargs.readonly;
        this.timestamp = hpargs.timestamp;
        this.users = users;
        this.unl = unl; // Not available in readonly mode.
        this.lclSeqNo = hpargs.lcl_seq_no; // Not available in readonly mode.
        this.lclHash = hpargs.lcl_hash; // Not available in readonly mode.
    }

    // Returns the config values in patch config.
    getConfig() {
        return this.#patchConfig.getConfig();
    }

    // Updates the config with given config object and save the patch config.
    updateConfig(config) {
        return this.#patchConfig.updateConfig(config);
    }

    // Updates the known-peers this node must attempt connections to.
    // toAdd: Array of strings containing peers to be added. Each string must be in the format of "<ip>:<port>".
    updatePeers(toAdd, toRemove) {
        return this.#controlChannel.send({
            type: common.controlMessages.peerChangeset,
            add: toAdd || [],
            remove: toRemove || []
        });
    }
}
;// CONCATENATED MODULE: ./src/control.js
const control_fs = __nccwpck_require2_(147);


class ControlChannel {

    #fd = null;
    #readStream = null;

    constructor(fd) {
        this.#fd = fd;
    }

    consume(onMessage) {

        if (this.#readStream)
            throw "Control channel already consumed.";

        this.#readStream = control_fs.createReadStream(null, { fd: this.#fd, highWaterMark: common.constants.MAX_SEQ_PACKET_SIZE });
        this.#readStream.on("data", onMessage);
        this.#readStream.on("error", (err) => { });
    }

    send(obj) {
        const buf = Buffer.from(JSON.stringify(obj));
        if (buf.length > common.constants.MAX_SEQ_PACKET_SIZE)
            throw ("Control message exceeds max size " + common.constants.MAX_SEQ_PACKET_SIZE);
        return (0,common.writeAsync)(this.#fd, buf);
    }

    close() {
        this.#readStream && this.#readStream.close();
    }
}
;// CONCATENATED MODULE: ./src/npl.js


const npl_fs = __nccwpck_require2_(147);

// Represents the node-party-line that can be used to communicate with unl nodes.
class NplChannel {

    #fd = null;
    #readStream = null;

    constructor(fd) {
        this.#fd = fd;
    }

    consume(onMessage) {

        if (this.#readStream)
            throw "NPL channel already consumed.";

        this.#readStream = npl_fs.createReadStream(null, { fd: this.#fd, highWaterMark: common.constants.MAX_SEQ_PACKET_SIZE });

        // When hotpocket is sending the npl messages, first it sends the public key of the particular node
        // and then the message, First data buffer is taken as public key and the second one as message,
        // then npl message object is constructed and the event is emmited.
        let publicKey = null;

        this.#readStream.on("data", (data) => {
            if (!publicKey) {
                publicKey = data.toString();
            }
            else {
                onMessage(publicKey, data);
                publicKey = null;
            }
        });

        this.#readStream.on("error", (err) => { });
    }

    send(msg) {
        const buf = Buffer.from(msg);
        if (buf.length > common.constants.MAX_SEQ_PACKET_SIZE)
            throw ("NPL message exceeds max size " + common.constants.MAX_SEQ_PACKET_SIZE);
        return (0,common.writeAsync)(this.#fd, buf);
    }

    close() {
        this.#readStream && this.#readStream.close();
    }
}

;// CONCATENATED MODULE: ./src/unl.js


class UnlCollection {

    #readonly = null;
    #pendingTasks = null;
    #channel = null;

    constructor(readonly, unl, channel, pendingTasks) {
        this.nodes = {};
        this.#readonly = readonly;
        this.#pendingTasks = pendingTasks;

        if (!readonly) {
            for (const [publicKey, stat] of Object.entries(unl)) {
                this.nodes[publicKey] = new UnlNode(publicKey, stat.active_on);
            }

            this.#channel = channel;
        }
    }

    // Returns the unl node for the specified public key. Returns null if not found.
    find(publicKey) {
        return this.nodes[publicKey];
    }

    // Returns all the unl nodes.
    list() {
        return Object.values(this.nodes);
    }

    count() {
        return Object.keys(this.nodes).length;
    }

    // Registers for NPL messages.
    onMessage(callback) {

        if (this.#readonly)
            throw "NPL messages not available in readonly mode.";

        this.#channel.consume((publicKey, msg) => {
            this.#pendingTasks.push((0,common.invokeCallback)(callback, this.nodes[publicKey], msg));
        });
    }

    // Broadcasts a message to all unl nodes (including self if self is part of unl).
    async send(msg) {
        if (this.#readonly)
            throw "NPL messages not available in readonly mode.";

        await this.#channel.send(msg);
    }
}

// Represents a node that's part of unl.
class UnlNode {

    constructor(publicKey, activeOn) {
        this.publicKey = publicKey;
        this.activeOn = activeOn;
    }
}
;// CONCATENATED MODULE: ./src/user.js


class UsersCollection {

    #users = {};
    #infd = null;

    constructor(userInputsFd, usersObj, clientProtocol) {
        this.#infd = userInputsFd;

        Object.entries(usersObj).forEach(([publicKey, arr]) => {

            const outfd = arr[0]; // First array element is the output fd.
            arr.splice(0, 1); // Remove first element (output fd). The rest are pairs of msg offset/length tuples.

            const channel = new UserChannel(outfd, clientProtocol);
            this.#users[publicKey] = new User(publicKey, channel, arr);
        });
    }

    // Returns the User for the specified public key. Returns null if not found.
    find(publicKey) {
        return this.#users[publicKey]
    }

    // Returns all the currently connected users.
    list() {
        return Object.values(this.#users);
    }

    count() {
        return Object.keys(this.#users).length;
    }

    async read(input) {
        const [offset, size] = input;
        const buf = Buffer.alloc(size);
        await (0,common.readAsync)(this.#infd, buf, offset, size);
        return buf;
    }
}

class User {

    #channel = null;

    constructor(publicKey, channel, inputs) {
        this.publicKey = publicKey;
        this.inputs = inputs;
        this.#channel = channel;
    }

    async send(msg) {
        await this.#channel.send(msg);
    }
}

class UserChannel {

    #outfd = null;
    #clientProtocol = null;

    constructor(outfd, clientProtocol) {
        this.#outfd = outfd;
        this.#clientProtocol = clientProtocol;
    }

    send(msg) {
        const messageBuf = this.serialize(msg);
        let headerBuf = Buffer.alloc(4);
        // Writing message length in big endian format.
        headerBuf.writeUInt32BE(messageBuf.byteLength)
        return (0,common.writevAsync)(this.#outfd, [headerBuf, messageBuf]);
    }

    serialize(msg) {

        if (!msg)
            throw "Cannot serialize null content.";

        if (Buffer.isBuffer(msg))
            return msg;
        else if (this.#clientProtocol == common.clientProtocols.bson)
            return Buffer.from(msg);
        else // json
            return Buffer.from(JSON.stringify(msg));
    }
}
;// CONCATENATED MODULE: ./src/hotpocket-contract.js







const hotpocket_contract_fs = __nccwpck_require2_(147);
const tty = __nccwpck_require2_(224);

class HotPocketContract {

    #controlChannel = null;
    #clientProtocol = null;
    #forceTerminate = false;

    init(contractFunc, clientProtocol = common.clientProtocols.json, forceTerminate = false) {

        return new Promise(resolve => {
            if (this.#controlChannel) { // Already initialized.
                resolve(false);
                return;
            }

            this.#clientProtocol = clientProtocol;

            // Check whether we are running on a console and provide error.
            if (tty.isatty(process.stdin.fd)) {
                console.error("Error: HotPocket smart contracts must be executed via HotPocket.");
                resolve(false);
                return;
            }

            this.#forceTerminate = forceTerminate;

            // Parse HotPocket args.
            hotpocket_contract_fs.readFile(process.stdin.fd, 'utf8', (err, argsJson) => {
                const hpargs = JSON.parse(argsJson);
                this.#controlChannel = new ControlChannel(hpargs.control_fd);
                this.#executeContract(hpargs, contractFunc);
                resolve(true);
            });
        });
    }

    #executeContract(hpargs, contractFunc) {
        // Keeps track of all the tasks (promises) that must be awaited before the termination.
        const pendingTasks = [];
        const nplChannel = new NplChannel(hpargs.npl_fd);

        const users = new UsersCollection(hpargs.user_in_fd, hpargs.users, this.#clientProtocol);
        const unl = new UnlCollection(hpargs.readonly, hpargs.unl, nplChannel, pendingTasks);
        const executionContext = new ContractContext(hpargs, users, unl, this.#controlChannel);

        (0,common.invokeCallback)(contractFunc, executionContext).catch(common.errHandler).finally(() => {
            // Wait for any pending tasks added during execution.
            Promise.all(pendingTasks).catch(common.errHandler).finally(() => {
                nplChannel.close();
                this.#terminate();
            });
        });
    }

    #terminate() {
        this.#controlChannel.close();
        if (this.#forceTerminate)
            process.kill(process.pid, 'SIGINT');
    }
}

/***/ }),

/***/ 53:
/***/ ((module, __unused_webpack_exports, __nccwpck_require2_) => {

const { clientProtocols, constants } = __nccwpck_require2_(782);
const { HotPocketContract } = __nccwpck_require2_(244);

module.exports = {
    Contract: HotPocketContract,
    clientProtocols,
    POST_EXEC_SCRIPT_NAME: constants.POST_EXEC_SCRIPT_NAME,
}

/***/ }),

/***/ 147:
/***/ ((module) => {

"use strict";
module.exports = __nccwpck_require__(147);

/***/ }),

/***/ 224:
/***/ ((module) => {

"use strict";
module.exports = __nccwpck_require__(224);

/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __nccwpck_require2_(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		var threw = true;
/******/ 		try {
/******/ 			__webpack_modules__[moduleId](module, module.exports, __nccwpck_require2_);
/******/ 			threw = false;
/******/ 		} finally {
/******/ 			if(threw) delete __webpack_module_cache__[moduleId];
/******/ 		}
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__nccwpck_require2_.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__nccwpck_require2_.o(definition, key) && !__nccwpck_require2_.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__nccwpck_require2_.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__nccwpck_require2_.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/compat */
/******/ 	
/******/ 	if (typeof __nccwpck_require2_ !== 'undefined') __nccwpck_require2_.ab = __dirname + "/";
/******/ 	
/************************************************************************/
/******/ 	
/******/ 	// startup
/******/ 	// Load entry module and return exports
/******/ 	// This entry module is referenced by other modules so it can't be inlined
/******/ 	var __webpack_exports__ = __nccwpck_require2_(53);
/******/ 	module.exports = __webpack_exports__;
/******/ 	
/******/ })()
;

/***/ }),

/***/ 655:
/***/ ((__unused_webpack_module, __webpack_exports__, __nccwpck_require__) => {

"use strict";
__nccwpck_require__.r(__webpack_exports__);
/* harmony export */ __nccwpck_require__.d(__webpack_exports__, {
/* harmony export */   "syncapp": () => (/* binding */ syncapp)
/* harmony export */ });
const fs = __nccwpck_require__(147);
const path = __nccwpck_require__(17);

const stateRoot = 'public'; 

class syncapp {
  sendOutput;
  sendRawOutput;
  
   clearSidedishVotes(voter, entry) {
  if (entry.voters?.includes(voter)) {
    entry.voters = entry.voters.filter(k => k !== voter);
  }
  if (entry.delete?.includes(voter)) {
    entry.delete = entry.delete.filter(k => k !== voter);
  }
}

async init(ctx) {
    this.contractCtx = ctx;
    const configRaw = await fs.promises.readFile('/contract/cfg/hp.cfg', 'utf8');
    const hpconfig = JSON.parse(configRaw);
    this.unl = new Set(hpconfig.contract?.unl || []);
    const threshold = hpconfig.contract?.consensus?.threshold || 100; 
    this.quorumThreshold = threshold / 100; 
  }

  async handleRequest(user, message, isReadOnly) {
    console.log(`[syncapp] Received message type: ${message.type}`);
console.log(`[syncapp] User index:`, user.userId || user.index || user);
console.log(`[syncapp] About to send output to user:`, user);
    if (isReadOnly) {
      await this.sendOutput(user, {
        type: 'error',
        error: 'Read-only mode is not supported for syncing.'
      });
      return;
    }

else if (message.type === 'go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = path.join('votes.json');
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }


  for (const [ver, entry] of Object.entries(votes)) {
    if (ver !== versionName && entry.goLive?.includes(voter)) {
      entry.goLive = entry.goLive.filter(pk => pk !== voter);
      console.log(`[syncapp] ${voter} removed go-live vote from '${ver}'`);
    }
  }


  votes[versionName].delete = votes[versionName].delete.filter(pk => pk !== voter);


  if (!votes[versionName].goLive.includes(voter)) {
    votes[versionName].goLive.push(voter);
    console.log(`[syncapp] ${voter} voted to go-live '${versionName}'`);
  }


  for (const entry of Object.values(votes)) {
    entry.goLive = entry.goLive.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(voteFilePath, JSON.stringify(votes));


  const totalVotes = votes[versionName].goLive.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const livePath = '/contract/contract_fs/seed/state/public';
    const versionsPath = `/contract/contract_fs/seed/state/versions/${versionName}`;

    if (!fs.existsSync(versionsPath)) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
      return;
    }

    if (fs.existsSync(livePath)) {
      fs.rmSync(livePath, { recursive: true, force: true });
    }

    fs.symlinkSync(versionsPath, livePath, 'dir');


    for (const entry of Object.values(votes)) {
      entry.goLive = [];
    }

    fs.writeFileSync(voteFilePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `Version '${versionName}' is now live by consensus.`
    });
    return;
  }

  await this.sendOutput(user, {
    type: 'info',
    message: `Vote recorded for '${versionName}'. Waiting for quorum (${totalVotes}/${quorum}).`
  });
  return;
}


else if (message.type === 'api-go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'api_votes.json';
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }


  for (const [ver, entry] of Object.entries(votes)) {
    if (ver !== versionName && entry.goLive?.includes(voter)) {
      entry.goLive = entry.goLive.filter(pk => pk !== voter);
    }
  }


  votes[versionName].delete = votes[versionName].delete.filter(pk => pk !== voter);


  if (!votes[versionName].goLive.includes(voter)) {
    votes[versionName].goLive.push(voter);
  }


  for (const entry of Object.values(votes)) {
    entry.goLive = entry.goLive.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(voteFilePath, JSON.stringify(votes));


  const totalVotes = votes[versionName].goLive.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const livePath = 'dynamic-endpoints-data.json';
    const versionsPath = `api_versions/${versionName}/dynamic-endpoints-data.json`;

    if (!fs.existsSync(versionsPath)) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
      return;
    }

    try {
      fs.copyFileSync(versionsPath, livePath);

      const liveDir = path.dirname(livePath);
      const liveFlagPath = path.join(liveDir, '.live_api_version');
      fs.writeFileSync(liveFlagPath, versionName);
    } catch (err) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Failed to promote dynamic-endpoints-data.json: ${err.message}`
      });
      return;
    }


    for (const entry of Object.values(votes)) {
      entry.goLive = [];
    }

    fs.writeFileSync(voteFilePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `dynamic-endpoints-data.json version '${versionName}' is now live.`
    });
  } else {
    await this.sendOutput(user, {
      type: 'info',
      message: `Vote recorded for API '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}


else if (message.type === 'api-vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'api_votes.json';
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  const livePath = 'dynamic-endpoints-data.json';
  const currentLive = fs.existsSync(livePath)
    ? fs.readFileSync(livePath, 'utf8')
    : null;

  const versionPath = path.join('api_versions', versionName, 'dynamic-endpoints-data.json');

  if (!fs.existsSync(versionPath)) {
    await this.sendOutput(user, {
      type: 'error',
      error: `Version '${versionName}' does not exist. Cannot vote to delete.`
    });
    return;
  }

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  const entry = votes[versionName];


  entry.goLive = entry.goLive.filter(pk => pk !== voter);


  if (!entry.delete.includes(voter)) {
    entry.delete.push(voter);
    console.log(`[api-vote-delete] ${voter} voted to delete '${versionName}'`);
  } else {
    console.log(`[api-vote-delete] ${voter} already voted to delete '${versionName}'`);
  }


  for (const e of Object.values(votes)) {
    e.goLive = e.goLive.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  console.log(`[api-vote-delete] Delete votes for '${versionName}':`, votes[versionName].delete);
  console.log(`[api-vote-delete] Total votes: ${totalVotes}, Quorum required: ${quorum}`);

  const liveVersion = fs.existsSync(livePath) && fs.existsSync(versionPath)
    ? fs.readFileSync(versionPath, 'utf8') === currentLive
    : false;

  if (liveVersion) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Cannot delete the live API version.'
    });
    return;
  }

  if (totalVotes >= quorum) {
    const folderPath = path.dirname(versionPath);
    if (fs.existsSync(folderPath)) {
      fs.rmSync(folderPath, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(voteFilePath, JSON.stringify(votes));
      console.log(`[api-vote-delete] API version '${versionName}' deleted by quorum.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `API version '${versionName}' has been deleted.`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
    }
  } else {
    fs.writeFileSync(voteFilePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `Voted to delete API version '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}

else if (message.type === 'vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = path.join('votes.json');
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  const liveSymlink = '/contract/contract_fs/seed/state/public';
  const liveVersion = fs.existsSync(liveSymlink)
    ? path.basename(fs.readlinkSync(liveSymlink))
    : null;

  if (versionName === liveVersion) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Cannot vote to delete the live version.'
    });
    return;
  }

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  votes[versionName].goLive = votes[versionName].goLive.filter(pk => pk !== voter);

  const entry = votes[versionName];
  const alreadyVoted = entry.delete.includes(voter);

  if (alreadyVoted) {
    entry.delete = entry.delete.filter(pk => pk !== voter);
    console.log(`[syncapp] ${voter} unvoted to delete '${versionName}'`);
  } else {
    entry.delete.push(voter);
    console.log(`[syncapp] ${voter} voted to delete '${versionName}'`);
  }

  for (const e of Object.values(votes)) {
    e.goLive = e.goLive.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const versionPath = path.join('versions', versionName);
    if (fs.existsSync(versionPath)) {
      fs.rmSync(versionPath, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(voteFilePath, JSON.stringify(votes));
      console.log(`[syncapp] Version '${versionName}' deleted by consensus.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `Version '${versionName}' has been deleted by consensus.`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
    }
  } else {
    fs.writeFileSync(voteFilePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `${alreadyVoted ? 'Unvoted' : 'Voted'} to delete '${versionName}'. (${totalVotes}/${quorum})`
    });
  }

  return;
}


else if (message.type === 'list-versions') {
      const versionsPath = path.join('versions');
      console.log(`[syncapp] list-versions triggered`);
      console.log(`[syncapp] Looking in: ${versionsPath}`);
     
      try {
        if (!fs.existsSync(versionsPath)) {
          console.warn(`[syncapp] No versions directory found.`);
          await this.sendOutput(user, { type: 'versions', data: [] });
          return;
        }
    
        const entries = fs.readdirSync(versionsPath);
        console.log(`[syncapp] Entries in versions/:`, entries);
    
        const versions = entries.filter(entry => {
          const full = path.join(versionsPath, entry);
          const isDir = fs.statSync(full).isDirectory();
      
          return isDir;
        });
    
        console.log(`[syncapp] Found version folders:`, versions);
    
        await this.sendOutput(user, {
          type: 'versions',
          data: versions
        });
    
      } catch (err) {
        console.error(`[syncapp] Error reading versions: ${err.message}`);
        await this.sendOutput(user, {
          type: 'error',
          error: `Failed to read versions: ${err.message}`
        });
      }
    
      return;
    }

else if (message.type === 'api-upload') {
  const { version, chunkNo, totalChunks, chunk } = message;
  console.log(`[api-upload] Upload received from ${user.publicKey}`);

  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized API uploader. Must be in UNL.'
    });
    return;
  }

  if (!version || !chunkNo || !totalChunks || !chunk) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Missing fields in API upload.'
    });
    return;
  }

  try {
    const versionedPath = path.join('api_versions', version, 'dynamic-endpoints-data.json');
    const versionDir = path.dirname(versionedPath);
    if (!fs.existsSync(versionDir)) {
      fs.mkdirSync(versionDir, { recursive: true });
      console.log(`[api-upload] Created folder: ${versionDir}`);
    }

    const chunkData = Buffer.from(chunk, 'hex');
    fs.appendFileSync(versionedPath, chunkData);
    console.log(`[api-upload] Wrote chunk ${chunkNo}/${totalChunks} → ${versionedPath}`);

    if (parseInt(chunkNo) === parseInt(totalChunks)) {
      console.log(`[api-upload] Upload complete.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `API uploaded as version '${version}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'ack',
        chunkNo
      });
    }

  } catch (err) {
    console.error(`[api-upload] Upload error: ${err.message}`);
    await this.sendOutput(user, {
      type: 'error',
      error: `Failed to store chunk ${chunkNo}: ${err.message}`
    });
  }
}


else if (message.type === 'more-upload') {
  const { version, chunkNo, totalChunks, chunk } = message;
  console.log(`[more-upload] Upload received from ${user.publicKey}`);

  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized more uploader. Must be in UNL.'
    });
    return;
  }

  if (!version || !chunkNo || !totalChunks || !chunk) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Missing fields in more upload.'
    });
    return;
  }

  try {
    const versionedPath = path.join('more_versions', version, 'morefunctions.json');
    const versionDir = path.dirname(versionedPath);
    if (!fs.existsSync(versionDir)) {
      fs.mkdirSync(versionDir, { recursive: true });
      console.log(`[more-upload] Created folder: ${versionDir}`);
    }

    const chunkData = Buffer.from(chunk, 'hex');
    fs.appendFileSync(versionedPath, chunkData);
    console.log(`[more-upload] Wrote chunk ${chunkNo}/${totalChunks} → ${versionedPath}`);

    if (parseInt(chunkNo) === parseInt(totalChunks)) {
      console.log(`[more-upload] Upload complete.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `more uploaded as version '${version}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'ack',
        chunkNo
      });
    }

  } catch (err) {
    console.error(`[more-upload] Upload error: ${err.message}`);
    await this.sendOutput(user, {
      type: 'error',
      error: `Failed to store chunk ${chunkNo}: ${err.message}`
    });
  }
}
else if (message.type === 'more-go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'more_votes.json';
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  for (const [ver, entry] of Object.entries(votes)) {
    if (ver !== versionName && entry.goLive?.includes(voter)) {
      entry.goLive = entry.goLive.filter(pk => pk !== voter);
    }
  }

  votes[versionName].delete = votes[versionName].delete.filter(pk => pk !== voter);

  if (!votes[versionName].goLive.includes(voter)) {
    votes[versionName].goLive.push(voter);
  }

  for (const entry of Object.values(votes)) {
    entry.goLive = entry.goLive.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(voteFilePath, JSON.stringify(votes));

  const totalVotes = votes[versionName].goLive.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const livePath = 'morefunctions.json';
    const versionsPath = path.join('more_versions', versionName, 'morefunctions.json');

    if (!fs.existsSync(versionsPath)) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
      return;
    }

    try {
      fs.copyFileSync(versionsPath, livePath);
      fs.writeFileSync('.live_more_version', versionName);
    } catch (err) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Failed to promote morefunctions.json: ${err.message}`
      });
      return;
    }

    for (const entry of Object.values(votes)) {
      entry.goLive = [];
    }

    fs.writeFileSync(voteFilePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `morefunctions.json version '${versionName}' is now live.`
    });
  } else {
    await this.sendOutput(user, {
      type: 'info',
      message: `Vote recorded for more '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}


else if (message.type === 'more-vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'more_votes.json';
  let votes = fs.existsSync(voteFilePath)
    ? JSON.parse(fs.readFileSync(voteFilePath, 'utf8'))
    : {};

  const liveFlagPath = '.live_more_version';
  const liveVersion = fs.existsSync(liveFlagPath)
    ? fs.readFileSync(liveFlagPath, 'utf8').trim()
    : null;

  if (liveVersion === versionName) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Cannot delete the live more version.'
    });
    return;
  }

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  const entry = votes[versionName];

  entry.goLive = entry.goLive.filter(pk => pk !== voter);

  const alreadyVoted = entry.delete.includes(voter);
  if (alreadyVoted) {
    entry.delete = entry.delete.filter(pk => pk !== voter);
  } else {
    entry.delete.push(voter);
  }

  for (const e of Object.values(votes)) {
    e.goLive = e.goLive.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  const versionFolder = path.join('more_versions', versionName);

  if (totalVotes >= quorum) {
    if (fs.existsSync(versionFolder)) {
      fs.rmSync(versionFolder, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(voteFilePath, JSON.stringify(votes));
      await this.sendOutput(user, {
        type: 'success',
        message: `more version '${versionName}' has been deleted.`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
    }
  } else {
    fs.writeFileSync(voteFilePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `${alreadyVoted ? 'Unvoted' : 'Voted'} to delete more version '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}

else if (message.type === 'sidedish-upload') {
  const { version, chunkNo, totalChunks, chunk } = message;
  console.log(`[sidedish] Upload received from ${user.publicKey}`);

  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized sidedish uploader. Must be in UNL.'
    });
    return;
  }

  if (!version || !chunkNo || !totalChunks || !chunk) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Missing fields in sidedish upload.'
    });
    return;
  }

  try {
    const versionedPath = path.join('sidedish_versions', version, 'sidedish.js');
    const versionDir = path.dirname(versionedPath);
    if (!fs.existsSync(versionDir)) {
      fs.mkdirSync(versionDir, { recursive: true });
      console.log(`[sidedish] Created folder: ${versionDir}`);
    }

    const chunkData = Buffer.from(chunk, 'hex');
    fs.appendFileSync(versionedPath, chunkData);
    console.log(`[sidedish] Wrote chunk ${chunkNo}/${totalChunks} → ${versionedPath}`);

    if (parseInt(chunkNo) === parseInt(totalChunks)) {
      console.log(`[sidedish] Upload complete.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `sidedish.js uploaded as version '${version}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'ack',
        chunkNo
      });
    }

  } catch (err) {
    console.error(`[sidedish] Upload error: ${err.message}`);
    await this.sendOutput(user, {
      type: 'error',
      error: `Failed to store chunk ${chunkNo}: ${err.message}`
    });
  }
}

else if (message.type === 'sidedish-go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  const votePath = 'sidedish_votes.json';
  let votes = fs.existsSync(votePath) ? JSON.parse(fs.readFileSync(votePath)) : {};

  const versionPath = path.join('sidedish_versions', versionName, 'sidedish.js');
  if (!fs.existsSync(versionPath)) {
    await this.sendOutput(user, { type: 'error', error: `Version '${versionName}' does not exist.` });
    return true;
  }

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, { type: 'error', error: 'Unauthorized voter.' });
    return true;
  }

  for (const entry of Object.values(votes)) {
    if (entry.voters?.includes(voter)) {
      entry.voters = entry.voters.filter(pk => pk !== voter);
    }
  }

  if (!votes[versionName]) votes[versionName] = {};
  if (!votes[versionName].voters) votes[versionName].voters = [];
  if (!votes[versionName].delete) votes[versionName].delete = [];

  votes[versionName].voters.push(voter);

  for (const entry of Object.values(votes)) {
    entry.voters = entry.voters.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(votePath, JSON.stringify(votes));

  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);
  const totalVotes = votes[versionName].voters.length;

  if (totalVotes >= quorum) {
    const livePath = 'sidedish.js';
    const sourcePath = path.join('sidedish_versions', versionName, 'sidedish.js');

    try {
      fs.copyFileSync(sourcePath, livePath);
      fs.writeFileSync('.live_sidedish_version', versionName);
      console.log(`[sidedish] Activated version '${versionName}'`);
    } catch (err) {
      console.error(`[sidedish] Activation error: ${err.message}`);
      await this.sendOutput(user, { type: 'error', error: `Activation failed: ${err.message}` });
      return true;
    }

    votes[versionName].voters = [];
    fs.writeFileSync(votePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `sidedish.js version '${versionName}' is now live.`
    });
  } else {
    await this.sendOutput(user, {
      type: 'info',
      message: `Vote recorded for '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return true;
}


else if (message.type === 'sidedish-vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  const votePath = 'sidedish_votes.json';
  let votes = fs.existsSync(votePath) ? JSON.parse(fs.readFileSync(votePath)) : {};

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter.'
    });
    return true;
  }

  if (!votes[versionName]) votes[versionName] = { voters: [], delete: [] };
  const entry = votes[versionName];

  entry.voters = entry.voters.filter(pk => pk !== voter);

  const alreadyVoted = entry.delete.includes(voter);
  if (alreadyVoted) {
    entry.delete = entry.delete.filter(pk => pk !== voter);
  } else {
    entry.delete.push(voter);
  }

  for (const e of Object.values(votes)) {
    e.voters = e.voters.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const versionPath = path.join('sidedish_versions', versionName);
    if (fs.existsSync(versionPath)) {
      fs.rmSync(versionPath, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(votePath, JSON.stringify(votes));
      await this.sendOutput(user, {
        type: 'success',
        message: `Deleted sidedish version '${versionName}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' not found.`
      });
    }
  } else {
    fs.writeFileSync(votePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `${alreadyVoted ? 'Unvoted' : 'Voted'} to delete '${versionName}'. (${totalVotes}/${quorum})`
    });
  }

  return true;
}

else if (message.type === 'upload') {
      const { filename, chunkNo, totalChunks, chunk, version } = message;
  console.log(`[syncapp] Received upload message from ${user.publicKey}`);
  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized uploader. Must be in UNL.'
    });
    return;
  }

      if (!filename || !chunkNo || !totalChunks || !chunk || !version) {
        console.log(`[syncapp] Missing upload fields.`);
        await this.sendOutput(user, {
          type: 'error',
          error: 'Missing required fields: filename, chunkNo, totalChunks, chunk, version'
        });
        return;
      }

      try {
        const normalizedFilename = filename.replace(/\\/g, '/');
        const versionedPath = path.join('versions', version, normalizedFilename);

        const versionDir = path.dirname(versionedPath);
        if (!fs.existsSync(versionDir)) {
          fs.mkdirSync(versionDir, { recursive: true });
          console.log(`[syncapp] Created version folder: ${versionDir}`);
        }

        const chunkData = Buffer.from(chunk, 'hex');
        fs.appendFileSync(versionedPath, chunkData);
        console.log(`[syncapp] Chunk ${chunkNo}/${totalChunks} written → ${versionedPath}`);

        if (parseInt(chunkNo) === parseInt(totalChunks)) {
          console.log(`[syncapp] File complete: ${versionedPath}`);
          await this.sendOutput(user, {
            type: 'success',
            message: `File '${normalizedFilename}' stored in version '${version}'.`
          });
        } else {
          await this.sendOutput(user, {
            type: 'ack',
            chunkNo
          });
        }

      } catch (err) {
        console.error(`[syncapp] Chunk ${chunkNo} error: ${err.message}`);
        await this.sendOutput(user, {
          type: 'error',
          error: `Failed to store chunk ${chunkNo}: ${err.message}`
        });
      }

    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: 'Unknown message type.'
      });
    }
  }
}


/***/ }),

/***/ 962:
/***/ ((module) => {

module.exports = eval("require")("./sidedish");


/***/ }),

/***/ 411:
/***/ ((module) => {

module.exports = eval("require")("./sidedish_fallback");


/***/ }),

/***/ 113:
/***/ ((module) => {

"use strict";
module.exports = require("crypto");

/***/ }),

/***/ 147:
/***/ ((module) => {

"use strict";
module.exports = require("fs");

/***/ }),

/***/ 17:
/***/ ((module) => {

"use strict";
module.exports = require("path");

/***/ }),

/***/ 224:
/***/ ((module) => {

"use strict";
module.exports = require("tty");

/***/ }),

/***/ 618:
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {

"use strict";


function isAnyArrayBuffer(value) {
    return ['[object ArrayBuffer]', '[object SharedArrayBuffer]'].includes(Object.prototype.toString.call(value));
}
function isUint8Array(value) {
    return Object.prototype.toString.call(value) === '[object Uint8Array]';
}
function isRegExp(d) {
    return Object.prototype.toString.call(d) === '[object RegExp]';
}
function isMap(d) {
    return Object.prototype.toString.call(d) === '[object Map]';
}
function isDate(d) {
    return Object.prototype.toString.call(d) === '[object Date]';
}
function defaultInspect(x, _options) {
    return JSON.stringify(x, (k, v) => {
        if (typeof v === 'bigint') {
            return { $numberLong: `${v}` };
        }
        else if (isMap(v)) {
            return Object.fromEntries(v);
        }
        return v;
    });
}
function getStylizeFunction(options) {
    const stylizeExists = options != null &&
        typeof options === 'object' &&
        'stylize' in options &&
        typeof options.stylize === 'function';
    if (stylizeExists) {
        return options.stylize;
    }
}

const BSON_MAJOR_VERSION = 6;
const BSON_INT32_MAX = 0x7fffffff;
const BSON_INT32_MIN = -0x80000000;
const BSON_INT64_MAX = Math.pow(2, 63) - 1;
const BSON_INT64_MIN = -Math.pow(2, 63);
const JS_INT_MAX = Math.pow(2, 53);
const JS_INT_MIN = -Math.pow(2, 53);
const BSON_DATA_NUMBER = 1;
const BSON_DATA_STRING = 2;
const BSON_DATA_OBJECT = 3;
const BSON_DATA_ARRAY = 4;
const BSON_DATA_BINARY = 5;
const BSON_DATA_UNDEFINED = 6;
const BSON_DATA_OID = 7;
const BSON_DATA_BOOLEAN = 8;
const BSON_DATA_DATE = 9;
const BSON_DATA_NULL = 10;
const BSON_DATA_REGEXP = 11;
const BSON_DATA_DBPOINTER = 12;
const BSON_DATA_CODE = 13;
const BSON_DATA_SYMBOL = 14;
const BSON_DATA_CODE_W_SCOPE = 15;
const BSON_DATA_INT = 16;
const BSON_DATA_TIMESTAMP = 17;
const BSON_DATA_LONG = 18;
const BSON_DATA_DECIMAL128 = 19;
const BSON_DATA_MIN_KEY = 0xff;
const BSON_DATA_MAX_KEY = 0x7f;
const BSON_BINARY_SUBTYPE_DEFAULT = 0;
const BSON_BINARY_SUBTYPE_UUID_NEW = 4;
const BSONType = Object.freeze({
    double: 1,
    string: 2,
    object: 3,
    array: 4,
    binData: 5,
    undefined: 6,
    objectId: 7,
    bool: 8,
    date: 9,
    null: 10,
    regex: 11,
    dbPointer: 12,
    javascript: 13,
    symbol: 14,
    javascriptWithScope: 15,
    int: 16,
    timestamp: 17,
    long: 18,
    decimal: 19,
    minKey: -1,
    maxKey: 127
});

class BSONError extends Error {
    get bsonError() {
        return true;
    }
    get name() {
        return 'BSONError';
    }
    constructor(message, options) {
        super(message, options);
    }
    static isBSONError(value) {
        return (value != null &&
            typeof value === 'object' &&
            'bsonError' in value &&
            value.bsonError === true &&
            'name' in value &&
            'message' in value &&
            'stack' in value);
    }
}
class BSONVersionError extends BSONError {
    get name() {
        return 'BSONVersionError';
    }
    constructor() {
        super(`Unsupported BSON version, bson types must be from bson ${BSON_MAJOR_VERSION}.x.x`);
    }
}
class BSONRuntimeError extends BSONError {
    get name() {
        return 'BSONRuntimeError';
    }
    constructor(message) {
        super(message);
    }
}

const FIRST_BIT = 0x80;
const FIRST_TWO_BITS = 0xc0;
const FIRST_THREE_BITS = 0xe0;
const FIRST_FOUR_BITS = 0xf0;
const FIRST_FIVE_BITS = 0xf8;
const TWO_BIT_CHAR = 0xc0;
const THREE_BIT_CHAR = 0xe0;
const FOUR_BIT_CHAR = 0xf0;
const CONTINUING_CHAR = 0x80;
function validateUtf8(bytes, start, end) {
    let continuation = 0;
    for (let i = start; i < end; i += 1) {
        const byte = bytes[i];
        if (continuation) {
            if ((byte & FIRST_TWO_BITS) !== CONTINUING_CHAR) {
                return false;
            }
            continuation -= 1;
        }
        else if (byte & FIRST_BIT) {
            if ((byte & FIRST_THREE_BITS) === TWO_BIT_CHAR) {
                continuation = 1;
            }
            else if ((byte & FIRST_FOUR_BITS) === THREE_BIT_CHAR) {
                continuation = 2;
            }
            else if ((byte & FIRST_FIVE_BITS) === FOUR_BIT_CHAR) {
                continuation = 3;
            }
            else {
                return false;
            }
        }
    }
    return !continuation;
}

function tryReadBasicLatin(uint8array, start, end) {
    if (uint8array.length === 0) {
        return '';
    }
    const stringByteLength = end - start;
    if (stringByteLength === 0) {
        return '';
    }
    if (stringByteLength > 20) {
        return null;
    }
    if (stringByteLength === 1 && uint8array[start] < 128) {
        return String.fromCharCode(uint8array[start]);
    }
    if (stringByteLength === 2 && uint8array[start] < 128 && uint8array[start + 1] < 128) {
        return String.fromCharCode(uint8array[start]) + String.fromCharCode(uint8array[start + 1]);
    }
    if (stringByteLength === 3 &&
        uint8array[start] < 128 &&
        uint8array[start + 1] < 128 &&
        uint8array[start + 2] < 128) {
        return (String.fromCharCode(uint8array[start]) +
            String.fromCharCode(uint8array[start + 1]) +
            String.fromCharCode(uint8array[start + 2]));
    }
    const latinBytes = [];
    for (let i = start; i < end; i++) {
        const byte = uint8array[i];
        if (byte > 127) {
            return null;
        }
        latinBytes.push(byte);
    }
    return String.fromCharCode(...latinBytes);
}
function tryWriteBasicLatin(destination, source, offset) {
    if (source.length === 0)
        return 0;
    if (source.length > 25)
        return null;
    if (destination.length - offset < source.length)
        return null;
    for (let charOffset = 0, destinationOffset = offset; charOffset < source.length; charOffset++, destinationOffset++) {
        const char = source.charCodeAt(charOffset);
        if (char > 127)
            return null;
        destination[destinationOffset] = char;
    }
    return source.length;
}

function nodejsMathRandomBytes(byteLength) {
    return nodeJsByteUtils.fromNumberArray(Array.from({ length: byteLength }, () => Math.floor(Math.random() * 256)));
}
const nodejsRandomBytes = (() => {
    try {
        return (__nccwpck_require__(113).randomBytes);
    }
    catch {
        return nodejsMathRandomBytes;
    }
})();
const nodeJsByteUtils = {
    toLocalBufferType(potentialBuffer) {
        if (Buffer.isBuffer(potentialBuffer)) {
            return potentialBuffer;
        }
        if (ArrayBuffer.isView(potentialBuffer)) {
            return Buffer.from(potentialBuffer.buffer, potentialBuffer.byteOffset, potentialBuffer.byteLength);
        }
        const stringTag = potentialBuffer?.[Symbol.toStringTag] ?? Object.prototype.toString.call(potentialBuffer);
        if (stringTag === 'ArrayBuffer' ||
            stringTag === 'SharedArrayBuffer' ||
            stringTag === '[object ArrayBuffer]' ||
            stringTag === '[object SharedArrayBuffer]') {
            return Buffer.from(potentialBuffer);
        }
        throw new BSONError(`Cannot create Buffer from ${String(potentialBuffer)}`);
    },
    allocate(size) {
        return Buffer.alloc(size);
    },
    allocateUnsafe(size) {
        return Buffer.allocUnsafe(size);
    },
    equals(a, b) {
        return nodeJsByteUtils.toLocalBufferType(a).equals(b);
    },
    fromNumberArray(array) {
        return Buffer.from(array);
    },
    fromBase64(base64) {
        return Buffer.from(base64, 'base64');
    },
    toBase64(buffer) {
        return nodeJsByteUtils.toLocalBufferType(buffer).toString('base64');
    },
    fromISO88591(codePoints) {
        return Buffer.from(codePoints, 'binary');
    },
    toISO88591(buffer) {
        return nodeJsByteUtils.toLocalBufferType(buffer).toString('binary');
    },
    fromHex(hex) {
        return Buffer.from(hex, 'hex');
    },
    toHex(buffer) {
        return nodeJsByteUtils.toLocalBufferType(buffer).toString('hex');
    },
    toUTF8(buffer, start, end, fatal) {
        const basicLatin = end - start <= 20 ? tryReadBasicLatin(buffer, start, end) : null;
        if (basicLatin != null) {
            return basicLatin;
        }
        const string = nodeJsByteUtils.toLocalBufferType(buffer).toString('utf8', start, end);
        if (fatal) {
            for (let i = 0; i < string.length; i++) {
                if (string.charCodeAt(i) === 0xfffd) {
                    if (!validateUtf8(buffer, start, end)) {
                        throw new BSONError('Invalid UTF-8 string in BSON document');
                    }
                    break;
                }
            }
        }
        return string;
    },
    utf8ByteLength(input) {
        return Buffer.byteLength(input, 'utf8');
    },
    encodeUTF8Into(buffer, source, byteOffset) {
        const latinBytesWritten = tryWriteBasicLatin(buffer, source, byteOffset);
        if (latinBytesWritten != null) {
            return latinBytesWritten;
        }
        return nodeJsByteUtils.toLocalBufferType(buffer).write(source, byteOffset, undefined, 'utf8');
    },
    randomBytes: nodejsRandomBytes
};

function isReactNative() {
    const { navigator } = globalThis;
    return typeof navigator === 'object' && navigator.product === 'ReactNative';
}
function webMathRandomBytes(byteLength) {
    if (byteLength < 0) {
        throw new RangeError(`The argument 'byteLength' is invalid. Received ${byteLength}`);
    }
    return webByteUtils.fromNumberArray(Array.from({ length: byteLength }, () => Math.floor(Math.random() * 256)));
}
const webRandomBytes = (() => {
    const { crypto } = globalThis;
    if (crypto != null && typeof crypto.getRandomValues === 'function') {
        return (byteLength) => {
            return crypto.getRandomValues(webByteUtils.allocate(byteLength));
        };
    }
    else {
        if (isReactNative()) {
            const { console } = globalThis;
            console?.warn?.('BSON: For React Native please polyfill crypto.getRandomValues, e.g. using: https://www.npmjs.com/package/react-native-get-random-values.');
        }
        return webMathRandomBytes;
    }
})();
const HEX_DIGIT = /(\d|[a-f])/i;
const webByteUtils = {
    toLocalBufferType(potentialUint8array) {
        const stringTag = potentialUint8array?.[Symbol.toStringTag] ??
            Object.prototype.toString.call(potentialUint8array);
        if (stringTag === 'Uint8Array') {
            return potentialUint8array;
        }
        if (ArrayBuffer.isView(potentialUint8array)) {
            return new Uint8Array(potentialUint8array.buffer.slice(potentialUint8array.byteOffset, potentialUint8array.byteOffset + potentialUint8array.byteLength));
        }
        if (stringTag === 'ArrayBuffer' ||
            stringTag === 'SharedArrayBuffer' ||
            stringTag === '[object ArrayBuffer]' ||
            stringTag === '[object SharedArrayBuffer]') {
            return new Uint8Array(potentialUint8array);
        }
        throw new BSONError(`Cannot make a Uint8Array from ${String(potentialUint8array)}`);
    },
    allocate(size) {
        if (typeof size !== 'number') {
            throw new TypeError(`The "size" argument must be of type number. Received ${String(size)}`);
        }
        return new Uint8Array(size);
    },
    allocateUnsafe(size) {
        return webByteUtils.allocate(size);
    },
    equals(a, b) {
        if (a.byteLength !== b.byteLength) {
            return false;
        }
        for (let i = 0; i < a.byteLength; i++) {
            if (a[i] !== b[i]) {
                return false;
            }
        }
        return true;
    },
    fromNumberArray(array) {
        return Uint8Array.from(array);
    },
    fromBase64(base64) {
        return Uint8Array.from(atob(base64), c => c.charCodeAt(0));
    },
    toBase64(uint8array) {
        return btoa(webByteUtils.toISO88591(uint8array));
    },
    fromISO88591(codePoints) {
        return Uint8Array.from(codePoints, c => c.charCodeAt(0) & 0xff);
    },
    toISO88591(uint8array) {
        return Array.from(Uint16Array.from(uint8array), b => String.fromCharCode(b)).join('');
    },
    fromHex(hex) {
        const evenLengthHex = hex.length % 2 === 0 ? hex : hex.slice(0, hex.length - 1);
        const buffer = [];
        for (let i = 0; i < evenLengthHex.length; i += 2) {
            const firstDigit = evenLengthHex[i];
            const secondDigit = evenLengthHex[i + 1];
            if (!HEX_DIGIT.test(firstDigit)) {
                break;
            }
            if (!HEX_DIGIT.test(secondDigit)) {
                break;
            }
            const hexDigit = Number.parseInt(`${firstDigit}${secondDigit}`, 16);
            buffer.push(hexDigit);
        }
        return Uint8Array.from(buffer);
    },
    toHex(uint8array) {
        return Array.from(uint8array, byte => byte.toString(16).padStart(2, '0')).join('');
    },
    toUTF8(uint8array, start, end, fatal) {
        const basicLatin = end - start <= 20 ? tryReadBasicLatin(uint8array, start, end) : null;
        if (basicLatin != null) {
            return basicLatin;
        }
        if (fatal) {
            try {
                return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
            }
            catch (cause) {
                throw new BSONError('Invalid UTF-8 string in BSON document', { cause });
            }
        }
        return new TextDecoder('utf8', { fatal }).decode(uint8array.slice(start, end));
    },
    utf8ByteLength(input) {
        return new TextEncoder().encode(input).byteLength;
    },
    encodeUTF8Into(uint8array, source, byteOffset) {
        const bytes = new TextEncoder().encode(source);
        uint8array.set(bytes, byteOffset);
        return bytes.byteLength;
    },
    randomBytes: webRandomBytes
};

const hasGlobalBuffer = typeof Buffer === 'function' && Buffer.prototype?._isBuffer !== true;
const ByteUtils = hasGlobalBuffer ? nodeJsByteUtils : webByteUtils;

class BSONValue {
    get [Symbol.for('@@mdb.bson.version')]() {
        return BSON_MAJOR_VERSION;
    }
    [Symbol.for('nodejs.util.inspect.custom')](depth, options, inspect) {
        return this.inspect(depth, options, inspect);
    }
}

class Binary extends BSONValue {
    get _bsontype() {
        return 'Binary';
    }
    constructor(buffer, subType) {
        super();
        if (!(buffer == null) &&
            typeof buffer === 'string' &&
            !ArrayBuffer.isView(buffer) &&
            !isAnyArrayBuffer(buffer) &&
            !Array.isArray(buffer)) {
            throw new BSONError('Binary can only be constructed from Uint8Array or number[]');
        }
        this.sub_type = subType ?? Binary.BSON_BINARY_SUBTYPE_DEFAULT;
        if (buffer == null) {
            this.buffer = ByteUtils.allocate(Binary.BUFFER_SIZE);
            this.position = 0;
        }
        else {
            this.buffer = Array.isArray(buffer)
                ? ByteUtils.fromNumberArray(buffer)
                : ByteUtils.toLocalBufferType(buffer);
            this.position = this.buffer.byteLength;
        }
    }
    put(byteValue) {
        if (typeof byteValue === 'string' && byteValue.length !== 1) {
            throw new BSONError('only accepts single character String');
        }
        else if (typeof byteValue !== 'number' && byteValue.length !== 1)
            throw new BSONError('only accepts single character Uint8Array or Array');
        let decodedByte;
        if (typeof byteValue === 'string') {
            decodedByte = byteValue.charCodeAt(0);
        }
        else if (typeof byteValue === 'number') {
            decodedByte = byteValue;
        }
        else {
            decodedByte = byteValue[0];
        }
        if (decodedByte < 0 || decodedByte > 255) {
            throw new BSONError('only accepts number in a valid unsigned byte range 0-255');
        }
        if (this.buffer.byteLength > this.position) {
            this.buffer[this.position++] = decodedByte;
        }
        else {
            const newSpace = ByteUtils.allocate(Binary.BUFFER_SIZE + this.buffer.length);
            newSpace.set(this.buffer, 0);
            this.buffer = newSpace;
            this.buffer[this.position++] = decodedByte;
        }
    }
    write(sequence, offset) {
        offset = typeof offset === 'number' ? offset : this.position;
        if (this.buffer.byteLength < offset + sequence.length) {
            const newSpace = ByteUtils.allocate(this.buffer.byteLength + sequence.length);
            newSpace.set(this.buffer, 0);
            this.buffer = newSpace;
        }
        if (ArrayBuffer.isView(sequence)) {
            this.buffer.set(ByteUtils.toLocalBufferType(sequence), offset);
            this.position =
                offset + sequence.byteLength > this.position ? offset + sequence.length : this.position;
        }
        else if (typeof sequence === 'string') {
            throw new BSONError('input cannot be string');
        }
    }
    read(position, length) {
        length = length && length > 0 ? length : this.position;
        return this.buffer.slice(position, position + length);
    }
    value() {
        return this.buffer.length === this.position
            ? this.buffer
            : this.buffer.subarray(0, this.position);
    }
    length() {
        return this.position;
    }
    toJSON() {
        return ByteUtils.toBase64(this.buffer);
    }
    toString(encoding) {
        if (encoding === 'hex')
            return ByteUtils.toHex(this.buffer);
        if (encoding === 'base64')
            return ByteUtils.toBase64(this.buffer);
        if (encoding === 'utf8' || encoding === 'utf-8')
            return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
        return ByteUtils.toUTF8(this.buffer, 0, this.buffer.byteLength, false);
    }
    toExtendedJSON(options) {
        options = options || {};
        const base64String = ByteUtils.toBase64(this.buffer);
        const subType = Number(this.sub_type).toString(16);
        if (options.legacy) {
            return {
                $binary: base64String,
                $type: subType.length === 1 ? '0' + subType : subType
            };
        }
        return {
            $binary: {
                base64: base64String,
                subType: subType.length === 1 ? '0' + subType : subType
            }
        };
    }
    toUUID() {
        if (this.sub_type === Binary.SUBTYPE_UUID) {
            return new UUID(this.buffer.slice(0, this.position));
        }
        throw new BSONError(`Binary sub_type "${this.sub_type}" is not supported for converting to UUID. Only "${Binary.SUBTYPE_UUID}" is currently supported.`);
    }
    static createFromHexString(hex, subType) {
        return new Binary(ByteUtils.fromHex(hex), subType);
    }
    static createFromBase64(base64, subType) {
        return new Binary(ByteUtils.fromBase64(base64), subType);
    }
    static fromExtendedJSON(doc, options) {
        options = options || {};
        let data;
        let type;
        if ('$binary' in doc) {
            if (options.legacy && typeof doc.$binary === 'string' && '$type' in doc) {
                type = doc.$type ? parseInt(doc.$type, 16) : 0;
                data = ByteUtils.fromBase64(doc.$binary);
            }
            else {
                if (typeof doc.$binary !== 'string') {
                    type = doc.$binary.subType ? parseInt(doc.$binary.subType, 16) : 0;
                    data = ByteUtils.fromBase64(doc.$binary.base64);
                }
            }
        }
        else if ('$uuid' in doc) {
            type = 4;
            data = UUID.bytesFromString(doc.$uuid);
        }
        if (!data) {
            throw new BSONError(`Unexpected Binary Extended JSON format ${JSON.stringify(doc)}`);
        }
        return type === BSON_BINARY_SUBTYPE_UUID_NEW ? new UUID(data) : new Binary(data, type);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        const base64 = ByteUtils.toBase64(this.buffer.subarray(0, this.position));
        const base64Arg = inspect(base64, options);
        const subTypeArg = inspect(this.sub_type, options);
        return `Binary.createFromBase64(${base64Arg}, ${subTypeArg})`;
    }
}
Binary.BSON_BINARY_SUBTYPE_DEFAULT = 0;
Binary.BUFFER_SIZE = 256;
Binary.SUBTYPE_DEFAULT = 0;
Binary.SUBTYPE_FUNCTION = 1;
Binary.SUBTYPE_BYTE_ARRAY = 2;
Binary.SUBTYPE_UUID_OLD = 3;
Binary.SUBTYPE_UUID = 4;
Binary.SUBTYPE_MD5 = 5;
Binary.SUBTYPE_ENCRYPTED = 6;
Binary.SUBTYPE_COLUMN = 7;
Binary.SUBTYPE_USER_DEFINED = 128;
const UUID_BYTE_LENGTH = 16;
const UUID_WITHOUT_DASHES = /^[0-9A-F]{32}$/i;
const UUID_WITH_DASHES = /^[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}$/i;
class UUID extends Binary {
    constructor(input) {
        let bytes;
        if (input == null) {
            bytes = UUID.generate();
        }
        else if (input instanceof UUID) {
            bytes = ByteUtils.toLocalBufferType(new Uint8Array(input.buffer));
        }
        else if (ArrayBuffer.isView(input) && input.byteLength === UUID_BYTE_LENGTH) {
            bytes = ByteUtils.toLocalBufferType(input);
        }
        else if (typeof input === 'string') {
            bytes = UUID.bytesFromString(input);
        }
        else {
            throw new BSONError('Argument passed in UUID constructor must be a UUID, a 16 byte Buffer or a 32/36 character hex string (dashes excluded/included, format: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx).');
        }
        super(bytes, BSON_BINARY_SUBTYPE_UUID_NEW);
    }
    get id() {
        return this.buffer;
    }
    set id(value) {
        this.buffer = value;
    }
    toHexString(includeDashes = true) {
        if (includeDashes) {
            return [
                ByteUtils.toHex(this.buffer.subarray(0, 4)),
                ByteUtils.toHex(this.buffer.subarray(4, 6)),
                ByteUtils.toHex(this.buffer.subarray(6, 8)),
                ByteUtils.toHex(this.buffer.subarray(8, 10)),
                ByteUtils.toHex(this.buffer.subarray(10, 16))
            ].join('-');
        }
        return ByteUtils.toHex(this.buffer);
    }
    toString(encoding) {
        if (encoding === 'hex')
            return ByteUtils.toHex(this.id);
        if (encoding === 'base64')
            return ByteUtils.toBase64(this.id);
        return this.toHexString();
    }
    toJSON() {
        return this.toHexString();
    }
    equals(otherId) {
        if (!otherId) {
            return false;
        }
        if (otherId instanceof UUID) {
            return ByteUtils.equals(otherId.id, this.id);
        }
        try {
            return ByteUtils.equals(new UUID(otherId).id, this.id);
        }
        catch {
            return false;
        }
    }
    toBinary() {
        return new Binary(this.id, Binary.SUBTYPE_UUID);
    }
    static generate() {
        const bytes = ByteUtils.randomBytes(UUID_BYTE_LENGTH);
        bytes[6] = (bytes[6] & 0x0f) | 0x40;
        bytes[8] = (bytes[8] & 0x3f) | 0x80;
        return bytes;
    }
    static isValid(input) {
        if (!input) {
            return false;
        }
        if (typeof input === 'string') {
            return UUID.isValidUUIDString(input);
        }
        if (isUint8Array(input)) {
            return input.byteLength === UUID_BYTE_LENGTH;
        }
        return (input._bsontype === 'Binary' &&
            input.sub_type === this.SUBTYPE_UUID &&
            input.buffer.byteLength === 16);
    }
    static createFromHexString(hexString) {
        const buffer = UUID.bytesFromString(hexString);
        return new UUID(buffer);
    }
    static createFromBase64(base64) {
        return new UUID(ByteUtils.fromBase64(base64));
    }
    static bytesFromString(representation) {
        if (!UUID.isValidUUIDString(representation)) {
            throw new BSONError('UUID string representation must be 32 hex digits or canonical hyphenated representation');
        }
        return ByteUtils.fromHex(representation.replace(/-/g, ''));
    }
    static isValidUUIDString(representation) {
        return UUID_WITHOUT_DASHES.test(representation) || UUID_WITH_DASHES.test(representation);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        return `new UUID(${inspect(this.toHexString(), options)})`;
    }
}

class Code extends BSONValue {
    get _bsontype() {
        return 'Code';
    }
    constructor(code, scope) {
        super();
        this.code = code.toString();
        this.scope = scope ?? null;
    }
    toJSON() {
        if (this.scope != null) {
            return { code: this.code, scope: this.scope };
        }
        return { code: this.code };
    }
    toExtendedJSON() {
        if (this.scope) {
            return { $code: this.code, $scope: this.scope };
        }
        return { $code: this.code };
    }
    static fromExtendedJSON(doc) {
        return new Code(doc.$code, doc.$scope);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        let parametersString = inspect(this.code, options);
        const multiLineFn = parametersString.includes('\n');
        if (this.scope != null) {
            parametersString += `,${multiLineFn ? '\n' : ' '}${inspect(this.scope, options)}`;
        }
        const endingNewline = multiLineFn && this.scope === null;
        return `new Code(${multiLineFn ? '\n' : ''}${parametersString}${endingNewline ? '\n' : ''})`;
    }
}

function isDBRefLike(value) {
    return (value != null &&
        typeof value === 'object' &&
        '$id' in value &&
        value.$id != null &&
        '$ref' in value &&
        typeof value.$ref === 'string' &&
        (!('$db' in value) || ('$db' in value && typeof value.$db === 'string')));
}
class DBRef extends BSONValue {
    get _bsontype() {
        return 'DBRef';
    }
    constructor(collection, oid, db, fields) {
        super();
        const parts = collection.split('.');
        if (parts.length === 2) {
            db = parts.shift();
            collection = parts.shift();
        }
        this.collection = collection;
        this.oid = oid;
        this.db = db;
        this.fields = fields || {};
    }
    get namespace() {
        return this.collection;
    }
    set namespace(value) {
        this.collection = value;
    }
    toJSON() {
        const o = Object.assign({
            $ref: this.collection,
            $id: this.oid
        }, this.fields);
        if (this.db != null)
            o.$db = this.db;
        return o;
    }
    toExtendedJSON(options) {
        options = options || {};
        let o = {
            $ref: this.collection,
            $id: this.oid
        };
        if (options.legacy) {
            return o;
        }
        if (this.db)
            o.$db = this.db;
        o = Object.assign(o, this.fields);
        return o;
    }
    static fromExtendedJSON(doc) {
        const copy = Object.assign({}, doc);
        delete copy.$ref;
        delete copy.$id;
        delete copy.$db;
        return new DBRef(doc.$ref, doc.$id, doc.$db, copy);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        const args = [
            inspect(this.namespace, options),
            inspect(this.oid, options),
            ...(this.db ? [inspect(this.db, options)] : []),
            ...(Object.keys(this.fields).length > 0 ? [inspect(this.fields, options)] : [])
        ];
        args[1] = inspect === defaultInspect ? `new ObjectId(${args[1]})` : args[1];
        return `new DBRef(${args.join(', ')})`;
    }
}

let wasm = undefined;
try {
    wasm = new WebAssembly.Instance(new WebAssembly.Module(new Uint8Array([0, 97, 115, 109, 1, 0, 0, 0, 1, 13, 2, 96, 0, 1, 127, 96, 4, 127, 127, 127, 127, 1, 127, 3, 7, 6, 0, 1, 1, 1, 1, 1, 6, 6, 1, 127, 1, 65, 0, 11, 7, 50, 6, 3, 109, 117, 108, 0, 1, 5, 100, 105, 118, 95, 115, 0, 2, 5, 100, 105, 118, 95, 117, 0, 3, 5, 114, 101, 109, 95, 115, 0, 4, 5, 114, 101, 109, 95, 117, 0, 5, 8, 103, 101, 116, 95, 104, 105, 103, 104, 0, 0, 10, 191, 1, 6, 4, 0, 35, 0, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 126, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 127, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 128, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 129, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11, 36, 1, 1, 126, 32, 0, 173, 32, 1, 173, 66, 32, 134, 132, 32, 2, 173, 32, 3, 173, 66, 32, 134, 132, 130, 34, 4, 66, 32, 135, 167, 36, 0, 32, 4, 167, 11])), {}).exports;
}
catch {
}
const TWO_PWR_16_DBL = 1 << 16;
const TWO_PWR_24_DBL = 1 << 24;
const TWO_PWR_32_DBL = TWO_PWR_16_DBL * TWO_PWR_16_DBL;
const TWO_PWR_64_DBL = TWO_PWR_32_DBL * TWO_PWR_32_DBL;
const TWO_PWR_63_DBL = TWO_PWR_64_DBL / 2;
const INT_CACHE = {};
const UINT_CACHE = {};
const MAX_INT64_STRING_LENGTH = 20;
const DECIMAL_REG_EX = /^(\+?0|(\+|-)?[1-9][0-9]*)$/;
class Long extends BSONValue {
    get _bsontype() {
        return 'Long';
    }
    get __isLong__() {
        return true;
    }
    constructor(low = 0, high, unsigned) {
        super();
        if (typeof low === 'bigint') {
            Object.assign(this, Long.fromBigInt(low, !!high));
        }
        else if (typeof low === 'string') {
            Object.assign(this, Long.fromString(low, !!high));
        }
        else {
            this.low = low | 0;
            this.high = high | 0;
            this.unsigned = !!unsigned;
        }
    }
    static fromBits(lowBits, highBits, unsigned) {
        return new Long(lowBits, highBits, unsigned);
    }
    static fromInt(value, unsigned) {
        let obj, cachedObj, cache;
        if (unsigned) {
            value >>>= 0;
            if ((cache = 0 <= value && value < 256)) {
                cachedObj = UINT_CACHE[value];
                if (cachedObj)
                    return cachedObj;
            }
            obj = Long.fromBits(value, (value | 0) < 0 ? -1 : 0, true);
            if (cache)
                UINT_CACHE[value] = obj;
            return obj;
        }
        else {
            value |= 0;
            if ((cache = -128 <= value && value < 128)) {
                cachedObj = INT_CACHE[value];
                if (cachedObj)
                    return cachedObj;
            }
            obj = Long.fromBits(value, value < 0 ? -1 : 0, false);
            if (cache)
                INT_CACHE[value] = obj;
            return obj;
        }
    }
    static fromNumber(value, unsigned) {
        if (isNaN(value))
            return unsigned ? Long.UZERO : Long.ZERO;
        if (unsigned) {
            if (value < 0)
                return Long.UZERO;
            if (value >= TWO_PWR_64_DBL)
                return Long.MAX_UNSIGNED_VALUE;
        }
        else {
            if (value <= -TWO_PWR_63_DBL)
                return Long.MIN_VALUE;
            if (value + 1 >= TWO_PWR_63_DBL)
                return Long.MAX_VALUE;
        }
        if (value < 0)
            return Long.fromNumber(-value, unsigned).neg();
        return Long.fromBits(value % TWO_PWR_32_DBL | 0, (value / TWO_PWR_32_DBL) | 0, unsigned);
    }
    static fromBigInt(value, unsigned) {
        return Long.fromString(value.toString(), unsigned);
    }
    static fromString(str, unsigned, radix) {
        if (str.length === 0)
            throw new BSONError('empty string');
        if (str === 'NaN' || str === 'Infinity' || str === '+Infinity' || str === '-Infinity')
            return Long.ZERO;
        if (typeof unsigned === 'number') {
            (radix = unsigned), (unsigned = false);
        }
        else {
            unsigned = !!unsigned;
        }
        radix = radix || 10;
        if (radix < 2 || 36 < radix)
            throw new BSONError('radix');
        let p;
        if ((p = str.indexOf('-')) > 0)
            throw new BSONError('interior hyphen');
        else if (p === 0) {
            return Long.fromString(str.substring(1), unsigned, radix).neg();
        }
        const radixToPower = Long.fromNumber(Math.pow(radix, 8));
        let result = Long.ZERO;
        for (let i = 0; i < str.length; i += 8) {
            const size = Math.min(8, str.length - i), value = parseInt(str.substring(i, i + size), radix);
            if (size < 8) {
                const power = Long.fromNumber(Math.pow(radix, size));
                result = result.mul(power).add(Long.fromNumber(value));
            }
            else {
                result = result.mul(radixToPower);
                result = result.add(Long.fromNumber(value));
            }
        }
        result.unsigned = unsigned;
        return result;
    }
    static fromBytes(bytes, unsigned, le) {
        return le ? Long.fromBytesLE(bytes, unsigned) : Long.fromBytesBE(bytes, unsigned);
    }
    static fromBytesLE(bytes, unsigned) {
        return new Long(bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24), bytes[4] | (bytes[5] << 8) | (bytes[6] << 16) | (bytes[7] << 24), unsigned);
    }
    static fromBytesBE(bytes, unsigned) {
        return new Long((bytes[4] << 24) | (bytes[5] << 16) | (bytes[6] << 8) | bytes[7], (bytes[0] << 24) | (bytes[1] << 16) | (bytes[2] << 8) | bytes[3], unsigned);
    }
    static isLong(value) {
        return (value != null &&
            typeof value === 'object' &&
            '__isLong__' in value &&
            value.__isLong__ === true);
    }
    static fromValue(val, unsigned) {
        if (typeof val === 'number')
            return Long.fromNumber(val, unsigned);
        if (typeof val === 'string')
            return Long.fromString(val, unsigned);
        return Long.fromBits(val.low, val.high, typeof unsigned === 'boolean' ? unsigned : val.unsigned);
    }
    add(addend) {
        if (!Long.isLong(addend))
            addend = Long.fromValue(addend);
        const a48 = this.high >>> 16;
        const a32 = this.high & 0xffff;
        const a16 = this.low >>> 16;
        const a00 = this.low & 0xffff;
        const b48 = addend.high >>> 16;
        const b32 = addend.high & 0xffff;
        const b16 = addend.low >>> 16;
        const b00 = addend.low & 0xffff;
        let c48 = 0, c32 = 0, c16 = 0, c00 = 0;
        c00 += a00 + b00;
        c16 += c00 >>> 16;
        c00 &= 0xffff;
        c16 += a16 + b16;
        c32 += c16 >>> 16;
        c16 &= 0xffff;
        c32 += a32 + b32;
        c48 += c32 >>> 16;
        c32 &= 0xffff;
        c48 += a48 + b48;
        c48 &= 0xffff;
        return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
    }
    and(other) {
        if (!Long.isLong(other))
            other = Long.fromValue(other);
        return Long.fromBits(this.low & other.low, this.high & other.high, this.unsigned);
    }
    compare(other) {
        if (!Long.isLong(other))
            other = Long.fromValue(other);
        if (this.eq(other))
            return 0;
        const thisNeg = this.isNegative(), otherNeg = other.isNegative();
        if (thisNeg && !otherNeg)
            return -1;
        if (!thisNeg && otherNeg)
            return 1;
        if (!this.unsigned)
            return this.sub(other).isNegative() ? -1 : 1;
        return other.high >>> 0 > this.high >>> 0 ||
            (other.high === this.high && other.low >>> 0 > this.low >>> 0)
            ? -1
            : 1;
    }
    comp(other) {
        return this.compare(other);
    }
    divide(divisor) {
        if (!Long.isLong(divisor))
            divisor = Long.fromValue(divisor);
        if (divisor.isZero())
            throw new BSONError('division by zero');
        if (wasm) {
            if (!this.unsigned &&
                this.high === -0x80000000 &&
                divisor.low === -1 &&
                divisor.high === -1) {
                return this;
            }
            const low = (this.unsigned ? wasm.div_u : wasm.div_s)(this.low, this.high, divisor.low, divisor.high);
            return Long.fromBits(low, wasm.get_high(), this.unsigned);
        }
        if (this.isZero())
            return this.unsigned ? Long.UZERO : Long.ZERO;
        let approx, rem, res;
        if (!this.unsigned) {
            if (this.eq(Long.MIN_VALUE)) {
                if (divisor.eq(Long.ONE) || divisor.eq(Long.NEG_ONE))
                    return Long.MIN_VALUE;
                else if (divisor.eq(Long.MIN_VALUE))
                    return Long.ONE;
                else {
                    const halfThis = this.shr(1);
                    approx = halfThis.div(divisor).shl(1);
                    if (approx.eq(Long.ZERO)) {
                        return divisor.isNegative() ? Long.ONE : Long.NEG_ONE;
                    }
                    else {
                        rem = this.sub(divisor.mul(approx));
                        res = approx.add(rem.div(divisor));
                        return res;
                    }
                }
            }
            else if (divisor.eq(Long.MIN_VALUE))
                return this.unsigned ? Long.UZERO : Long.ZERO;
            if (this.isNegative()) {
                if (divisor.isNegative())
                    return this.neg().div(divisor.neg());
                return this.neg().div(divisor).neg();
            }
            else if (divisor.isNegative())
                return this.div(divisor.neg()).neg();
            res = Long.ZERO;
        }
        else {
            if (!divisor.unsigned)
                divisor = divisor.toUnsigned();
            if (divisor.gt(this))
                return Long.UZERO;
            if (divisor.gt(this.shru(1)))
                return Long.UONE;
            res = Long.UZERO;
        }
        rem = this;
        while (rem.gte(divisor)) {
            approx = Math.max(1, Math.floor(rem.toNumber() / divisor.toNumber()));
            const log2 = Math.ceil(Math.log(approx) / Math.LN2);
            const delta = log2 <= 48 ? 1 : Math.pow(2, log2 - 48);
            let approxRes = Long.fromNumber(approx);
            let approxRem = approxRes.mul(divisor);
            while (approxRem.isNegative() || approxRem.gt(rem)) {
                approx -= delta;
                approxRes = Long.fromNumber(approx, this.unsigned);
                approxRem = approxRes.mul(divisor);
            }
            if (approxRes.isZero())
                approxRes = Long.ONE;
            res = res.add(approxRes);
            rem = rem.sub(approxRem);
        }
        return res;
    }
    div(divisor) {
        return this.divide(divisor);
    }
    equals(other) {
        if (!Long.isLong(other))
            other = Long.fromValue(other);
        if (this.unsigned !== other.unsigned && this.high >>> 31 === 1 && other.high >>> 31 === 1)
            return false;
        return this.high === other.high && this.low === other.low;
    }
    eq(other) {
        return this.equals(other);
    }
    getHighBits() {
        return this.high;
    }
    getHighBitsUnsigned() {
        return this.high >>> 0;
    }
    getLowBits() {
        return this.low;
    }
    getLowBitsUnsigned() {
        return this.low >>> 0;
    }
    getNumBitsAbs() {
        if (this.isNegative()) {
            return this.eq(Long.MIN_VALUE) ? 64 : this.neg().getNumBitsAbs();
        }
        const val = this.high !== 0 ? this.high : this.low;
        let bit;
        for (bit = 31; bit > 0; bit--)
            if ((val & (1 << bit)) !== 0)
                break;
        return this.high !== 0 ? bit + 33 : bit + 1;
    }
    greaterThan(other) {
        return this.comp(other) > 0;
    }
    gt(other) {
        return this.greaterThan(other);
    }
    greaterThanOrEqual(other) {
        return this.comp(other) >= 0;
    }
    gte(other) {
        return this.greaterThanOrEqual(other);
    }
    ge(other) {
        return this.greaterThanOrEqual(other);
    }
    isEven() {
        return (this.low & 1) === 0;
    }
    isNegative() {
        return !this.unsigned && this.high < 0;
    }
    isOdd() {
        return (this.low & 1) === 1;
    }
    isPositive() {
        return this.unsigned || this.high >= 0;
    }
    isZero() {
        return this.high === 0 && this.low === 0;
    }
    lessThan(other) {
        return this.comp(other) < 0;
    }
    lt(other) {
        return this.lessThan(other);
    }
    lessThanOrEqual(other) {
        return this.comp(other) <= 0;
    }
    lte(other) {
        return this.lessThanOrEqual(other);
    }
    modulo(divisor) {
        if (!Long.isLong(divisor))
            divisor = Long.fromValue(divisor);
        if (wasm) {
            const low = (this.unsigned ? wasm.rem_u : wasm.rem_s)(this.low, this.high, divisor.low, divisor.high);
            return Long.fromBits(low, wasm.get_high(), this.unsigned);
        }
        return this.sub(this.div(divisor).mul(divisor));
    }
    mod(divisor) {
        return this.modulo(divisor);
    }
    rem(divisor) {
        return this.modulo(divisor);
    }
    multiply(multiplier) {
        if (this.isZero())
            return Long.ZERO;
        if (!Long.isLong(multiplier))
            multiplier = Long.fromValue(multiplier);
        if (wasm) {
            const low = wasm.mul(this.low, this.high, multiplier.low, multiplier.high);
            return Long.fromBits(low, wasm.get_high(), this.unsigned);
        }
        if (multiplier.isZero())
            return Long.ZERO;
        if (this.eq(Long.MIN_VALUE))
            return multiplier.isOdd() ? Long.MIN_VALUE : Long.ZERO;
        if (multiplier.eq(Long.MIN_VALUE))
            return this.isOdd() ? Long.MIN_VALUE : Long.ZERO;
        if (this.isNegative()) {
            if (multiplier.isNegative())
                return this.neg().mul(multiplier.neg());
            else
                return this.neg().mul(multiplier).neg();
        }
        else if (multiplier.isNegative())
            return this.mul(multiplier.neg()).neg();
        if (this.lt(Long.TWO_PWR_24) && multiplier.lt(Long.TWO_PWR_24))
            return Long.fromNumber(this.toNumber() * multiplier.toNumber(), this.unsigned);
        const a48 = this.high >>> 16;
        const a32 = this.high & 0xffff;
        const a16 = this.low >>> 16;
        const a00 = this.low & 0xffff;
        const b48 = multiplier.high >>> 16;
        const b32 = multiplier.high & 0xffff;
        const b16 = multiplier.low >>> 16;
        const b00 = multiplier.low & 0xffff;
        let c48 = 0, c32 = 0, c16 = 0, c00 = 0;
        c00 += a00 * b00;
        c16 += c00 >>> 16;
        c00 &= 0xffff;
        c16 += a16 * b00;
        c32 += c16 >>> 16;
        c16 &= 0xffff;
        c16 += a00 * b16;
        c32 += c16 >>> 16;
        c16 &= 0xffff;
        c32 += a32 * b00;
        c48 += c32 >>> 16;
        c32 &= 0xffff;
        c32 += a16 * b16;
        c48 += c32 >>> 16;
        c32 &= 0xffff;
        c32 += a00 * b32;
        c48 += c32 >>> 16;
        c32 &= 0xffff;
        c48 += a48 * b00 + a32 * b16 + a16 * b32 + a00 * b48;
        c48 &= 0xffff;
        return Long.fromBits((c16 << 16) | c00, (c48 << 16) | c32, this.unsigned);
    }
    mul(multiplier) {
        return this.multiply(multiplier);
    }
    negate() {
        if (!this.unsigned && this.eq(Long.MIN_VALUE))
            return Long.MIN_VALUE;
        return this.not().add(Long.ONE);
    }
    neg() {
        return this.negate();
    }
    not() {
        return Long.fromBits(~this.low, ~this.high, this.unsigned);
    }
    notEquals(other) {
        return !this.equals(other);
    }
    neq(other) {
        return this.notEquals(other);
    }
    ne(other) {
        return this.notEquals(other);
    }
    or(other) {
        if (!Long.isLong(other))
            other = Long.fromValue(other);
        return Long.fromBits(this.low | other.low, this.high | other.high, this.unsigned);
    }
    shiftLeft(numBits) {
        if (Long.isLong(numBits))
            numBits = numBits.toInt();
        if ((numBits &= 63) === 0)
            return this;
        else if (numBits < 32)
            return Long.fromBits(this.low << numBits, (this.high << numBits) | (this.low >>> (32 - numBits)), this.unsigned);
        else
            return Long.fromBits(0, this.low << (numBits - 32), this.unsigned);
    }
    shl(numBits) {
        return this.shiftLeft(numBits);
    }
    shiftRight(numBits) {
        if (Long.isLong(numBits))
            numBits = numBits.toInt();
        if ((numBits &= 63) === 0)
            return this;
        else if (numBits < 32)
            return Long.fromBits((this.low >>> numBits) | (this.high << (32 - numBits)), this.high >> numBits, this.unsigned);
        else
            return Long.fromBits(this.high >> (numBits - 32), this.high >= 0 ? 0 : -1, this.unsigned);
    }
    shr(numBits) {
        return this.shiftRight(numBits);
    }
    shiftRightUnsigned(numBits) {
        if (Long.isLong(numBits))
            numBits = numBits.toInt();
        numBits &= 63;
        if (numBits === 0)
            return this;
        else {
            const high = this.high;
            if (numBits < 32) {
                const low = this.low;
                return Long.fromBits((low >>> numBits) | (high << (32 - numBits)), high >>> numBits, this.unsigned);
            }
            else if (numBits === 32)
                return Long.fromBits(high, 0, this.unsigned);
            else
                return Long.fromBits(high >>> (numBits - 32), 0, this.unsigned);
        }
    }
    shr_u(numBits) {
        return this.shiftRightUnsigned(numBits);
    }
    shru(numBits) {
        return this.shiftRightUnsigned(numBits);
    }
    subtract(subtrahend) {
        if (!Long.isLong(subtrahend))
            subtrahend = Long.fromValue(subtrahend);
        return this.add(subtrahend.neg());
    }
    sub(subtrahend) {
        return this.subtract(subtrahend);
    }
    toInt() {
        return this.unsigned ? this.low >>> 0 : this.low;
    }
    toNumber() {
        if (this.unsigned)
            return (this.high >>> 0) * TWO_PWR_32_DBL + (this.low >>> 0);
        return this.high * TWO_PWR_32_DBL + (this.low >>> 0);
    }
    toBigInt() {
        return BigInt(this.toString());
    }
    toBytes(le) {
        return le ? this.toBytesLE() : this.toBytesBE();
    }
    toBytesLE() {
        const hi = this.high, lo = this.low;
        return [
            lo & 0xff,
            (lo >>> 8) & 0xff,
            (lo >>> 16) & 0xff,
            lo >>> 24,
            hi & 0xff,
            (hi >>> 8) & 0xff,
            (hi >>> 16) & 0xff,
            hi >>> 24
        ];
    }
    toBytesBE() {
        const hi = this.high, lo = this.low;
        return [
            hi >>> 24,
            (hi >>> 16) & 0xff,
            (hi >>> 8) & 0xff,
            hi & 0xff,
            lo >>> 24,
            (lo >>> 16) & 0xff,
            (lo >>> 8) & 0xff,
            lo & 0xff
        ];
    }
    toSigned() {
        if (!this.unsigned)
            return this;
        return Long.fromBits(this.low, this.high, false);
    }
    toString(radix) {
        radix = radix || 10;
        if (radix < 2 || 36 < radix)
            throw new BSONError('radix');
        if (this.isZero())
            return '0';
        if (this.isNegative()) {
            if (this.eq(Long.MIN_VALUE)) {
                const radixLong = Long.fromNumber(radix), div = this.div(radixLong), rem1 = div.mul(radixLong).sub(this);
                return div.toString(radix) + rem1.toInt().toString(radix);
            }
            else
                return '-' + this.neg().toString(radix);
        }
        const radixToPower = Long.fromNumber(Math.pow(radix, 6), this.unsigned);
        let rem = this;
        let result = '';
        while (true) {
            const remDiv = rem.div(radixToPower);
            const intval = rem.sub(remDiv.mul(radixToPower)).toInt() >>> 0;
            let digits = intval.toString(radix);
            rem = remDiv;
            if (rem.isZero()) {
                return digits + result;
            }
            else {
                while (digits.length < 6)
                    digits = '0' + digits;
                result = '' + digits + result;
            }
        }
    }
    toUnsigned() {
        if (this.unsigned)
            return this;
        return Long.fromBits(this.low, this.high, true);
    }
    xor(other) {
        if (!Long.isLong(other))
            other = Long.fromValue(other);
        return Long.fromBits(this.low ^ other.low, this.high ^ other.high, this.unsigned);
    }
    eqz() {
        return this.isZero();
    }
    le(other) {
        return this.lessThanOrEqual(other);
    }
    toExtendedJSON(options) {
        if (options && options.relaxed)
            return this.toNumber();
        return { $numberLong: this.toString() };
    }
    static fromExtendedJSON(doc, options) {
        const { useBigInt64 = false, relaxed = true } = { ...options };
        if (doc.$numberLong.length > MAX_INT64_STRING_LENGTH) {
            throw new BSONError('$numberLong string is too long');
        }
        if (!DECIMAL_REG_EX.test(doc.$numberLong)) {
            throw new BSONError(`$numberLong string "${doc.$numberLong}" is in an invalid format`);
        }
        if (useBigInt64) {
            const bigIntResult = BigInt(doc.$numberLong);
            return BigInt.asIntN(64, bigIntResult);
        }
        const longResult = Long.fromString(doc.$numberLong);
        if (relaxed) {
            return longResult.toNumber();
        }
        return longResult;
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        const longVal = inspect(this.toString(), options);
        const unsignedVal = this.unsigned ? `, ${inspect(this.unsigned, options)}` : '';
        return `new Long(${longVal}${unsignedVal})`;
    }
}
Long.TWO_PWR_24 = Long.fromInt(TWO_PWR_24_DBL);
Long.MAX_UNSIGNED_VALUE = Long.fromBits(0xffffffff | 0, 0xffffffff | 0, true);
Long.ZERO = Long.fromInt(0);
Long.UZERO = Long.fromInt(0, true);
Long.ONE = Long.fromInt(1);
Long.UONE = Long.fromInt(1, true);
Long.NEG_ONE = Long.fromInt(-1);
Long.MAX_VALUE = Long.fromBits(0xffffffff | 0, 0x7fffffff | 0, false);
Long.MIN_VALUE = Long.fromBits(0, 0x80000000 | 0, false);

const PARSE_STRING_REGEXP = /^(\+|-)?(\d+|(\d*\.\d*))?(E|e)?([-+])?(\d+)?$/;
const PARSE_INF_REGEXP = /^(\+|-)?(Infinity|inf)$/i;
const PARSE_NAN_REGEXP = /^(\+|-)?NaN$/i;
const EXPONENT_MAX = 6111;
const EXPONENT_MIN = -6176;
const EXPONENT_BIAS = 6176;
const MAX_DIGITS = 34;
const NAN_BUFFER = ByteUtils.fromNumberArray([
    0x7c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
].reverse());
const INF_NEGATIVE_BUFFER = ByteUtils.fromNumberArray([
    0xf8, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
].reverse());
const INF_POSITIVE_BUFFER = ByteUtils.fromNumberArray([
    0x78, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
].reverse());
const EXPONENT_REGEX = /^([-+])?(\d+)?$/;
const COMBINATION_MASK = 0x1f;
const EXPONENT_MASK = 0x3fff;
const COMBINATION_INFINITY = 30;
const COMBINATION_NAN = 31;
function isDigit(value) {
    return !isNaN(parseInt(value, 10));
}
function divideu128(value) {
    const DIVISOR = Long.fromNumber(1000 * 1000 * 1000);
    let _rem = Long.fromNumber(0);
    if (!value.parts[0] && !value.parts[1] && !value.parts[2] && !value.parts[3]) {
        return { quotient: value, rem: _rem };
    }
    for (let i = 0; i <= 3; i++) {
        _rem = _rem.shiftLeft(32);
        _rem = _rem.add(new Long(value.parts[i], 0));
        value.parts[i] = _rem.div(DIVISOR).low;
        _rem = _rem.modulo(DIVISOR);
    }
    return { quotient: value, rem: _rem };
}
function multiply64x2(left, right) {
    if (!left && !right) {
        return { high: Long.fromNumber(0), low: Long.fromNumber(0) };
    }
    const leftHigh = left.shiftRightUnsigned(32);
    const leftLow = new Long(left.getLowBits(), 0);
    const rightHigh = right.shiftRightUnsigned(32);
    const rightLow = new Long(right.getLowBits(), 0);
    let productHigh = leftHigh.multiply(rightHigh);
    let productMid = leftHigh.multiply(rightLow);
    const productMid2 = leftLow.multiply(rightHigh);
    let productLow = leftLow.multiply(rightLow);
    productHigh = productHigh.add(productMid.shiftRightUnsigned(32));
    productMid = new Long(productMid.getLowBits(), 0)
        .add(productMid2)
        .add(productLow.shiftRightUnsigned(32));
    productHigh = productHigh.add(productMid.shiftRightUnsigned(32));
    productLow = productMid.shiftLeft(32).add(new Long(productLow.getLowBits(), 0));
    return { high: productHigh, low: productLow };
}
function lessThan(left, right) {
    const uhleft = left.high >>> 0;
    const uhright = right.high >>> 0;
    if (uhleft < uhright) {
        return true;
    }
    else if (uhleft === uhright) {
        const ulleft = left.low >>> 0;
        const ulright = right.low >>> 0;
        if (ulleft < ulright)
            return true;
    }
    return false;
}
function invalidErr(string, message) {
    throw new BSONError(`"${string}" is not a valid Decimal128 string - ${message}`);
}
class Decimal128 extends BSONValue {
    get _bsontype() {
        return 'Decimal128';
    }
    constructor(bytes) {
        super();
        if (typeof bytes === 'string') {
            this.bytes = Decimal128.fromString(bytes).bytes;
        }
        else if (isUint8Array(bytes)) {
            if (bytes.byteLength !== 16) {
                throw new BSONError('Decimal128 must take a Buffer of 16 bytes');
            }
            this.bytes = bytes;
        }
        else {
            throw new BSONError('Decimal128 must take a Buffer or string');
        }
    }
    static fromString(representation) {
        return Decimal128._fromString(representation, { allowRounding: false });
    }
    static fromStringWithRounding(representation) {
        return Decimal128._fromString(representation, { allowRounding: true });
    }
    static _fromString(representation, options) {
        let isNegative = false;
        let sawSign = false;
        let sawRadix = false;
        let foundNonZero = false;
        let significantDigits = 0;
        let nDigitsRead = 0;
        let nDigits = 0;
        let radixPosition = 0;
        let firstNonZero = 0;
        const digits = [0];
        let nDigitsStored = 0;
        let digitsInsert = 0;
        let lastDigit = 0;
        let exponent = 0;
        let significandHigh = new Long(0, 0);
        let significandLow = new Long(0, 0);
        let biasedExponent = 0;
        let index = 0;
        if (representation.length >= 7000) {
            throw new BSONError('' + representation + ' not a valid Decimal128 string');
        }
        const stringMatch = representation.match(PARSE_STRING_REGEXP);
        const infMatch = representation.match(PARSE_INF_REGEXP);
        const nanMatch = representation.match(PARSE_NAN_REGEXP);
        if ((!stringMatch && !infMatch && !nanMatch) || representation.length === 0) {
            throw new BSONError('' + representation + ' not a valid Decimal128 string');
        }
        if (stringMatch) {
            const unsignedNumber = stringMatch[2];
            const e = stringMatch[4];
            const expSign = stringMatch[5];
            const expNumber = stringMatch[6];
            if (e && expNumber === undefined)
                invalidErr(representation, 'missing exponent power');
            if (e && unsignedNumber === undefined)
                invalidErr(representation, 'missing exponent base');
            if (e === undefined && (expSign || expNumber)) {
                invalidErr(representation, 'missing e before exponent');
            }
        }
        if (representation[index] === '+' || representation[index] === '-') {
            sawSign = true;
            isNegative = representation[index++] === '-';
        }
        if (!isDigit(representation[index]) && representation[index] !== '.') {
            if (representation[index] === 'i' || representation[index] === 'I') {
                return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);
            }
            else if (representation[index] === 'N') {
                return new Decimal128(NAN_BUFFER);
            }
        }
        while (isDigit(representation[index]) || representation[index] === '.') {
            if (representation[index] === '.') {
                if (sawRadix)
                    invalidErr(representation, 'contains multiple periods');
                sawRadix = true;
                index = index + 1;
                continue;
            }
            if (nDigitsStored < MAX_DIGITS) {
                if (representation[index] !== '0' || foundNonZero) {
                    if (!foundNonZero) {
                        firstNonZero = nDigitsRead;
                    }
                    foundNonZero = true;
                    digits[digitsInsert++] = parseInt(representation[index], 10);
                    nDigitsStored = nDigitsStored + 1;
                }
            }
            if (foundNonZero)
                nDigits = nDigits + 1;
            if (sawRadix)
                radixPosition = radixPosition + 1;
            nDigitsRead = nDigitsRead + 1;
            index = index + 1;
        }
        if (sawRadix && !nDigitsRead)
            throw new BSONError('' + representation + ' not a valid Decimal128 string');
        if (representation[index] === 'e' || representation[index] === 'E') {
            const match = representation.substr(++index).match(EXPONENT_REGEX);
            if (!match || !match[2])
                return new Decimal128(NAN_BUFFER);
            exponent = parseInt(match[0], 10);
            index = index + match[0].length;
        }
        if (representation[index])
            return new Decimal128(NAN_BUFFER);
        if (!nDigitsStored) {
            digits[0] = 0;
            nDigits = 1;
            nDigitsStored = 1;
            significantDigits = 0;
        }
        else {
            lastDigit = nDigitsStored - 1;
            significantDigits = nDigits;
            if (significantDigits !== 1) {
                while (representation[firstNonZero + significantDigits - 1 + Number(sawSign) + Number(sawRadix)] === '0') {
                    significantDigits = significantDigits - 1;
                }
            }
        }
        if (exponent <= radixPosition && radixPosition > exponent + (1 << 14)) {
            exponent = EXPONENT_MIN;
        }
        else {
            exponent = exponent - radixPosition;
        }
        while (exponent > EXPONENT_MAX) {
            lastDigit = lastDigit + 1;
            if (lastDigit >= MAX_DIGITS) {
                if (significantDigits === 0) {
                    exponent = EXPONENT_MAX;
                    break;
                }
                invalidErr(representation, 'overflow');
            }
            exponent = exponent - 1;
        }
        if (options.allowRounding) {
            while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) {
                if (lastDigit === 0 && significantDigits < nDigitsStored) {
                    exponent = EXPONENT_MIN;
                    significantDigits = 0;
                    break;
                }
                if (nDigitsStored < nDigits) {
                    nDigits = nDigits - 1;
                }
                else {
                    lastDigit = lastDigit - 1;
                }
                if (exponent < EXPONENT_MAX) {
                    exponent = exponent + 1;
                }
                else {
                    const digitsString = digits.join('');
                    if (digitsString.match(/^0+$/)) {
                        exponent = EXPONENT_MAX;
                        break;
                    }
                    invalidErr(representation, 'overflow');
                }
            }
            if (lastDigit + 1 < significantDigits) {
                let endOfString = nDigitsRead;
                if (sawRadix) {
                    firstNonZero = firstNonZero + 1;
                    endOfString = endOfString + 1;
                }
                if (sawSign) {
                    firstNonZero = firstNonZero + 1;
                    endOfString = endOfString + 1;
                }
                const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10);
                let roundBit = 0;
                if (roundDigit >= 5) {
                    roundBit = 1;
                    if (roundDigit === 5) {
                        roundBit = digits[lastDigit] % 2 === 1 ? 1 : 0;
                        for (let i = firstNonZero + lastDigit + 2; i < endOfString; i++) {
                            if (parseInt(representation[i], 10)) {
                                roundBit = 1;
                                break;
                            }
                        }
                    }
                }
                if (roundBit) {
                    let dIdx = lastDigit;
                    for (; dIdx >= 0; dIdx--) {
                        if (++digits[dIdx] > 9) {
                            digits[dIdx] = 0;
                            if (dIdx === 0) {
                                if (exponent < EXPONENT_MAX) {
                                    exponent = exponent + 1;
                                    digits[dIdx] = 1;
                                }
                                else {
                                    return new Decimal128(isNegative ? INF_NEGATIVE_BUFFER : INF_POSITIVE_BUFFER);
                                }
                            }
                        }
                        else {
                            break;
                        }
                    }
                }
            }
        }
        else {
            while (exponent < EXPONENT_MIN || nDigitsStored < nDigits) {
                if (lastDigit === 0) {
                    if (significantDigits === 0) {
                        exponent = EXPONENT_MIN;
                        break;
                    }
                    invalidErr(representation, 'exponent underflow');
                }
                if (nDigitsStored < nDigits) {
                    if (representation[nDigits - 1 + Number(sawSign) + Number(sawRadix)] !== '0' &&
                        significantDigits !== 0) {
                        invalidErr(representation, 'inexact rounding');
                    }
                    nDigits = nDigits - 1;
                }
                else {
                    if (digits[lastDigit] !== 0) {
                        invalidErr(representation, 'inexact rounding');
                    }
                    lastDigit = lastDigit - 1;
                }
                if (exponent < EXPONENT_MAX) {
                    exponent = exponent + 1;
                }
                else {
                    invalidErr(representation, 'overflow');
                }
            }
            if (lastDigit + 1 < significantDigits) {
                if (sawRadix) {
                    firstNonZero = firstNonZero + 1;
                }
                if (sawSign) {
                    firstNonZero = firstNonZero + 1;
                }
                const roundDigit = parseInt(representation[firstNonZero + lastDigit + 1], 10);
                if (roundDigit !== 0) {
                    invalidErr(representation, 'inexact rounding');
                }
            }
        }
        significandHigh = Long.fromNumber(0);
        significandLow = Long.fromNumber(0);
        if (significantDigits === 0) {
            significandHigh = Long.fromNumber(0);
            significandLow = Long.fromNumber(0);
        }
        else if (lastDigit < 17) {
            let dIdx = 0;
            significandLow = Long.fromNumber(digits[dIdx++]);
            significandHigh = new Long(0, 0);
            for (; dIdx <= lastDigit; dIdx++) {
                significandLow = significandLow.multiply(Long.fromNumber(10));
                significandLow = significandLow.add(Long.fromNumber(digits[dIdx]));
            }
        }
        else {
            let dIdx = 0;
            significandHigh = Long.fromNumber(digits[dIdx++]);
            for (; dIdx <= lastDigit - 17; dIdx++) {
                significandHigh = significandHigh.multiply(Long.fromNumber(10));
                significandHigh = significandHigh.add(Long.fromNumber(digits[dIdx]));
            }
            significandLow = Long.fromNumber(digits[dIdx++]);
            for (; dIdx <= lastDigit; dIdx++) {
                significandLow = significandLow.multiply(Long.fromNumber(10));
                significandLow = significandLow.add(Long.fromNumber(digits[dIdx]));
            }
        }
        const significand = multiply64x2(significandHigh, Long.fromString('100000000000000000'));
        significand.low = significand.low.add(significandLow);
        if (lessThan(significand.low, significandLow)) {
            significand.high = significand.high.add(Long.fromNumber(1));
        }
        biasedExponent = exponent + EXPONENT_BIAS;
        const dec = { low: Long.fromNumber(0), high: Long.fromNumber(0) };
        if (significand.high.shiftRightUnsigned(49).and(Long.fromNumber(1)).equals(Long.fromNumber(1))) {
            dec.high = dec.high.or(Long.fromNumber(0x3).shiftLeft(61));
            dec.high = dec.high.or(Long.fromNumber(biasedExponent).and(Long.fromNumber(0x3fff).shiftLeft(47)));
            dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x7fffffffffff)));
        }
        else {
            dec.high = dec.high.or(Long.fromNumber(biasedExponent & 0x3fff).shiftLeft(49));
            dec.high = dec.high.or(significand.high.and(Long.fromNumber(0x1ffffffffffff)));
        }
        dec.low = significand.low;
        if (isNegative) {
            dec.high = dec.high.or(Long.fromString('9223372036854775808'));
        }
        const buffer = ByteUtils.allocateUnsafe(16);
        index = 0;
        buffer[index++] = dec.low.low & 0xff;
        buffer[index++] = (dec.low.low >> 8) & 0xff;
        buffer[index++] = (dec.low.low >> 16) & 0xff;
        buffer[index++] = (dec.low.low >> 24) & 0xff;
        buffer[index++] = dec.low.high & 0xff;
        buffer[index++] = (dec.low.high >> 8) & 0xff;
        buffer[index++] = (dec.low.high >> 16) & 0xff;
        buffer[index++] = (dec.low.high >> 24) & 0xff;
        buffer[index++] = dec.high.low & 0xff;
        buffer[index++] = (dec.high.low >> 8) & 0xff;
        buffer[index++] = (dec.high.low >> 16) & 0xff;
        buffer[index++] = (dec.high.low >> 24) & 0xff;
        buffer[index++] = dec.high.high & 0xff;
        buffer[index++] = (dec.high.high >> 8) & 0xff;
        buffer[index++] = (dec.high.high >> 16) & 0xff;
        buffer[index++] = (dec.high.high >> 24) & 0xff;
        return new Decimal128(buffer);
    }
    toString() {
        let biased_exponent;
        let significand_digits = 0;
        const significand = new Array(36);
        for (let i = 0; i < significand.length; i++)
            significand[i] = 0;
        let index = 0;
        let is_zero = false;
        let significand_msb;
        let significand128 = { parts: [0, 0, 0, 0] };
        let j, k;
        const string = [];
        index = 0;
        const buffer = this.bytes;
        const low = buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);
        const midl = buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);
        const midh = buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);
        const high = buffer[index++] | (buffer[index++] << 8) | (buffer[index++] << 16) | (buffer[index++] << 24);
        index = 0;
        const dec = {
            low: new Long(low, midl),
            high: new Long(midh, high)
        };
        if (dec.high.lessThan(Long.ZERO)) {
            string.push('-');
        }
        const combination = (high >> 26) & COMBINATION_MASK;
        if (combination >> 3 === 3) {
            if (combination === COMBINATION_INFINITY) {
                return string.join('') + 'Infinity';
            }
            else if (combination === COMBINATION_NAN) {
                return 'NaN';
            }
            else {
                biased_exponent = (high >> 15) & EXPONENT_MASK;
                significand_msb = 0x08 + ((high >> 14) & 0x01);
            }
        }
        else {
            significand_msb = (high >> 14) & 0x07;
            biased_exponent = (high >> 17) & EXPONENT_MASK;
        }
        const exponent = biased_exponent - EXPONENT_BIAS;
        significand128.parts[0] = (high & 0x3fff) + ((significand_msb & 0xf) << 14);
        significand128.parts[1] = midh;
        significand128.parts[2] = midl;
        significand128.parts[3] = low;
        if (significand128.parts[0] === 0 &&
            significand128.parts[1] === 0 &&
            significand128.parts[2] === 0 &&
            significand128.parts[3] === 0) {
            is_zero = true;
        }
        else {
            for (k = 3; k >= 0; k--) {
                let least_digits = 0;
                const result = divideu128(significand128);
                significand128 = result.quotient;
                least_digits = result.rem.low;
                if (!least_digits)
                    continue;
                for (j = 8; j >= 0; j--) {
                    significand[k * 9 + j] = least_digits % 10;
                    least_digits = Math.floor(least_digits / 10);
                }
            }
        }
        if (is_zero) {
            significand_digits = 1;
            significand[index] = 0;
        }
        else {
            significand_digits = 36;
            while (!significand[index]) {
                significand_digits = significand_digits - 1;
                index = index + 1;
            }
        }
        const scientific_exponent = significand_digits - 1 + exponent;
        if (scientific_exponent >= 34 || scientific_exponent <= -7 || exponent > 0) {
            if (significand_digits > 34) {
                string.push(`${0}`);
                if (exponent > 0)
                    string.push(`E+${exponent}`);
                else if (exponent < 0)
                    string.push(`E${exponent}`);
                return string.join('');
            }
            string.push(`${significand[index++]}`);
            significand_digits = significand_digits - 1;
            if (significand_digits) {
                string.push('.');
            }
            for (let i = 0; i < significand_digits; i++) {
                string.push(`${significand[index++]}`);
            }
            string.push('E');
            if (scientific_exponent > 0) {
                string.push(`+${scientific_exponent}`);
            }
            else {
                string.push(`${scientific_exponent}`);
            }
        }
        else {
            if (exponent >= 0) {
                for (let i = 0; i < significand_digits; i++) {
                    string.push(`${significand[index++]}`);
                }
            }
            else {
                let radix_position = significand_digits + exponent;
                if (radix_position > 0) {
                    for (let i = 0; i < radix_position; i++) {
                        string.push(`${significand[index++]}`);
                    }
                }
                else {
                    string.push('0');
                }
                string.push('.');
                while (radix_position++ < 0) {
                    string.push('0');
                }
                for (let i = 0; i < significand_digits - Math.max(radix_position - 1, 0); i++) {
                    string.push(`${significand[index++]}`);
                }
            }
        }
        return string.join('');
    }
    toJSON() {
        return { $numberDecimal: this.toString() };
    }
    toExtendedJSON() {
        return { $numberDecimal: this.toString() };
    }
    static fromExtendedJSON(doc) {
        return Decimal128.fromString(doc.$numberDecimal);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        const d128string = inspect(this.toString(), options);
        return `new Decimal128(${d128string})`;
    }
}

class Double extends BSONValue {
    get _bsontype() {
        return 'Double';
    }
    constructor(value) {
        super();
        if (value instanceof Number) {
            value = value.valueOf();
        }
        this.value = +value;
    }
    valueOf() {
        return this.value;
    }
    toJSON() {
        return this.value;
    }
    toString(radix) {
        return this.value.toString(radix);
    }
    toExtendedJSON(options) {
        if (options && (options.legacy || (options.relaxed && isFinite(this.value)))) {
            return this.value;
        }
        if (Object.is(Math.sign(this.value), -0)) {
            return { $numberDouble: '-0.0' };
        }
        return {
            $numberDouble: Number.isInteger(this.value) ? this.value.toFixed(1) : this.value.toString()
        };
    }
    static fromExtendedJSON(doc, options) {
        const doubleValue = parseFloat(doc.$numberDouble);
        return options && options.relaxed ? doubleValue : new Double(doubleValue);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        return `new Double(${inspect(this.value, options)})`;
    }
}

class Int32 extends BSONValue {
    get _bsontype() {
        return 'Int32';
    }
    constructor(value) {
        super();
        if (value instanceof Number) {
            value = value.valueOf();
        }
        this.value = +value | 0;
    }
    valueOf() {
        return this.value;
    }
    toString(radix) {
        return this.value.toString(radix);
    }
    toJSON() {
        return this.value;
    }
    toExtendedJSON(options) {
        if (options && (options.relaxed || options.legacy))
            return this.value;
        return { $numberInt: this.value.toString() };
    }
    static fromExtendedJSON(doc, options) {
        return options && options.relaxed ? parseInt(doc.$numberInt, 10) : new Int32(doc.$numberInt);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        return `new Int32(${inspect(this.value, options)})`;
    }
}

class MaxKey extends BSONValue {
    get _bsontype() {
        return 'MaxKey';
    }
    toExtendedJSON() {
        return { $maxKey: 1 };
    }
    static fromExtendedJSON() {
        return new MaxKey();
    }
    inspect() {
        return 'new MaxKey()';
    }
}

class MinKey extends BSONValue {
    get _bsontype() {
        return 'MinKey';
    }
    toExtendedJSON() {
        return { $minKey: 1 };
    }
    static fromExtendedJSON() {
        return new MinKey();
    }
    inspect() {
        return 'new MinKey()';
    }
}

const FLOAT = new Float64Array(1);
const FLOAT_BYTES = new Uint8Array(FLOAT.buffer, 0, 8);
const NumberUtils = {
    getInt32LE(source, offset) {
        return (source[offset] |
            (source[offset + 1] << 8) |
            (source[offset + 2] << 16) |
            (source[offset + 3] << 24));
    },
    getUint32LE(source, offset) {
        return (source[offset] +
            source[offset + 1] * 256 +
            source[offset + 2] * 65536 +
            source[offset + 3] * 16777216);
    },
    getUint32BE(source, offset) {
        return (source[offset + 3] +
            source[offset + 2] * 256 +
            source[offset + 1] * 65536 +
            source[offset] * 16777216);
    },
    getBigInt64LE(source, offset) {
        const lo = NumberUtils.getUint32LE(source, offset);
        const hi = NumberUtils.getUint32LE(source, offset + 4);
        return (BigInt(hi) << BigInt(32)) + BigInt(lo);
    },
    getFloat64LE(source, offset) {
        FLOAT_BYTES[0] = source[offset];
        FLOAT_BYTES[1] = source[offset + 1];
        FLOAT_BYTES[2] = source[offset + 2];
        FLOAT_BYTES[3] = source[offset + 3];
        FLOAT_BYTES[4] = source[offset + 4];
        FLOAT_BYTES[5] = source[offset + 5];
        FLOAT_BYTES[6] = source[offset + 6];
        FLOAT_BYTES[7] = source[offset + 7];
        return FLOAT[0];
    },
    setInt32BE(destination, offset, value) {
        destination[offset + 3] = value;
        value >>>= 8;
        destination[offset + 2] = value;
        value >>>= 8;
        destination[offset + 1] = value;
        value >>>= 8;
        destination[offset] = value;
        return 4;
    },
    setInt32LE(destination, offset, value) {
        destination[offset] = value;
        value >>>= 8;
        destination[offset + 1] = value;
        value >>>= 8;
        destination[offset + 2] = value;
        value >>>= 8;
        destination[offset + 3] = value;
        return 4;
    },
    setBigInt64LE(destination, offset, value) {
        const mask32bits = BigInt(4294967295);
        let lo = Number(value & mask32bits);
        destination[offset] = lo;
        lo >>= 8;
        destination[offset + 1] = lo;
        lo >>= 8;
        destination[offset + 2] = lo;
        lo >>= 8;
        destination[offset + 3] = lo;
        let hi = Number((value >> BigInt(32)) & mask32bits);
        destination[offset + 4] = hi;
        hi >>= 8;
        destination[offset + 5] = hi;
        hi >>= 8;
        destination[offset + 6] = hi;
        hi >>= 8;
        destination[offset + 7] = hi;
        return 8;
    },
    setFloat64LE(destination, offset, value) {
        FLOAT[0] = value;
        destination[offset] = FLOAT_BYTES[0];
        destination[offset + 1] = FLOAT_BYTES[1];
        destination[offset + 2] = FLOAT_BYTES[2];
        destination[offset + 3] = FLOAT_BYTES[3];
        destination[offset + 4] = FLOAT_BYTES[4];
        destination[offset + 5] = FLOAT_BYTES[5];
        destination[offset + 6] = FLOAT_BYTES[6];
        destination[offset + 7] = FLOAT_BYTES[7];
        return 8;
    }
};

const checkForHexRegExp = new RegExp('^[0-9a-fA-F]{24}$');
let PROCESS_UNIQUE = null;
class ObjectId extends BSONValue {
    get _bsontype() {
        return 'ObjectId';
    }
    constructor(inputId) {
        super();
        let workingId;
        if (typeof inputId === 'object' && inputId && 'id' in inputId) {
            if (typeof inputId.id !== 'string' && !ArrayBuffer.isView(inputId.id)) {
                throw new BSONError('Argument passed in must have an id that is of type string or Buffer');
            }
            if ('toHexString' in inputId && typeof inputId.toHexString === 'function') {
                workingId = ByteUtils.fromHex(inputId.toHexString());
            }
            else {
                workingId = inputId.id;
            }
        }
        else {
            workingId = inputId;
        }
        if (workingId == null || typeof workingId === 'number') {
            this.buffer = ObjectId.generate(typeof workingId === 'number' ? workingId : undefined);
        }
        else if (ArrayBuffer.isView(workingId) && workingId.byteLength === 12) {
            this.buffer = ByteUtils.toLocalBufferType(workingId);
        }
        else if (typeof workingId === 'string') {
            if (workingId.length === 24 && checkForHexRegExp.test(workingId)) {
                this.buffer = ByteUtils.fromHex(workingId);
            }
            else {
                throw new BSONError('input must be a 24 character hex string, 12 byte Uint8Array, or an integer');
            }
        }
        else {
            throw new BSONError('Argument passed in does not match the accepted types');
        }
        if (ObjectId.cacheHexString) {
            this.__id = ByteUtils.toHex(this.id);
        }
    }
    get id() {
        return this.buffer;
    }
    set id(value) {
        this.buffer = value;
        if (ObjectId.cacheHexString) {
            this.__id = ByteUtils.toHex(value);
        }
    }
    toHexString() {
        if (ObjectId.cacheHexString && this.__id) {
            return this.__id;
        }
        const hexString = ByteUtils.toHex(this.id);
        if (ObjectId.cacheHexString && !this.__id) {
            this.__id = hexString;
        }
        return hexString;
    }
    static getInc() {
        return (ObjectId.index = (ObjectId.index + 1) % 0xffffff);
    }
    static generate(time) {
        if ('number' !== typeof time) {
            time = Math.floor(Date.now() / 1000);
        }
        const inc = ObjectId.getInc();
        const buffer = ByteUtils.allocateUnsafe(12);
        NumberUtils.setInt32BE(buffer, 0, time);
        if (PROCESS_UNIQUE === null) {
            PROCESS_UNIQUE = ByteUtils.randomBytes(5);
        }
        buffer[4] = PROCESS_UNIQUE[0];
        buffer[5] = PROCESS_UNIQUE[1];
        buffer[6] = PROCESS_UNIQUE[2];
        buffer[7] = PROCESS_UNIQUE[3];
        buffer[8] = PROCESS_UNIQUE[4];
        buffer[11] = inc & 0xff;
        buffer[10] = (inc >> 8) & 0xff;
        buffer[9] = (inc >> 16) & 0xff;
        return buffer;
    }
    toString(encoding) {
        if (encoding === 'base64')
            return ByteUtils.toBase64(this.id);
        if (encoding === 'hex')
            return this.toHexString();
        return this.toHexString();
    }
    toJSON() {
        return this.toHexString();
    }
    static is(variable) {
        return (variable != null &&
            typeof variable === 'object' &&
            '_bsontype' in variable &&
            variable._bsontype === 'ObjectId');
    }
    equals(otherId) {
        if (otherId === undefined || otherId === null) {
            return false;
        }
        if (ObjectId.is(otherId)) {
            return (this.buffer[11] === otherId.buffer[11] && ByteUtils.equals(this.buffer, otherId.buffer));
        }
        if (typeof otherId === 'string') {
            return otherId.toLowerCase() === this.toHexString();
        }
        if (typeof otherId === 'object' && typeof otherId.toHexString === 'function') {
            const otherIdString = otherId.toHexString();
            const thisIdString = this.toHexString();
            return typeof otherIdString === 'string' && otherIdString.toLowerCase() === thisIdString;
        }
        return false;
    }
    getTimestamp() {
        const timestamp = new Date();
        const time = NumberUtils.getUint32BE(this.buffer, 0);
        timestamp.setTime(Math.floor(time) * 1000);
        return timestamp;
    }
    static createPk() {
        return new ObjectId();
    }
    serializeInto(uint8array, index) {
        uint8array[index] = this.buffer[0];
        uint8array[index + 1] = this.buffer[1];
        uint8array[index + 2] = this.buffer[2];
        uint8array[index + 3] = this.buffer[3];
        uint8array[index + 4] = this.buffer[4];
        uint8array[index + 5] = this.buffer[5];
        uint8array[index + 6] = this.buffer[6];
        uint8array[index + 7] = this.buffer[7];
        uint8array[index + 8] = this.buffer[8];
        uint8array[index + 9] = this.buffer[9];
        uint8array[index + 10] = this.buffer[10];
        uint8array[index + 11] = this.buffer[11];
        return 12;
    }
    static createFromTime(time) {
        const buffer = ByteUtils.allocate(12);
        for (let i = 11; i >= 4; i--)
            buffer[i] = 0;
        NumberUtils.setInt32BE(buffer, 0, time);
        return new ObjectId(buffer);
    }
    static createFromHexString(hexString) {
        if (hexString?.length !== 24) {
            throw new BSONError('hex string must be 24 characters');
        }
        return new ObjectId(ByteUtils.fromHex(hexString));
    }
    static createFromBase64(base64) {
        if (base64?.length !== 16) {
            throw new BSONError('base64 string must be 16 characters');
        }
        return new ObjectId(ByteUtils.fromBase64(base64));
    }
    static isValid(id) {
        if (id == null)
            return false;
        try {
            new ObjectId(id);
            return true;
        }
        catch {
            return false;
        }
    }
    toExtendedJSON() {
        if (this.toHexString)
            return { $oid: this.toHexString() };
        return { $oid: this.toString('hex') };
    }
    static fromExtendedJSON(doc) {
        return new ObjectId(doc.$oid);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        return `new ObjectId(${inspect(this.toHexString(), options)})`;
    }
}
ObjectId.index = Math.floor(Math.random() * 0xffffff);

function internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined) {
    let totalLength = 4 + 1;
    if (Array.isArray(object)) {
        for (let i = 0; i < object.length; i++) {
            totalLength += calculateElement(i.toString(), object[i], serializeFunctions, true, ignoreUndefined);
        }
    }
    else {
        if (typeof object?.toBSON === 'function') {
            object = object.toBSON();
        }
        for (const key of Object.keys(object)) {
            totalLength += calculateElement(key, object[key], serializeFunctions, false, ignoreUndefined);
        }
    }
    return totalLength;
}
function calculateElement(name, value, serializeFunctions = false, isArray = false, ignoreUndefined = false) {
    if (typeof value?.toBSON === 'function') {
        value = value.toBSON();
    }
    switch (typeof value) {
        case 'string':
            return 1 + ByteUtils.utf8ByteLength(name) + 1 + 4 + ByteUtils.utf8ByteLength(value) + 1;
        case 'number':
            if (Math.floor(value) === value &&
                value >= JS_INT_MIN &&
                value <= JS_INT_MAX) {
                if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
                    return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (4 + 1);
                }
                else {
                    return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
                }
            }
            else {
                return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
            }
        case 'undefined':
            if (isArray || !ignoreUndefined)
                return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
            return 0;
        case 'boolean':
            return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 1);
        case 'object':
            if (value != null &&
                typeof value._bsontype === 'string' &&
                value[Symbol.for('@@mdb.bson.version')] !== BSON_MAJOR_VERSION) {
                throw new BSONVersionError();
            }
            else if (value == null || value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
                return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + 1;
            }
            else if (value._bsontype === 'ObjectId') {
                return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (12 + 1);
            }
            else if (value instanceof Date || isDate(value)) {
                return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
            }
            else if (ArrayBuffer.isView(value) ||
                value instanceof ArrayBuffer ||
                isAnyArrayBuffer(value)) {
                return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (1 + 4 + 1) + value.byteLength);
            }
            else if (value._bsontype === 'Long' ||
                value._bsontype === 'Double' ||
                value._bsontype === 'Timestamp') {
                return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (8 + 1);
            }
            else if (value._bsontype === 'Decimal128') {
                return (name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (16 + 1);
            }
            else if (value._bsontype === 'Code') {
                if (value.scope != null && Object.keys(value.scope).length > 0) {
                    return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                        1 +
                        4 +
                        4 +
                        ByteUtils.utf8ByteLength(value.code.toString()) +
                        1 +
                        internalCalculateObjectSize(value.scope, serializeFunctions, ignoreUndefined));
                }
                else {
                    return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                        1 +
                        4 +
                        ByteUtils.utf8ByteLength(value.code.toString()) +
                        1);
                }
            }
            else if (value._bsontype === 'Binary') {
                const binary = value;
                if (binary.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
                    return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                        (binary.position + 1 + 4 + 1 + 4));
                }
                else {
                    return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) + (binary.position + 1 + 4 + 1));
                }
            }
            else if (value._bsontype === 'Symbol') {
                return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                    ByteUtils.utf8ByteLength(value.value) +
                    4 +
                    1 +
                    1);
            }
            else if (value._bsontype === 'DBRef') {
                const ordered_values = Object.assign({
                    $ref: value.collection,
                    $id: value.oid
                }, value.fields);
                if (value.db != null) {
                    ordered_values['$db'] = value.db;
                }
                return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                    1 +
                    internalCalculateObjectSize(ordered_values, serializeFunctions, ignoreUndefined));
            }
            else if (value instanceof RegExp || isRegExp(value)) {
                return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                    1 +
                    ByteUtils.utf8ByteLength(value.source) +
                    1 +
                    (value.global ? 1 : 0) +
                    (value.ignoreCase ? 1 : 0) +
                    (value.multiline ? 1 : 0) +
                    1);
            }
            else if (value._bsontype === 'BSONRegExp') {
                return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                    1 +
                    ByteUtils.utf8ByteLength(value.pattern) +
                    1 +
                    ByteUtils.utf8ByteLength(value.options) +
                    1);
            }
            else {
                return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                    internalCalculateObjectSize(value, serializeFunctions, ignoreUndefined) +
                    1);
            }
        case 'function':
            if (serializeFunctions) {
                return ((name != null ? ByteUtils.utf8ByteLength(name) + 1 : 0) +
                    1 +
                    4 +
                    ByteUtils.utf8ByteLength(value.toString()) +
                    1);
            }
    }
    return 0;
}

function alphabetize(str) {
    return str.split('').sort().join('');
}
class BSONRegExp extends BSONValue {
    get _bsontype() {
        return 'BSONRegExp';
    }
    constructor(pattern, options) {
        super();
        this.pattern = pattern;
        this.options = alphabetize(options ?? '');
        if (this.pattern.indexOf('\x00') !== -1) {
            throw new BSONError(`BSON Regex patterns cannot contain null bytes, found: ${JSON.stringify(this.pattern)}`);
        }
        if (this.options.indexOf('\x00') !== -1) {
            throw new BSONError(`BSON Regex options cannot contain null bytes, found: ${JSON.stringify(this.options)}`);
        }
        for (let i = 0; i < this.options.length; i++) {
            if (!(this.options[i] === 'i' ||
                this.options[i] === 'm' ||
                this.options[i] === 'x' ||
                this.options[i] === 'l' ||
                this.options[i] === 's' ||
                this.options[i] === 'u')) {
                throw new BSONError(`The regular expression option [${this.options[i]}] is not supported`);
            }
        }
    }
    static parseOptions(options) {
        return options ? options.split('').sort().join('') : '';
    }
    toExtendedJSON(options) {
        options = options || {};
        if (options.legacy) {
            return { $regex: this.pattern, $options: this.options };
        }
        return { $regularExpression: { pattern: this.pattern, options: this.options } };
    }
    static fromExtendedJSON(doc) {
        if ('$regex' in doc) {
            if (typeof doc.$regex !== 'string') {
                if (doc.$regex._bsontype === 'BSONRegExp') {
                    return doc;
                }
            }
            else {
                return new BSONRegExp(doc.$regex, BSONRegExp.parseOptions(doc.$options));
            }
        }
        if ('$regularExpression' in doc) {
            return new BSONRegExp(doc.$regularExpression.pattern, BSONRegExp.parseOptions(doc.$regularExpression.options));
        }
        throw new BSONError(`Unexpected BSONRegExp EJSON object form: ${JSON.stringify(doc)}`);
    }
    inspect(depth, options, inspect) {
        const stylize = getStylizeFunction(options) ?? (v => v);
        inspect ??= defaultInspect;
        const pattern = stylize(inspect(this.pattern), 'regexp');
        const flags = stylize(inspect(this.options), 'regexp');
        return `new BSONRegExp(${pattern}, ${flags})`;
    }
}

class BSONSymbol extends BSONValue {
    get _bsontype() {
        return 'BSONSymbol';
    }
    constructor(value) {
        super();
        this.value = value;
    }
    valueOf() {
        return this.value;
    }
    toString() {
        return this.value;
    }
    toJSON() {
        return this.value;
    }
    toExtendedJSON() {
        return { $symbol: this.value };
    }
    static fromExtendedJSON(doc) {
        return new BSONSymbol(doc.$symbol);
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        return `new BSONSymbol(${inspect(this.value, options)})`;
    }
}

const LongWithoutOverridesClass = Long;
class Timestamp extends LongWithoutOverridesClass {
    get _bsontype() {
        return 'Timestamp';
    }
    constructor(low) {
        if (low == null) {
            super(0, 0, true);
        }
        else if (typeof low === 'bigint') {
            super(low, true);
        }
        else if (Long.isLong(low)) {
            super(low.low, low.high, true);
        }
        else if (typeof low === 'object' && 't' in low && 'i' in low) {
            if (typeof low.t !== 'number' && (typeof low.t !== 'object' || low.t._bsontype !== 'Int32')) {
                throw new BSONError('Timestamp constructed from { t, i } must provide t as a number');
            }
            if (typeof low.i !== 'number' && (typeof low.i !== 'object' || low.i._bsontype !== 'Int32')) {
                throw new BSONError('Timestamp constructed from { t, i } must provide i as a number');
            }
            const t = Number(low.t);
            const i = Number(low.i);
            if (t < 0 || Number.isNaN(t)) {
                throw new BSONError('Timestamp constructed from { t, i } must provide a positive t');
            }
            if (i < 0 || Number.isNaN(i)) {
                throw new BSONError('Timestamp constructed from { t, i } must provide a positive i');
            }
            if (t > 4294967295) {
                throw new BSONError('Timestamp constructed from { t, i } must provide t equal or less than uint32 max');
            }
            if (i > 4294967295) {
                throw new BSONError('Timestamp constructed from { t, i } must provide i equal or less than uint32 max');
            }
            super(i, t, true);
        }
        else {
            throw new BSONError('A Timestamp can only be constructed with: bigint, Long, or { t: number; i: number }');
        }
    }
    toJSON() {
        return {
            $timestamp: this.toString()
        };
    }
    static fromInt(value) {
        return new Timestamp(Long.fromInt(value, true));
    }
    static fromNumber(value) {
        return new Timestamp(Long.fromNumber(value, true));
    }
    static fromBits(lowBits, highBits) {
        return new Timestamp({ i: lowBits, t: highBits });
    }
    static fromString(str, optRadix) {
        return new Timestamp(Long.fromString(str, true, optRadix));
    }
    toExtendedJSON() {
        return { $timestamp: { t: this.high >>> 0, i: this.low >>> 0 } };
    }
    static fromExtendedJSON(doc) {
        const i = Long.isLong(doc.$timestamp.i)
            ? doc.$timestamp.i.getLowBitsUnsigned()
            : doc.$timestamp.i;
        const t = Long.isLong(doc.$timestamp.t)
            ? doc.$timestamp.t.getLowBitsUnsigned()
            : doc.$timestamp.t;
        return new Timestamp({ t, i });
    }
    inspect(depth, options, inspect) {
        inspect ??= defaultInspect;
        const t = inspect(this.high >>> 0, options);
        const i = inspect(this.low >>> 0, options);
        return `new Timestamp({ t: ${t}, i: ${i} })`;
    }
}
Timestamp.MAX_VALUE = Long.MAX_UNSIGNED_VALUE;

const JS_INT_MAX_LONG = Long.fromNumber(JS_INT_MAX);
const JS_INT_MIN_LONG = Long.fromNumber(JS_INT_MIN);
function internalDeserialize(buffer, options, isArray) {
    options = options == null ? {} : options;
    const index = options && options.index ? options.index : 0;
    const size = NumberUtils.getInt32LE(buffer, index);
    if (size < 5) {
        throw new BSONError(`bson size must be >= 5, is ${size}`);
    }
    if (options.allowObjectSmallerThanBufferSize && buffer.length < size) {
        throw new BSONError(`buffer length ${buffer.length} must be >= bson size ${size}`);
    }
    if (!options.allowObjectSmallerThanBufferSize && buffer.length !== size) {
        throw new BSONError(`buffer length ${buffer.length} must === bson size ${size}`);
    }
    if (size + index > buffer.byteLength) {
        throw new BSONError(`(bson size ${size} + options.index ${index} must be <= buffer length ${buffer.byteLength})`);
    }
    if (buffer[index + size - 1] !== 0) {
        throw new BSONError("One object, sized correctly, with a spot for an EOO, but the EOO isn't 0x00");
    }
    return deserializeObject(buffer, index, options, isArray);
}
const allowedDBRefKeys = /^\$ref$|^\$id$|^\$db$/;
function deserializeObject(buffer, index, options, isArray = false) {
    const fieldsAsRaw = options['fieldsAsRaw'] == null ? null : options['fieldsAsRaw'];
    const raw = options['raw'] == null ? false : options['raw'];
    const bsonRegExp = typeof options['bsonRegExp'] === 'boolean' ? options['bsonRegExp'] : false;
    const promoteBuffers = options.promoteBuffers ?? false;
    const promoteLongs = options.promoteLongs ?? true;
    const promoteValues = options.promoteValues ?? true;
    const useBigInt64 = options.useBigInt64 ?? false;
    if (useBigInt64 && !promoteValues) {
        throw new BSONError('Must either request bigint or Long for int64 deserialization');
    }
    if (useBigInt64 && !promoteLongs) {
        throw new BSONError('Must either request bigint or Long for int64 deserialization');
    }
    const validation = options.validation == null ? { utf8: true } : options.validation;
    let globalUTFValidation = true;
    let validationSetting;
    let utf8KeysSet;
    const utf8ValidatedKeys = validation.utf8;
    if (typeof utf8ValidatedKeys === 'boolean') {
        validationSetting = utf8ValidatedKeys;
    }
    else {
        globalUTFValidation = false;
        const utf8ValidationValues = Object.keys(utf8ValidatedKeys).map(function (key) {
            return utf8ValidatedKeys[key];
        });
        if (utf8ValidationValues.length === 0) {
            throw new BSONError('UTF-8 validation setting cannot be empty');
        }
        if (typeof utf8ValidationValues[0] !== 'boolean') {
            throw new BSONError('Invalid UTF-8 validation option, must specify boolean values');
        }
        validationSetting = utf8ValidationValues[0];
        if (!utf8ValidationValues.every(item => item === validationSetting)) {
            throw new BSONError('Invalid UTF-8 validation option - keys must be all true or all false');
        }
    }
    if (!globalUTFValidation) {
        utf8KeysSet = new Set();
        for (const key of Object.keys(utf8ValidatedKeys)) {
            utf8KeysSet.add(key);
        }
    }
    const startIndex = index;
    if (buffer.length < 5)
        throw new BSONError('corrupt bson message < 5 bytes long');
    const size = NumberUtils.getInt32LE(buffer, index);
    index += 4;
    if (size < 5 || size > buffer.length)
        throw new BSONError('corrupt bson message');
    const object = isArray ? [] : {};
    let arrayIndex = 0;
    const done = false;
    let isPossibleDBRef = isArray ? false : null;
    while (!done) {
        const elementType = buffer[index++];
        if (elementType === 0)
            break;
        let i = index;
        while (buffer[i] !== 0x00 && i < buffer.length) {
            i++;
        }
        if (i >= buffer.byteLength)
            throw new BSONError('Bad BSON Document: illegal CString');
        const name = isArray ? arrayIndex++ : ByteUtils.toUTF8(buffer, index, i, false);
        let shouldValidateKey = true;
        if (globalUTFValidation || utf8KeysSet?.has(name)) {
            shouldValidateKey = validationSetting;
        }
        else {
            shouldValidateKey = !validationSetting;
        }
        if (isPossibleDBRef !== false && name[0] === '$') {
            isPossibleDBRef = allowedDBRefKeys.test(name);
        }
        let value;
        index = i + 1;
        if (elementType === BSON_DATA_STRING) {
            const stringSize = NumberUtils.getInt32LE(buffer, index);
            index += 4;
            if (stringSize <= 0 ||
                stringSize > buffer.length - index ||
                buffer[index + stringSize - 1] !== 0) {
                throw new BSONError('bad string length in bson');
            }
            value = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
            index = index + stringSize;
        }
        else if (elementType === BSON_DATA_OID) {
            const oid = ByteUtils.allocateUnsafe(12);
            for (let i = 0; i < 12; i++)
                oid[i] = buffer[index + i];
            value = new ObjectId(oid);
            index = index + 12;
        }
        else if (elementType === BSON_DATA_INT && promoteValues === false) {
            value = new Int32(NumberUtils.getInt32LE(buffer, index));
            index += 4;
        }
        else if (elementType === BSON_DATA_INT) {
            value = NumberUtils.getInt32LE(buffer, index);
            index += 4;
        }
        else if (elementType === BSON_DATA_NUMBER) {
            value = NumberUtils.getFloat64LE(buffer, index);
            index += 8;
            if (promoteValues === false)
                value = new Double(value);
        }
        else if (elementType === BSON_DATA_DATE) {
            const lowBits = NumberUtils.getInt32LE(buffer, index);
            const highBits = NumberUtils.getInt32LE(buffer, index + 4);
            index += 8;
            value = new Date(new Long(lowBits, highBits).toNumber());
        }
        else if (elementType === BSON_DATA_BOOLEAN) {
            if (buffer[index] !== 0 && buffer[index] !== 1)
                throw new BSONError('illegal boolean type value');
            value = buffer[index++] === 1;
        }
        else if (elementType === BSON_DATA_OBJECT) {
            const _index = index;
            const objectSize = NumberUtils.getInt32LE(buffer, index);
            if (objectSize <= 0 || objectSize > buffer.length - index)
                throw new BSONError('bad embedded document length in bson');
            if (raw) {
                value = buffer.slice(index, index + objectSize);
            }
            else {
                let objectOptions = options;
                if (!globalUTFValidation) {
                    objectOptions = { ...options, validation: { utf8: shouldValidateKey } };
                }
                value = deserializeObject(buffer, _index, objectOptions, false);
            }
            index = index + objectSize;
        }
        else if (elementType === BSON_DATA_ARRAY) {
            const _index = index;
            const objectSize = NumberUtils.getInt32LE(buffer, index);
            let arrayOptions = options;
            const stopIndex = index + objectSize;
            if (fieldsAsRaw && fieldsAsRaw[name]) {
                arrayOptions = { ...options, raw: true };
            }
            if (!globalUTFValidation) {
                arrayOptions = { ...arrayOptions, validation: { utf8: shouldValidateKey } };
            }
            value = deserializeObject(buffer, _index, arrayOptions, true);
            index = index + objectSize;
            if (buffer[index - 1] !== 0)
                throw new BSONError('invalid array terminator byte');
            if (index !== stopIndex)
                throw new BSONError('corrupted array bson');
        }
        else if (elementType === BSON_DATA_UNDEFINED) {
            value = undefined;
        }
        else if (elementType === BSON_DATA_NULL) {
            value = null;
        }
        else if (elementType === BSON_DATA_LONG) {
            if (useBigInt64) {
                value = NumberUtils.getBigInt64LE(buffer, index);
                index += 8;
            }
            else {
                const lowBits = NumberUtils.getInt32LE(buffer, index);
                const highBits = NumberUtils.getInt32LE(buffer, index + 4);
                index += 8;
                const long = new Long(lowBits, highBits);
                if (promoteLongs && promoteValues === true) {
                    value =
                        long.lessThanOrEqual(JS_INT_MAX_LONG) && long.greaterThanOrEqual(JS_INT_MIN_LONG)
                            ? long.toNumber()
                            : long;
                }
                else {
                    value = long;
                }
            }
        }
        else if (elementType === BSON_DATA_DECIMAL128) {
            const bytes = ByteUtils.allocateUnsafe(16);
            for (let i = 0; i < 16; i++)
                bytes[i] = buffer[index + i];
            index = index + 16;
            value = new Decimal128(bytes);
        }
        else if (elementType === BSON_DATA_BINARY) {
            let binarySize = NumberUtils.getInt32LE(buffer, index);
            index += 4;
            const totalBinarySize = binarySize;
            const subType = buffer[index++];
            if (binarySize < 0)
                throw new BSONError('Negative binary type element size found');
            if (binarySize > buffer.byteLength)
                throw new BSONError('Binary type size larger than document size');
            if (buffer['slice'] != null) {
                if (subType === Binary.SUBTYPE_BYTE_ARRAY) {
                    binarySize = NumberUtils.getInt32LE(buffer, index);
                    index += 4;
                    if (binarySize < 0)
                        throw new BSONError('Negative binary type element size found for subtype 0x02');
                    if (binarySize > totalBinarySize - 4)
                        throw new BSONError('Binary type with subtype 0x02 contains too long binary size');
                    if (binarySize < totalBinarySize - 4)
                        throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
                }
                if (promoteBuffers && promoteValues) {
                    value = ByteUtils.toLocalBufferType(buffer.slice(index, index + binarySize));
                }
                else {
                    value = new Binary(buffer.slice(index, index + binarySize), subType);
                    if (subType === BSON_BINARY_SUBTYPE_UUID_NEW && UUID.isValid(value)) {
                        value = value.toUUID();
                    }
                }
            }
            else {
                if (subType === Binary.SUBTYPE_BYTE_ARRAY) {
                    binarySize = NumberUtils.getInt32LE(buffer, index);
                    index += 4;
                    if (binarySize < 0)
                        throw new BSONError('Negative binary type element size found for subtype 0x02');
                    if (binarySize > totalBinarySize - 4)
                        throw new BSONError('Binary type with subtype 0x02 contains too long binary size');
                    if (binarySize < totalBinarySize - 4)
                        throw new BSONError('Binary type with subtype 0x02 contains too short binary size');
                }
                if (promoteBuffers && promoteValues) {
                    value = ByteUtils.allocateUnsafe(binarySize);
                    for (i = 0; i < binarySize; i++) {
                        value[i] = buffer[index + i];
                    }
                }
                else {
                    value = new Binary(buffer.slice(index, index + binarySize), subType);
                    if (subType === BSON_BINARY_SUBTYPE_UUID_NEW && UUID.isValid(value)) {
                        value = value.toUUID();
                    }
                }
            }
            index = index + binarySize;
        }
        else if (elementType === BSON_DATA_REGEXP && bsonRegExp === false) {
            i = index;
            while (buffer[i] !== 0x00 && i < buffer.length) {
                i++;
            }
            if (i >= buffer.length)
                throw new BSONError('Bad BSON Document: illegal CString');
            const source = ByteUtils.toUTF8(buffer, index, i, false);
            index = i + 1;
            i = index;
            while (buffer[i] !== 0x00 && i < buffer.length) {
                i++;
            }
            if (i >= buffer.length)
                throw new BSONError('Bad BSON Document: illegal CString');
            const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
            index = i + 1;
            const optionsArray = new Array(regExpOptions.length);
            for (i = 0; i < regExpOptions.length; i++) {
                switch (regExpOptions[i]) {
                    case 'm':
                        optionsArray[i] = 'm';
                        break;
                    case 's':
                        optionsArray[i] = 'g';
                        break;
                    case 'i':
                        optionsArray[i] = 'i';
                        break;
                }
            }
            value = new RegExp(source, optionsArray.join(''));
        }
        else if (elementType === BSON_DATA_REGEXP && bsonRegExp === true) {
            i = index;
            while (buffer[i] !== 0x00 && i < buffer.length) {
                i++;
            }
            if (i >= buffer.length)
                throw new BSONError('Bad BSON Document: illegal CString');
            const source = ByteUtils.toUTF8(buffer, index, i, false);
            index = i + 1;
            i = index;
            while (buffer[i] !== 0x00 && i < buffer.length) {
                i++;
            }
            if (i >= buffer.length)
                throw new BSONError('Bad BSON Document: illegal CString');
            const regExpOptions = ByteUtils.toUTF8(buffer, index, i, false);
            index = i + 1;
            value = new BSONRegExp(source, regExpOptions);
        }
        else if (elementType === BSON_DATA_SYMBOL) {
            const stringSize = NumberUtils.getInt32LE(buffer, index);
            index += 4;
            if (stringSize <= 0 ||
                stringSize > buffer.length - index ||
                buffer[index + stringSize - 1] !== 0) {
                throw new BSONError('bad string length in bson');
            }
            const symbol = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
            value = promoteValues ? symbol : new BSONSymbol(symbol);
            index = index + stringSize;
        }
        else if (elementType === BSON_DATA_TIMESTAMP) {
            value = new Timestamp({
                i: NumberUtils.getUint32LE(buffer, index),
                t: NumberUtils.getUint32LE(buffer, index + 4)
            });
            index += 8;
        }
        else if (elementType === BSON_DATA_MIN_KEY) {
            value = new MinKey();
        }
        else if (elementType === BSON_DATA_MAX_KEY) {
            value = new MaxKey();
        }
        else if (elementType === BSON_DATA_CODE) {
            const stringSize = NumberUtils.getInt32LE(buffer, index);
            index += 4;
            if (stringSize <= 0 ||
                stringSize > buffer.length - index ||
                buffer[index + stringSize - 1] !== 0) {
                throw new BSONError('bad string length in bson');
            }
            const functionString = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
            value = new Code(functionString);
            index = index + stringSize;
        }
        else if (elementType === BSON_DATA_CODE_W_SCOPE) {
            const totalSize = NumberUtils.getInt32LE(buffer, index);
            index += 4;
            if (totalSize < 4 + 4 + 4 + 1) {
                throw new BSONError('code_w_scope total size shorter minimum expected length');
            }
            const stringSize = NumberUtils.getInt32LE(buffer, index);
            index += 4;
            if (stringSize <= 0 ||
                stringSize > buffer.length - index ||
                buffer[index + stringSize - 1] !== 0) {
                throw new BSONError('bad string length in bson');
            }
            const functionString = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, shouldValidateKey);
            index = index + stringSize;
            const _index = index;
            const objectSize = NumberUtils.getInt32LE(buffer, index);
            const scopeObject = deserializeObject(buffer, _index, options, false);
            index = index + objectSize;
            if (totalSize < 4 + 4 + objectSize + stringSize) {
                throw new BSONError('code_w_scope total size is too short, truncating scope');
            }
            if (totalSize > 4 + 4 + objectSize + stringSize) {
                throw new BSONError('code_w_scope total size is too long, clips outer document');
            }
            value = new Code(functionString, scopeObject);
        }
        else if (elementType === BSON_DATA_DBPOINTER) {
            const stringSize = NumberUtils.getInt32LE(buffer, index);
            index += 4;
            if (stringSize <= 0 ||
                stringSize > buffer.length - index ||
                buffer[index + stringSize - 1] !== 0)
                throw new BSONError('bad string length in bson');
            if (validation != null && validation.utf8) {
                if (!validateUtf8(buffer, index, index + stringSize - 1)) {
                    throw new BSONError('Invalid UTF-8 string in BSON document');
                }
            }
            const namespace = ByteUtils.toUTF8(buffer, index, index + stringSize - 1, false);
            index = index + stringSize;
            const oidBuffer = ByteUtils.allocateUnsafe(12);
            for (let i = 0; i < 12; i++)
                oidBuffer[i] = buffer[index + i];
            const oid = new ObjectId(oidBuffer);
            index = index + 12;
            value = new DBRef(namespace, oid);
        }
        else {
            throw new BSONError(`Detected unknown BSON type ${elementType.toString(16)} for fieldname "${name}"`);
        }
        if (name === '__proto__') {
            Object.defineProperty(object, name, {
                value,
                writable: true,
                enumerable: true,
                configurable: true
            });
        }
        else {
            object[name] = value;
        }
    }
    if (size !== index - startIndex) {
        if (isArray)
            throw new BSONError('corrupt array bson');
        throw new BSONError('corrupt object bson');
    }
    if (!isPossibleDBRef)
        return object;
    if (isDBRefLike(object)) {
        const copy = Object.assign({}, object);
        delete copy.$ref;
        delete copy.$id;
        delete copy.$db;
        return new DBRef(object.$ref, object.$id, object.$db, copy);
    }
    return object;
}

const regexp = /\x00/;
const ignoreKeys = new Set(['$db', '$ref', '$id', '$clusterTime']);
function serializeString(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_STRING;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes + 1;
    buffer[index - 1] = 0;
    const size = ByteUtils.encodeUTF8Into(buffer, value, index + 4);
    NumberUtils.setInt32LE(buffer, index, size + 1);
    index = index + 4 + size;
    buffer[index++] = 0;
    return index;
}
function serializeNumber(buffer, key, value, index) {
    const isNegativeZero = Object.is(value, -0);
    const type = !isNegativeZero &&
        Number.isSafeInteger(value) &&
        value <= BSON_INT32_MAX &&
        value >= BSON_INT32_MIN
        ? BSON_DATA_INT
        : BSON_DATA_NUMBER;
    buffer[index++] = type;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0x00;
    if (type === BSON_DATA_INT) {
        index += NumberUtils.setInt32LE(buffer, index, value);
    }
    else {
        index += NumberUtils.setFloat64LE(buffer, index, value);
    }
    return index;
}
function serializeBigInt(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_LONG;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index += numberOfWrittenBytes;
    buffer[index++] = 0;
    index += NumberUtils.setBigInt64LE(buffer, index, value);
    return index;
}
function serializeNull(buffer, key, _, index) {
    buffer[index++] = BSON_DATA_NULL;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    return index;
}
function serializeBoolean(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_BOOLEAN;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    buffer[index++] = value ? 1 : 0;
    return index;
}
function serializeDate(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_DATE;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    const dateInMilis = Long.fromNumber(value.getTime());
    const lowBits = dateInMilis.getLowBits();
    const highBits = dateInMilis.getHighBits();
    index += NumberUtils.setInt32LE(buffer, index, lowBits);
    index += NumberUtils.setInt32LE(buffer, index, highBits);
    return index;
}
function serializeRegExp(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_REGEXP;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    if (value.source && value.source.match(regexp) != null) {
        throw new BSONError('value ' + value.source + ' must not contain null bytes');
    }
    index = index + ByteUtils.encodeUTF8Into(buffer, value.source, index);
    buffer[index++] = 0x00;
    if (value.ignoreCase)
        buffer[index++] = 0x69;
    if (value.global)
        buffer[index++] = 0x73;
    if (value.multiline)
        buffer[index++] = 0x6d;
    buffer[index++] = 0x00;
    return index;
}
function serializeBSONRegExp(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_REGEXP;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    if (value.pattern.match(regexp) != null) {
        throw new BSONError('pattern ' + value.pattern + ' must not contain null bytes');
    }
    index = index + ByteUtils.encodeUTF8Into(buffer, value.pattern, index);
    buffer[index++] = 0x00;
    const sortedOptions = value.options.split('').sort().join('');
    index = index + ByteUtils.encodeUTF8Into(buffer, sortedOptions, index);
    buffer[index++] = 0x00;
    return index;
}
function serializeMinMax(buffer, key, value, index) {
    if (value === null) {
        buffer[index++] = BSON_DATA_NULL;
    }
    else if (value._bsontype === 'MinKey') {
        buffer[index++] = BSON_DATA_MIN_KEY;
    }
    else {
        buffer[index++] = BSON_DATA_MAX_KEY;
    }
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    return index;
}
function serializeObjectId(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_OID;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    index += value.serializeInto(buffer, index);
    return index;
}
function serializeBuffer(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_BINARY;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    const size = value.length;
    index += NumberUtils.setInt32LE(buffer, index, size);
    buffer[index++] = BSON_BINARY_SUBTYPE_DEFAULT;
    if (size <= 16) {
        for (let i = 0; i < size; i++)
            buffer[index + i] = value[i];
    }
    else {
        buffer.set(value, index);
    }
    index = index + size;
    return index;
}
function serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path) {
    if (path.has(value)) {
        throw new BSONError('Cannot convert circular structure to BSON');
    }
    path.add(value);
    buffer[index++] = Array.isArray(value) ? BSON_DATA_ARRAY : BSON_DATA_OBJECT;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    const endIndex = serializeInto(buffer, value, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
    path.delete(value);
    return endIndex;
}
function serializeDecimal128(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_DECIMAL128;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    for (let i = 0; i < 16; i++)
        buffer[index + i] = value.bytes[i];
    return index + 16;
}
function serializeLong(buffer, key, value, index) {
    buffer[index++] =
        value._bsontype === 'Long' ? BSON_DATA_LONG : BSON_DATA_TIMESTAMP;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    const lowBits = value.getLowBits();
    const highBits = value.getHighBits();
    index += NumberUtils.setInt32LE(buffer, index, lowBits);
    index += NumberUtils.setInt32LE(buffer, index, highBits);
    return index;
}
function serializeInt32(buffer, key, value, index) {
    value = value.valueOf();
    buffer[index++] = BSON_DATA_INT;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    index += NumberUtils.setInt32LE(buffer, index, value);
    return index;
}
function serializeDouble(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_NUMBER;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    index += NumberUtils.setFloat64LE(buffer, index, value.value);
    return index;
}
function serializeFunction(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_CODE;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    const functionString = value.toString();
    const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
    NumberUtils.setInt32LE(buffer, index, size);
    index = index + 4 + size - 1;
    buffer[index++] = 0;
    return index;
}
function serializeCode(buffer, key, value, index, checkKeys = false, depth = 0, serializeFunctions = false, ignoreUndefined = true, path) {
    if (value.scope && typeof value.scope === 'object') {
        buffer[index++] = BSON_DATA_CODE_W_SCOPE;
        const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
        index = index + numberOfWrittenBytes;
        buffer[index++] = 0;
        let startIndex = index;
        const functionString = value.code;
        index = index + 4;
        const codeSize = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
        NumberUtils.setInt32LE(buffer, index, codeSize);
        buffer[index + 4 + codeSize - 1] = 0;
        index = index + codeSize + 4;
        const endIndex = serializeInto(buffer, value.scope, checkKeys, index, depth + 1, serializeFunctions, ignoreUndefined, path);
        index = endIndex - 1;
        const totalSize = endIndex - startIndex;
        startIndex += NumberUtils.setInt32LE(buffer, startIndex, totalSize);
        buffer[index++] = 0;
    }
    else {
        buffer[index++] = BSON_DATA_CODE;
        const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
        index = index + numberOfWrittenBytes;
        buffer[index++] = 0;
        const functionString = value.code.toString();
        const size = ByteUtils.encodeUTF8Into(buffer, functionString, index + 4) + 1;
        NumberUtils.setInt32LE(buffer, index, size);
        index = index + 4 + size - 1;
        buffer[index++] = 0;
    }
    return index;
}
function serializeBinary(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_BINARY;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    const data = value.buffer;
    let size = value.position;
    if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY)
        size = size + 4;
    index += NumberUtils.setInt32LE(buffer, index, size);
    buffer[index++] = value.sub_type;
    if (value.sub_type === Binary.SUBTYPE_BYTE_ARRAY) {
        size = size - 4;
        index += NumberUtils.setInt32LE(buffer, index, size);
    }
    if (size <= 16) {
        for (let i = 0; i < size; i++)
            buffer[index + i] = data[i];
    }
    else {
        buffer.set(data, index);
    }
    index = index + value.position;
    return index;
}
function serializeSymbol(buffer, key, value, index) {
    buffer[index++] = BSON_DATA_SYMBOL;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    const size = ByteUtils.encodeUTF8Into(buffer, value.value, index + 4) + 1;
    NumberUtils.setInt32LE(buffer, index, size);
    index = index + 4 + size - 1;
    buffer[index++] = 0;
    return index;
}
function serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path) {
    buffer[index++] = BSON_DATA_OBJECT;
    const numberOfWrittenBytes = ByteUtils.encodeUTF8Into(buffer, key, index);
    index = index + numberOfWrittenBytes;
    buffer[index++] = 0;
    let startIndex = index;
    let output = {
        $ref: value.collection || value.namespace,
        $id: value.oid
    };
    if (value.db != null) {
        output.$db = value.db;
    }
    output = Object.assign(output, value.fields);
    const endIndex = serializeInto(buffer, output, false, index, depth + 1, serializeFunctions, true, path);
    const size = endIndex - startIndex;
    startIndex += NumberUtils.setInt32LE(buffer, index, size);
    return endIndex;
}
function serializeInto(buffer, object, checkKeys, startingIndex, depth, serializeFunctions, ignoreUndefined, path) {
    if (path == null) {
        if (object == null) {
            buffer[0] = 0x05;
            buffer[1] = 0x00;
            buffer[2] = 0x00;
            buffer[3] = 0x00;
            buffer[4] = 0x00;
            return 5;
        }
        if (Array.isArray(object)) {
            throw new BSONError('serialize does not support an array as the root input');
        }
        if (typeof object !== 'object') {
            throw new BSONError('serialize does not support non-object as the root input');
        }
        else if ('_bsontype' in object && typeof object._bsontype === 'string') {
            throw new BSONError(`BSON types cannot be serialized as a document`);
        }
        else if (isDate(object) ||
            isRegExp(object) ||
            isUint8Array(object) ||
            isAnyArrayBuffer(object)) {
            throw new BSONError(`date, regexp, typedarray, and arraybuffer cannot be BSON documents`);
        }
        path = new Set();
    }
    path.add(object);
    let index = startingIndex + 4;
    if (Array.isArray(object)) {
        for (let i = 0; i < object.length; i++) {
            const key = `${i}`;
            let value = object[i];
            if (typeof value?.toBSON === 'function') {
                value = value.toBSON();
            }
            if (typeof value === 'string') {
                index = serializeString(buffer, key, value, index);
            }
            else if (typeof value === 'number') {
                index = serializeNumber(buffer, key, value, index);
            }
            else if (typeof value === 'bigint') {
                index = serializeBigInt(buffer, key, value, index);
            }
            else if (typeof value === 'boolean') {
                index = serializeBoolean(buffer, key, value, index);
            }
            else if (value instanceof Date || isDate(value)) {
                index = serializeDate(buffer, key, value, index);
            }
            else if (value === undefined) {
                index = serializeNull(buffer, key, value, index);
            }
            else if (value === null) {
                index = serializeNull(buffer, key, value, index);
            }
            else if (isUint8Array(value)) {
                index = serializeBuffer(buffer, key, value, index);
            }
            else if (value instanceof RegExp || isRegExp(value)) {
                index = serializeRegExp(buffer, key, value, index);
            }
            else if (typeof value === 'object' && value._bsontype == null) {
                index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
            }
            else if (typeof value === 'object' &&
                value[Symbol.for('@@mdb.bson.version')] !== BSON_MAJOR_VERSION) {
                throw new BSONVersionError();
            }
            else if (value._bsontype === 'ObjectId') {
                index = serializeObjectId(buffer, key, value, index);
            }
            else if (value._bsontype === 'Decimal128') {
                index = serializeDecimal128(buffer, key, value, index);
            }
            else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
                index = serializeLong(buffer, key, value, index);
            }
            else if (value._bsontype === 'Double') {
                index = serializeDouble(buffer, key, value, index);
            }
            else if (typeof value === 'function' && serializeFunctions) {
                index = serializeFunction(buffer, key, value, index);
            }
            else if (value._bsontype === 'Code') {
                index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
            }
            else if (value._bsontype === 'Binary') {
                index = serializeBinary(buffer, key, value, index);
            }
            else if (value._bsontype === 'BSONSymbol') {
                index = serializeSymbol(buffer, key, value, index);
            }
            else if (value._bsontype === 'DBRef') {
                index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
            }
            else if (value._bsontype === 'BSONRegExp') {
                index = serializeBSONRegExp(buffer, key, value, index);
            }
            else if (value._bsontype === 'Int32') {
                index = serializeInt32(buffer, key, value, index);
            }
            else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
                index = serializeMinMax(buffer, key, value, index);
            }
            else if (typeof value._bsontype !== 'undefined') {
                throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
            }
        }
    }
    else if (object instanceof Map || isMap(object)) {
        const iterator = object.entries();
        let done = false;
        while (!done) {
            const entry = iterator.next();
            done = !!entry.done;
            if (done)
                continue;
            const key = entry.value[0];
            let value = entry.value[1];
            if (typeof value?.toBSON === 'function') {
                value = value.toBSON();
            }
            const type = typeof value;
            if (typeof key === 'string' && !ignoreKeys.has(key)) {
                if (key.match(regexp) != null) {
                    throw new BSONError('key ' + key + ' must not contain null bytes');
                }
                if (checkKeys) {
                    if ('$' === key[0]) {
                        throw new BSONError('key ' + key + " must not start with '$'");
                    }
                    else if (key.includes('.')) {
                        throw new BSONError('key ' + key + " must not contain '.'");
                    }
                }
            }
            if (type === 'string') {
                index = serializeString(buffer, key, value, index);
            }
            else if (type === 'number') {
                index = serializeNumber(buffer, key, value, index);
            }
            else if (type === 'bigint') {
                index = serializeBigInt(buffer, key, value, index);
            }
            else if (type === 'boolean') {
                index = serializeBoolean(buffer, key, value, index);
            }
            else if (value instanceof Date || isDate(value)) {
                index = serializeDate(buffer, key, value, index);
            }
            else if (value === null || (value === undefined && ignoreUndefined === false)) {
                index = serializeNull(buffer, key, value, index);
            }
            else if (isUint8Array(value)) {
                index = serializeBuffer(buffer, key, value, index);
            }
            else if (value instanceof RegExp || isRegExp(value)) {
                index = serializeRegExp(buffer, key, value, index);
            }
            else if (type === 'object' && value._bsontype == null) {
                index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
            }
            else if (typeof value === 'object' &&
                value[Symbol.for('@@mdb.bson.version')] !== BSON_MAJOR_VERSION) {
                throw new BSONVersionError();
            }
            else if (value._bsontype === 'ObjectId') {
                index = serializeObjectId(buffer, key, value, index);
            }
            else if (type === 'object' && value._bsontype === 'Decimal128') {
                index = serializeDecimal128(buffer, key, value, index);
            }
            else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
                index = serializeLong(buffer, key, value, index);
            }
            else if (value._bsontype === 'Double') {
                index = serializeDouble(buffer, key, value, index);
            }
            else if (value._bsontype === 'Code') {
                index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
            }
            else if (typeof value === 'function' && serializeFunctions) {
                index = serializeFunction(buffer, key, value, index);
            }
            else if (value._bsontype === 'Binary') {
                index = serializeBinary(buffer, key, value, index);
            }
            else if (value._bsontype === 'BSONSymbol') {
                index = serializeSymbol(buffer, key, value, index);
            }
            else if (value._bsontype === 'DBRef') {
                index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
            }
            else if (value._bsontype === 'BSONRegExp') {
                index = serializeBSONRegExp(buffer, key, value, index);
            }
            else if (value._bsontype === 'Int32') {
                index = serializeInt32(buffer, key, value, index);
            }
            else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
                index = serializeMinMax(buffer, key, value, index);
            }
            else if (typeof value._bsontype !== 'undefined') {
                throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
            }
        }
    }
    else {
        if (typeof object?.toBSON === 'function') {
            object = object.toBSON();
            if (object != null && typeof object !== 'object') {
                throw new BSONError('toBSON function did not return an object');
            }
        }
        for (const key of Object.keys(object)) {
            let value = object[key];
            if (typeof value?.toBSON === 'function') {
                value = value.toBSON();
            }
            const type = typeof value;
            if (typeof key === 'string' && !ignoreKeys.has(key)) {
                if (key.match(regexp) != null) {
                    throw new BSONError('key ' + key + ' must not contain null bytes');
                }
                if (checkKeys) {
                    if ('$' === key[0]) {
                        throw new BSONError('key ' + key + " must not start with '$'");
                    }
                    else if (key.includes('.')) {
                        throw new BSONError('key ' + key + " must not contain '.'");
                    }
                }
            }
            if (type === 'string') {
                index = serializeString(buffer, key, value, index);
            }
            else if (type === 'number') {
                index = serializeNumber(buffer, key, value, index);
            }
            else if (type === 'bigint') {
                index = serializeBigInt(buffer, key, value, index);
            }
            else if (type === 'boolean') {
                index = serializeBoolean(buffer, key, value, index);
            }
            else if (value instanceof Date || isDate(value)) {
                index = serializeDate(buffer, key, value, index);
            }
            else if (value === undefined) {
                if (ignoreUndefined === false)
                    index = serializeNull(buffer, key, value, index);
            }
            else if (value === null) {
                index = serializeNull(buffer, key, value, index);
            }
            else if (isUint8Array(value)) {
                index = serializeBuffer(buffer, key, value, index);
            }
            else if (value instanceof RegExp || isRegExp(value)) {
                index = serializeRegExp(buffer, key, value, index);
            }
            else if (type === 'object' && value._bsontype == null) {
                index = serializeObject(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
            }
            else if (typeof value === 'object' &&
                value[Symbol.for('@@mdb.bson.version')] !== BSON_MAJOR_VERSION) {
                throw new BSONVersionError();
            }
            else if (value._bsontype === 'ObjectId') {
                index = serializeObjectId(buffer, key, value, index);
            }
            else if (type === 'object' && value._bsontype === 'Decimal128') {
                index = serializeDecimal128(buffer, key, value, index);
            }
            else if (value._bsontype === 'Long' || value._bsontype === 'Timestamp') {
                index = serializeLong(buffer, key, value, index);
            }
            else if (value._bsontype === 'Double') {
                index = serializeDouble(buffer, key, value, index);
            }
            else if (value._bsontype === 'Code') {
                index = serializeCode(buffer, key, value, index, checkKeys, depth, serializeFunctions, ignoreUndefined, path);
            }
            else if (typeof value === 'function' && serializeFunctions) {
                index = serializeFunction(buffer, key, value, index);
            }
            else if (value._bsontype === 'Binary') {
                index = serializeBinary(buffer, key, value, index);
            }
            else if (value._bsontype === 'BSONSymbol') {
                index = serializeSymbol(buffer, key, value, index);
            }
            else if (value._bsontype === 'DBRef') {
                index = serializeDBRef(buffer, key, value, index, depth, serializeFunctions, path);
            }
            else if (value._bsontype === 'BSONRegExp') {
                index = serializeBSONRegExp(buffer, key, value, index);
            }
            else if (value._bsontype === 'Int32') {
                index = serializeInt32(buffer, key, value, index);
            }
            else if (value._bsontype === 'MinKey' || value._bsontype === 'MaxKey') {
                index = serializeMinMax(buffer, key, value, index);
            }
            else if (typeof value._bsontype !== 'undefined') {
                throw new BSONError(`Unrecognized or invalid _bsontype: ${String(value._bsontype)}`);
            }
        }
    }
    path.delete(object);
    buffer[index++] = 0x00;
    const size = index - startingIndex;
    startingIndex += NumberUtils.setInt32LE(buffer, startingIndex, size);
    return index;
}

function isBSONType(value) {
    return (value != null &&
        typeof value === 'object' &&
        '_bsontype' in value &&
        typeof value._bsontype === 'string');
}
const keysToCodecs = {
    $oid: ObjectId,
    $binary: Binary,
    $uuid: Binary,
    $symbol: BSONSymbol,
    $numberInt: Int32,
    $numberDecimal: Decimal128,
    $numberDouble: Double,
    $numberLong: Long,
    $minKey: MinKey,
    $maxKey: MaxKey,
    $regex: BSONRegExp,
    $regularExpression: BSONRegExp,
    $timestamp: Timestamp
};
function deserializeValue(value, options = {}) {
    if (typeof value === 'number') {
        const in32BitRange = value <= BSON_INT32_MAX && value >= BSON_INT32_MIN;
        const in64BitRange = value <= BSON_INT64_MAX && value >= BSON_INT64_MIN;
        if (options.relaxed || options.legacy) {
            return value;
        }
        if (Number.isInteger(value) && !Object.is(value, -0)) {
            if (in32BitRange) {
                return new Int32(value);
            }
            if (in64BitRange) {
                if (options.useBigInt64) {
                    return BigInt(value);
                }
                return Long.fromNumber(value);
            }
        }
        return new Double(value);
    }
    if (value == null || typeof value !== 'object')
        return value;
    if (value.$undefined)
        return null;
    const keys = Object.keys(value).filter(k => k.startsWith('$') && value[k] != null);
    for (let i = 0; i < keys.length; i++) {
        const c = keysToCodecs[keys[i]];
        if (c)
            return c.fromExtendedJSON(value, options);
    }
    if (value.$date != null) {
        const d = value.$date;
        const date = new Date();
        if (options.legacy) {
            if (typeof d === 'number')
                date.setTime(d);
            else if (typeof d === 'string')
                date.setTime(Date.parse(d));
            else if (typeof d === 'bigint')
                date.setTime(Number(d));
            else
                throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`);
        }
        else {
            if (typeof d === 'string')
                date.setTime(Date.parse(d));
            else if (Long.isLong(d))
                date.setTime(d.toNumber());
            else if (typeof d === 'number' && options.relaxed)
                date.setTime(d);
            else if (typeof d === 'bigint')
                date.setTime(Number(d));
            else
                throw new BSONRuntimeError(`Unrecognized type for EJSON date: ${typeof d}`);
        }
        return date;
    }
    if (value.$code != null) {
        const copy = Object.assign({}, value);
        if (value.$scope) {
            copy.$scope = deserializeValue(value.$scope);
        }
        return Code.fromExtendedJSON(value);
    }
    if (isDBRefLike(value) || value.$dbPointer) {
        const v = value.$ref ? value : value.$dbPointer;
        if (v instanceof DBRef)
            return v;
        const dollarKeys = Object.keys(v).filter(k => k.startsWith('$'));
        let valid = true;
        dollarKeys.forEach(k => {
            if (['$ref', '$id', '$db'].indexOf(k) === -1)
                valid = false;
        });
        if (valid)
            return DBRef.fromExtendedJSON(v);
    }
    return value;
}
function serializeArray(array, options) {
    return array.map((v, index) => {
        options.seenObjects.push({ propertyName: `index ${index}`, obj: null });
        try {
            return serializeValue(v, options);
        }
        finally {
            options.seenObjects.pop();
        }
    });
}
function getISOString(date) {
    const isoStr = date.toISOString();
    return date.getUTCMilliseconds() !== 0 ? isoStr : isoStr.slice(0, -5) + 'Z';
}
function serializeValue(value, options) {
    if (value instanceof Map || isMap(value)) {
        const obj = Object.create(null);
        for (const [k, v] of value) {
            if (typeof k !== 'string') {
                throw new BSONError('Can only serialize maps with string keys');
            }
            obj[k] = v;
        }
        return serializeValue(obj, options);
    }
    if ((typeof value === 'object' || typeof value === 'function') && value !== null) {
        const index = options.seenObjects.findIndex(entry => entry.obj === value);
        if (index !== -1) {
            const props = options.seenObjects.map(entry => entry.propertyName);
            const leadingPart = props
                .slice(0, index)
                .map(prop => `${prop} -> `)
                .join('');
            const alreadySeen = props[index];
            const circularPart = ' -> ' +
                props
                    .slice(index + 1, props.length - 1)
                    .map(prop => `${prop} -> `)
                    .join('');
            const current = props[props.length - 1];
            const leadingSpace = ' '.repeat(leadingPart.length + alreadySeen.length / 2);
            const dashes = '-'.repeat(circularPart.length + (alreadySeen.length + current.length) / 2 - 1);
            throw new BSONError('Converting circular structure to EJSON:\n' +
                `    ${leadingPart}${alreadySeen}${circularPart}${current}\n` +
                `    ${leadingSpace}\\${dashes}/`);
        }
        options.seenObjects[options.seenObjects.length - 1].obj = value;
    }
    if (Array.isArray(value))
        return serializeArray(value, options);
    if (value === undefined)
        return null;
    if (value instanceof Date || isDate(value)) {
        const dateNum = value.getTime(), inRange = dateNum > -1 && dateNum < 253402318800000;
        if (options.legacy) {
            return options.relaxed && inRange
                ? { $date: value.getTime() }
                : { $date: getISOString(value) };
        }
        return options.relaxed && inRange
            ? { $date: getISOString(value) }
            : { $date: { $numberLong: value.getTime().toString() } };
    }
    if (typeof value === 'number' && (!options.relaxed || !isFinite(value))) {
        if (Number.isInteger(value) && !Object.is(value, -0)) {
            if (value >= BSON_INT32_MIN && value <= BSON_INT32_MAX) {
                return { $numberInt: value.toString() };
            }
            if (value >= BSON_INT64_MIN && value <= BSON_INT64_MAX) {
                return { $numberLong: value.toString() };
            }
        }
        return { $numberDouble: Object.is(value, -0) ? '-0.0' : value.toString() };
    }
    if (typeof value === 'bigint') {
        if (!options.relaxed) {
            return { $numberLong: BigInt.asIntN(64, value).toString() };
        }
        return Number(BigInt.asIntN(64, value));
    }
    if (value instanceof RegExp || isRegExp(value)) {
        let flags = value.flags;
        if (flags === undefined) {
            const match = value.toString().match(/[gimuy]*$/);
            if (match) {
                flags = match[0];
            }
        }
        const rx = new BSONRegExp(value.source, flags);
        return rx.toExtendedJSON(options);
    }
    if (value != null && typeof value === 'object')
        return serializeDocument(value, options);
    return value;
}
const BSON_TYPE_MAPPINGS = {
    Binary: (o) => new Binary(o.value(), o.sub_type),
    Code: (o) => new Code(o.code, o.scope),
    DBRef: (o) => new DBRef(o.collection || o.namespace, o.oid, o.db, o.fields),
    Decimal128: (o) => new Decimal128(o.bytes),
    Double: (o) => new Double(o.value),
    Int32: (o) => new Int32(o.value),
    Long: (o) => Long.fromBits(o.low != null ? o.low : o.low_, o.low != null ? o.high : o.high_, o.low != null ? o.unsigned : o.unsigned_),
    MaxKey: () => new MaxKey(),
    MinKey: () => new MinKey(),
    ObjectId: (o) => new ObjectId(o),
    BSONRegExp: (o) => new BSONRegExp(o.pattern, o.options),
    BSONSymbol: (o) => new BSONSymbol(o.value),
    Timestamp: (o) => Timestamp.fromBits(o.low, o.high)
};
function serializeDocument(doc, options) {
    if (doc == null || typeof doc !== 'object')
        throw new BSONError('not an object instance');
    const bsontype = doc._bsontype;
    if (typeof bsontype === 'undefined') {
        const _doc = {};
        for (const name of Object.keys(doc)) {
            options.seenObjects.push({ propertyName: name, obj: null });
            try {
                const value = serializeValue(doc[name], options);
                if (name === '__proto__') {
                    Object.defineProperty(_doc, name, {
                        value,
                        writable: true,
                        enumerable: true,
                        configurable: true
                    });
                }
                else {
                    _doc[name] = value;
                }
            }
            finally {
                options.seenObjects.pop();
            }
        }
        return _doc;
    }
    else if (doc != null &&
        typeof doc === 'object' &&
        typeof doc._bsontype === 'string' &&
        doc[Symbol.for('@@mdb.bson.version')] !== BSON_MAJOR_VERSION) {
        throw new BSONVersionError();
    }
    else if (isBSONType(doc)) {
        let outDoc = doc;
        if (typeof outDoc.toExtendedJSON !== 'function') {
            const mapper = BSON_TYPE_MAPPINGS[doc._bsontype];
            if (!mapper) {
                throw new BSONError('Unrecognized or invalid _bsontype: ' + doc._bsontype);
            }
            outDoc = mapper(outDoc);
        }
        if (bsontype === 'Code' && outDoc.scope) {
            outDoc = new Code(outDoc.code, serializeValue(outDoc.scope, options));
        }
        else if (bsontype === 'DBRef' && outDoc.oid) {
            outDoc = new DBRef(serializeValue(outDoc.collection, options), serializeValue(outDoc.oid, options), serializeValue(outDoc.db, options), serializeValue(outDoc.fields, options));
        }
        return outDoc.toExtendedJSON(options);
    }
    else {
        throw new BSONError('_bsontype must be a string, but was: ' + typeof bsontype);
    }
}
function parse(text, options) {
    const ejsonOptions = {
        useBigInt64: options?.useBigInt64 ?? false,
        relaxed: options?.relaxed ?? true,
        legacy: options?.legacy ?? false
    };
    return JSON.parse(text, (key, value) => {
        if (key.indexOf('\x00') !== -1) {
            throw new BSONError(`BSON Document field names cannot contain null bytes, found: ${JSON.stringify(key)}`);
        }
        return deserializeValue(value, ejsonOptions);
    });
}
function stringify(value, replacer, space, options) {
    if (space != null && typeof space === 'object') {
        options = space;
        space = 0;
    }
    if (replacer != null && typeof replacer === 'object' && !Array.isArray(replacer)) {
        options = replacer;
        replacer = undefined;
        space = 0;
    }
    const serializeOptions = Object.assign({ relaxed: true, legacy: false }, options, {
        seenObjects: [{ propertyName: '(root)', obj: null }]
    });
    const doc = serializeValue(value, serializeOptions);
    return JSON.stringify(doc, replacer, space);
}
function EJSONserialize(value, options) {
    options = options || {};
    return JSON.parse(stringify(value, options));
}
function EJSONdeserialize(ejson, options) {
    options = options || {};
    return parse(JSON.stringify(ejson), options);
}
const EJSON = Object.create(null);
EJSON.parse = parse;
EJSON.stringify = stringify;
EJSON.serialize = EJSONserialize;
EJSON.deserialize = EJSONdeserialize;
Object.freeze(EJSON);

const MAXSIZE = 1024 * 1024 * 17;
let buffer = ByteUtils.allocate(MAXSIZE);
function setInternalBufferSize(size) {
    if (buffer.length < size) {
        buffer = ByteUtils.allocate(size);
    }
}
function serialize(object, options = {}) {
    const checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
    const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
    const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
    const minInternalBufferSize = typeof options.minInternalBufferSize === 'number' ? options.minInternalBufferSize : MAXSIZE;
    if (buffer.length < minInternalBufferSize) {
        buffer = ByteUtils.allocate(minInternalBufferSize);
    }
    const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
    const finishedBuffer = ByteUtils.allocateUnsafe(serializationIndex);
    finishedBuffer.set(buffer.subarray(0, serializationIndex), 0);
    return finishedBuffer;
}
function serializeWithBufferAndIndex(object, finalBuffer, options = {}) {
    const checkKeys = typeof options.checkKeys === 'boolean' ? options.checkKeys : false;
    const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
    const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
    const startIndex = typeof options.index === 'number' ? options.index : 0;
    const serializationIndex = serializeInto(buffer, object, checkKeys, 0, 0, serializeFunctions, ignoreUndefined, null);
    finalBuffer.set(buffer.subarray(0, serializationIndex), startIndex);
    return startIndex + serializationIndex - 1;
}
function deserialize(buffer, options = {}) {
    return internalDeserialize(ByteUtils.toLocalBufferType(buffer), options);
}
function calculateObjectSize(object, options = {}) {
    options = options || {};
    const serializeFunctions = typeof options.serializeFunctions === 'boolean' ? options.serializeFunctions : false;
    const ignoreUndefined = typeof options.ignoreUndefined === 'boolean' ? options.ignoreUndefined : true;
    return internalCalculateObjectSize(object, serializeFunctions, ignoreUndefined);
}
function deserializeStream(data, startIndex, numberOfDocuments, documents, docStartIndex, options) {
    const internalOptions = Object.assign({ allowObjectSmallerThanBufferSize: true, index: 0 }, options);
    const bufferData = ByteUtils.toLocalBufferType(data);
    let index = startIndex;
    for (let i = 0; i < numberOfDocuments; i++) {
        const size = NumberUtils.getInt32LE(bufferData, index);
        internalOptions.index = index;
        documents[docStartIndex + i] = internalDeserialize(bufferData, internalOptions);
        index = index + size;
    }
    return index;
}

var bson = /*#__PURE__*/Object.freeze({
    __proto__: null,
    BSONError: BSONError,
    BSONRegExp: BSONRegExp,
    BSONRuntimeError: BSONRuntimeError,
    BSONSymbol: BSONSymbol,
    BSONType: BSONType,
    BSONValue: BSONValue,
    BSONVersionError: BSONVersionError,
    Binary: Binary,
    Code: Code,
    DBRef: DBRef,
    Decimal128: Decimal128,
    Double: Double,
    EJSON: EJSON,
    Int32: Int32,
    Long: Long,
    MaxKey: MaxKey,
    MinKey: MinKey,
    ObjectId: ObjectId,
    Timestamp: Timestamp,
    UUID: UUID,
    calculateObjectSize: calculateObjectSize,
    deserialize: deserialize,
    deserializeStream: deserializeStream,
    serialize: serialize,
    serializeWithBufferAndIndex: serializeWithBufferAndIndex,
    setInternalBufferSize: setInternalBufferSize
});

exports.BSON = bson;
exports.BSONError = BSONError;
exports.BSONRegExp = BSONRegExp;
exports.BSONRuntimeError = BSONRuntimeError;
exports.BSONSymbol = BSONSymbol;
exports.BSONType = BSONType;
exports.BSONValue = BSONValue;
exports.BSONVersionError = BSONVersionError;
exports.Binary = Binary;
exports.Code = Code;
exports.DBRef = DBRef;
exports.Decimal128 = Decimal128;
exports.Double = Double;
exports.EJSON = EJSON;
exports.Int32 = Int32;
exports.Long = Long;
exports.MaxKey = MaxKey;
exports.MinKey = MinKey;
exports.ObjectId = ObjectId;
exports.Timestamp = Timestamp;
exports.UUID = UUID;
exports.calculateObjectSize = calculateObjectSize;
exports.deserialize = deserialize;
exports.deserializeStream = deserializeStream;
exports.serialize = serialize;
exports.serializeWithBufferAndIndex = serializeWithBufferAndIndex;
exports.setInternalBufferSize = setInternalBufferSize;
//# sourceMappingURL=bson.cjs.map


/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __nccwpck_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			// no module.id needed
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		var threw = true;
/******/ 		try {
/******/ 			__webpack_modules__[moduleId](module, module.exports, __nccwpck_require__);
/******/ 			threw = false;
/******/ 		} finally {
/******/ 			if(threw) delete __webpack_module_cache__[moduleId];
/******/ 		}
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__nccwpck_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/make namespace object */
/******/ 	(() => {
/******/ 		// define __esModule on exports
/******/ 		__nccwpck_require__.r = (exports) => {
/******/ 			if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ 				Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ 			}
/******/ 			Object.defineProperty(exports, '__esModule', { value: true });
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/compat */
/******/ 	
/******/ 	if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/";
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
const HotPocket = __nccwpck_require__(875);
const bson = __nccwpck_require__(618);
const { syncapp } = __nccwpck_require__(655);

let SideDish;
try {
  SideDish = __nccwpck_require__(962);
} catch (err) {
  console.warn('[contract] No active sidedish.js found, using fallback.');
  SideDish = __nccwpck_require__(411);
}

async function contract(ctx) {
  const isReadOnly = ctx.readonly;
  const app = new syncapp();

  let sidedish;

  try {
    sidedish = new SideDish();
    await sidedish.init(ctx, bson);
  } catch (err) {
    console.warn('[sidedish] init() failed, falling back:', err);
    const Fallback = __nccwpck_require__(411);
    sidedish = new Fallback();
    await sidedish.init(ctx, bson);
  }


  const sendOutput = async (user, output) => user.send(bson.serialize(output));
  const sendRawOutput = async (user, output) => user.send(output);
  const sendSideOutput = async (user, output) => user.send(bson.serialize(output));
  const sendSideRawOutput = async (user, output) => user.send(output);

  await app.init(ctx);
  app.sendOutput = sendOutput;
  app.sendRawOutput = sendRawOutput;
  sidedish.sideOutput = sendSideOutput;
  sidedish.sideRawOutput = sendSideRawOutput;

  for (const user of ctx.users.list()) {
    for (const input of user.inputs) {
      const buf = await ctx.users.read(input);
      const message = bson.deserialize(buf);

      let handled = false;

      try {
        handled = await sidedish.handleRequest(user, message, isReadOnly);
      } catch (err) {
        console.warn('[sidedish] handleRequest() failed, falling back:', err);
        const Fallback = __nccwpck_require__(411);
        sidedish = new Fallback();
        await sidedish.init(ctx, bson);
        handled = await sidedish.handleRequest(user, message, isReadOnly);
      }

      if (!handled) {
        await app.handleRequest(user, message, isReadOnly);
      }
    }
  }

  if (!isReadOnly && app.tempFilesWritten?.size > 0 && app.cleanupTempFiles) {
    await app.cleanupTempFiles();
  }
}

const hpc = new HotPocket.Contract();
hpc.init(contract);

})();

module.exports = __webpack_exports__;
/******/ })()
;
install.sh

Code: Select all

#!/bin/bash
echo "Prerequisite installer script"
exit 0
sidedish_fallback.js

Code: Select all

class SideDish {
  async init(ctx, bson) {
    this.ctx = ctx;
    this.bson = bson;

    const configRaw = await require('fs').promises.readFile('/contract/cfg/hp.cfg', 'utf8');
    const hpconfig = JSON.parse(configRaw);

    // Normalize UNL keys as lowercase hex strings
    this.unl = new Set((hpconfig.contract?.unl || []).map(k => k.toLowerCase()));

    console.log('[sidedish] Hotpocket is the lunch, but what is the sidedish?');
  }

  async handleRequest(user, message, isReadOnly) {
    console.log('[sidedish] Received message:', message);

    if (message.type === 'echo') {
      const reply = {
        type: 'echo-reply',
        message: `Echo from sidedish: ${message.payload}`,
      };
      console.log('[sidedish] Sending reply:', reply);
      await user.send(this.bson.serialize(reply));
      return true;
    }

    if (message.type === 'check-unl') {
      const userHexKey = user.publicKey.toString('hex').toLowerCase();
      const isInUNL = this.unl.has(userHexKey);
      const reply = {
        type: 'check-unl-reply',
        inUnl: isInUNL,
        publicKey: userHexKey,
      };
      console.log('[sidedish] UNL check result:', reply);
      await user.send(this.bson.serialize(reply));
      return true;
    }

    if (message.type === 'send-keys') {
      const reply = {
        type: 'send-keys-reply',
        receivedPublicKey: message.publicKey || null,
        receivedPrivateKey: message.privateKey || null,
      };

      if (!message.publicKey || !message.privateKey) {
        reply.error = 'Missing public or private key in request';
      }

      console.log('[sidedish] Sending key echo reply:', reply);
      await user.send(this.bson.serialize(reply));
      return true;
    }

    // Fallback for unknown message types
    const errorReply = {
      type: 'error',
      message: `Unknown message type: ${message.type || 'undefined'}`,
    };
    console.warn('[sidedish] Message not for me, sending it to the next in line.', message);
    
    return false;
  }
}

module.exports = SideDish;

(Back to contract folder)

Files to add in contract folder (4):
contract.js

Code: Select all

const HotPocket = require('hotpocket-nodejs-contract');
const bson = require('bson');
const { syncapp } = require('./syncapp');

let SideDish;
try {
  SideDish = require('./sidedish');
} catch (err) {
  console.warn('[contract] No active sidedish.js found, using fallback.');
  SideDish = require('./sidedish_fallback');
}

async function contract(ctx) {
  const isReadOnly = ctx.readonly;
  const app = new syncapp();

  let sidedish;

  try {
    sidedish = new SideDish();
    await sidedish.init(ctx, bson);
  } catch (err) {
    console.warn('[sidedish] init() failed, falling back:', err);
    const Fallback = require('./sidedish_fallback');
    sidedish = new Fallback();
    await sidedish.init(ctx, bson);
  }


  const sendOutput = async (user, output) => user.send(bson.serialize(output));
  const sendRawOutput = async (user, output) => user.send(output);
  const sendSideOutput = async (user, output) => user.send(bson.serialize(output));
  const sendSideRawOutput = async (user, output) => user.send(output);

  await app.init(ctx);
  app.sendOutput = sendOutput;
  app.sendRawOutput = sendRawOutput;
  sidedish.sideOutput = sendSideOutput;
  sidedish.sideRawOutput = sendSideRawOutput;

  for (const user of ctx.users.list()) {
    for (const input of user.inputs) {
      const buf = await ctx.users.read(input);
      const message = bson.deserialize(buf);

      let handled = false;

      try {
        handled = await sidedish.handleRequest(user, message, isReadOnly);
      } catch (err) {
        console.warn('[sidedish] handleRequest() failed, falling back:', err);
        const Fallback = require('./sidedish_fallback');
        sidedish = new Fallback();
        await sidedish.init(ctx, bson);
        handled = await sidedish.handleRequest(user, message, isReadOnly);
      }

      if (!handled) {
        await app.handleRequest(user, message, isReadOnly);
      }
    }
  }

  if (!isReadOnly && app.tempFilesWritten?.size > 0 && app.cleanupTempFiles) {
    await app.cleanupTempFiles();
  }
}

const hpc = new HotPocket.Contract();
hpc.init(contract);
package.json

Code: Select all

{
    "name": "synccontract",
    "version": "1.0.1",
    "author": {
        "name": "Mr See Me No More"
    },
    "funding": "https://xumm.app/detect/request:rsmYqAFi4hQtTY6k6S3KPJZh7axhUwxT31",
    "scripts": {
        "build": "npx ncc build contract.js -o dist",
        "build:prod": "npx ncc build contract.js --minify -o dist",
        "start": "npm run build && hpdevkit deploy dist"
    },
    "dependencies": {
        "@vercel/ncc": "0.34.0",
        "bson": "6.4.0",
        "hotpocket-nodejs-contract": "^0.7.3",
        "ncc": "^0.3.6"
    }
}
package-lock.json

Code: Select all

{
    "name": "synccontract",
    "version": "1.0.1",
    "lockfileVersion": 3,
    "requires": true,
    "packages": {
        "": {
            "name": "synccontract",
            "version": "1.0.1",
            "dependencies": {
                "@vercel/ncc": "0.34.0",
                "bson": "6.4.0",
                "hotpocket-nodejs-contract": "^0.7.3",
                "ncc": "^0.3.6"
            },
            "funding": {
                "url": "https://xumm.app/detect/request:rsmYqAFi4hQtTY6k6S3KPJZh7axhUwxT31"
            }
        },
        "node_modules/@vercel/ncc": {
            "version": "0.34.0",
            "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz",
            "integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==",
            "bin": {
                "ncc": "dist/ncc/cli.js"
            }
        },
        "node_modules/balanced-match": {
            "version": "1.0.2",
            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
            "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="
        },
        "node_modules/brace-expansion": {
            "version": "1.1.11",
            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
            "dependencies": {
                "balanced-match": "^1.0.0",
                "concat-map": "0.0.1"
            }
        },
        "node_modules/bson": {
            "version": "6.4.0",
            "resolved": "https://registry.npmjs.org/bson/-/bson-6.4.0.tgz",
            "integrity": "sha512-6/gSSEdbkuFlSb+ufj5jUSU4+wo8xQOwm2bDSqwmxiPE17JTpsP63eAwoN8iF8Oy4gJYj+PAL3zdRCTdaw5Y1g==",
            "deprecated": "a critical bug affecting zSeries s390x big-endian systems is fixed in bson@6.5.0",
            "engines": {
                "node": ">=16.20.1"
            }
        },
        "node_modules/colors": {
            "version": "1.2.3",
            "resolved": "https://registry.npmjs.org/colors/-/colors-1.2.3.tgz",
            "integrity": "sha512-qTfM2pNFeMZcLvf/RbrVAzDEVttZjFhaApfx9dplNjvHSX88Ui66zBRb/4YGob/xUWxDceirgoC1lT676asfCQ==",
            "engines": {
                "node": ">=0.1.90"
            }
        },
        "node_modules/concat-map": {
            "version": "0.0.1",
            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
            "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg=="
        },
        "node_modules/dateformat": {
            "version": "3.0.3",
            "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
            "integrity": "sha512-jyCETtSl3VMZMWeRo7iY1FL19ges1t55hMo5yaam4Jrsm5EPL89UQkoQRyiI+Yf4k8r2ZpdngkV8hr1lIdjb3Q==",
            "engines": {
                "node": "*"
            }
        },
        "node_modules/fs.realpath": {
            "version": "1.0.0",
            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
            "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw=="
        },
        "node_modules/glob": {
            "version": "7.2.3",
            "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
            "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
            "deprecated": "Glob versions prior to v9 are no longer supported",
            "dependencies": {
                "fs.realpath": "^1.0.0",
                "inflight": "^1.0.4",
                "inherits": "2",
                "minimatch": "^3.1.1",
                "once": "^1.3.0",
                "path-is-absolute": "^1.0.0"
            },
            "engines": {
                "node": "*"
            },
            "funding": {
                "url": "https://github.com/sponsors/isaacs"
            }
        },
        "node_modules/hotpocket-nodejs-contract": {
            "version": "0.7.4",
            "resolved": "https://registry.npmjs.org/hotpocket-nodejs-contract/-/hotpocket-nodejs-contract-0.7.4.tgz",
            "integrity": "sha512-rLCZm1bWoqV5j+xUFbnWF6XWurO5D43dus9h4SRftusO52n4GOdMsHmFIW7kezXGsHCre9iLzFl0R33aZ9/nSg=="
        },
        "node_modules/inflight": {
            "version": "1.0.6",
            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
            "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
            "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.",
            "dependencies": {
                "once": "^1.3.0",
                "wrappy": "1"
            }
        },
        "node_modules/inherits": {
            "version": "2.0.4",
            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
        },
        "node_modules/minimatch": {
            "version": "3.1.2",
            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
            "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
            "dependencies": {
                "brace-expansion": "^1.1.7"
            },
            "engines": {
                "node": "*"
            }
        },
        "node_modules/minimist": {
            "version": "1.2.8",
            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
            "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/mkdirp": {
            "version": "0.5.6",
            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
            "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
            "dependencies": {
                "minimist": "^1.2.6"
            },
            "bin": {
                "mkdirp": "bin/cmd.js"
            }
        },
        "node_modules/ncc": {
            "version": "0.3.6",
            "resolved": "https://registry.npmjs.org/ncc/-/ncc-0.3.6.tgz",
            "integrity": "sha512-OXudTB2Ebt/FnOuDoPQbaa17+tdVqSOWA+gLfPxccWwsNED1uA2zEhpoB1hwdFC9yYbio/mdV5cvOtQI3Zrx1w==",
            "dependencies": {
                "mkdirp": "^0.5.1",
                "rimraf": "^2.6.1",
                "tracer": "^0.8.7",
                "ws": "^2.3.1"
            }
        },
        "node_modules/once": {
            "version": "1.4.0",
            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
            "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
            "dependencies": {
                "wrappy": "1"
            }
        },
        "node_modules/path-is-absolute": {
            "version": "1.0.1",
            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
            "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
            "engines": {
                "node": ">=0.10.0"
            }
        },
        "node_modules/rimraf": {
            "version": "2.7.1",
            "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
            "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
            "deprecated": "Rimraf versions prior to v4 are no longer supported",
            "dependencies": {
                "glob": "^7.1.3"
            },
            "bin": {
                "rimraf": "bin.js"
            }
        },
        "node_modules/safe-buffer": {
            "version": "5.0.1",
            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.0.1.tgz",
            "integrity": "sha512-cr7dZWLwOeaFBLTIuZeYdkfO7UzGIKhjYENJFAxUOMKWGaWDm2nJM2rzxNRm5Owu0DH3ApwNo6kx5idXZfb/Iw=="
        },
        "node_modules/tinytim": {
            "version": "0.1.1",
            "resolved": "https://registry.npmjs.org/tinytim/-/tinytim-0.1.1.tgz",
            "integrity": "sha512-NIpsp9lBIxPNzB++HnMmUd4byzJSVbbO4F+As1Gb1IG/YQT5QvmBDjpx8SpDS8fhGC+t+Qw8ldQgbcAIaU+2cA==",
            "engines": {
                "node": ">= 0.2.0"
            }
        },
        "node_modules/tracer": {
            "version": "0.8.15",
            "resolved": "https://registry.npmjs.org/tracer/-/tracer-0.8.15.tgz",
            "integrity": "sha512-ZQzlhd6zZFIpAhACiZkxLjl65XqVwi8t8UEBVGRIHAQN6nj55ftJWiFell+WSqWCP/vEycrIbUSuiyMwul+TFw==",
            "dependencies": {
                "colors": "1.2.3",
                "dateformat": "3.0.3",
                "mkdirp": "^0.5.1",
                "tinytim": "0.1.1"
            },
            "engines": {
                "node": ">= 0.10.0"
            }
        },
        "node_modules/ultron": {
            "version": "1.1.1",
            "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz",
            "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og=="
        },
        "node_modules/wrappy": {
            "version": "1.0.2",
            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
            "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="
        },
        "node_modules/ws": {
            "version": "2.3.1",
            "resolved": "https://registry.npmjs.org/ws/-/ws-2.3.1.tgz",
            "integrity": "sha512-61a+9LgtYZxTq1hAonhX8Xwpo2riK4IOR/BIVxioFbCfc3QFKmpE4x9dLExfLHKtUfVZigYa36tThVhO57erEw==",
            "dependencies": {
                "safe-buffer": "~5.0.1",
                "ultron": "~1.1.0"
            }
        }
    }
}
syncapp.js

Code: Select all

const fs = require('fs');
const path = require('path');

const stateRoot = 'public'; 

export class syncapp {
  sendOutput;
  sendRawOutput;
  
   clearSidedishVotes(voter, entry) {
  if (entry.voters?.includes(voter)) {
    entry.voters = entry.voters.filter(k => k !== voter);
  }
  if (entry.delete?.includes(voter)) {
    entry.delete = entry.delete.filter(k => k !== voter);
  }
}

async init(ctx) {
    this.contractCtx = ctx;
    const configRaw = await fs.promises.readFile('/contract/cfg/hp.cfg', 'utf8');
    const hpconfig = JSON.parse(configRaw);
    this.unl = new Set(hpconfig.contract?.unl || []);
    const threshold = hpconfig.contract?.consensus?.threshold || 100; 
    this.quorumThreshold = threshold / 100; 
  }

  async handleRequest(user, message, isReadOnly) {
    console.log(`[syncapp] Received message type: ${message.type}`);
console.log(`[syncapp] User index:`, user.userId || user.index || user);
console.log(`[syncapp] About to send output to user:`, user);
    if (isReadOnly) {
      await this.sendOutput(user, {
        type: 'error',
        error: 'Read-only mode is not supported for syncing.'
      });
      return;
    }

else if (message.type === 'go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = path.join('votes.json');
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }


  for (const [ver, entry] of Object.entries(votes)) {
    if (ver !== versionName && entry.goLive?.includes(voter)) {
      entry.goLive = entry.goLive.filter(pk => pk !== voter);
      console.log(`[syncapp] ${voter} removed go-live vote from '${ver}'`);
    }
  }


  votes[versionName].delete = votes[versionName].delete.filter(pk => pk !== voter);


  if (!votes[versionName].goLive.includes(voter)) {
    votes[versionName].goLive.push(voter);
    console.log(`[syncapp] ${voter} voted to go-live '${versionName}'`);
  }


  for (const entry of Object.values(votes)) {
    entry.goLive = entry.goLive.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(voteFilePath, JSON.stringify(votes));


  const totalVotes = votes[versionName].goLive.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const livePath = '/contract/contract_fs/seed/state/public';
    const versionsPath = `/contract/contract_fs/seed/state/versions/${versionName}`;

    if (!fs.existsSync(versionsPath)) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
      return;
    }

    if (fs.existsSync(livePath)) {
      fs.rmSync(livePath, { recursive: true, force: true });
    }

    fs.symlinkSync(versionsPath, livePath, 'dir');


    for (const entry of Object.values(votes)) {
      entry.goLive = [];
    }

    fs.writeFileSync(voteFilePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `Version '${versionName}' is now live by consensus.`
    });
    return;
  }

  await this.sendOutput(user, {
    type: 'info',
    message: `Vote recorded for '${versionName}'. Waiting for quorum (${totalVotes}/${quorum}).`
  });
  return;
}


else if (message.type === 'api-go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'api_votes.json';
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }


  for (const [ver, entry] of Object.entries(votes)) {
    if (ver !== versionName && entry.goLive?.includes(voter)) {
      entry.goLive = entry.goLive.filter(pk => pk !== voter);
    }
  }


  votes[versionName].delete = votes[versionName].delete.filter(pk => pk !== voter);


  if (!votes[versionName].goLive.includes(voter)) {
    votes[versionName].goLive.push(voter);
  }


  for (const entry of Object.values(votes)) {
    entry.goLive = entry.goLive.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(voteFilePath, JSON.stringify(votes));


  const totalVotes = votes[versionName].goLive.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const livePath = 'dynamic-endpoints-data.json';
    const versionsPath = `api_versions/${versionName}/dynamic-endpoints-data.json`;

    if (!fs.existsSync(versionsPath)) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
      return;
    }

    try {
      fs.copyFileSync(versionsPath, livePath);

      const liveDir = path.dirname(livePath);
      const liveFlagPath = path.join(liveDir, '.live_api_version');
      fs.writeFileSync(liveFlagPath, versionName);
    } catch (err) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Failed to promote dynamic-endpoints-data.json: ${err.message}`
      });
      return;
    }


    for (const entry of Object.values(votes)) {
      entry.goLive = [];
    }

    fs.writeFileSync(voteFilePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `dynamic-endpoints-data.json version '${versionName}' is now live.`
    });
  } else {
    await this.sendOutput(user, {
      type: 'info',
      message: `Vote recorded for API '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}


else if (message.type === 'api-vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'api_votes.json';
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  const livePath = 'dynamic-endpoints-data.json';
  const currentLive = fs.existsSync(livePath)
    ? fs.readFileSync(livePath, 'utf8')
    : null;

  const versionPath = path.join('api_versions', versionName, 'dynamic-endpoints-data.json');

  if (!fs.existsSync(versionPath)) {
    await this.sendOutput(user, {
      type: 'error',
      error: `Version '${versionName}' does not exist. Cannot vote to delete.`
    });
    return;
  }

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  const entry = votes[versionName];


  entry.goLive = entry.goLive.filter(pk => pk !== voter);


  if (!entry.delete.includes(voter)) {
    entry.delete.push(voter);
    console.log(`[api-vote-delete] ${voter} voted to delete '${versionName}'`);
  } else {
    console.log(`[api-vote-delete] ${voter} already voted to delete '${versionName}'`);
  }


  for (const e of Object.values(votes)) {
    e.goLive = e.goLive.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  console.log(`[api-vote-delete] Delete votes for '${versionName}':`, votes[versionName].delete);
  console.log(`[api-vote-delete] Total votes: ${totalVotes}, Quorum required: ${quorum}`);

  const liveVersion = fs.existsSync(livePath) && fs.existsSync(versionPath)
    ? fs.readFileSync(versionPath, 'utf8') === currentLive
    : false;

  if (liveVersion) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Cannot delete the live API version.'
    });
    return;
  }

  if (totalVotes >= quorum) {
    const folderPath = path.dirname(versionPath);
    if (fs.existsSync(folderPath)) {
      fs.rmSync(folderPath, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(voteFilePath, JSON.stringify(votes));
      console.log(`[api-vote-delete] API version '${versionName}' deleted by quorum.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `API version '${versionName}' has been deleted.`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
    }
  } else {
    fs.writeFileSync(voteFilePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `Voted to delete API version '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}

else if (message.type === 'vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = path.join('votes.json');
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  const liveSymlink = '/contract/contract_fs/seed/state/public';
  const liveVersion = fs.existsSync(liveSymlink)
    ? path.basename(fs.readlinkSync(liveSymlink))
    : null;

  if (versionName === liveVersion) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Cannot vote to delete the live version.'
    });
    return;
  }

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  votes[versionName].goLive = votes[versionName].goLive.filter(pk => pk !== voter);

  const entry = votes[versionName];
  const alreadyVoted = entry.delete.includes(voter);

  if (alreadyVoted) {
    entry.delete = entry.delete.filter(pk => pk !== voter);
    console.log(`[syncapp] ${voter} unvoted to delete '${versionName}'`);
  } else {
    entry.delete.push(voter);
    console.log(`[syncapp] ${voter} voted to delete '${versionName}'`);
  }

  for (const e of Object.values(votes)) {
    e.goLive = e.goLive.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const versionPath = path.join('versions', versionName);
    if (fs.existsSync(versionPath)) {
      fs.rmSync(versionPath, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(voteFilePath, JSON.stringify(votes));
      console.log(`[syncapp] Version '${versionName}' deleted by consensus.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `Version '${versionName}' has been deleted by consensus.`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
    }
  } else {
    fs.writeFileSync(voteFilePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `${alreadyVoted ? 'Unvoted' : 'Voted'} to delete '${versionName}'. (${totalVotes}/${quorum})`
    });
  }

  return;
}


else if (message.type === 'list-versions') {
      const versionsPath = path.join('versions');
      console.log(`[syncapp] list-versions triggered`);
      console.log(`[syncapp] Looking in: ${versionsPath}`);
     
      try {
        if (!fs.existsSync(versionsPath)) {
          console.warn(`[syncapp] No versions directory found.`);
          await this.sendOutput(user, { type: 'versions', data: [] });
          return;
        }
    
        const entries = fs.readdirSync(versionsPath);
        console.log(`[syncapp] Entries in versions/:`, entries);
    
        const versions = entries.filter(entry => {
          const full = path.join(versionsPath, entry);
          const isDir = fs.statSync(full).isDirectory();
      
          return isDir;
        });
    
        console.log(`[syncapp] Found version folders:`, versions);
    
        await this.sendOutput(user, {
          type: 'versions',
          data: versions
        });
    
      } catch (err) {
        console.error(`[syncapp] Error reading versions: ${err.message}`);
        await this.sendOutput(user, {
          type: 'error',
          error: `Failed to read versions: ${err.message}`
        });
      }
    
      return;
    }

else if (message.type === 'api-upload') {
  const { version, chunkNo, totalChunks, chunk } = message;
  console.log(`[api-upload] Upload received from ${user.publicKey}`);

  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized API uploader. Must be in UNL.'
    });
    return;
  }

  if (!version || !chunkNo || !totalChunks || !chunk) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Missing fields in API upload.'
    });
    return;
  }

  try {
    const versionedPath = path.join('api_versions', version, 'dynamic-endpoints-data.json');
    const versionDir = path.dirname(versionedPath);
    if (!fs.existsSync(versionDir)) {
      fs.mkdirSync(versionDir, { recursive: true });
      console.log(`[api-upload] Created folder: ${versionDir}`);
    }

    const chunkData = Buffer.from(chunk, 'hex');
    fs.appendFileSync(versionedPath, chunkData);
    console.log(`[api-upload] Wrote chunk ${chunkNo}/${totalChunks} → ${versionedPath}`);

    if (parseInt(chunkNo) === parseInt(totalChunks)) {
      console.log(`[api-upload] Upload complete.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `API uploaded as version '${version}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'ack',
        chunkNo
      });
    }

  } catch (err) {
    console.error(`[api-upload] Upload error: ${err.message}`);
    await this.sendOutput(user, {
      type: 'error',
      error: `Failed to store chunk ${chunkNo}: ${err.message}`
    });
  }
}


else if (message.type === 'more-upload') {
  const { version, chunkNo, totalChunks, chunk } = message;
  console.log(`[more-upload] Upload received from ${user.publicKey}`);

  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized more uploader. Must be in UNL.'
    });
    return;
  }

  if (!version || !chunkNo || !totalChunks || !chunk) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Missing fields in more upload.'
    });
    return;
  }

  try {
    const versionedPath = path.join('more_versions', version, 'morefunctions.json');
    const versionDir = path.dirname(versionedPath);
    if (!fs.existsSync(versionDir)) {
      fs.mkdirSync(versionDir, { recursive: true });
      console.log(`[more-upload] Created folder: ${versionDir}`);
    }

    const chunkData = Buffer.from(chunk, 'hex');
    fs.appendFileSync(versionedPath, chunkData);
    console.log(`[more-upload] Wrote chunk ${chunkNo}/${totalChunks} → ${versionedPath}`);

    if (parseInt(chunkNo) === parseInt(totalChunks)) {
      console.log(`[more-upload] Upload complete.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `more uploaded as version '${version}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'ack',
        chunkNo
      });
    }

  } catch (err) {
    console.error(`[more-upload] Upload error: ${err.message}`);
    await this.sendOutput(user, {
      type: 'error',
      error: `Failed to store chunk ${chunkNo}: ${err.message}`
    });
  }
}
else if (message.type === 'more-go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'more_votes.json';
  let votes = fs.existsSync(voteFilePath) ? JSON.parse(fs.readFileSync(voteFilePath)) : {};

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  for (const [ver, entry] of Object.entries(votes)) {
    if (ver !== versionName && entry.goLive?.includes(voter)) {
      entry.goLive = entry.goLive.filter(pk => pk !== voter);
    }
  }

  votes[versionName].delete = votes[versionName].delete.filter(pk => pk !== voter);

  if (!votes[versionName].goLive.includes(voter)) {
    votes[versionName].goLive.push(voter);
  }

  for (const entry of Object.values(votes)) {
    entry.goLive = entry.goLive.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(voteFilePath, JSON.stringify(votes));

  const totalVotes = votes[versionName].goLive.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const livePath = 'morefunctions.json';
    const versionsPath = path.join('more_versions', versionName, 'morefunctions.json');

    if (!fs.existsSync(versionsPath)) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
      return;
    }

    try {
      fs.copyFileSync(versionsPath, livePath);
      fs.writeFileSync('.live_more_version', versionName);
    } catch (err) {
      await this.sendOutput(user, {
        type: 'error',
        error: `Failed to promote morefunctions.json: ${err.message}`
      });
      return;
    }

    for (const entry of Object.values(votes)) {
      entry.goLive = [];
    }

    fs.writeFileSync(voteFilePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `morefunctions.json version '${versionName}' is now live.`
    });
  } else {
    await this.sendOutput(user, {
      type: 'info',
      message: `Vote recorded for more '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}


else if (message.type === 'more-vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter. Must be in UNL.'
    });
    return;
  }

  const voteFilePath = 'more_votes.json';
  let votes = fs.existsSync(voteFilePath)
    ? JSON.parse(fs.readFileSync(voteFilePath, 'utf8'))
    : {};

  const liveFlagPath = '.live_more_version';
  const liveVersion = fs.existsSync(liveFlagPath)
    ? fs.readFileSync(liveFlagPath, 'utf8').trim()
    : null;

  if (liveVersion === versionName) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Cannot delete the live more version.'
    });
    return;
  }

  if (!votes[versionName]) {
    votes[versionName] = { goLive: [], delete: [] };
  }

  const entry = votes[versionName];

  entry.goLive = entry.goLive.filter(pk => pk !== voter);

  const alreadyVoted = entry.delete.includes(voter);
  if (alreadyVoted) {
    entry.delete = entry.delete.filter(pk => pk !== voter);
  } else {
    entry.delete.push(voter);
  }

  for (const e of Object.values(votes)) {
    e.goLive = e.goLive.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  const versionFolder = path.join('more_versions', versionName);

  if (totalVotes >= quorum) {
    if (fs.existsSync(versionFolder)) {
      fs.rmSync(versionFolder, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(voteFilePath, JSON.stringify(votes));
      await this.sendOutput(user, {
        type: 'success',
        message: `more version '${versionName}' has been deleted.`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' does not exist.`
      });
    }
  } else {
    fs.writeFileSync(voteFilePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `${alreadyVoted ? 'Unvoted' : 'Voted'} to delete more version '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return;
}

else if (message.type === 'sidedish-upload') {
  const { version, chunkNo, totalChunks, chunk } = message;
  console.log(`[sidedish] Upload received from ${user.publicKey}`);

  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized sidedish uploader. Must be in UNL.'
    });
    return;
  }

  if (!version || !chunkNo || !totalChunks || !chunk) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Missing fields in sidedish upload.'
    });
    return;
  }

  try {
    const versionedPath = path.join('sidedish_versions', version, 'sidedish.js');
    const versionDir = path.dirname(versionedPath);
    if (!fs.existsSync(versionDir)) {
      fs.mkdirSync(versionDir, { recursive: true });
      console.log(`[sidedish] Created folder: ${versionDir}`);
    }

    const chunkData = Buffer.from(chunk, 'hex');
    fs.appendFileSync(versionedPath, chunkData);
    console.log(`[sidedish] Wrote chunk ${chunkNo}/${totalChunks} → ${versionedPath}`);

    if (parseInt(chunkNo) === parseInt(totalChunks)) {
      console.log(`[sidedish] Upload complete.`);
      await this.sendOutput(user, {
        type: 'success',
        message: `sidedish.js uploaded as version '${version}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'ack',
        chunkNo
      });
    }

  } catch (err) {
    console.error(`[sidedish] Upload error: ${err.message}`);
    await this.sendOutput(user, {
      type: 'error',
      error: `Failed to store chunk ${chunkNo}: ${err.message}`
    });
  }
}

else if (message.type === 'sidedish-go-live') {
  const versionName = message.version;
  const voter = user.publicKey;

  const votePath = 'sidedish_votes.json';
  let votes = fs.existsSync(votePath) ? JSON.parse(fs.readFileSync(votePath)) : {};

  const versionPath = path.join('sidedish_versions', versionName, 'sidedish.js');
  if (!fs.existsSync(versionPath)) {
    await this.sendOutput(user, { type: 'error', error: `Version '${versionName}' does not exist.` });
    return true;
  }

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, { type: 'error', error: 'Unauthorized voter.' });
    return true;
  }

  for (const entry of Object.values(votes)) {
    if (entry.voters?.includes(voter)) {
      entry.voters = entry.voters.filter(pk => pk !== voter);
    }
  }

  if (!votes[versionName]) votes[versionName] = {};
  if (!votes[versionName].voters) votes[versionName].voters = [];
  if (!votes[versionName].delete) votes[versionName].delete = [];

  votes[versionName].voters.push(voter);

  for (const entry of Object.values(votes)) {
    entry.voters = entry.voters.filter(pk => this.unl.has(pk));
    entry.delete = entry.delete.filter(pk => this.unl.has(pk));
  }

  fs.writeFileSync(votePath, JSON.stringify(votes));

  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);
  const totalVotes = votes[versionName].voters.length;

  if (totalVotes >= quorum) {
    const livePath = 'sidedish.js';
    const sourcePath = path.join('sidedish_versions', versionName, 'sidedish.js');

    try {
      fs.copyFileSync(sourcePath, livePath);
      fs.writeFileSync('.live_sidedish_version', versionName);
      console.log(`[sidedish] Activated version '${versionName}'`);
    } catch (err) {
      console.error(`[sidedish] Activation error: ${err.message}`);
      await this.sendOutput(user, { type: 'error', error: `Activation failed: ${err.message}` });
      return true;
    }

    votes[versionName].voters = [];
    fs.writeFileSync(votePath, JSON.stringify(votes));

    await this.sendOutput(user, {
      type: 'success',
      message: `sidedish.js version '${versionName}' is now live.`
    });
  } else {
    await this.sendOutput(user, {
      type: 'info',
      message: `Vote recorded for '${versionName}' (${totalVotes}/${quorum})`
    });
  }

  return true;
}


else if (message.type === 'sidedish-vote-delete') {
  const versionName = message.version;
  const voter = user.publicKey;

  const votePath = 'sidedish_votes.json';
  let votes = fs.existsSync(votePath) ? JSON.parse(fs.readFileSync(votePath)) : {};

  if (!this.unl.has(voter)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized voter.'
    });
    return true;
  }

  if (!votes[versionName]) votes[versionName] = { voters: [], delete: [] };
  const entry = votes[versionName];

  entry.voters = entry.voters.filter(pk => pk !== voter);

  const alreadyVoted = entry.delete.includes(voter);
  if (alreadyVoted) {
    entry.delete = entry.delete.filter(pk => pk !== voter);
  } else {
    entry.delete.push(voter);
  }

  for (const e of Object.values(votes)) {
    e.voters = e.voters.filter(pk => this.unl.has(pk));
    e.delete = e.delete.filter(pk => this.unl.has(pk));
  }

  const totalVotes = votes[versionName].delete.length;
  const quorum = Math.ceil(this.unl.size * this.quorumThreshold);

  if (totalVotes >= quorum) {
    const versionPath = path.join('sidedish_versions', versionName);
    if (fs.existsSync(versionPath)) {
      fs.rmSync(versionPath, { recursive: true, force: true });
      delete votes[versionName];
      fs.writeFileSync(votePath, JSON.stringify(votes));
      await this.sendOutput(user, {
        type: 'success',
        message: `Deleted sidedish version '${versionName}'`
      });
    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: `Version '${versionName}' not found.`
      });
    }
  } else {
    fs.writeFileSync(votePath, JSON.stringify(votes));
    await this.sendOutput(user, {
      type: 'info',
      message: `${alreadyVoted ? 'Unvoted' : 'Voted'} to delete '${versionName}'. (${totalVotes}/${quorum})`
    });
  }

  return true;
}

else if (message.type === 'upload') {
      const { filename, chunkNo, totalChunks, chunk, version } = message;
  console.log(`[syncapp] Received upload message from ${user.publicKey}`);
  if (!this.unl.has(user.publicKey)) {
    await this.sendOutput(user, {
      type: 'error',
      error: 'Unauthorized uploader. Must be in UNL.'
    });
    return;
  }

      if (!filename || !chunkNo || !totalChunks || !chunk || !version) {
        console.log(`[syncapp] Missing upload fields.`);
        await this.sendOutput(user, {
          type: 'error',
          error: 'Missing required fields: filename, chunkNo, totalChunks, chunk, version'
        });
        return;
      }

      try {
        const normalizedFilename = filename.replace(/\\/g, '/');
        const versionedPath = path.join('versions', version, normalizedFilename);

        const versionDir = path.dirname(versionedPath);
        if (!fs.existsSync(versionDir)) {
          fs.mkdirSync(versionDir, { recursive: true });
          console.log(`[syncapp] Created version folder: ${versionDir}`);
        }

        const chunkData = Buffer.from(chunk, 'hex');
        fs.appendFileSync(versionedPath, chunkData);
        console.log(`[syncapp] Chunk ${chunkNo}/${totalChunks} written → ${versionedPath}`);

        if (parseInt(chunkNo) === parseInt(totalChunks)) {
          console.log(`[syncapp] File complete: ${versionedPath}`);
          await this.sendOutput(user, {
            type: 'success',
            message: `File '${normalizedFilename}' stored in version '${version}'.`
          });
        } else {
          await this.sendOutput(user, {
            type: 'ack',
            chunkNo
          });
        }

      } catch (err) {
        console.error(`[syncapp] Chunk ${chunkNo} error: ${err.message}`);
        await this.sendOutput(user, {
          type: 'error',
          error: `Failed to store chunk ${chunkNo}: ${err.message}`
        });
      }

    } else {
      await this.sendOutput(user, {
        type: 'error',
        error: 'Unknown message type.'
      });
    }
  }
}
(back to root folder and into public folder)

Files to add to public folder (1):

index.html

Code: Select all

<html>

<p>Yes, This Works.</p>

</html>
(back to root folder)

Files to add into root folder (17):
COPYING

Code: Select all

COPYING file for Everwebhost Software

Everwebhost Software, incorporating Evernode Hotpocket Technology

Copyright (c) 2025 Everwebhost Authors

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

1. The above copyright notice and this permission notice shall be included in
   all copies or substantial portions of the Software.

2. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
   IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
   FITNESS FOR A PARTICULAR PURPOSE, AND NONINFRINGEMENT. IN NO EVENT SHALL
   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES, OR OTHER
   LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT, OR OTHERWISE, ARISING
   FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
   DEALINGS IN THE SOFTWARE.

3. Users of the Software are solely responsible for their use, modification,
   distribution, or any actions taken with the Software, including but not
   limited to any legal, technical, or commercial consequences. The authors
   or copyright holders shall not be responsible for any issues, damages, or
   liabilities resulting from the use of the Software.

4. Anyone is free to copy, fork, or use the Software for any purpose,
   including commercial purposes, provided they comply with the above
   conditions. Users assume full responsibility for ensuring the Software
   meets their needs and for any outcomes resulting from its use.

This license applies to the Everwebhost Software and its use of Evernode
Hotpocket Technology, to the extent permitted by applicable law.
Dockerfile

Code: Select all

FROM evernode/sashimono:hp.latest-ubt.20.04-njs.20
ENV DEBIAN_FRONTEND=noninteractive
RUN apt update && \
    apt install -y tzdata && \
    ln -fs /usr/share/zoneinfo/Etc/UTC /etc/localtime && \
    dpkg-reconfigure --frontend noninteractive tzdata && \
    apt install -y python curl && \
    curl -qL https://www.npmjs.com/install.sh | bash && \
    apt install -y openssh-server openssl sudo bash nano && \
    apt-get install -y supervisor && \
    mkdir -p /var/log/supervisor && \
    rm -rf /var/lib/apt/lists/*

RUN useradd -m -s /bin/bash everweb && \
    echo "everweb:default" | chpasswd && \
    usermod -aG sudo everweb && \
    echo "everweb ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers

RUN mkdir /var/run/sshd && \
    echo "Port 22" >> /etc/ssh/sshd_config && \
    echo "Port 36526" >> /etc/ssh/sshd_config && \
    echo "Port 36528" >> /etc/ssh/sshd_config && \
    echo "Port 36530" >> /etc/ssh/sshd_config && \
    echo "PermitRootLogin no" >> /etc/ssh/sshd_config && \
    echo "ListenAddress 0.0.0.0" >> /etc/ssh/sshd_config

COPY admin /home/everweb/admin
COPY COPYING /home/everweb/COPYING
COPY README.md /home/everweb/README.md
COPY node_modules /home/everweb/node_modules
COPY index.js /home/everweb/index.js
COPY dynamic-endpoints.js /home/everweb/dynamic-endpoints.js
COPY dynamic-endpoints-data.json /home/everweb/dynamic-endpoints-data.json
COPY settings.json /home/everweb/settings.json
COPY morefunctions.json /home/everweb/morefunctions.json
COPY extra-modules.json /home/everweb/extra-modules.json
COPY extra-vars.json /home/everweb/extra-vars.json
COPY package-lock.json /home/everweb/package-lock.json
COPY package.json /home/everweb/package.json
COPY sidedish.js /home/everweb/sidedish.js
COPY public /home/everweb/public
COPY password.hash /home/everweb/password.hash
COPY contract/dist/ /home/everweb/contract/
COPY start.sh /start.sh
COPY supervisord.conf /home/everweb/supervisord.conf
RUN chmod +x /start.sh
WORKDIR /home/everweb
ENTRYPOINT ["/start.sh"]
dynamic-endpoints.js

Code: Select all

const fs = require('fs');
const path = require('path');

const LOCAL_FILE = path.join(__dirname, 'dynamic-endpoints-data.json');
const CLUSTER_FILE = '/contract/contract_fs/seed/state/dynamic-endpoints-data.json';

let useCluster = false;
let endpoints = [];

function loadFromDisk() {
  const fileToUse = useCluster ? CLUSTER_FILE : LOCAL_FILE;
  if (fs.existsSync(fileToUse)) {
    try {
      endpoints = JSON.parse(fs.readFileSync(fileToUse, 'utf8'));
    } catch (err) {
      console.error(`[dynamic-endpoints] Failed to parse ${fileToUse}:`, err);
      endpoints = [];
    }
  } else {
    endpoints = [];
  }
}

function saveToDisk() {
  if (useCluster) return; // prevent writes to cluster copy
  try {
    fs.writeFileSync(LOCAL_FILE, JSON.stringify(endpoints, null, 2));
  } catch (err) {
    console.error('[dynamic-endpoints] Failed to save local endpoints:', err);
  }
}

function getEndpoints(explicitPath) {
  const filePath = explicitPath
    ? path.resolve(explicitPath) // <- FIX here
    : (
        useCluster
          ? '/contract/contract_fs/seed/state/dynamic-endpoints-data.json'
          : path.resolve(__dirname, 'dynamic-endpoints-data.json')
      );

  try {
    delete require.cache[require.resolve(filePath)];
    return require(filePath);
  } catch (err) {
    if (err.code === 'MODULE_NOT_FOUND') {
      console.warn(`[ENDPOINTS] File not found: ${filePath}. Returning empty list.`);
      return [];
    }
    throw err;
  }
}

function addEndpoint({ path, code, method, auth = false }) {
  const existing = endpoints.find(e => e.path === path && e.method === method.toLowerCase());
  if (existing) throw new Error(`Endpoint ${method.toUpperCase()} ${path} already exists.`);
  endpoints.push({ method: method.toLowerCase(), path, code, auth: !!auth });
  saveToDisk();
}

function editEndpoint({ method = 'get', path, code, auth }) {
  const m = method.toLowerCase();
  const i = endpoints.findIndex(e => e.path === path && e.method === m);
  if (i === -1) throw new Error(`Endpoint ${method.toUpperCase()} ${path} not found.`);
  endpoints[i].code = code;
  if (typeof auth !== 'undefined') endpoints[i].auth = !!auth;
  saveToDisk();
}

function removeEndpoint(method, path) {
  const m = method.toLowerCase();
  const i = endpoints.findIndex(e => e.path === path && e.method === m);
  if (i === -1) throw new Error(`Endpoint ${method.toUpperCase()} ${path} not found.`);
  endpoints.splice(i, 1);
  saveToDisk();
}

function setUseCluster(flag) {
  useCluster = flag;
  loadFromDisk();
}
function getUseCluster() {
  return useCluster;
}
loadFromDisk();

module.exports = {
  getEndpoints,
  addEndpoint,
  editEndpoint,
  removeEndpoint,
  setUseCluster,
  getUseCluster
};
dynamic-endpoints-data.json

Code: Select all

[]
extra-modules.json

Code: Select all

[]
extra-vars.json

Code: Select all

[]
index.js

Code: Select all

const https = require('https');
const fs = require('fs');
const fsp = require('fs').promises;
const express = require('express');
const multer = require('multer');
const path = require('path');
const cors = require('cors');
const bodyParser = require('body-parser');
const basicAuth = require('express-basic-auth');
const HotPocket = require('hotpocket-js-client');
const BSON = require('bson');
const crypto = require('crypto');
const app = express();
const rateLimit = require('express-rate-limit');
const vm = require('vm');
const axios = require('axios');
const mime = require('mime-types');
const settingsPath = path.join(__dirname, 'settings.json');
const HOTPOCKET_WS_URL = 'wss://localhost:26201';
const { exec } = require('child_process');
const adminRouter = express.Router();
const chokidar = require('chokidar');
const sslOptions = {
 key: fs.readFileSync('/contract/cfg/tlskey.pem'),
  cert: fs.readFileSync('/contract/cfg/tlscert.pem'),
};


function persistLoadedModules() {
  fs.writeFileSync(LOADED_MODULES_PATH, JSON.stringify(Object.keys(loadedModules), null, 2));
}
async function detectFileType(fullPath) {
  const { fromBuffer } = await import('file-type');
  const buffer = fs.readFileSync(fullPath);
  const type = await fromBuffer(buffer);
  return type;
}

app.use(cors());
app.use(bodyParser.json());

app.use((req, res, next) => {

  const settings = fs.existsSync(settingsPath)
    ? JSON.parse(fs.readFileSync(settingsPath, 'utf-8'))
    : { useCluster: false };

  const servePath = settings.useCluster
    ? '/contract/contract_fs/seed/state/public'
    : path.join(__dirname, 'public');

  express.static(servePath)(req, res, next);
});

function ensureAdmin(req, res) {
  const auth = req.headers.authorization;
  if (!auth || !auth.startsWith('Basic ')) return false;

  const base64 = auth.split(' ')[1];
  const [username] = Buffer.from(base64, 'base64').toString('utf-8').split(':');

  return username === 'admin';
}

const adminRateLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15 minutes
  max: 10,
  skipSuccessfulRequests: true,
  message: 'Too many attempts, please try again later.',
  standardHeaders: true, 
  legacyHeaders: false, 
});

app.use('/admin', express.static(path.join(__dirname, 'admin')));
function getUploadSuffix() {
  const now = new Date();
  const pad = (n) => n.toString().padStart(2, '0');
  return `version-${now.getFullYear()}${pad(now.getMonth() + 1)}${pad(now.getDate())}-${pad(now.getHours())}${pad(now.getMinutes())}${pad(now.getSeconds())}`;
}

const storage = multer.diskStorage({
  destination: function (req, file, cb) {
    let folder = req.headers['x-upload-folder'] || '';
    folder = decodeURIComponent(folder).trim();

    const safeFolder = path.normalize(folder).replace(/^([\.\/]+)+/, '').replace(/^(\.{2,})(\/|\|$)/, '');
    const folderPath = path.join(__dirname, 'public', safeFolder);
    console.log('Uploading to folder:', folderPath);

    fs.mkdirSync(folderPath, { recursive: true });
    cb(null, folderPath);
  },
  filename: function (req, file, cb) {
    cb(null, file.originalname);
  }
});

const upload = multer({ storage });

function loadUserCredentials() {
  const hashFile = path.join(__dirname, 'password.hash');
  const credentials = {};

  if (fs.existsSync(hashFile)) {
    const lines = fs.readFileSync(hashFile, 'utf-8').split('\n');
    for (const line of lines) {
      const trimmed = line.trim();
      if (trimmed && trimmed.includes(':')) {
        const [user, hash] = trimmed.split(':');
        credentials[user.trim()] = hash.trim();
      }
    }
  }

  return credentials;
}
function myAuthorizer(username, password) {
  const users = loadUserCredentials();
  const storedHash = users[username];

  if (!storedHash) return false;

  const inputHash = crypto.createHash('sha256').update(password).digest('hex');

 // console.log(`[AUTH DEBUG] username=${username}`);
 // console.log(`[AUTH DEBUG] expected=${storedHash}`);
 // console.log(`[AUTH DEBUG] received=${inputHash}`);

  return storedHash === inputHash;
}

const authMiddleware = basicAuth({
  authorizer: myAuthorizer,
  authorizeAsync: false,
  challenge: true
});

app.post('/admin/upload', adminRateLimiter, authMiddleware, upload.fields([{ name: 'files' }]), (req, res) => {
  const chunkNo = parseInt(req.body.chunkNo);
  const totalChunks = parseInt(req.body.totalChunks);
  const filename = req.body.filename;
  const uploadPath = decodeURIComponent(req.headers['x-upload-folder'] || '').trim();

  if (!filename || isNaN(chunkNo) || isNaN(totalChunks)) {
    return res.status(400).json({ error: 'Missing chunk metadata or filename.' });
  }

  const safeFolder = path.normalize(uploadPath).replace(/^([\.\/]+)+/, '');
  const tempDir = path.join(__dirname, 'uploads_temp', safeFolder, filename);

  fs.mkdirSync(tempDir, { recursive: true });

  const fileChunk = req.files['files']?.[0];
  if (!fileChunk) {
    return res.status(400).json({ error: 'No file chunk received.' });
  }

  const chunkPath = path.join(tempDir, `chunk_${chunkNo}`);
  fs.writeFileSync(chunkPath, fs.readFileSync(fileChunk.path));

  // Clean up multer temp file
  fs.unlinkSync(fileChunk.path);

  const chunks = fs.readdirSync(tempDir).filter(f => f.startsWith('chunk_'));

  if (chunks.length === totalChunks) {
    const finalDir = path.join(__dirname, 'public', safeFolder);
    fs.mkdirSync(finalDir, { recursive: true });

    const finalPath = path.join(finalDir, filename);
    const writeStream = fs.createWriteStream(finalPath);

    for (let i = 1; i <= totalChunks; i++) {
      const chunk = fs.readFileSync(path.join(tempDir, `chunk_${i}`));
      writeStream.write(chunk);
    }

    writeStream.end();
    fs.rmSync(tempDir, { recursive: true, force: true });

    return res.send({ message: `Upload of '${filename}' complete.` });
  }

  res.send({ message: `Chunk ${chunkNo}/${totalChunks} received.` });
});



const extraModules = {};

const EXTRA_MODULES_PATH = path.join(__dirname, 'extra-modules.json');

adminRouter.get('/modules/available', adminRateLimiter, authMiddleware, (req, res) => {
  const nodeModulesPath = path.join(__dirname, 'node_modules');
  const modules = [];

  fs.readdirSync(nodeModulesPath).forEach(name => {
    const fullPath = path.join(nodeModulesPath, name);
    if (name.startsWith('@')) {
      fs.readdirSync(fullPath).forEach(sub => {
        const scopedName = `${name}/${sub}`;
        const scopedPath = path.join(fullPath, sub);
        if (fs.statSync(scopedPath).isDirectory()) {
          modules.push(scopedName);
        }
      });
    } else {
      if (fs.statSync(fullPath).isDirectory()) {
        modules.push(name);
      }
    }
  });

  res.json({ available: modules });
});

console.log("Initial extraModules:", Object.keys(extraModules));

adminRouter.get('/modules/extra', adminRateLimiter, authMiddleware, (req, res) => {
  res.json({ extra: Object.keys(extraModules) });
});

adminRouter.post('/modules/extra', adminRateLimiter, authMiddleware, (req, res) => {
const { name } = req.body;

  if (!name) return res.status(400).json({ error: 'Module name required' });
  if (extraModules[name]) return res.json({ message: `${name} already loaded` });

  try {
    // Try resolving from the node_modules folder directly
    const resolvedPath = require.resolve(name, {
      paths: [path.join(__dirname, 'node_modules')],
    });

    const mod = require(resolvedPath);
    extraModules[name] = mod;
    saveExtraModules();

    res.json({ message: `${name} loaded`, keys: Object.keys(mod) });
  } catch (err) {
    res.status(500).json({ error: `Failed to load ${name}: ${err.message}` });
  }
});




if (fs.existsSync(EXTRA_MODULES_PATH)) {
  try {
    const moduleNames = JSON.parse(fs.readFileSync(EXTRA_MODULES_PATH, 'utf-8'));
    if (!Array.isArray(moduleNames)) {
      throw new Error('extra-modules.json must contain an array of module names');
    }

    for (const name of moduleNames) {
      try {
        global[name] = require(name);
        console.log(`Loaded module: ${name}`);
      } catch (err) {
        console.warn(`Could not load module "${name}": ${err.message}`);
      }
    }
  } catch (err) {
    console.error(`Error reading extra modules: ${err.message}`);
  }
}

function saveExtraModules() {
  fs.writeFileSync(EXTRA_MODULES_PATH, JSON.stringify(Object.keys(extraModules), null, 2));
}


function reloadExtraModules() {
  try {
    const names = JSON.parse(fs.readFileSync(EXTRA_MODULES_PATH, 'utf-8'));
    const currentSet = new Set(names);

    for (const name of names) {
      try {
        const mod = require(name);
        extraModules[name] = mod;
        global[name] = mod;
        console.log(`Loaded or refreshed: ${name}`);
      } catch (err) {
        console.warn(`Failed to load module "${name}": ${err.message}`);
      }
    }

    for (const name of Object.keys(extraModules)) {
      if (!currentSet.has(name)) {
        delete extraModules[name];
        delete global[name];
        console.log(`Unloaded removed module: ${name}`);
      }
    }
  } catch (err) {
    console.error('Error reading extra modules file:', err.message);
  }
}


adminRouter.get('/vars/extra', adminRateLimiter, authMiddleware, (req, res) => {
  res.json({ extra: extraVars });
});

adminRouter.post('/vars/extra', adminRateLimiter, authMiddleware, (req, res) => {
  const { name, value } = req.body;

  if (!name) return res.status(400).json({ error: 'Variable name is required' });
  if (typeof value === 'undefined') return res.status(400).json({ error: 'Variable value is required' });

  extraVars[name] = value;
  global[name] = value;
  saveExtraVars();

  res.json({ message: `Variable "${name}" saved`, value });
});

adminRouter.delete('/vars/extra', adminRateLimiter, authMiddleware, (req, res) => {
  const { name } = req.body;

  if (!name) return res.status(400).json({ error: 'Variable name is required' });
  if (!extraVars[name]) return res.status(404).json({ error: `Variable "${name}" not found` });

  delete extraVars[name];
  delete global[name];
  saveExtraVars();

  res.json({ message: `Variable "${name}" deleted` });
});



const EXTRA_VARS_PATH = path.join(__dirname, 'extra-vars.json');
const extraVars = {};


if (fs.existsSync(EXTRA_VARS_PATH)) {
  try {
    const vars = JSON.parse(fs.readFileSync(EXTRA_VARS_PATH, 'utf-8'));
    if (typeof vars !== 'object' || Array.isArray(vars)) {
      throw new Error('extra-vars.json must contain an object of key-value pairs');
    }
    Object.entries(vars).forEach(([key, value]) => {
      extraVars[key] = value;
      global[key] = value;
    });
    console.log('Loaded extra vars:', Object.keys(extraVars));
  } catch (err) {
    console.error(`Error reading extra vars: ${err.message}`);
  }
}


function saveExtraVars() {
  fs.writeFileSync(EXTRA_VARS_PATH, JSON.stringify(extraVars, null, 2));
}


const dynamicRouter = express.Router();
const dynamicEndpoints = require('./dynamic-endpoints');
dynamicEndpoints.setUseCluster(false); 
registerDynamicRoutes(dynamicEndpoints.getEndpoints());


const deps = { exec, fs, axios, extraVars, HOTPOCKET_WS_URL, require };

function registerDynamicRoutes(routes) {
  const router = express.Router();

  for (const route of routes) {
    try {
      const handler = new Function('req', 'res', 'deps', `
        const { exec, fs, axios, extraVars, HOTPOCKET_WS_URL, require } = deps;

        (async () => {
          try {
            ${route.code}
          } catch (err) {
            console.error("Runtime error in handler:", err.stack || err);
            res.status(500).send("Internal Server Error");
          }
        })();
      `);

      const method = route.method.toLowerCase();
      const useAuth = route.auth === true;
      const boundHandler = (req, res) => handler(req, res, deps);

      if (typeof router[method] !== 'function') {
        console.error('Invalid HTTP method:', method);
        continue;
      }

      if (useAuth) {
        router[method](route.path, adminRateLimiter, authMiddleware, boundHandler);
      } else {
        router[method](route.path, boundHandler);
      }

      console.log(`Registered ${method.toUpperCase()} ${route.path}`);
    } catch (err) {
      console.error(`Failed to register ${route.method.toUpperCase()} ${route.path}:`, err.message);
    }
  }

  dynamicRouter.stack = router.stack;
}



const filesToWatch = [
  './dynamic-endpoints-data.json',
  '/contract/contract_fs/seed/state/dynamic-endpoints-data.json',
  '/contract/contract_fs/seed/state/dynamic-endpoints.json',
  './dynamic-endpoints.json',
  './extra-modules.json',
  '/contract/contract_fs/seed/state/extra-modules.json'
];

const EXTRA_MODULE_PATHS = new Set([
  path.resolve('./extra-modules.json'),
  path.resolve('/contract/contract_fs/seed/state/extra-modules.json')
]);

const reloadRoutes = (path) => {
  try {
    const routes = dynamicEndpoints.getEndpoints(path);
    registerDynamicRoutes(routes);
    console.log('[WATCHER] Routes reloaded');
  } catch (err) {
    console.error('[WATCHER] Route reload error:', err.message);
  }
};

const handleChange = (changedPath) => {
  const resolved = path.resolve(changedPath);

  console.log(`[WATCHER] File changed: ${resolved}`);

  if (EXTRA_MODULE_PATHS.has(resolved)) {
    reloadExtraModules();
    console.log(`[WATCHER] extra modules reloaded`);
  } else {
    reloadRoutes(resolved);
  }
};

const watcher = chokidar.watch(filesToWatch, {
  persistent: true,
  ignoreInitial: true,
  usePolling: true,
  interval: 500,
  awaitWriteFinish: {
    stabilityThreshold: 300,
    pollInterval: 100
  }
});

watcher
  .on('change', handleChange)
  .on('add', handleChange)
  .on('unlink', (path) => {
    console.warn(`[WATCHER] File removed: ${path}`);
  });




adminRouter.post('/set-api-mode', adminRateLimiter, authMiddleware, (req, res) => {
  const { useCluster } = req.body;
  try {
    dynamicEndpoints.setUseCluster(!!useCluster);
    registerDynamicRoutes(dynamicEndpoints.getEndpoints());
    res.json({ success: true });
   
  } catch (err) {
    res.status(500).json({ error: err.message });
  }
});


app.use((req, res, next) => dynamicRouter(req, res, next));


adminRouter.post('/add-endpoint', adminRateLimiter, authMiddleware, (req, res) => {
  const { path, code, method, auth } = req.body;

  if (!path || !code || !method) {
    return res.status(400).json({ error: 'Path, method, and code are required.' });
  }

  try {
    dynamicEndpoints.addEndpoint({
      path,
      code,
      method: method.toLowerCase(),
      auth: !!auth
    });

    registerDynamicRoutes(dynamicEndpoints.getEndpoints());

    res.json({ message: `Endpoint ${method.toUpperCase()} ${path} added.` });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

adminRouter.get('/get-api-mode', adminRateLimiter, authMiddleware, (req, res) => {
  res.json({ useCluster: dynamicEndpoints.getUseCluster() });
});


adminRouter.get('/list-endpoints', adminRateLimiter, authMiddleware, (req, res) => {
  res.json(dynamicEndpoints.getEndpoints());
  registerDynamicRoutes(dynamicEndpoints.getEndpoints());
});

adminRouter.post('/edit-endpoint', adminRateLimiter, authMiddleware, (req, res) => {
  const { path, code, method, auth } = req.body;

  if (!path || !code || !method) {
    return res.status(400).json({ error: 'Path, method, and code are required.' });
  }

  try {
    dynamicEndpoints.editEndpoint({ path, code, method, auth });
    registerDynamicRoutes(dynamicEndpoints.getEndpoints());
    res.json({ message: `Endpoint ${method.toUpperCase()} ${path} updated.` });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

function readSettings() {
  if (!fs.existsSync(settingsPath)) return {};
  try {
    return JSON.parse(fs.readFileSync(settingsPath, 'utf8'));
  } catch {
    return {};
  }
}

function writeSettings(newSettings) {
  const current = readSettings();
  const merged = { ...current, ...newSettings };
  fs.writeFileSync(settingsPath, JSON.stringify(merged, null, 2));
  return merged;
}

app.post('/toggle-morefunctions-source', adminRateLimiter, authMiddleware, (req, res) => {
  const { useCluster } = req.body;

  if (typeof useCluster !== 'boolean') {
    return res.status(400).json({ error: 'Invalid value for useCluster' });
  }

  const updated = writeSettings({ clusterMore: useCluster });
  res.json({ clusterMore: updated.clusterMore });
});

app.get('/toggle-morefunctions-source', adminRateLimiter, authMiddleware, (req, res) => {
  const current = readSettings();
  res.json({ clusterMore: !!current.clusterMore });
});


adminRouter.post('/delete-endpoint', adminRateLimiter, authMiddleware, (req, res) => {
  const { path, method } = req.body;

  if (!path || !method) {
    return res.status(400).json({ error: 'Path and method are required.' });
  }

  try {
    dynamicEndpoints.removeEndpoint(method, path);
    registerDynamicRoutes(dynamicEndpoints.getEndpoints());
    res.json({ message: `Endpoint ${method.toUpperCase()} ${path} deleted.` });
  } catch (err) {
    res.status(400).json({ error: err.message });
  }
});

adminRouter.post('/install', adminRateLimiter, authMiddleware, (req, res) => {
const { package, version } = req.body;
if (!package) return res.status(400).send('Package name is required');

const pkgWithVersion = version ? package + '@' + version : package;
exec('npm install ' + pkgWithVersion + ' --save --cache /home/everweb/npm-cache', (error, stdout, stderr) => {
  if (error) return res.status(500).send(stderr);
  res.send({ message: 'Installed ' + pkgWithVersion, output: stdout });
  setTimeout(() => {
    process.exit(0);
  }, 1000);
});
});

adminRouter.post('/uninstall', adminRateLimiter, authMiddleware, (req, res) => {
const { package } = req.body;
if (!package) return res.status(400).send('Package name is required');

exec('npm uninstall ' + package + ' --save --cache /home/everweb/npm-cache', (error, stdout, stderr) => {
  if (error) return res.status(500).send(stderr);
  res.send({ message: 'Uninstalled ' + package, output: stdout });
});
});


const mfFile = path.join(__dirname, 'morefunctions.json');

app.get('/morefunctions/load', adminRateLimiter, authMiddleware, (req, res) => {
  const { clusterMore } = readSettings();
  const localPath = path.join(__dirname, 'morefunctions.json');
  const clusterPath = '/contract/contract_fs/seed/state/morefunctions.json';
  const selectedPath = clusterMore ? clusterPath : localPath;

  if (!fs.existsSync(selectedPath)) return res.json([]);
  const data = fs.readFileSync(selectedPath, 'utf8');
  res.json(JSON.parse(data));
});


app.post('/morefunctions/save', adminRateLimiter, authMiddleware, (req, res) => {
  const { cards } = req.body;
  if (!Array.isArray(cards)) return res.status(400).json({ error: 'Invalid format' });

  fs.writeFileSync(mfFile, JSON.stringify(cards, null, 2));
  res.json({ success: true });
});

app.get('/admin/folders', adminRateLimiter, authMiddleware, (req, res) => {
 // console.log('AUTH HEADER:', req.headers.authorization);
  const root = path.join(__dirname, 'public');

  function buildTree(dirPath) {
    const result = {};
    const items = fs.readdirSync(dirPath, { withFileTypes: true });
    for (const item of items) {
      if (item.isDirectory()) {
        result[item.name] = buildTree(path.join(dirPath, item.name));
      }
    }
    return result;
  }

  try {
    const tree = buildTree(root);
    res.json(tree);
  } catch (err) {
    res.status(500).json({ error: 'Failed to read folders' });
  }
});

const NODELOG_PATHS = {
  stdout: '/home/everweb/webadmin.out.log',
  stderr: '/home/everweb/webadmin.err.log'
};

adminRouter.get('/nodestdout', adminRateLimiter, authMiddleware, (req, res) => {
  const file = NODELOG_PATHS.stdout;

  if (!fs.existsSync(file)) {
    return res.status(400).json({ error: 'File not found' });
  }

  try {
    const data = fs.readFileSync(file, 'utf8');
    res.set('Content-Type', 'text/plain');
res.send(data);
  } catch (err) {
    res.status(500).json({ error: 'Failed to read stdout log' });
  }
});

adminRouter.get('/nodestderr', adminRateLimiter, authMiddleware, (req, res) => {
  const file = NODELOG_PATHS.stderr;

  if (!fs.existsSync(file)) {
    return res.status(400).json({ error: 'File not found' });
  }

  try {
    const data = fs.readFileSync(file, 'utf8');
    res.set('Content-Type', 'text/plain');
res.send(data);
  } catch (err) {
    res.status(500).json({ error: 'Failed to read stderr log' });
  }
});



function loadHpKeypair(configPath) {
  const raw = fs.readFileSync(configPath, 'utf8');
  const cfg = JSON.parse(raw);

  if (!cfg.node?.public_key || !cfg.node?.private_key) {
    throw new Error('Missing public_key or private_key in hp.cfg');
  }

  return {
    publicKey: Buffer.from(cfg.node.public_key, 'hex'),
    privateKey: Buffer.from(cfg.node.private_key, 'hex')
  };
}

adminRouter.get('/my-vote', async (req, res) => {
  try {
    const raw = fs.readFileSync('/contract/cfg/hp.cfg', 'utf8');
    const cfg = JSON.parse(raw);
    const voterKey = cfg.node?.public_key;

    if (!voterKey) {
      return res.status(500).json({ error: 'Node public key not found in hp.cfg' });
    }

    const voteFilePath = '/contract/contract_fs/seed/state/votes.json';
    let votes = {};

    if (fs.existsSync(voteFilePath)) {
      votes = JSON.parse(fs.readFileSync(voteFilePath));
    }

    const activeVote = Object.entries(votes).find(
      ([version, entry]) =>
        entry.type === 'go-live' && entry.voters.includes(voterKey)
    );

    return res.json({ version: activeVote ? activeVote[0] : null });
  } catch (err) {
    console.error('[my-vote] Error:', err);
    return res.status(500).json({ error: 'Failed to retrieve vote' });
  }
});

adminRouter.get('/my-sidedish-vote', async (req, res) => {
  try {
    const raw = fs.readFileSync('/contract/cfg/hp.cfg', 'utf8');
    const cfg = JSON.parse(raw);
    const voterKey = cfg.node?.public_key;

    if (!voterKey) {
      return res.status(500).json({ error: 'Node public key not found in hp.cfg' });
    }

    // Read live version
    const liveFlagPath = '/contract/contract_fs/seed/state/.live_sidedish_version';
    let liveVersion = null;
    if (fs.existsSync(liveFlagPath)) {
      try {
        liveVersion = fs.readFileSync(liveFlagPath, 'utf8').trim();
      } catch (err) {
        console.warn('[my-sidedish-vote] Failed to read .live_sidedish_version:', err.message);
      }
    }

    // Read vote file
    const voteFilePath = '/contract/contract_fs/seed/state/sidedish_votes.json';
    let votes = {};
    if (fs.existsSync(voteFilePath)) {
      votes = JSON.parse(fs.readFileSync(voteFilePath));
    }

    let votedVersion = null;
    const transformed = {};

    for (const [version, data] of Object.entries(votes)) {
      const goLive = data.voters || [];
      const deleteVote = data.delete || [];

      if (goLive.includes(voterKey)) votedVersion = version;

      transformed[version] = {
        votes: goLive.length,
        votedByMe: goLive.includes(voterKey),
        deleteVotes: deleteVote.length,
        deleteVotedByMe: deleteVote.includes(voterKey)
      };
    }

    return res.json({
      sidedish: votedVersion,
      sidedishVotes: transformed,
      live: liveVersion || null
    });
  } catch (err) {
    console.error('[my-sidedish-vote] Error:', err);
    return res.status(500).json({ error: 'Failed to retrieve sidedish votes' });
  }
});


function writeHpCfgMultipleTimes(config, path, retries = 3, delay = 100) {
  for (let i = 0; i < retries; i++) {
    setTimeout(() => {
      fs.writeFileSync(path, JSON.stringify(config, null, 2), 'utf8');
      console.log(`[import-key] Write attempt ${i + 1} complete.`);
    }, i * delay);
  }
}
adminRouter.get('/read-sd-local', adminRateLimiter, authMiddleware, (req, res) => {
  const fullPath = path.join(__dirname, 'sidedish.js');

  if (!fs.existsSync(fullPath)) {
    return res.status(404).json({ error: 'Local sidedish.js not found.' });
  }

  try {
    const content = fs.readFileSync(fullPath, 'utf8');
    res.send({ content });
  } catch (err) {
    console.error('[read-sd-local] Error:', err.message);
    res.status(500).json({ error: 'Failed to read local sidedish.js.' });
  }
});

adminRouter.post('/save-sd-local', adminRateLimiter, authMiddleware, (req, res) => {
  const { content } = req.body;
  const fullPath = path.join(__dirname, 'sidedish.js');

  try {
    fs.writeFileSync(fullPath, content, 'utf8');
    res.send({ message: 'Local sidedish.js saved.' });
  } catch (err) {
    console.error('[save-sd-local] Error:', err.message);
    res.status(500).json({ error: 'Failed to save local sidedish.js.' });
  }
});

adminRouter.post('/fetch-sidedish', adminRateLimiter, authMiddleware, (req, res) => {
  const src = '/contract/contract_fs/seed/state/sidedish.js';
  const dest = '/home/everweb/sidedish.js';

  try {
    if (!fs.existsSync(src)) {
      return res.status(404).json({ error: 'sidedish.js not found in cluster.' });
    }

    fs.copyFileSync(src, dest);
    res.json({ message: 'sidedish.js copied from cluster to local seed path.' });
  } catch (err) {
    console.error('[fetch-sidedish] Error:', err.message);
    res.status(500).json({ error: 'Failed to fetch sidedish.js: ' + err.message });
  }
});

adminRouter.post('/promote-sidedish', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    if (!(await client.connect())) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const payload = BSON.serialize({ type: 'sidedish-go-live', version });

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected the input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    return res.json(response);
  } catch (err) {
    console.error('[promote-sidedish] failed:', err);
    return res.status(500).json({ error: err.message });
  }
});

adminRouter.post('/push-sidedish', adminRateLimiter, authMiddleware, async (req, res) => {
  const sidedishPath = path.join(__dirname, 'sidedish.js');

  if (!fs.existsSync(sidedishPath)) {
    return res.status(404).json({ error: 'Local sidedish.js not found.' });
  }

  const versionName = getUploadSuffix();
  const clusterDest = path.join(CLUSTER_ROOT, 'sidedish_versions', versionName);

  try {
    fs.mkdirSync(clusterDest, { recursive: true });
    fs.copyFileSync(sidedishPath, path.join(clusterDest, 'sidedish.js'));

    return res.json({ success: true, version: versionName, message: `Pushed to sidedish_versions/${versionName}/` });
  } catch (err) {
    console.error('[push-sidedish] Error:', err);
    return res.status(500).json({ error: 'Failed to push sidedish version: ' + err.message });
  }
});

adminRouter.post('/vote-delete-sd', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;
  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    if (!(await client.connect())) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const payload = BSON.serialize({ type: 'sidedish-vote-delete', version });

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    return res.json(response);
  } catch (err) {
    console.error('[vote-delete-sd] failed:', err);
    return res.status(500).json({ error: err.message });
  }
});

adminRouter.post('/import-key', adminRateLimiter, authMiddleware, async (req, res) => {
  if (!ensureAdmin(req, res)) {
    return res.status(403).json({ error: 'Access denied. Admin only.' });
  }

  const { private_key, public_key } = req.body;

  if (!private_key || !public_key) {
    return res.status(400).json({ error: 'Missing key(s).' });
  }

  const hpConfigPath = '/contract/cfg/hp.cfg';

  try {
    if (!fs.existsSync(hpConfigPath)) {
      return res.status(500).json({ error: 'hp.cfg not found.' });
    }

    const raw = fs.readFileSync(hpConfigPath, 'utf8');
    const config = JSON.parse(raw);

    const oldPubKey = config.node.public_key;

    config.node.private_key = private_key;
    config.node.public_key = public_key;

    if (Array.isArray(config.contract.unl)) {
      const idx = config.contract.unl.findIndex(k => k === oldPubKey);
      if (idx !== -1) {
        config.contract.unl[idx] = public_key;
        console.log(`[import-key] Replaced old UNL key at index ${idx}`);
      } else {
        console.warn('[import-key] Old public key not found in UNL list — no changes made to UNL.');
      }
    }

    // Write multiple times with delay (for some reason it gets overwritten otherwise)
    writeHpCfgMultipleTimes(config, hpConfigPath, 5, 200);

    return res.json({ success: true });
  } catch (err) {
    console.error('[import-key] Failed to update hp.cfg:', err);
    return res.status(500).json({ error: 'Failed to update hp.cfg: ' + err.message });
  }
});

adminRouter.get('/show-keypair', adminRateLimiter, authMiddleware, async (req, res) => {
  if (!ensureAdmin(req, res)) return res.status(403).json({ error: 'Access denied. Admin only.' });

  try {
    const cfgRaw = await fsp.readFile('/contract/cfg/hp.cfg', 'utf8');
    const cfg = JSON.parse(cfgRaw);

    const pub = cfg.node?.public_key;
    const priv = cfg.node?.private_key;

    if (!pub || !priv) {
      return res.status(500).json({ error: 'Keypair not found in config.' });
    }

    res.json({ publicKey: pub, privateKey: priv });
  } catch (err) {
    console.error('[show-keypair] Error:', err.message);
    res.status(500).json({ error: 'Failed to read keypair: ' + err.message });
  }
});

adminRouter.post('/sidedish-upload', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const uploadSuffix = getUploadSuffix();
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    const sidedishPath = path.join(__dirname, 'sidedish.js');
    if (!fs.existsSync(sidedishPath)) {
      return res.status(404).json({ error: 'sidedish.js not found on local system.' });
    }

    const fileBuffer = fs.readFileSync(sidedishPath);
    const chunkSize = 40 * 64 * 1024; // ~2.5MB
    const totalChunks = Math.ceil(fileBuffer.length / chunkSize);

    const connectOK = await client.connect();
    if (!connectOK) {
      return res.status(500).json({ error: 'Failed to connect to HotPocket cluster.' });
    }

    for (let i = 0; i < totalChunks; i++) {
      const start = i * chunkSize;
      const end = Math.min(start + chunkSize, fileBuffer.length);
      const chunk = fileBuffer.slice(start, end);

      const payload = {
        type: 'sidedish-upload',
        version: uploadSuffix,
        chunkNo: i + 1,
        totalChunks,
        chunk: chunk.toString('hex')
      };

      const submission = await client.submitContractInput(BSON.serialize(payload));
      const result = await submission.submissionStatus;

      if (result.status !== 'accepted') {
        client.close();
        return res.status(500).json({ error: `Chunk ${i + 1} rejected by contract.` });
      }
    }

    client.close();
    return res.json({ message: `sidedish.js uploaded as version '${uploadSuffix}'` });
  } catch (err) {
    console.error('[sidedish-upload] Error:', err);
    return res.status(500).json({ error: 'Failed to upload sidedish.js: ' + err.message });
  }
});

adminRouter.post('/promote-version', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg'); 

    const client = await HotPocket.createClient(
      [HOTPOCKET_WS_URL],
      keyPair,
      { protocol: HotPocket.protocols.bson }
    );

    const connected = await client.connect();
    if (!connected) {
      console.error('[error] Failed to connect to HotPocket cluster.');
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    console.log('[debug] Connected to HotPocket cluster');

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Timeout waiting for contract output'));
      }, 10000);

      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const payload = BSON.serialize({ type: 'go-live', version });
    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      console.error('[error] Contract rejected input:', status);
      return res.status(500).json({ error: 'Contract rejected input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    if (response.type === 'success' || response.type === 'info') {
      console.log('[debug] Version promotion successful');
      return res.json({ message: response.message });
    } else {
      console.error('[error] Unexpected contract error:', response.error);
      return res.status(500).json({ error: response.message || 'Unexpected contract error' });
    }

  } catch (err) {
    console.error('[admin] promote-version failed:', err);
    return res.status(500).json({ error: err.message });
  }
});

const hpConfigPath = '/contract/cfg/hp.cfg';
adminRouter.get('/hpinfo', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const data = await fsp.readFile(hpConfigPath, 'utf8');
    const config = JSON.parse(data);

    res.json({
      public_key: config.node.public_key,
      contract_id: config.contract.id,
      unl: config.contract.unl,
      known_peers: config.mesh.known_peers || [],
      user_port: config.user.port,
      mesh_port: config.mesh.port,
      roundtime: config.contract.consensus.roundtime,
      stage_slice: config.contract.consensus.stage_slice,
      threshold: config.contract.consensus.threshold,
    });
  } catch (error) {
    console.error('[hpinfo] ERROR reading hp.cfg:', error.message);
    res.status(500).json({ error: 'Failed to load hp config: ' + error.message });
  }  
});


adminRouter.post('/morefunctions-upload', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const version = getUploadSuffix(); // e.g. timestamp-based
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    const mfPath = path.join(__dirname, 'morefunctions.json');
    if (!fs.existsSync(mfPath)) {
      return res.status(404).json({ error: 'morefunctions.json not found on local system.' });
    }

    const fileBuffer = fs.readFileSync(mfPath);
    const chunkSize = 40 * 64 * 1024; // ~2.5MB
    const totalChunks = Math.ceil(fileBuffer.length / chunkSize);

    const connectOK = await client.connect();
    if (!connectOK) {
      return res.status(500).json({ error: 'Failed to connect to HotPocket cluster.' });
    }

    for (let i = 0; i < totalChunks; i++) {
      const start = i * chunkSize;
      const end = Math.min(start + chunkSize, fileBuffer.length);
      const chunk = fileBuffer.slice(start, end);

      const payload = {
        type: 'more-upload',
        version,
        chunkNo: i + 1,
        totalChunks,
        chunk: chunk.toString('hex')
      };

      const submission = await client.submitContractInput(BSON.serialize(payload));
      const result = await submission.submissionStatus;

      if (result.status !== 'accepted') {
        client.close();
        return res.status(500).json({ error: `Chunk ${i + 1} rejected by contract.` });
      }
    }

    client.close();
    return res.json({ message: `morefunctions.json uploaded as version '${version}'` });
  } catch (err) {
    console.error('[morefunctions-upload] Error:', err);
    return res.status(500).json({ error: 'Failed to upload morefunctions.json: ' + err.message });
  }
});


adminRouter.get('/list-morefunctions', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
  
    const raw = fs.readFileSync('/contract/cfg/hp.cfg', 'utf8');
    const cfg = JSON.parse(raw);
    const myPublicKey = cfg.node?.public_key;

    if (!myPublicKey) {
      return res.status(500).json({ error: 'Missing public key in hp.cfg' });
    }

    const liveFlagPath = '/contract/contract_fs/seed/state/.live_more_version';
    let liveVersion = null;
    if (fs.existsSync(liveFlagPath)) {
      try {
        const liveRaw = fs.readFileSync(liveFlagPath, 'utf8').trim();
        if (liveRaw.length > 0) {
          liveVersion = liveRaw;
        }
      } catch (err) {
        console.warn('[list-morefunctions] Failed to read .live_more_version:', err.message);
      }
    }

 
    const versionsRoot = '/contract/contract_fs/seed/state/more_versions';
    let versionNames = [];
    if (fs.existsSync(versionsRoot)) {
      const entries = fs.readdirSync(versionsRoot, { withFileTypes: true });
      versionNames = entries.filter(d => d.isDirectory()).map(d => d.name);
    }


    const votePath = '/contract/contract_fs/seed/state/more_votes.json';
    let votes = {};
    if (fs.existsSync(votePath)) {
      try {
        votes = JSON.parse(fs.readFileSync(votePath, 'utf8'));
      } catch (err) {
        console.warn('[list-morefunctions] Failed to parse more_votes.json:', err.message);
      }
    }

 
    const result = versionNames.map(name => {
      const voteEntry = votes[name] || {};
      const goLiveVoters = voteEntry.goLive || [];
      const deleteVoters = voteEntry.delete || [];

      return {
        name,
        votes: goLiveVoters.length,
        votedByMe: goLiveVoters.includes(myPublicKey),
        deleteVotes: deleteVoters.length,
        deleteVotedByMe: deleteVoters.includes(myPublicKey),
        isLive: name === liveVersion
      };
    });

    res.json(result);
  } catch (err) {
    console.error('[list-morefunctions] Error:', err);
    res.status(500).json({ error: 'Failed to list morefunctions versions' });
  }
});

adminRouter.post('/morefunctions-fetch', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const sourcePath = path.join(
      '/contract/contract_fs/seed/state/more_versions',
      version,
      'morefunctions.json'
    );

    if (!fs.existsSync(sourcePath)) {
      return res.status(404).json({ error: `Version '${version}' does not exist.` });
    }

    const content = fs.readFileSync(sourcePath, 'utf8');

    const destPath = path.join(__dirname, 'morefunctions.json');
    fs.writeFileSync(destPath, content);

    res.json({ message: `Version '${version}' saved to local morefunctions.json.` });
  } catch (err) {
    console.error('[morefunctions-fetch] Error:', err);
    res.status(500).json({ error: 'Failed to fetch version: ' + (err?.message || String(err)) });
  }
});


adminRouter.post('/morefunctions-promote', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    const connected = await client.connect();
    if (!connected) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const payload = BSON.serialize({ type: 'more-go-live', version });
    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    if (response.type === 'success' || response.type === 'info') {
      return res.json({ message: response.message });
    } else {
      return res.status(500).json({ error: response.error || 'Unexpected error' });
    }

  } catch (err) {
    console.error('[morefunctions-promote] Error:', err);
    res.status(500).json({ error: err.message });
  }
});

adminRouter.post('/morefunctions-deletevote', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    const connected = await client.connect();
    if (!connected) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const payload = BSON.serialize({ type: 'more-vote-delete', version });
    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    if (response.type === 'success' || response.type === 'info') {
      return res.json({ message: response.message });
    } else {
      return res.status(500).json({ error: response.error || 'Unexpected error' });
    }

  } catch (err) {
    console.error('[morefunctions-deletevote] Error:', err);
    res.status(500).json({ error: err.message });
  }
});






adminRouter.post('/generate-keypair', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const keypair = await HotPocket.generateKeys();

    const publicKeyHex = Buffer.from(keypair.publicKey).toString('hex');   // 64 hex chars
    const privateKeyHex = Buffer.from(keypair.privateKey).toString('hex'); // 128 hex chars

    res.json({
      success: true,
      publicKey: publicKeyHex,
      privateKey: privateKeyHex
    });
  } catch (err) {
    console.error('[generate-keypair] Failed:', err);
    res.status(500).json({ error: 'Failed to generate keypair.' });
  }
});

adminRouter.post('/add-user', adminRateLimiter, authMiddleware, async (req, res) => {
  const { username, password } = req.body;

  if (!username || !password || typeof username !== 'string' || typeof password !== 'string') {
    return res.status(400).json({ error: 'Username and password are required.' });
  }

  const hashFile = path.join(__dirname, 'password.hash');
  const users = loadUserCredentials();

  if (users[username]) {
    return res.status(400).json({ error: 'User already exists.' });
  }

  const hash = crypto.createHash('sha256').update(password).digest('hex');
  const entry = `${username}:${hash}\n`;
let current = '';
if (fs.existsSync(hashFile)) {
  current = fs.readFileSync(hashFile, 'utf-8');
  if (!current.endsWith('\n')) current += '\n';
}
fs.writeFileSync(hashFile, current + entry, 'utf-8');
  return res.json({ success: true, message: `User '${username}' added.` });
});

adminRouter.post('/remove-user', adminRateLimiter, authMiddleware, async (req, res) => {
  const { username } = req.body;

  if (!username || typeof username !== 'string') {
    return res.status(400).json({ error: 'Username is required.' });
  }

  if (username === 'admin') {
    return res.status(403).json({ error: 'Cannot remove protected user "admin".' });
  }

  const hashFile = path.join(__dirname, 'password.hash');
  if (!fs.existsSync(hashFile)) {
    return res.status(500).json({ error: 'Password file not found.' });
  }

  const users = loadUserCredentials();

  if (!users[username]) {
    return res.status(404).json({ error: 'User not found.' });
  }

  const newLines = Object.entries(users)
    .filter(([user]) => user !== username)
    .map(([user, hash]) => `${user}:${hash}`)
    .join('\n') + '\n';

  fs.writeFileSync(hashFile, newLines, 'utf-8');
  return res.json({ success: true, message: `User '${username}' removed.` });
});

adminRouter.post('/change-password', adminRateLimiter, authMiddleware, async (req, res) => {
  const { username, oldPassword, newPassword, newPasswordConfirm } = req.body;

  if (!username || !oldPassword || !newPassword || !newPasswordConfirm) {
    return res.status(400).json({ error: 'All fields are required.' });
  }

  if (newPassword !== newPasswordConfirm) {
    return res.status(400).json({ error: 'New passwords do not match.' });
  }

  const users = loadUserCredentials();
  const storedHash = users[username];

  if (!storedHash) {
    return res.status(404).json({ error: 'User not found.' });
  }

  const oldHash = crypto.createHash('sha256').update(oldPassword).digest('hex');
  if (oldHash !== storedHash) {
    return res.status(403).json({ error: 'Old password is incorrect.' });
  }

  const newHash = crypto.createHash('sha256').update(newPassword).digest('hex');
  users[username] = newHash;

  const newContent = Object.entries(users)
    .map(([user, hash]) => `${user}:${hash}`)
    .join('\n') + '\n';

  fs.writeFileSync(path.join(__dirname, 'password.hash'), newContent, 'utf-8');
  return res.json({ success: true, message: 'Password updated successfully.' });
});

adminRouter.post('/refresh-unl', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const keyPair = await HotPocket.generateKeys(null);
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    if (!(await client.connect())) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for contract output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const payload = BSON.serialize({ type: 'refresh-unl' });
    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    if (response.type === 'success') {
      return res.json({ message: response.message });
    } else {
      return res.status(500).json({ error: response.error || 'Unexpected contract error' });
    }
  } catch (err) {
    console.error('[refresh-unl] failed:', err.message);
    res.status(500).json({ error: err.message });
  }
});

adminRouter.post('/hpinfo', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const data = await fsp.readFile(hpConfigPath, 'utf8');
    const config = JSON.parse(data);

    const updates = req.body;

    if (updates.public_key) config.node.public_key = updates.public_key;
    if (updates.contract_id) config.contract.id = updates.contract_id;
    if (updates.unl) config.contract.unl = updates.unl;
    if (updates.user_port) config.user.port = updates.user_port;
    if (updates.mesh_port) config.mesh.port = updates.mesh_port;
    if (updates.known_peers) config.mesh.known_peers = updates.known_peers;
    if (updates.roundtime) config.contract.consensus.roundtime = updates.roundtime;
    if (updates.stage_slice) config.contract.consensus.stage_slice = updates.stage_slice;
    if (updates.threshold) config.contract.consensus.threshold = updates.threshold;

    await fsp.writeFile(hpConfigPath, JSON.stringify(config, null, 4), 'utf8');

    res.json({ success: true });
  } catch (error) {
    console.error('[hpinfo POST] ERROR:', error.message);
    res.status(500).json({ error: 'Failed to update hp config: ' + error.message });
  }
});

const logMap = {
  hp: '/contract/log/hp.log',
  stdout: '/contract/log/contract/rw.stdout.log',
  stderr: '/contract/log/contract/rw.stderr.log'
};

adminRouter.post('/restart-hotpocket', adminRateLimiter, authMiddleware, async (req, res) => {
  const { exec } = require('child_process');

  exec('supervisorctl restart hotpocket', (error, stdout, stderr) => {
    if (error) {
      console.error('[restart-hotpocket] Exec error:', error);
      console.error('[restart-hotpocket] Stderr:', stderr);
      return res.status(500).json({ error: `Restart failed: ${stderr || error.message}` });
    }

    console.log('[restart-hotpocket] Stdout:', stdout);
    res.json({ message: 'HotPocket restarted.', output: stdout.trim() });
  });
});

adminRouter.post('/stop-hotpocket', adminRateLimiter, authMiddleware, async (req, res) => {
  const { exec } = require('child_process');

  exec('supervisorctl stop hotpocket', (error, stdout, stderr) => {
    if (error) {
      console.error('[stop-hotpocket] Exec error:', error);
      console.error('[stop-hotpocket] Stderr:', stderr);
      return res.status(500).json({ error: `Stop failed: ${stderr || error.message}` });
    }

    console.log('[stop-hotpocket] Stdout:', stdout);
    res.json({ message: 'HotPocket stopped.', output: stdout.trim() });
  });
});
adminRouter.get('/hotpocket-status', adminRateLimiter, authMiddleware, async (req, res) => {
  const { exec } = require('child_process');

  exec('supervisorctl status hotpocket', (error, stdout, stderr) => {
    if (error) {
      console.error('[hotpocket-status] Error:', error);
      return res.status(500).json({ error: 'Failed to check status.' });
    }

    const isRunning = stdout.includes('RUNNING');
    res.json({ running: isRunning, raw: stdout.trim() });
  });
});

adminRouter.get('/trace-log', adminRateLimiter, authMiddleware, (req, res) => {
  const fileKey = req.query.file;
  const logFile = logMap[fileKey];
  if (!logFile || !fs.existsSync(logFile)) {
    return res.status(400).json({ error: 'Invalid file' });
  }

  try {
    const data = fs.readFileSync(logFile, 'utf8');
    res.json({ log: data });
  } catch (err) {
    res.status(500).json({ error: 'Failed to read log file' });
  }
});

adminRouter.get('/checksum', adminRateLimiter, authMiddleware, (req, res) => {
  const relPath = req.query.path;
  if (!relPath || relPath.includes('..')) {
    return res.status(400).json({ error: 'Invalid path' });
  }

  const fullPath = path.join(__dirname, 'public', relPath);
  if (!fs.existsSync(fullPath) || fs.statSync(fullPath).isDirectory()) {
    return res.status(404).json({ error: 'File not found' });
  }

  const fileBuffer = fs.readFileSync(fullPath);
  const checksum = crypto.createHash('sha256').update(fileBuffer).digest('hex');
  res.json({ checksum });
});
adminRouter.get('/cluster-checksum', adminRateLimiter, authMiddleware, (req, res) => {
  const relPath = req.query.path;
  if (!relPath || relPath.includes('..')) {
    return res.status(400).json({ error: 'Invalid path' });
  }

  const fullPath = path.join(CLUSTER_ROOT, relPath);
  if (!fs.existsSync(fullPath) || fs.statSync(fullPath).isDirectory()) {
    return res.status(404).json({ error: 'File not found' });
  }

  const fileBuffer = fs.readFileSync(fullPath);
  const checksum = crypto.createHash('sha256').update(fileBuffer).digest('hex');
  res.json({ checksum });
});
adminRouter.post('/cluster-checksums', adminRateLimiter, authMiddleware, (req, res) => {
  const files = req.body.files;
  if (!Array.isArray(files)) {
    return res.status(400).json({ error: 'Expected array of file paths' });
  }

  const checksums = {};
  for (const relPath of files) {
    if (relPath.includes('..')) continue;

    const fullPath = path.join(CLUSTER_ROOT, relPath);
    if (fs.existsSync(fullPath) && fs.statSync(fullPath).isFile()) {
      const buffer = fs.readFileSync(fullPath);
      const hash = crypto.createHash('sha256').update(buffer).digest('hex');
      checksums[relPath] = hash;
    } else {
      checksums[relPath] = null;
    }
  }

  res.json(checksums);
});

adminRouter.get('/file-info', adminRateLimiter, authMiddleware, (req, res) => {
  const relPath = req.query.path;
  if (!relPath || relPath.includes('..')) {
    return res.status(400).json({ error: 'Invalid path' });
  }

  const fullPath = path.join(__dirname, 'public', relPath);
  if (!fs.existsSync(fullPath)) {
    return res.status(404).json({ error: 'File not found' });
  }

  const mimeType = mime.lookup(fullPath) || 'application/octet-stream';
  const extension = path.extname(fullPath).toLowerCase();

  res.json({ mimeType, extension });
});

adminRouter.post('/fetch-sidedish', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  const clusterPath = path.join('/contract/contract_fs/seed/state/sidedish_versions', version, 'sidedish.js');
  const localPath = path.join(__dirname, 'public', 'sidedish.js');

  try {
    if (!fs.existsSync(clusterPath)) {
      return res.status(404).json({ error: `sidedish.js version '${version}' not found in cluster.` });
    }

    fs.copyFileSync(clusterPath, localPath);

    res.json({ message: `sidedish.js version '${version}' has been fetched and applied locally.` });
  } catch (err) {
    console.error('[fetch-sidedish] Error:', err);
    res.status(500).json({ error: 'Failed to fetch sidedish.js: ' + err.message });
  }
});

adminRouter.post('/fetch-version', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  const versionPath = path.join('/contract/contract_fs/seed/state/versions', version);
  const localRoot = path.join(__dirname, 'public');

  try {
    if (!fs.existsSync(versionPath)) {
      return res.status(404).json({ error: 'Version folder not found in cluster.' });
    }

    const copyRecursive = (srcDir, destDir) => {
      const entries = fs.readdirSync(srcDir, { withFileTypes: true });

      for (const entry of entries) {
        const src = path.join(srcDir, entry.name);
        const dest = path.join(destDir, entry.name);

        if (entry.isDirectory()) {
          fs.mkdirSync(dest, { recursive: true });
          copyRecursive(src, dest);
        } else if (entry.isFile()) {
          fs.copyFileSync(src, dest);
        }
      }
    };

    copyRecursive(versionPath, localRoot);

    res.json({ message: `Files from version '${version}' have been fetched and applied locally.` });
  } catch (err) {
    console.error('[fetch-version] Error:', err);
    res.status(500).json({ error: 'Failed to fetch files from version: ' + err.message });
  }
});

adminRouter.get('/list-versions', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const raw = fs.readFileSync('/contract/cfg/hp.cfg', 'utf8');
    const cfg = JSON.parse(raw);
    const myPublicKey = cfg.node?.public_key;

    if (!myPublicKey) {
      return res.status(500).json({ error: 'Missing public key in hp.cfg' });
    }

    const liveSymlink = '/contract/contract_fs/seed/state/public';
    let liveVersion = null;
    if (fs.existsSync(liveSymlink)) {
      const target = fs.readlinkSync(liveSymlink);
      liveVersion = path.basename(target);
    }

    const versionsRoot = '/contract/contract_fs/seed/state/versions';
    const entries = fs.readdirSync(versionsRoot, { withFileTypes: true });
    const versionNames = entries.filter(d => d.isDirectory()).map(d => d.name);

    const votePath = '/contract/contract_fs/seed/state/votes.json';
    let votes = {};
    if (fs.existsSync(votePath)) {
      votes = JSON.parse(fs.readFileSync(votePath, 'utf8'));
    }

    const result = versionNames.map(name => {
      const voteEntry = votes[name] || {};
const goLiveVoters = voteEntry.goLive || [];
const deleteVoters = voteEntry.delete || [];

      return {
        name,
        votes: goLiveVoters.length,
        votedByMe: goLiveVoters.includes(myPublicKey),
        deleteVotes: deleteVoters.length,
        deleteVotedByMe: deleteVoters.includes(myPublicKey),
        isLive: name === liveVersion
      };
    });

    res.json(result);
  } catch (err) {
    console.error('[list-versions] Error:', err);
    res.status(500).json({ error: 'Failed to read version list' });
  }
});

adminRouter.post('/vote-delete', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');

    const client = await HotPocket.createClient(
      [HOTPOCKET_WS_URL],
      keyPair,
      { protocol: HotPocket.protocols.bson }
    );

    const connected = await client.connect();
    if (!connected) {
      return res.status(500).json({ error: 'Failed to connect to HotPocket cluster.' });
    }

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for contract output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const payload = BSON.serialize({ type: 'vote-delete', version });
    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected delete vote.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    if (response.type === 'success' || response.type === 'info') {
      return res.json({ message: response.message });
    } else {
      return res.status(500).json({ error: response.error || 'Unexpected contract error' });
    }

  } catch (err) {
    console.error('[vote-delete] failed:', err);
    return res.status(500).json({ error: err.message });
  }
});

const servePath = path.join(__dirname, 'public', 'live');
app.use(express.static(servePath));
adminRouter.get('/list-files', (req, res) => {
  const relPath = req.query.folder || '';
  const targetPath = path.join(__dirname, 'public', relPath);

  if (!fs.existsSync(targetPath)) {
    return res.status(404).json({ error: 'Folder not found' });
  }

  const items = fs.readdirSync(targetPath, { withFileTypes: true });
  const result = items.map(item => ({
    name: item.name,
    type: item.isDirectory() ? 'folder' : 'file'
  }));

  res.json(result);
});

adminRouter.post('/delete-files', adminRateLimiter, authMiddleware, (req, res) => {
  const files = req.body.files || [];
  files.forEach(file => {
    const filePath = path.join(__dirname, 'public', file);
    if (fs.existsSync(filePath)) fs.unlinkSync(filePath);
  });
  res.send({ message: 'Files deleted successfully' });
});

adminRouter.post('/create-folder', adminRateLimiter, authMiddleware, (req, res) => {
  let basePath = 'public';
  if (typeof req.body.base === 'string' && req.body.base.trim() !== '') {
    basePath = path.join('public', req.body.base);
  }
  const folderPath = path.join(__dirname, basePath, req.body.folder);
  fs.mkdirSync(folderPath, { recursive: true });
  res.send({ message: 'Folder created' });
});

adminRouter.post('/delete-folder', adminRateLimiter, authMiddleware, (req, res) => {
  if (!req.body.folder || req.body.folder.trim() === '') {
    return res.status(400).send({ error: 'Deleting the root public folder is not allowed' });
  }
  const folderPath = path.join(__dirname, 'public', req.body.folder);
  if (fs.existsSync(folderPath)) {
    fs.rmSync(folderPath, { recursive: true, force: true });
    res.send({ message: 'Folder deleted' });
  } else {
    res.status(404).send({ error: 'Folder not found' });
  }
});

adminRouter.get('/file', adminRateLimiter, authMiddleware, (req, res) => {
  const relPath = req.query.path;
  if (!relPath) return res.status(400).json({ error: 'Missing path' });

  const fullPath = path.join(__dirname, 'public', relPath);
  if (!fs.existsSync(fullPath)) return res.status(404).json({ error: 'File not found' });

  const content = fs.readFileSync(fullPath, 'utf-8');
  res.send({ content });
});

adminRouter.post('/file', adminRateLimiter, authMiddleware, (req, res) => {
  const { path: filePath, newPath, content = '' } = req.body;
  if (!filePath) return res.status(400).json({ error: 'Missing file path' });

  const baseDir = path.join(__dirname, 'public');
  const oldFull = path.join(baseDir, filePath);
  const finalFull = newPath ? path.join(baseDir, newPath) : oldFull;

  if (newPath && newPath !== filePath) {
    if (fs.existsSync(oldFull)) {
      fs.renameSync(oldFull, finalFull);
    }
  }

  fs.writeFileSync(finalFull, content, 'utf-8');
  res.send({ message: 'File saved successfully' });
});

adminRouter.get('/settings', (req, res) => {
  if (!fs.existsSync(settingsPath)) {
    return res.json({ useCluster: false });
  }
  const settings = JSON.parse(fs.readFileSync(settingsPath, 'utf-8'));
  res.json(settings);
});

adminRouter.post('/settings', adminRateLimiter, authMiddleware, (req, res) => {
  const { useCluster } = req.body;
  if (typeof useCluster !== 'boolean') {
    return res.status(400).json({ error: 'useCluster must be a boolean' });
  }
  fs.writeFileSync(settingsPath, JSON.stringify({ useCluster }, null, 2));
  res.json({ message: 'Settings updated' });
});

adminRouter.post('/sync-to-cluster', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    await syncLocalFilesToCluster();
    res.json({ message: 'Sync completed successfully.' });
  } catch (err) {
    console.error('Sync failed:', err);
    res.status(500).json({ error: 'Sync failed: ' + err.message });
  }
});

const clients = [];

app.get('/admin/sync-progress', (req, res) => {
  res.setHeader('Content-Type', 'text/event-stream');
  res.setHeader('Cache-Control', 'no-cache');
  res.setHeader('Connection', 'keep-alive');
  res.flushHeaders();

  clients.push(res);

  req.on('close', () => {
    const idx = clients.indexOf(res);
    if (idx !== -1) clients.splice(idx, 1);
  });
});

function broadcastProgress(message) {
  const data = `data: ${JSON.stringify({ message })}\n\n`;
  clients.forEach(res => res.write(data));
}

adminRouter.post('/go-live', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;
  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const keyPair = await HotPocket.generateKeys(null);
    const client = await HotPocket.createClient(
      [HOTPOCKET_WS_URL],
      keyPair,
      { protocol: HotPocket.protocols.bson }
    );

    const connected = await client.connect();
    if (!connected) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => {
        reject(new Error('Timeout waiting for contract output'));
      }, 10000);

      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const payload = BSON.serialize({ type: 'go-live', version });
    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
if (response.type === 'success' || response.type === 'info') {
  return res.json({ message: response.message });
} else {
  return res.status(500).json({ error: response.error || 'Unexpected contract error' });
}

  } catch (err) {
    console.error('[go-live] failed:', err);
    res.status(500).json({ error: err.message });
  }
});

app.use('/admin', adminRateLimiter, adminRouter);

async function syncLocalFilesToCluster() {
  const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
  const uploadSuffix = getUploadSuffix();
  const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
    protocol: HotPocket.protocols.bson
  });

  function report(msg) {
    console.log(msg);
    broadcastProgress(msg);  
  }

  if (!(await client.connect())) {
    report("Failed to connect to HotPocket instance.");
    return;
  }

  report("Connected to HotPocket cluster.");

  const localDir = path.join(__dirname, 'public');

  function listAllFiles(dir, base = '') {
    const entries = fs.readdirSync(dir, { withFileTypes: true });
    let files = [];

    for (const entry of entries) {
      const fullPath = path.join(dir, entry.name);
      const relativePath = path.join(base, entry.name);

      if (entry.isDirectory()) {
        files = files.concat(listAllFiles(fullPath, relativePath));
      } else if (entry.isFile()) {
        files.push(relativePath);
      }
    }

    return files;
  }

  const files = listAllFiles(localDir);
  const chunkSize = 40 * 64 * 1024; // 2,4mb or so

  for (const relPath of files) {
    const fullPath = path.join(localDir, relPath);
    const fileBuffer = fs.readFileSync(fullPath);
    const totalChunks = Math.ceil(fileBuffer.length / chunkSize);
    const normalizedRelPath = relPath.replace(/\\/g, '/');

    report(`Uploading ${normalizedRelPath} (${fileBuffer.length} bytes, ${totalChunks} chunks)`);

    for (let i = 0; i < totalChunks; i++) {
      const start = i * chunkSize;
      const end = Math.min(start + chunkSize, fileBuffer.length);
      const chunk = fileBuffer.slice(start, end);
      const chunkHex = chunk.toString('hex');

      const payload = {
        type: 'upload',
        filename: normalizedRelPath,
        chunkNo: i + 1,
        totalChunks,
        chunk: chunkHex,
        version: uploadSuffix
      };

      const contractInput = await client.submitContractInput(BSON.serialize(payload));
      const result = await contractInput.submissionStatus;

      if (result.status !== 'accepted') {
        report(`Chunk ${i + 1} of ${normalizedRelPath} was rejected.`);
        client.close();
        return;
      }

      report(`Chunk ${i + 1}/${totalChunks} sent for ${normalizedRelPath}`);
    }

    report(`Finished uploading ${normalizedRelPath}`);
  }

  client.close();
  report("Disconnected from HotPocket.");
}

const CLUSTER_ROOT = '/contract/contract_fs/seed/state';

adminRouter.get('/browse-cluster', adminRateLimiter, authMiddleware, (req, res) => {
  const result = {};

  function buildTree(dirPath) {
    const tree = {};
    const items = fs.readdirSync(dirPath, { withFileTypes: true });
    for (const item of items) {
      const fullPath = path.join(dirPath, item.name);
      if (item.isDirectory()) {
        tree[item.name] = buildTree(fullPath);
      } else {
        tree[item.name] = null; // File = leaf
      }
    }
    return tree;
  }

  try {
    const tree = buildTree(CLUSTER_ROOT);
    res.json(tree);
  } catch (err) {
    console.error('Failed to browse cluster:', err);
    res.status(500).json({ error: 'Cluster browse failed' });
  }
});

adminRouter.get('/read-cluster', adminRateLimiter, authMiddleware, (req, res) => {
  const relPath = req.query.path;
  if (!relPath || relPath.includes('..')) {
    console.warn('Invalid path attempt:', relPath);
    return res.status(400).json({ error: 'Invalid path' });
  }

  const fullPath = path.join(CLUSTER_ROOT, relPath);
  console.log('Full path requested:', fullPath);

  if (!fs.existsSync(fullPath)) {
    console.warn('File does not exist:', fullPath);
    return res.status(404).json({ error: 'File not found' });
  }

  if (fs.statSync(fullPath).isDirectory()) {
    console.warn('Attempted to read directory:', fullPath);
    return res.status(400).json({ error: 'Path is a directory' });
  }

  const mimeType = mime.lookup(fullPath) || 'application/octet-stream';
  const ext = path.extname(fullPath).toLowerCase();
  console.log(`MIME type: ${mimeType}, Extension: ${ext}`);

  const allowedMimeTypes = [
    'text/plain',
    'text/html',
    'text/css',
    'application/json',
    'application/javascript',
    'application/xml',
    'text/x-python',
    'text/x-c',
    'text/x-c++',
    'text/markdown',
    'text/x-sh',
    'application/x-httpd-php',
    'application/x-yaml',
    'application/x-sql',
  ];

  const allowedExtensions = [
    '.sh',
    '.cfg',
    '.override',
    '.conf',
    '.env',
    '.log',
    '.ini'
  ];

  if (!allowedMimeTypes.includes(mimeType) && !allowedExtensions.includes(ext)) {
    return res.status(415).json({ error: 'Unsupported Media Type for preview' });
  }

  res.setHeader('Content-Type', mimeType);
  res.sendFile(fullPath, err => {
    if (err) {
      console.error('Failed to send file:', err);
      res.status(500).json({ error: 'Internal server error' });
    }
  });
});

adminRouter.get('/download-cluster', authMiddleware, (req, res) => {
  const relPath = req.query.path;
  if (!relPath || relPath.includes('..')) {
    return res.status(400).json({ error: 'Invalid path' });
  }

  const fullPath = path.join(CLUSTER_ROOT, relPath);
  if (!fs.existsSync(fullPath) || fs.statSync(fullPath).isDirectory()) {
    return res.status(404).json({ error: 'File not found' });
  }

  res.download(fullPath, err => {
    if (err) {
      console.error('Failed to download file:', err);
      res.status(500).json({ error: 'Download error' });
    }
  });
});


















adminRouter.get('/my-api-vote', async (req, res) => {
  try {
    const raw = fs.readFileSync('/contract/cfg/hp.cfg', 'utf8');
    const cfg = JSON.parse(raw);
    const voterKey = cfg.node?.public_key;

    if (!voterKey) {
      return res.status(500).json({ error: 'Node public key not found in hp.cfg' });
    }

    const voteFilePath = '/contract/contract_fs/seed/state/api_votes.json';
    let votes = {};

    if (fs.existsSync(voteFilePath)) {
      votes = JSON.parse(fs.readFileSync(voteFilePath));
    }

    let votedVersion = null;
    const transformed = {};

    for (const [version, data] of Object.entries(votes)) {
      const goLive = data.voters || [];
      const deleteVote = data.delete || [];

      if (goLive.includes(voterKey)) votedVersion = version;

      transformed[version] = {
        votes: goLive.length,
        votedByMe: goLive.includes(voterKey),
        deleteVotes: deleteVote.length,
        deleteVotedByMe: deleteVote.includes(voterKey)
      };
    }

    return res.json({
      api: votedVersion,
      apiVotes: transformed
    });
  } catch (err) {
    console.error('[my-api-vote] Error:', err);
    return res.status(500).json({ error: 'Failed to retrieve api votes' });
  }
});

function writeHpCfgMultipleTimes(config, path, retries = 3, delay = 100) {
  for (let i = 0; i < retries; i++) {
    setTimeout(() => {
      fs.writeFileSync(path, JSON.stringify(config, null, 2), 'utf8');
      console.log(`[import-key] Write attempt ${i + 1} complete.`);
    }, i * delay);
  }
}

adminRouter.get('/list-api-versions', adminRateLimiter, authMiddleware, async (req, res) => {
  try {
    const raw = fs.readFileSync('/contract/cfg/hp.cfg', 'utf8');
    const cfg = JSON.parse(raw);
    const myPublicKey = cfg.node?.public_key;

    if (!myPublicKey) {
      return res.status(500).json({ error: 'Missing public key in hp.cfg' });
    }

    // Read live version flag from file
    const liveFlagPath = '/contract/contract_fs/seed/state/.live_api_version';
    let liveVersion = null;
    if (fs.existsSync(liveFlagPath)) {
      try {
        const liveRaw = fs.readFileSync(liveFlagPath, 'utf8').trim();
        if (liveRaw.length > 0) {
          liveVersion = liveRaw;
        }
      } catch (err) {
        console.warn('[list-api-versions] Failed to read .live_api_version:', err.message);
      }
    }

    // Read version folders
    const versionsRoot = '/contract/contract_fs/seed/state/api_versions';
    let versionNames = [];
    if (fs.existsSync(versionsRoot)) {
      const entries = fs.readdirSync(versionsRoot, { withFileTypes: true });
      versionNames = entries.filter(d => d.isDirectory()).map(d => d.name);
    }

    // Read votes
    const votePath = '/contract/contract_fs/seed/state/api_votes.json';
    let votes = {};
    if (fs.existsSync(votePath)) {
      try {
        votes = JSON.parse(fs.readFileSync(votePath, 'utf8'));
      } catch (err) {
        console.warn('[list-api-versions] Failed to parse api_votes.json:', err.message);
      }
    }

    // Assemble result
    const result = versionNames.map(name => {
      const voteEntry = votes[name] || {};
      const goLiveVoters = voteEntry.goLive || [];
      const deleteVoters = voteEntry.delete || [];

      return {
        name,
        votes: goLiveVoters.length,
        votedByMe: goLiveVoters.includes(myPublicKey),
        deleteVotes: deleteVoters.length,
        deleteVotedByMe: deleteVoters.includes(myPublicKey),
        isLive: name === liveVersion
      };
    });

    res.json(result);
  } catch (err) {
    console.error('[list-api-versions] Error:', err);
    res.status(500).json({ error: 'Failed to list API versions' });
  }
});



adminRouter.get('/read-api-cluster', adminRateLimiter, authMiddleware, (req, res) => {
  const clusterPath = '/contract/contract_fs/seed/state/dynamic-endpoints-data.json';

  if (!fs.existsSync(clusterPath)) {
    return res.status(404).json({ error: 'Cluster dynamic-endpoints-data.json not found.' });
  }

  try {
    const content = fs.readFileSync(clusterPath, 'utf8');
    res.json({ content });
  } catch (err) {
    console.error('[read-api-cluster] Error:', err.message);
    res.status(500).json({ error: 'Failed to read cluster dynamic-endpoints-data.json.' });
  }
});


adminRouter.get('/read-api-local', adminRateLimiter, authMiddleware, (req, res) => {
  const fullPath = path.join(__dirname, 'dynamic-endpoints-data.json');

  if (!fs.existsSync(fullPath)) {
    return res.status(404).json({ error: 'Local dynamic-endpoints-data.json not found.' });
  }

  try {
    const content = fs.readFileSync(fullPath, 'utf8');
    res.send({ content });
  } catch (err) {
    console.error('[read-api-local] Error:', err.message);
    res.status(500).json({ error: 'Failed to read local dynamic-endpoints-data.json.' });
  }
});

adminRouter.post('/save-api-local', adminRateLimiter, authMiddleware, (req, res) => {
  const { content } = req.body;
  const fullPath = path.join(__dirname, 'dynamic-endpoints-data.json');

  try {
    fs.writeFileSync(fullPath, content, 'utf8');
    res.send({ message: 'Local dynamic-endpoints-data.json saved.' });
  } catch (err) {
    console.error('[save-api-local] Error:', err.message);
    res.status(500).json({ error: 'Failed to save local dynamic-endpoints-data.json.' });
  }
});

adminRouter.post('/fetch-api', adminRateLimiter, authMiddleware, (req, res) => {
  const src = '/contract/contract_fs/seed/state/dynamic-endpoints-data.json';
  const dest = '/home/everweb/dynamic-endpoints-data.json';

  try {
    if (!fs.existsSync(src)) {
      return res.status(404).json({ error: 'dynamic-endpoints-data.json not found in cluster.' });
    }

    fs.copyFileSync(src, dest);
    res.json({ message: 'dynamic-endpoints-data.json copied from cluster to local seed path.' });
  } catch (err) {
    console.error('[fetch-api] Error:', err.message);
    res.status(500).json({ error: 'Failed to fetch dynamic-endpoints-data.json: ' + err.message });
  }
});

adminRouter.post('/promote-api', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;

  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    if (!(await client.connect())) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const payload = BSON.serialize({ type: 'api-go-live', version });

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected the input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    return res.json(response);
  } catch (err) {
    console.error('[promote-api] failed:', err);
    return res.status(500).json({ error: err.message });
  }
});

adminRouter.post('/push-api', adminRateLimiter, authMiddleware, async (req, res) => {
  const filePath = path.join(__dirname, 'dynamic-endpoints-data.json');

  if (!fs.existsSync(filePath)) {
    return res.status(404).json({ error: 'Local dynamic-endpoints-data.json not found.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    if (!(await client.connect())) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const fileBuffer = fs.readFileSync(filePath);
    const chunkSize = 1024 * 1024; // 1MB
    const totalChunks = Math.ceil(fileBuffer.length / chunkSize);
    const version = getUploadSuffix();

    for (let i = 0; i < totalChunks; i++) {
      const chunk = fileBuffer.slice(i * chunkSize, (i + 1) * chunkSize);
      const payload = BSON.serialize({
        type: 'api-upload',
        version,
        chunkNo: i + 1,
        totalChunks,
        chunk: chunk.toString('hex')
      });

      const submission = await client.submitContractInput(payload);
      const status = await submission.submissionStatus;

      if (status.status !== 'accepted') {
        client.close();
        return res.status(500).json({ error: `Contract rejected chunk ${i + 1}` });
      }

      const result = await new Promise((resolve, reject) => {
        const timeout = setTimeout(() => reject(new Error('Timeout waiting for contract response')), 10000);
        client.on(HotPocket.events.contractOutput, output => {
          clearTimeout(timeout);
          resolve(output);
        });
      });

      const decoded = BSON.deserialize(result.outputs[0]);
      if (decoded.type === 'error') {
        client.close();
        return res.status(500).json({ error: decoded.error });
      }
    }

    client.close();
    return res.json({
      success: true,
      version,
      message: `Uploaded dynamic-endpoints-data.json to api_versions/${version}/`
    });

  } catch (err) {
    console.error('[push-api] Upload failed:', err);
    return res.status(500).json({ error: 'Upload failed: ' + err.message });
  }
});


adminRouter.post('/vote-delete-api', adminRateLimiter, authMiddleware, async (req, res) => {
  const { version } = req.body;
  if (!version || typeof version !== 'string') {
    return res.status(400).json({ error: 'Missing or invalid version name.' });
  }

  try {
    const keyPair = loadHpKeypair('/contract/cfg/hp.cfg');
    const client = await HotPocket.createClient([HOTPOCKET_WS_URL], keyPair, {
      protocol: HotPocket.protocols.bson
    });

    if (!(await client.connect())) {
      return res.status(500).json({ error: 'Failed to connect to cluster.' });
    }

    const payload = BSON.serialize({ type: 'api-vote-delete', version });

    const waitForOutput = new Promise((resolve, reject) => {
      const timeout = setTimeout(() => reject(new Error('Timeout waiting for output')), 10000);
      client.on(HotPocket.events.contractOutput, result => {
        clearTimeout(timeout);
        resolve(result);
      });
    });

    const submission = await client.submitContractInput(payload);
    const status = await submission.submissionStatus;

    if (status.status !== 'accepted') {
      client.close();
      return res.status(500).json({ error: 'Contract rejected input.' });
    }

    const result = await waitForOutput;
    client.close();

    const response = BSON.deserialize(result.outputs[0]);
    return res.json(response);
  } catch (err) {
    console.error('[vote-delete-api] failed:', err);
    return res.status(500).json({ error: err.message });
  }
});










const PORTS = [36525, 36527, 36529];

// Optionally read EXTERNAL_GPTCP1_PORT from env.vars
const envFile = '/contract/env.vars';
if (fs.existsSync(envFile)) {
    const content = fs.readFileSync(envFile, 'utf-8');
    const lines = content.split('\n');

    lines.forEach(line => {
        const trimmed = line.trim();
        if (trimmed.startsWith('EXTERNAL_GPTCP1_PORT=')) {
            const [, value] = trimmed.split('=');
            const externalPort = parseInt(value, 10);
            if (!isNaN(externalPort) && !PORTS.includes(externalPort)) {
                PORTS.push(externalPort);
                console.log(`Added external port from env.vars: ${externalPort}`);
            }
        }
    });
}


PORTS.forEach(port => {
    const server = https.createServer(sslOptions, app);
    server.listen(port, () => {
        console.log(`HTTPS Server running at https://localhost:${port}`);
    });
    server.on('error', (err) => {
        console.error(`Failed to start server on port ${port}:`, err.message);
    });
});

morefunctions.json

Code: Select all

[]
package.json

Code: Select all

{
    "name": "evernode-webcbuilder",
    "version": "1.0.0",
    "description": "An Evernode Webcluster builder",
    "main": "index.js",
    "type": "commonjs",
    "scripts": {
        "start": "node index.js"
    },
    "author": "",
    "funding": "https://xumm.app/detect/request:rsmYqAFi4hQtTY6k6S3KPJZh7axhUwxT31",
    "license": "MIT",
    "dependencies": {
        "@vercel/ncc": "0.34.0",
        "axios": "^1.9.0",
        "basic-auth": "^2.0.1",
        "body-parser": "^1.20.2",
        "bson": "6.4.0",
        "chokidar": "^3.5.3",
        "cors": "^2.8.5",
        "express": "^4.18.4",
        "express-basic-auth": "^1.2.0",
        "file-type": "^17.1.6",
        "fs-extra": "^11.2.0",
        "hotpocket-js-client": "",
        "hotpocket-nodejs-contract": "^0.7.3",
        "mime-types": "^2.1.35",
        "express-rate-limit" : "",
        "multer": ""
    }
}
package-lock.json

Code: Select all

{
    "name": "evernode-webcbuilder",
    "version": "1.0.0",
    "lockfileVersion": 3,
    "requires": true,
    "packages": {
        "": {
            "name": "evernode-webcbuilder",
            "version": "1.0.0",
            "license": "MIT",
            "dependencies": {
                "@vercel/ncc": "0.34.0",
                "axios": "^1.9.0",
                "basic-auth": "^2.0.1",
                "body-parser": "^1.20.2",
                "bson": "6.4.0",
                "chokidar": "^3.5.3",
                "cors": "^2.8.5",
                "express": "^4.18.4",
                "express-basic-auth": "^1.2.0",
                "express-rate-limit": "",
                "file-type": "^17.1.6",
                "fs-extra": "^11.2.0",
                "hotpocket-js-client": "",
                "hotpocket-nodejs-contract": "^0.7.3",
                "mime-types": "^2.1.35",
                "multer": ""
            },
            "funding": {
                "url": "https://xumm.app/detect/request:rsmYqAFi4hQtTY6k6S3KPJZh7axhUwxT31"
            }
        },
        "node_modules/@tokenizer/token": {
            "version": "0.3.0",
            "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz",
            "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A=="
        },
        "node_modules/@vercel/ncc": {
            "version": "0.34.0",
            "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.34.0.tgz",
            "integrity": "sha512-G9h5ZLBJ/V57Ou9vz5hI8pda/YQX5HQszCs3AmIus3XzsmRn/0Ptic5otD3xVST8QLKk7AMk7AqpsyQGN7MZ9A==",
            "bin": {
                "ncc": "dist/ncc/cli.js"
            }
        },
        "node_modules/abort-controller": {
            "version": "3.0.0",
            "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz",
            "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==",
            "dependencies": {
                "event-target-shim": "^5.0.0"
            },
            "engines": {
                "node": ">=6.5"
            }
        },
        "node_modules/accepts": {
            "version": "1.3.8",
            "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz",
            "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==",
            "dependencies": {
                "mime-types": "~2.1.34",
                "negotiator": "0.6.3"
            },
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/anymatch": {
            "version": "3.1.3",
            "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
            "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
            "dependencies": {
                "normalize-path": "^3.0.0",
                "picomatch": "^2.0.4"
            },
            "engines": {
                "node": ">= 8"
            }
        },
        "node_modules/append-field": {
            "version": "1.0.0",
            "resolved": "https://registry.npmjs.org/append-field/-/append-field-1.0.0.tgz",
            "integrity": "sha512-klpgFSWLW1ZEs8svjfb7g4qWY0YS5imI82dTg+QahUvJ8YqAY0P10Uk8tTyh9ZGuYEZEMaeJYCF5BFuX552hsw=="
        },
        "node_modules/array-flatten": {
            "version": "1.1.1",
            "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
            "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg=="
        },
        "node_modules/asynckit": {
            "version": "0.4.0",
            "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
            "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q=="
        },
        "node_modules/axios": {
            "version": "1.9.0",
            "resolved": "https://registry.npmjs.org/axios/-/axios-1.9.0.tgz",
            "integrity": "sha512-re4CqKTJaURpzbLHtIi6XpDv20/CnpXOtjRY5/CU32L8gU8ek9UIivcfvSWvmKEngmVbrUtPpdDwWDWL7DNHvg==",
            "dependencies": {
                "follow-redirects": "^1.15.6",
                "form-data": "^4.0.0",
                "proxy-from-env": "^1.1.0"
            }
        },
        "node_modules/base64-js": {
            "version": "1.5.1",
            "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz",
            "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==",
            "funding": [
                {
                    "type": "github",
                    "url": "https://github.com/sponsors/feross"
                },
                {
                    "type": "patreon",
                    "url": "https://www.patreon.com/feross"
                },
                {
                    "type": "consulting",
                    "url": "https://feross.org/support"
                }
            ]
        },
        "node_modules/basic-auth": {
            "version": "2.0.1",
            "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz",
            "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==",
            "dependencies": {
                "safe-buffer": "5.1.2"
            },
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/basic-auth/node_modules/safe-buffer": {
            "version": "5.1.2",
            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
        },
        "node_modules/binary-extensions": {
            "version": "2.3.0",
            "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
            "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
            "engines": {
                "node": ">=8"
            },
            "funding": {
                "url": "https://github.com/sponsors/sindresorhus"
            }
        },
        "node_modules/blake3": {
            "version": "2.1.4",
            "resolved": "https://registry.npmjs.org/blake3/-/blake3-2.1.4.tgz",
            "integrity": "sha512-70hmx0lPd6zmtNwxPT4/1P0pqaEUlTJ0noUBvCXPLfMpN0o8PPaK3q7ZlpRIyhrqcXxeMAJSowNm/L9oi/x1XA==",
            "hasInstallScript": true
        },
        "node_modules/body-parser": {
            "version": "1.20.3",
            "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.3.tgz",
            "integrity": "sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==",
            "dependencies": {
                "bytes": "3.1.2",
                "content-type": "~1.0.5",
                "debug": "2.6.9",
                "depd": "2.0.0",
                "destroy": "1.2.0",
                "http-errors": "2.0.0",
                "iconv-lite": "0.4.24",
                "on-finished": "2.4.1",
                "qs": "6.13.0",
                "raw-body": "2.5.2",
                "type-is": "~1.6.18",
                "unpipe": "1.0.0"
            },
            "engines": {
                "node": ">= 0.8",
                "npm": "1.2.8000 || >= 1.4.16"
            }
        },
        "node_modules/braces": {
            "version": "3.0.3",
            "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
            "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
            "dependencies": {
                "fill-range": "^7.1.1"
            },
            "engines": {
                "node": ">=8"
            }
        },
        "node_modules/bson": {
            "version": "6.4.0",
            "resolved": "https://registry.npmjs.org/bson/-/bson-6.4.0.tgz",
            "integrity": "sha512-6/gSSEdbkuFlSb+ufj5jUSU4+wo8xQOwm2bDSqwmxiPE17JTpsP63eAwoN8iF8Oy4gJYj+PAL3zdRCTdaw5Y1g==",
            "deprecated": "a critical bug affecting zSeries s390x big-endian systems is fixed in bson@6.5.0",
            "engines": {
                "node": ">=16.20.1"
            }
        },
        "node_modules/buffer": {
            "version": "5.7.1",
            "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz",
            "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==",
            "funding": [
                {
                    "type": "github",
                    "url": "https://github.com/sponsors/feross"
                },
                {
                    "type": "patreon",
                    "url": "https://www.patreon.com/feross"
                },
                {
                    "type": "consulting",
                    "url": "https://feross.org/support"
                }
            ],
            "dependencies": {
                "base64-js": "^1.3.1",
                "ieee754": "^1.1.13"
            }
        },
        "node_modules/buffer-from": {
            "version": "1.1.2",
            "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz",
            "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
        },
        "node_modules/busboy": {
            "version": "1.6.0",
            "resolved": "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz",
            "integrity": "sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==",
            "dependencies": {
                "streamsearch": "^1.1.0"
            },
            "engines": {
                "node": ">=10.16.0"
            }
        },
        "node_modules/bytes": {
            "version": "3.1.2",
            "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz",
            "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/call-bind-apply-helpers": {
            "version": "1.0.2",
            "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz",
            "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==",
            "dependencies": {
                "es-errors": "^1.3.0",
                "function-bind": "^1.1.2"
            },
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/call-bound": {
            "version": "1.0.4",
            "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz",
            "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==",
            "dependencies": {
                "call-bind-apply-helpers": "^1.0.2",
                "get-intrinsic": "^1.3.0"
            },
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/chokidar": {
            "version": "3.6.0",
            "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
            "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
            "dependencies": {
                "anymatch": "~3.1.2",
                "braces": "~3.0.2",
                "glob-parent": "~5.1.2",
                "is-binary-path": "~2.1.0",
                "is-glob": "~4.0.1",
                "normalize-path": "~3.0.0",
                "readdirp": "~3.6.0"
            },
            "engines": {
                "node": ">= 8.10.0"
            },
            "funding": {
                "url": "https://paulmillr.com/funding/"
            },
            "optionalDependencies": {
                "fsevents": "~2.3.2"
            }
        },
        "node_modules/combined-stream": {
            "version": "1.0.8",
            "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
            "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
            "dependencies": {
                "delayed-stream": "~1.0.0"
            },
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/concat-stream": {
            "version": "1.6.2",
            "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz",
            "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==",
            "engines": [
                "node >= 0.8"
            ],
            "dependencies": {
                "buffer-from": "^1.0.0",
                "inherits": "^2.0.3",
                "readable-stream": "^2.2.2",
                "typedarray": "^0.0.6"
            }
        },
        "node_modules/content-disposition": {
            "version": "0.5.4",
            "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz",
            "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==",
            "dependencies": {
                "safe-buffer": "5.2.1"
            },
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/content-type": {
            "version": "1.0.5",
            "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz",
            "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/cookie": {
            "version": "0.7.1",
            "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.1.tgz",
            "integrity": "sha512-6DnInpx7SJ2AK3+CTUE/ZM0vWTUboZCegxhC2xiIydHR9jNuTAASBrfEpHhiGOZw/nX51bHt6YQl8jsGo4y/0w==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/cookie-signature": {
            "version": "1.0.6",
            "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz",
            "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ=="
        },
        "node_modules/core-util-is": {
            "version": "1.0.3",
            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
            "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
        },
        "node_modules/cors": {
            "version": "2.8.5",
            "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
            "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
            "dependencies": {
                "object-assign": "^4",
                "vary": "^1"
            },
            "engines": {
                "node": ">= 0.10"
            }
        },
        "node_modules/debug": {
            "version": "2.6.9",
            "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
            "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
            "dependencies": {
                "ms": "2.0.0"
            }
        },
        "node_modules/delayed-stream": {
            "version": "1.0.0",
            "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
            "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==",
            "engines": {
                "node": ">=0.4.0"
            }
        },
        "node_modules/depd": {
            "version": "2.0.0",
            "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
            "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/destroy": {
            "version": "1.2.0",
            "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz",
            "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==",
            "engines": {
                "node": ">= 0.8",
                "npm": "1.2.8000 || >= 1.4.16"
            }
        },
        "node_modules/dunder-proto": {
            "version": "1.0.1",
            "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz",
            "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==",
            "dependencies": {
                "call-bind-apply-helpers": "^1.0.1",
                "es-errors": "^1.3.0",
                "gopd": "^1.2.0"
            },
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/ee-first": {
            "version": "1.1.1",
            "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz",
            "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow=="
        },
        "node_modules/encodeurl": {
            "version": "2.0.0",
            "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz",
            "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/es-define-property": {
            "version": "1.0.1",
            "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz",
            "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==",
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/es-errors": {
            "version": "1.3.0",
            "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz",
            "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==",
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/es-object-atoms": {
            "version": "1.1.1",
            "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
            "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
            "dependencies": {
                "es-errors": "^1.3.0"
            },
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/es-set-tostringtag": {
            "version": "2.1.0",
            "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz",
            "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==",
            "dependencies": {
                "es-errors": "^1.3.0",
                "get-intrinsic": "^1.2.6",
                "has-tostringtag": "^1.0.2",
                "hasown": "^2.0.2"
            },
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/escape-html": {
            "version": "1.0.3",
            "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
            "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow=="
        },
        "node_modules/etag": {
            "version": "1.8.1",
            "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz",
            "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/event-target-shim": {
            "version": "5.0.1",
            "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz",
            "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==",
            "engines": {
                "node": ">=6"
            }
        },
        "node_modules/events": {
            "version": "3.3.0",
            "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz",
            "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==",
            "engines": {
                "node": ">=0.8.x"
            }
        },
        "node_modules/express": {
            "version": "4.21.2",
            "resolved": "https://registry.npmjs.org/express/-/express-4.21.2.tgz",
            "integrity": "sha512-28HqgMZAmih1Czt9ny7qr6ek2qddF4FclbMzwhCREB6OFfH+rXAnuNCwo1/wFvrtbgsQDb4kSbX9de9lFbrXnA==",
            "dependencies": {
                "accepts": "~1.3.8",
                "array-flatten": "1.1.1",
                "body-parser": "1.20.3",
                "content-disposition": "0.5.4",
                "content-type": "~1.0.4",
                "cookie": "0.7.1",
                "cookie-signature": "1.0.6",
                "debug": "2.6.9",
                "depd": "2.0.0",
                "encodeurl": "~2.0.0",
                "escape-html": "~1.0.3",
                "etag": "~1.8.1",
                "finalhandler": "1.3.1",
                "fresh": "0.5.2",
                "http-errors": "2.0.0",
                "merge-descriptors": "1.0.3",
                "methods": "~1.1.2",
                "on-finished": "2.4.1",
                "parseurl": "~1.3.3",
                "path-to-regexp": "0.1.12",
                "proxy-addr": "~2.0.7",
                "qs": "6.13.0",
                "range-parser": "~1.2.1",
                "safe-buffer": "5.2.1",
                "send": "0.19.0",
                "serve-static": "1.16.2",
                "setprototypeof": "1.2.0",
                "statuses": "2.0.1",
                "type-is": "~1.6.18",
                "utils-merge": "1.0.1",
                "vary": "~1.1.2"
            },
            "engines": {
                "node": ">= 0.10.0"
            },
            "funding": {
                "type": "opencollective",
                "url": "https://opencollective.com/express"
            }
        },
        "node_modules/express-basic-auth": {
            "version": "1.2.1",
            "resolved": "https://registry.npmjs.org/express-basic-auth/-/express-basic-auth-1.2.1.tgz",
            "integrity": "sha512-L6YQ1wQ/mNjVLAmK3AG1RK6VkokA1BIY6wmiH304Xtt/cLTps40EusZsU1Uop+v9lTDPxdtzbFmdXfFO3KEnwA==",
            "dependencies": {
                "basic-auth": "^2.0.1"
            }
        },
        "node_modules/express-rate-limit": {
            "version": "7.5.0",
            "resolved": "https://registry.npmjs.org/express-rate-limit/-/express-rate-limit-7.5.0.tgz",
            "integrity": "sha512-eB5zbQh5h+VenMPM3fh+nw1YExi5nMr6HUCR62ELSP11huvxm/Uir1H1QEyTkk5QX6A58pX6NmaTMceKZ0Eodg==",
            "engines": {
                "node": ">= 16"
            },
            "funding": {
                "url": "https://github.com/sponsors/express-rate-limit"
            },
            "peerDependencies": {
                "express": "^4.11 || 5 || ^5.0.0-beta.1"
            }
        },
        "node_modules/file-type": {
            "version": "17.1.6",
            "resolved": "https://registry.npmjs.org/file-type/-/file-type-17.1.6.tgz",
            "integrity": "sha512-hlDw5Ev+9e883s0pwUsuuYNu4tD7GgpUnOvykjv1Gya0ZIjuKumthDRua90VUn6/nlRKAjcxLUnHNTIUWwWIiw==",
            "dependencies": {
                "readable-web-to-node-stream": "^3.0.2",
                "strtok3": "^7.0.0-alpha.9",
                "token-types": "^5.0.0-alpha.2"
            },
            "engines": {
                "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
            },
            "funding": {
                "url": "https://github.com/sindresorhus/file-type?sponsor=1"
            }
        },
        "node_modules/fill-range": {
            "version": "7.1.1",
            "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
            "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
            "dependencies": {
                "to-regex-range": "^5.0.1"
            },
            "engines": {
                "node": ">=8"
            }
        },
        "node_modules/finalhandler": {
            "version": "1.3.1",
            "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.1.tgz",
            "integrity": "sha512-6BN9trH7bp3qvnrRyzsBz+g3lZxTNZTbVO2EV1CS0WIcDbawYVdYvGflME/9QP0h0pYlCDBCTjYa9nZzMDpyxQ==",
            "dependencies": {
                "debug": "2.6.9",
                "encodeurl": "~2.0.0",
                "escape-html": "~1.0.3",
                "on-finished": "2.4.1",
                "parseurl": "~1.3.3",
                "statuses": "2.0.1",
                "unpipe": "~1.0.0"
            },
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/follow-redirects": {
            "version": "1.15.9",
            "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz",
            "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==",
            "funding": [
                {
                    "type": "individual",
                    "url": "https://github.com/sponsors/RubenVerborgh"
                }
            ],
            "engines": {
                "node": ">=4.0"
            },
            "peerDependenciesMeta": {
                "debug": {
                    "optional": true
                }
            }
        },
        "node_modules/form-data": {
            "version": "4.0.2",
            "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.2.tgz",
            "integrity": "sha512-hGfm/slu0ZabnNt4oaRZ6uREyfCj6P4fT/n6A1rGV+Z0VdGXjfOhVUpkn6qVQONHGIFwmveGXyDs75+nr6FM8w==",
            "dependencies": {
                "asynckit": "^0.4.0",
                "combined-stream": "^1.0.8",
                "es-set-tostringtag": "^2.1.0",
                "mime-types": "^2.1.12"
            },
            "engines": {
                "node": ">= 6"
            }
        },
        "node_modules/forwarded": {
            "version": "0.2.0",
            "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz",
            "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/fresh": {
            "version": "0.5.2",
            "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
            "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/fs-extra": {
            "version": "11.3.0",
            "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.0.tgz",
            "integrity": "sha512-Z4XaCL6dUDHfP/jT25jJKMmtxvuwbkrD1vNSMFlo9lNLY2c5FHYSQgHPRZUjAB26TpDEoW9HCOgplrdbaPV/ew==",
            "dependencies": {
                "graceful-fs": "^4.2.0",
                "jsonfile": "^6.0.1",
                "universalify": "^2.0.0"
            },
            "engines": {
                "node": ">=14.14"
            }
        },
        "node_modules/fsevents": {
            "version": "2.3.3",
            "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
            "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
            "hasInstallScript": true,
            "optional": true,
            "os": [
                "darwin"
            ],
            "engines": {
                "node": "^8.16.0 || ^10.6.0 || >=11.0.0"
            }
        },
        "node_modules/function-bind": {
            "version": "1.1.2",
            "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz",
            "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==",
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/get-intrinsic": {
            "version": "1.3.0",
            "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz",
            "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==",
            "dependencies": {
                "call-bind-apply-helpers": "^1.0.2",
                "es-define-property": "^1.0.1",
                "es-errors": "^1.3.0",
                "es-object-atoms": "^1.1.1",
                "function-bind": "^1.1.2",
                "get-proto": "^1.0.1",
                "gopd": "^1.2.0",
                "has-symbols": "^1.1.0",
                "hasown": "^2.0.2",
                "math-intrinsics": "^1.1.0"
            },
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/get-proto": {
            "version": "1.0.1",
            "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz",
            "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==",
            "dependencies": {
                "dunder-proto": "^1.0.1",
                "es-object-atoms": "^1.0.0"
            },
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/glob-parent": {
            "version": "5.1.2",
            "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
            "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
            "dependencies": {
                "is-glob": "^4.0.1"
            },
            "engines": {
                "node": ">= 6"
            }
        },
        "node_modules/gopd": {
            "version": "1.2.0",
            "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz",
            "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==",
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/graceful-fs": {
            "version": "4.2.11",
            "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
            "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ=="
        },
        "node_modules/has-symbols": {
            "version": "1.1.0",
            "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz",
            "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==",
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/has-tostringtag": {
            "version": "1.0.2",
            "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz",
            "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==",
            "dependencies": {
                "has-symbols": "^1.0.3"
            },
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/hasown": {
            "version": "2.0.2",
            "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz",
            "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==",
            "dependencies": {
                "function-bind": "^1.1.2"
            },
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/hotpocket-js-client": {
            "version": "0.5.7",
            "resolved": "https://registry.npmjs.org/hotpocket-js-client/-/hotpocket-js-client-0.5.7.tgz",
            "integrity": "sha512-s30I112Cf+H5rkbyGp3m2krIqyWOBp/3Dxip93m7lr9cqjWdTmksf13Ve4FcAip/VfKq+Jc5XN055QAau+a3gw==",
            "dependencies": {
                "blake3": "2.1.4",
                "bson": "4.5.3",
                "libsodium-wrappers": "0.7.9",
                "ws": "8.18.0"
            }
        },
        "node_modules/hotpocket-js-client/node_modules/bson": {
            "version": "4.5.3",
            "resolved": "https://registry.npmjs.org/bson/-/bson-4.5.3.tgz",
            "integrity": "sha512-qVX7LX79Mtj7B3NPLzCfBiCP6RAsjiV8N63DjlaVVpZW+PFoDTxQ4SeDbSpcqgE6mXksM5CAwZnXxxxn/XwC0g==",
            "dependencies": {
                "buffer": "^5.6.0"
            },
            "engines": {
                "node": ">=6.9.0"
            }
        },
        "node_modules/hotpocket-nodejs-contract": {
            "version": "0.7.4",
            "resolved": "https://registry.npmjs.org/hotpocket-nodejs-contract/-/hotpocket-nodejs-contract-0.7.4.tgz",
            "integrity": "sha512-rLCZm1bWoqV5j+xUFbnWF6XWurO5D43dus9h4SRftusO52n4GOdMsHmFIW7kezXGsHCre9iLzFl0R33aZ9/nSg=="
        },
        "node_modules/http-errors": {
            "version": "2.0.0",
            "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz",
            "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==",
            "dependencies": {
                "depd": "2.0.0",
                "inherits": "2.0.4",
                "setprototypeof": "1.2.0",
                "statuses": "2.0.1",
                "toidentifier": "1.0.1"
            },
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/iconv-lite": {
            "version": "0.4.24",
            "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
            "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==",
            "dependencies": {
                "safer-buffer": ">= 2.1.2 < 3"
            },
            "engines": {
                "node": ">=0.10.0"
            }
        },
        "node_modules/ieee754": {
            "version": "1.2.1",
            "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz",
            "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==",
            "funding": [
                {
                    "type": "github",
                    "url": "https://github.com/sponsors/feross"
                },
                {
                    "type": "patreon",
                    "url": "https://www.patreon.com/feross"
                },
                {
                    "type": "consulting",
                    "url": "https://feross.org/support"
                }
            ]
        },
        "node_modules/inherits": {
            "version": "2.0.4",
            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
        },
        "node_modules/ipaddr.js": {
            "version": "1.9.1",
            "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz",
            "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==",
            "engines": {
                "node": ">= 0.10"
            }
        },
        "node_modules/is-binary-path": {
            "version": "2.1.0",
            "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
            "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
            "dependencies": {
                "binary-extensions": "^2.0.0"
            },
            "engines": {
                "node": ">=8"
            }
        },
        "node_modules/is-extglob": {
            "version": "2.1.1",
            "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
            "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
            "engines": {
                "node": ">=0.10.0"
            }
        },
        "node_modules/is-glob": {
            "version": "4.0.3",
            "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
            "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
            "dependencies": {
                "is-extglob": "^2.1.1"
            },
            "engines": {
                "node": ">=0.10.0"
            }
        },
        "node_modules/is-number": {
            "version": "7.0.0",
            "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
            "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
            "engines": {
                "node": ">=0.12.0"
            }
        },
        "node_modules/isarray": {
            "version": "1.0.0",
            "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
            "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
        },
        "node_modules/jsonfile": {
            "version": "6.1.0",
            "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
            "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
            "dependencies": {
                "universalify": "^2.0.0"
            },
            "optionalDependencies": {
                "graceful-fs": "^4.1.6"
            }
        },
        "node_modules/libsodium": {
            "version": "0.7.15",
            "resolved": "https://registry.npmjs.org/libsodium/-/libsodium-0.7.15.tgz",
            "integrity": "sha512-sZwRknt/tUpE2AwzHq3jEyUU5uvIZHtSssktXq7owd++3CSgn8RGrv6UZJJBpP7+iBghBqe7Z06/2M31rI2NKw=="
        },
        "node_modules/libsodium-wrappers": {
            "version": "0.7.9",
            "resolved": "https://registry.npmjs.org/libsodium-wrappers/-/libsodium-wrappers-0.7.9.tgz",
            "integrity": "sha512-9HaAeBGk1nKTRFRHkt7nzxqCvnkWTjn1pdjKgcUnZxj0FyOP4CnhgFhMdrFfgNsukijBGyBLpP2m2uKT1vuWhQ==",
            "dependencies": {
                "libsodium": "^0.7.0"
            }
        },
        "node_modules/math-intrinsics": {
            "version": "1.1.0",
            "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz",
            "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==",
            "engines": {
                "node": ">= 0.4"
            }
        },
        "node_modules/media-typer": {
            "version": "0.3.0",
            "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz",
            "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/merge-descriptors": {
            "version": "1.0.3",
            "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz",
            "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==",
            "funding": {
                "url": "https://github.com/sponsors/sindresorhus"
            }
        },
        "node_modules/methods": {
            "version": "1.1.2",
            "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
            "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/mime": {
            "version": "1.6.0",
            "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz",
            "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==",
            "bin": {
                "mime": "cli.js"
            },
            "engines": {
                "node": ">=4"
            }
        },
        "node_modules/mime-db": {
            "version": "1.52.0",
            "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz",
            "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/mime-types": {
            "version": "2.1.35",
            "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
            "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==",
            "dependencies": {
                "mime-db": "1.52.0"
            },
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/minimist": {
            "version": "1.2.8",
            "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz",
            "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==",
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/mkdirp": {
            "version": "0.5.6",
            "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz",
            "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==",
            "dependencies": {
                "minimist": "^1.2.6"
            },
            "bin": {
                "mkdirp": "bin/cmd.js"
            }
        },
        "node_modules/ms": {
            "version": "2.0.0",
            "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
            "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="
        },
        "node_modules/multer": {
            "version": "1.4.5-lts.2",
            "resolved": "https://registry.npmjs.org/multer/-/multer-1.4.5-lts.2.tgz",
            "integrity": "sha512-VzGiVigcG9zUAoCNU+xShztrlr1auZOlurXynNvO9GiWD1/mTBbUljOKY+qMeazBqXgRnjzeEgJI/wyjJUHg9A==",
            "dependencies": {
                "append-field": "^1.0.0",
                "busboy": "^1.0.0",
                "concat-stream": "^1.5.2",
                "mkdirp": "^0.5.4",
                "object-assign": "^4.1.1",
                "type-is": "^1.6.4",
                "xtend": "^4.0.0"
            },
            "engines": {
                "node": ">= 6.0.0"
            }
        },
        "node_modules/negotiator": {
            "version": "0.6.3",
            "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
            "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/normalize-path": {
            "version": "3.0.0",
            "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
            "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
            "engines": {
                "node": ">=0.10.0"
            }
        },
        "node_modules/object-assign": {
            "version": "4.1.1",
            "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
            "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
            "engines": {
                "node": ">=0.10.0"
            }
        },
        "node_modules/object-inspect": {
            "version": "1.13.4",
            "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz",
            "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==",
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/on-finished": {
            "version": "2.4.1",
            "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
            "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==",
            "dependencies": {
                "ee-first": "1.1.1"
            },
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/parseurl": {
            "version": "1.3.3",
            "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
            "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/path-to-regexp": {
            "version": "0.1.12",
            "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.12.tgz",
            "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ=="
        },
        "node_modules/peek-readable": {
            "version": "5.4.2",
            "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-5.4.2.tgz",
            "integrity": "sha512-peBp3qZyuS6cNIJ2akRNG1uo1WJ1d0wTxg/fxMdZ0BqCVhx242bSFHM9eNqflfJVS9SsgkzgT/1UgnsurBOTMg==",
            "engines": {
                "node": ">=14.16"
            },
            "funding": {
                "type": "github",
                "url": "https://github.com/sponsors/Borewit"
            }
        },
        "node_modules/picomatch": {
            "version": "2.3.1",
            "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
            "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
            "engines": {
                "node": ">=8.6"
            },
            "funding": {
                "url": "https://github.com/sponsors/jonschlinkert"
            }
        },
        "node_modules/process": {
            "version": "0.11.10",
            "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
            "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==",
            "engines": {
                "node": ">= 0.6.0"
            }
        },
        "node_modules/process-nextick-args": {
            "version": "2.0.1",
            "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
            "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
        },
        "node_modules/proxy-addr": {
            "version": "2.0.7",
            "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
            "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==",
            "dependencies": {
                "forwarded": "0.2.0",
                "ipaddr.js": "1.9.1"
            },
            "engines": {
                "node": ">= 0.10"
            }
        },
        "node_modules/proxy-from-env": {
            "version": "1.1.0",
            "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz",
            "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="
        },
        "node_modules/qs": {
            "version": "6.13.0",
            "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz",
            "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==",
            "dependencies": {
                "side-channel": "^1.0.6"
            },
            "engines": {
                "node": ">=0.6"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/range-parser": {
            "version": "1.2.1",
            "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
            "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==",
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/raw-body": {
            "version": "2.5.2",
            "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz",
            "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==",
            "dependencies": {
                "bytes": "3.1.2",
                "http-errors": "2.0.0",
                "iconv-lite": "0.4.24",
                "unpipe": "1.0.0"
            },
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/readable-stream": {
            "version": "2.3.8",
            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
            "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
            "dependencies": {
                "core-util-is": "~1.0.0",
                "inherits": "~2.0.3",
                "isarray": "~1.0.0",
                "process-nextick-args": "~2.0.0",
                "safe-buffer": "~5.1.1",
                "string_decoder": "~1.1.1",
                "util-deprecate": "~1.0.1"
            }
        },
        "node_modules/readable-stream/node_modules/safe-buffer": {
            "version": "5.1.2",
            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
        },
        "node_modules/readable-web-to-node-stream": {
            "version": "3.0.4",
            "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.4.tgz",
            "integrity": "sha512-9nX56alTf5bwXQ3ZDipHJhusu9NTQJ/CVPtb/XHAJCXihZeitfJvIRS4GqQ/mfIoOE3IelHMrpayVrosdHBuLw==",
            "dependencies": {
                "readable-stream": "^4.7.0"
            },
            "engines": {
                "node": ">=8"
            },
            "funding": {
                "type": "github",
                "url": "https://github.com/sponsors/Borewit"
            }
        },
        "node_modules/readable-web-to-node-stream/node_modules/buffer": {
            "version": "6.0.3",
            "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz",
            "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==",
            "funding": [
                {
                    "type": "github",
                    "url": "https://github.com/sponsors/feross"
                },
                {
                    "type": "patreon",
                    "url": "https://www.patreon.com/feross"
                },
                {
                    "type": "consulting",
                    "url": "https://feross.org/support"
                }
            ],
            "dependencies": {
                "base64-js": "^1.3.1",
                "ieee754": "^1.2.1"
            }
        },
        "node_modules/readable-web-to-node-stream/node_modules/readable-stream": {
            "version": "4.7.0",
            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz",
            "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==",
            "dependencies": {
                "abort-controller": "^3.0.0",
                "buffer": "^6.0.3",
                "events": "^3.3.0",
                "process": "^0.11.10",
                "string_decoder": "^1.3.0"
            },
            "engines": {
                "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
            }
        },
        "node_modules/readable-web-to-node-stream/node_modules/string_decoder": {
            "version": "1.3.0",
            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz",
            "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==",
            "dependencies": {
                "safe-buffer": "~5.2.0"
            }
        },
        "node_modules/readdirp": {
            "version": "3.6.0",
            "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
            "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
            "dependencies": {
                "picomatch": "^2.2.1"
            },
            "engines": {
                "node": ">=8.10.0"
            }
        },
        "node_modules/safe-buffer": {
            "version": "5.2.1",
            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
            "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
            "funding": [
                {
                    "type": "github",
                    "url": "https://github.com/sponsors/feross"
                },
                {
                    "type": "patreon",
                    "url": "https://www.patreon.com/feross"
                },
                {
                    "type": "consulting",
                    "url": "https://feross.org/support"
                }
            ]
        },
        "node_modules/safer-buffer": {
            "version": "2.1.2",
            "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
            "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
        },
        "node_modules/send": {
            "version": "0.19.0",
            "resolved": "https://registry.npmjs.org/send/-/send-0.19.0.tgz",
            "integrity": "sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==",
            "dependencies": {
                "debug": "2.6.9",
                "depd": "2.0.0",
                "destroy": "1.2.0",
                "encodeurl": "~1.0.2",
                "escape-html": "~1.0.3",
                "etag": "~1.8.1",
                "fresh": "0.5.2",
                "http-errors": "2.0.0",
                "mime": "1.6.0",
                "ms": "2.1.3",
                "on-finished": "2.4.1",
                "range-parser": "~1.2.1",
                "statuses": "2.0.1"
            },
            "engines": {
                "node": ">= 0.8.0"
            }
        },
        "node_modules/send/node_modules/encodeurl": {
            "version": "1.0.2",
            "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz",
            "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/send/node_modules/ms": {
            "version": "2.1.3",
            "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
            "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="
        },
        "node_modules/serve-static": {
            "version": "1.16.2",
            "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.2.tgz",
            "integrity": "sha512-VqpjJZKadQB/PEbEwvFdO43Ax5dFBZ2UECszz8bQ7pi7wt//PWe1P6MN7eCnjsatYtBT6EuiClbjSWP2WrIoTw==",
            "dependencies": {
                "encodeurl": "~2.0.0",
                "escape-html": "~1.0.3",
                "parseurl": "~1.3.3",
                "send": "0.19.0"
            },
            "engines": {
                "node": ">= 0.8.0"
            }
        },
        "node_modules/setprototypeof": {
            "version": "1.2.0",
            "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz",
            "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw=="
        },
        "node_modules/side-channel": {
            "version": "1.1.0",
            "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
            "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
            "dependencies": {
                "es-errors": "^1.3.0",
                "object-inspect": "^1.13.3",
                "side-channel-list": "^1.0.0",
                "side-channel-map": "^1.0.1",
                "side-channel-weakmap": "^1.0.2"
            },
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/side-channel-list": {
            "version": "1.0.0",
            "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
            "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
            "dependencies": {
                "es-errors": "^1.3.0",
                "object-inspect": "^1.13.3"
            },
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/side-channel-map": {
            "version": "1.0.1",
            "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz",
            "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==",
            "dependencies": {
                "call-bound": "^1.0.2",
                "es-errors": "^1.3.0",
                "get-intrinsic": "^1.2.5",
                "object-inspect": "^1.13.3"
            },
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/side-channel-weakmap": {
            "version": "1.0.2",
            "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz",
            "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==",
            "dependencies": {
                "call-bound": "^1.0.2",
                "es-errors": "^1.3.0",
                "get-intrinsic": "^1.2.5",
                "object-inspect": "^1.13.3",
                "side-channel-map": "^1.0.1"
            },
            "engines": {
                "node": ">= 0.4"
            },
            "funding": {
                "url": "https://github.com/sponsors/ljharb"
            }
        },
        "node_modules/statuses": {
            "version": "2.0.1",
            "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz",
            "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/streamsearch": {
            "version": "1.1.0",
            "resolved": "https://registry.npmjs.org/streamsearch/-/streamsearch-1.1.0.tgz",
            "integrity": "sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg==",
            "engines": {
                "node": ">=10.0.0"
            }
        },
        "node_modules/string_decoder": {
            "version": "1.1.1",
            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
            "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
            "dependencies": {
                "safe-buffer": "~5.1.0"
            }
        },
        "node_modules/string_decoder/node_modules/safe-buffer": {
            "version": "5.1.2",
            "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
            "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
        },
        "node_modules/strtok3": {
            "version": "7.1.1",
            "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-7.1.1.tgz",
            "integrity": "sha512-mKX8HA/cdBqMKUr0MMZAFssCkIGoZeSCMXgnt79yKxNFguMLVFgRe6wB+fsL0NmoHDbeyZXczy7vEPSoo3rkzg==",
            "dependencies": {
                "@tokenizer/token": "^0.3.0",
                "peek-readable": "^5.1.3"
            },
            "engines": {
                "node": ">=16"
            },
            "funding": {
                "type": "github",
                "url": "https://github.com/sponsors/Borewit"
            }
        },
        "node_modules/to-regex-range": {
            "version": "5.0.1",
            "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
            "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
            "dependencies": {
                "is-number": "^7.0.0"
            },
            "engines": {
                "node": ">=8.0"
            }
        },
        "node_modules/toidentifier": {
            "version": "1.0.1",
            "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz",
            "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==",
            "engines": {
                "node": ">=0.6"
            }
        },
        "node_modules/token-types": {
            "version": "5.0.1",
            "resolved": "https://registry.npmjs.org/token-types/-/token-types-5.0.1.tgz",
            "integrity": "sha512-Y2fmSnZjQdDb9W4w4r1tswlMHylzWIeOKpx0aZH9BgGtACHhrk3OkT52AzwcuqTRBZtvvnTjDBh8eynMulu8Vg==",
            "dependencies": {
                "@tokenizer/token": "^0.3.0",
                "ieee754": "^1.2.1"
            },
            "engines": {
                "node": ">=14.16"
            },
            "funding": {
                "type": "github",
                "url": "https://github.com/sponsors/Borewit"
            }
        },
        "node_modules/type-is": {
            "version": "1.6.18",
            "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz",
            "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==",
            "dependencies": {
                "media-typer": "0.3.0",
                "mime-types": "~2.1.24"
            },
            "engines": {
                "node": ">= 0.6"
            }
        },
        "node_modules/typedarray": {
            "version": "0.0.6",
            "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz",
            "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA=="
        },
        "node_modules/universalify": {
            "version": "2.0.1",
            "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
            "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
            "engines": {
                "node": ">= 10.0.0"
            }
        },
        "node_modules/unpipe": {
            "version": "1.0.0",
            "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
            "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/util-deprecate": {
            "version": "1.0.2",
            "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
            "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="
        },
        "node_modules/utils-merge": {
            "version": "1.0.1",
            "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
            "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==",
            "engines": {
                "node": ">= 0.4.0"
            }
        },
        "node_modules/vary": {
            "version": "1.1.2",
            "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
            "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
            "engines": {
                "node": ">= 0.8"
            }
        },
        "node_modules/ws": {
            "version": "8.18.0",
            "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
            "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
            "engines": {
                "node": ">=10.0.0"
            },
            "peerDependencies": {
                "bufferutil": "^4.0.1",
                "utf-8-validate": ">=5.0.2"
            },
            "peerDependenciesMeta": {
                "bufferutil": {
                    "optional": true
                },
                "utf-8-validate": {
                    "optional": true
                }
            }
        },
        "node_modules/xtend": {
            "version": "4.0.2",
            "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz",
            "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
            "engines": {
                "node": ">=0.4"
            }
        }
    }
}
password.hash

Code: Select all

admin:5e884898da28047151d0e56f8dc6292773603d0d6aabbdd62a11ef721d1542d8
README.md

Code: Select all

# webcbuilder
This is a web cluster builder that is utilizing Evernode. With this software you can easily merge instances into a cluster, work on a smart contract in real time, upload a front-end to your evernode deployment and much more.

# Deploy Quickly To Evernode
This builder exist on the docker hub, you can deploy it with image everweb/webcbuilder:latest OR, if you need ssh access on gptcp2, everweb/webcbuilder-ssh:latest the ssl certificate and port is handled automatically if there's an env.vars file. If there isn't one, then the frontend runs on port 36525, 36527, 36529 and ssh (if used) on 36526, 36528 and 36530.

SSH user and password: everweb:default

The idea with this project is that it constantly keeps getting improved, anyone is welcome to contribute and fork this, anyone is welcome to whitelabel and "steal" the code. The intention behind this builder is that YOU can use it in any (legal of course) way you wish.

# How it generally works
This software use supervisord to run hotpocket, sshd(if used) and a nodejs server for the frontend. 

The smart contract is syncapp.js (compiled into an index.js) and it loops through the "sidedish". What you add to the sidedish from the front end will be a part of the hotpocket smartcontract. This allows you to work on your smart contract application in real-time.

A youtube video showcasing the builder can be found below:
https://www.youtube.com/watch?v=TYOCalPWhlE

# What you need to do if you want to create your own docker container with this system
You need to browse into the main folder and write npm i, after that you need to enter the contract folder and write npm i as-well. That command will create the node_modules folder, which are needed.

When you got those folders, go to the main folder and use following commands to get your system to docker:
docker build -t youruseraccount/yourdockerimagename -f ./Dockerfile . 
docker image push --all-tags youruseraccount/yourdockerimagename

# To learn more about evernode or contribute in the community, find us at:
https://evernode.forum
https://discord.gg/DAQszjKEBV
https://github.com/EvernodeXRPL
https://t.me/EvernodeCommunity/

# Know Issues:
Sometimes it doesn't work to activate a freshly installed npm package.
settings.json

Code: Select all

{
  "useCluster": false,
  "clusterMore": false
}
sidedish.js

Code: Select all

class SideDish {
  async init(ctx, bson) {
    this.ctx = ctx;
    this.bson = bson;

    const configRaw = await require('fs').promises.readFile('/contract/cfg/hp.cfg', 'utf8');
    const hpconfig = JSON.parse(configRaw);

    // Normalize UNL keys as lowercase hex strings
    this.unl = new Set((hpconfig.contract?.unl || []).map(k => k.toLowerCase()));

    console.log('[sidedish] Hotpocket is the lunch, but what is the sidedish?');
  }

  async handleRequest(user, message, isReadOnly) {
    console.log('[sidedish] Received message:', message);

    if (message.type === 'echo') {
      const reply = {
        type: 'echo-reply',
        message: `Echo from sidedish: ${message.payload}`,
      };
      console.log('[sidedish] Sending reply:', reply);
      await user.send(this.bson.serialize(reply));
      return true;
    }

    if (message.type === 'check-unl') {
      const userHexKey = user.publicKey.toString('hex').toLowerCase();
      const isInUNL = this.unl.has(userHexKey);
      const reply = {
        type: 'check-unl-reply',
        inUnl: isInUNL,
        publicKey: userHexKey,
      };
      console.log('[sidedish] UNL check result:', reply);
      await user.send(this.bson.serialize(reply));
      return true;
    }

    if (message.type === 'send-keys') {
      const reply = {
        type: 'send-keys-reply',
        receivedPublicKey: message.publicKey || null,
        receivedPrivateKey: message.privateKey || null,
      };

      if (!message.publicKey || !message.privateKey) {
        reply.error = 'Missing public or private key in request';
      }

      console.log('[sidedish] Sending key echo reply:', reply);
      await user.send(this.bson.serialize(reply));
      return true;
    }

    // Fallback for unknown message types
    const errorReply = {
      type: 'error',
      message: `Unknown message type: ${message.type || 'undefined'}`,
    };
    console.warn('[sidedish] Message not for me, sending it to the next in line.', message);
    
    return false;
  }
}

module.exports = SideDish;
start.sh

Code: Select all

#!/bin/bash
source /contract/env.vars
# Generate SSH keys if missing
[ ! -f /etc/ssh/ssh_host_rsa_key ] && ssh-keygen -A

chown -R everweb:everweb /home/everweb

CFG_FILE="/contract/cfg/hp.cfg"
HTML_FILE="/home/everweb/index.js"

userport=$(awk '
  $0 ~ /"user"/ { user_block=1 }
  user_block && $0 ~ /"port"/ {
    gsub(/[ ,]/, "", $2);
    gsub(/[^0-9]/, "", $2);
    print $2;
    exit
  }
' "$CFG_FILE")

if [[ -z "$userport" ]]; then
  echo "Could not extract user.port from $CFG_FILE"
  exit 1
fi

sed -i -E "s|(wss://localhost:)[0-9]+|\1$userport|g" "$HTML_FILE"
echo "Replaced port with $userport in $HTML_FILE"

SSHD_CONFIG="/etc/ssh/sshd_config"
PORT_LINE="Port $EXTERNAL_GPTCP2_PORT"

if [[ -n "${EXTERNAL_GPTCP2_PORT// }" ]]; then
  if ! grep -q "^$PORT_LINE\$" "$SSHD_CONFIG"; then
    echo "$PORT_LINE" >> "$SSHD_CONFIG"
  else
    echo "Port line already present, skipping append."
  fi
fi

# Patch config values
sed -i \
  -e "s|\"bin_path\": \".*\"|\"bin_path\": \"/usr/bin/node\"|" \
  -e "s|\"bin_args\": \".*\"|\"bin_args\": \"index.js\"|" \
  "$CFG_FILE"

sed -i '/"peer_discovery"/,/}/s/"enabled": true/"enabled": false/' "$CFG_FILE"

chown everweb:everweb "$CFG_FILE"
chown -R everweb:everweb /contract/cfg/

# Clean up state folder
if [ -d "/home/everweb/contract" ]; then
  cp -r /home/everweb/contract/* /contract/contract_fs/seed/state/
fi
rm -f /contract/contract_fs/seed/state/bootstrap_*
rm -f /contract/contract_fs/seed/state/install.sh
rm -f /contract/contract_fs/seed/state/hp.cfg.override
if [ -d "/home/everweb/contract" ]; then
rm -rf /home/everweb/contract/
fi

sed -i 's/"log_level": "err"/"log_level": "inf"/' /contract/cfg/hp.cfg
#/usr/bin/node /home/everweb/index.js &

# Start supervisord
exec /usr/bin/supervisord -c /home/everweb/supervisord.conf
supervisord.conf

Code: Select all

[supervisord]
nodaemon=true
logfile=/var/log/supervisor/supervisord.log
pidfile=/var/run/supervisord.pid

[unix_http_server]
file=/var/run/supervisor.sock
chmod=0770
chown=everweb:everweb

[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface

[supervisorctl]
serverurl=unix:///var/run/supervisor.sock

[program:sshd]
startretries=100
startsecs=15
command=/usr/sbin/sshd -D
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/sshd.err.log
stdout_logfile=/var/log/supervisor/sshd.out.log
priority=10

[program:hotpocket]
startretries=100
startsecs=15
command=/usr/local/bin/hotpocket/hpcore run /contract
autostart=true
autorestart=true
stderr_logfile=/var/log/supervisor/hotpocket.err.log
stdout_logfile=/var/log/supervisor/hotpocket.out.log
priority=20

[program:webadmin]
startretries=100
startsecs=15
command=/usr/bin/node /home/everweb/index.js
directory=/home/everweb
user=everweb
autostart=true
autorestart=true
stderr_logfile=/home/everweb/webadmin.err.log
stdout_logfile=/home/everweb/webadmin.out.log
priority=30
TERMS.md

Code: Select all

Terms and Conditions and Legal Disclaimer for Everwebhost Software
Effective Date: May 13, 2025
Welcome to Everwebhost, a software project built with Evernode Hotpocket Technology. We’re excited to share this open-source software with you! By using, copying, modifying, or sharing Everwebhost ("Software"), you agree to these Terms and Conditions and the Legal Disclaimer below. They’re designed to keep things simple, protect everyone involved, and make sure you feel confident using the Software.
1.	You’re Free to Use the Software
Open-Source Freedom: Everwebhost is released under a permissive open-source license (see the COPYING file included with the Software). This means you can use, copy, modify, share, or even sell the Software for any purpose—personal, commercial, or otherwise.
Fork and Create: Feel free to fork the project, build something new, or integrate it into your own work. There are no restrictions on what you can do, as long as you follow the license terms in the COPYING file.
Safe to Use: We’ve made the Software open and accessible so you can trust it’s yours to explore and build with. The community is welcome to contribute and make it even better!
2.	We’re Not Responsible for How You Use It
Your Responsibility: You’re in charge of how you use the Software. Whether you’re running it on a server, building a business, or just experimenting, you’re responsible for the results. This includes making sure it works for your needs and complies with any laws or regulations.
No Liability: The author(s) of Everwebhost (that’s us) aren’t responsible for anything that happens when you use the Software. This includes any issues, losses, or problems—technical, legal, or otherwise. You’re using it at your own risk, but we’ve made it open so you can verify and customize it to feel secure.
Feel Confident: We’re not liable because we trust you to use the Software responsibly. You have full control to test, modify, and secure it to meet your needs, which makes it a safe choice for your projects.
3.	No Promises, Just Possibilities
As-Is Software: The Software comes "AS IS," with no guarantees. We don’t promise it’s perfect, bug-free, or ideal for every situation. But that’s the beauty of open-source—you can check it out, fix what you need, and make it your own.
No Support Required: We don’t have to provide updates, fixes, or support, but we hope the community (including you!) will keep the project alive and thriving.
Why This Matters: By not making promises, we keep the Software free and open for everyone. You’re empowered to make it work for you, which is why so many developers love open-source projects like this.
4.	You’re in Control
Check It Yourself: You’re responsible for testing the Software to ensure it’s safe and suitable for your needs. Open-source means you can see every line of code, so you can trust what you’re using.
Third-Party Components: Everwebhost uses Evernode Hotpocket Technology and may include other third-party tools, which have their own licenses. You’ll need to check those terms (usually included with the Software) to make sure you’re compliant. We’ve made sure everything is standard and easy to follow.
Stay Legal: Make sure your use of the Software follows all relevant laws (like data privacy or intellectual property rules). This is on you, but it’s standard for any software project and nothing to worry about if you’re diligent.
5.	Our Rights
We Own the Original: The Software is licensed, not sold. We (the authors) keep the intellectual property rights, but you get to use it freely under the COPYING license.
Give Credit: If you share or modify the Software, please include the copyright notice and license terms from the COPYING file. It’s a small way to keep the project open and fair for everyone.
6.	If Something Goes Wrong
Stopping Use: If you don’t follow these terms or the COPYING license, your right to use the Software ends automatically. But we’re all about keeping things open, so just stick to the rules, and you’re good!
Disputes: If there’s ever a disagreement about these terms, we’ll handle it through arbitration in San Francisco, California, under the rules of the American Arbitration Association. This is a fair and standard way to resolve issues.
Laws: These terms follow the laws of California, USA. It’s a common choice for software projects and keeps things clear.
7.	Keeping Things Flexible
Updates to Terms: We might update these terms in the future. If we do, we’ll make the new version available with the Software. Keep using it, and you’re agreeing to the updates.
If Something’s Off: If any part of these terms isn’t valid under the law, the rest still applies. This keeps everything fair and functional.
No Surprises: These terms, along with the COPYING file, are the full deal. No hidden rules or surprises.
Legal Disclaimer
We’re Not Liable: We, the authors, aren’t responsible for any problems, damages, or losses from using Everwebhost. Whether it’s a technical glitch, a legal issue, or something else, you’re responsible for your use of the Software. This is standard for open-source projects and helps keep the Software free for everyone.
No Guarantees: The Software has no warranties—none at all. We don’t promise it’s flawless or fits every need. But you can inspect and tweak it, which makes it a trustworthy choice for developers.
You’re Covered: You’re responsible for making sure the Software is secure and legal for your use. This includes checking Evernode Hotpocket Technology or other components. It’s easy to do, and we’ve kept everything transparent to help you feel safe.
Protecting Us: If someone makes a claim against us because of how you used the Software, you agree to cover us (this is called indemnification). It’s a standard way to ensure we’re not dragged into issues we didn’t cause.
Why You’re Safe: Open-source software like Everwebhost is used by millions because it’s transparent and flexible. You can verify the code, customize it, and join a community of users. We’ve designed these terms to protect you by giving you control and clarity.

Re: Web Cluster Builder Source Code

Posted: Fri Sep 19, 2025 2:08 am
by webcbuilder
This code is available for anyone to fork, white-label, improve or change!

Any issues or questions, write in here!


And please, someone, make a github for this :)


# webcbuilder
This is a web cluster builder that is utilizing Evernode. With this software you can easily merge instances into a cluster, work on a smart contract in real time, upload a front-end to your evernode deployment and much more.

# Deploy Quickly To Evernode
This builder exist on the docker hub, you can deploy it with image everweb/webcbuilder:latest OR, if you need ssh access on gptcp2, everweb/webcbuilder-ssh:latest the ssl certificate and port is handled automatically if there's an env.vars file. If there isn't one, then the frontend runs on port 36525, 36527, 36529 and ssh (if used) on 36526, 36528 and 36530.

SSH user and password: everweb:default

Remember to turn off ssh in supervisord.conf for your docker container!

The idea with this project is that it constantly keeps getting improved, anyone is welcome to contribute and fork this, anyone is welcome to whitelabel and "steal" the code. The intention behind this builder is that YOU can use it in any (legal of course) way you wish.

# How it generally works
This software use supervisord to run hotpocket, sshd(if used) and a nodejs server for the frontend.

The smart contract is syncapp.js (compiled into an index.js) and it loops through the "sidedish". What you add to the sidedish from the front end will be a part of the hotpocket smartcontract. This allows you to work on your smart contract application in real-time.

A youtube video showcasing the builder can be found below:
https://www.youtube.com/watch?v=TYOCalPWhlE

# What you need to do if you want to create your own docker container with this system
You need to browse into the main folder and write npm i, after that you need to enter the contract folder and write npm i as-well. That command will create the node_modules folder, which are needed.

When you got those folders, go to the main folder and use following commands to get your system to docker:
docker build -t youruseraccount/yourdockerimagename -f ./Dockerfile .
docker image push --all-tags youruseraccount/yourdockerimagename

# To learn more about evernode or contribute in the community, find us at:
https://evernode.forum
https://discord.gg/DAQszjKEBV
https://github.com/EvernodeXRPL
https://t.me/EvernodeCommunity/

# Know Issues:
Sometimes it doesn't work to activate a freshly installed npm package.