dcp-client 4.2.22 → 4.2.24

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,6 +12,28 @@
12
12
  /******/ (() => { // webpackBootstrap
13
13
  /******/ var __webpack_modules__ = ({
14
14
 
15
+ /***/ "./node_modules/@kingsds/socket.io-client/node_modules/debug/src/browser.js":
16
+ /*!**********************************************************************************!*\
17
+ !*** ./node_modules/@kingsds/socket.io-client/node_modules/debug/src/browser.js ***!
18
+ \**********************************************************************************/
19
+ /***/ ((module, exports, __webpack_require__) => {
20
+
21
+ "use strict";
22
+ eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_modules/process/browser.js */ \"./node_modules/process/browser.js\");\n\n\nfunction _typeof(obj) { if (typeof Symbol === \"function\" && typeof Symbol.iterator === \"symbol\") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === \"function\" && obj.constructor === Symbol && obj !== Symbol.prototype ? \"symbol\" : typeof obj; }; } return _typeof(obj); }\n\n/* eslint-env browser */\n\n/**\n * This is the web browser implementation of `debug()`.\n */\nexports.log = log;\nexports.formatArgs = formatArgs;\nexports.save = save;\nexports.load = load;\nexports.useColors = useColors;\nexports.storage = localstorage();\n/**\n * Colors.\n */\n\nexports.colors = ['#0000CC', '#0000FF', '#0033CC', '#0033FF', '#0066CC', '#0066FF', '#0099CC', '#0099FF', '#00CC00', '#00CC33', '#00CC66', '#00CC99', '#00CCCC', '#00CCFF', '#3300CC', '#3300FF', '#3333CC', '#3333FF', '#3366CC', '#3366FF', '#3399CC', '#3399FF', '#33CC00', '#33CC33', '#33CC66', '#33CC99', '#33CCCC', '#33CCFF', '#6600CC', '#6600FF', '#6633CC', '#6633FF', '#66CC00', '#66CC33', '#9900CC', '#9900FF', '#9933CC', '#9933FF', '#99CC00', '#99CC33', '#CC0000', '#CC0033', '#CC0066', '#CC0099', '#CC00CC', '#CC00FF', '#CC3300', '#CC3333', '#CC3366', '#CC3399', '#CC33CC', '#CC33FF', '#CC6600', '#CC6633', '#CC9900', '#CC9933', '#CCCC00', '#CCCC33', '#FF0000', '#FF0033', '#FF0066', '#FF0099', '#FF00CC', '#FF00FF', '#FF3300', '#FF3333', '#FF3366', '#FF3399', '#FF33CC', '#FF33FF', '#FF6600', '#FF6633', '#FF9900', '#FF9933', '#FFCC00', '#FFCC33'];\n/**\n * Currently only WebKit-based Web Inspectors, Firefox >= v31,\n * and the Firebug extension (any Firefox version) are known\n * to support \"%c\" CSS customizations.\n *\n * TODO: add a `localStorage` variable to explicitly enable/disable colors\n */\n// eslint-disable-next-line complexity\n\nfunction useColors() {\n // NB: In an Electron preload script, document will be defined but not fully\n // initialized. Since we know we're in Chrome, we'll just detect this case\n // explicitly\n if (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {\n return true;\n } // Internet Explorer and Edge do not support colors.\n\n\n if (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\\/(\\d+)/)) {\n return false;\n } // Is webkit? http://stackoverflow.com/a/16459606/376773\n // document is undefined in react-native: https://github.com/facebook/react-native/pull/1632\n\n\n return typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance || // Is firebug? http://stackoverflow.com/a/398120/376773\n typeof window !== 'undefined' && window.console && (window.console.firebug || window.console.exception && window.console.table) || // Is firefox >= v31?\n // https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages\n typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/) && parseInt(RegExp.$1, 10) >= 31 || // Double check webkit in userAgent just in case we are in a worker\n typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\\/(\\d+)/);\n}\n/**\n * Colorize log arguments if enabled.\n *\n * @api public\n */\n\n\nfunction formatArgs(args) {\n args[0] = (this.useColors ? '%c' : '') + this.namespace + (this.useColors ? ' %c' : ' ') + args[0] + (this.useColors ? '%c ' : ' ') + '+' + module.exports.humanize(this.diff);\n\n if (!this.useColors) {\n return;\n }\n\n var c = 'color: ' + this.color;\n args.splice(1, 0, c, 'color: inherit'); // The final \"%c\" is somewhat tricky, because there could be other\n // arguments passed either before or after the %c, so we need to\n // figure out the correct index to insert the CSS into\n\n var index = 0;\n var lastC = 0;\n args[0].replace(/%[a-zA-Z%]/g, function (match) {\n if (match === '%%') {\n return;\n }\n\n index++;\n\n if (match === '%c') {\n // We only are interested in the *last* %c\n // (the user may have provided their own)\n lastC = index;\n }\n });\n args.splice(lastC, 0, c);\n}\n/**\n * Invokes `console.log()` when available.\n * No-op when `console.log` is not a \"function\".\n *\n * @api public\n */\n\n\nfunction log() {\n var _console;\n\n // This hackery is required for IE8/9, where\n // the `console.log` function doesn't have 'apply'\n return (typeof console === \"undefined\" ? \"undefined\" : _typeof(console)) === 'object' && console.log && (_console = console).log.apply(_console, arguments);\n}\n/**\n * Save `namespaces`.\n *\n * @param {String} namespaces\n * @api private\n */\n\n\nfunction save(namespaces) {\n try {\n if (namespaces) {\n exports.storage.setItem('debug', namespaces);\n } else {\n exports.storage.removeItem('debug');\n }\n } catch (error) {// Swallow\n // XXX (@Qix-) should we be logging these?\n }\n}\n/**\n * Load `namespaces`.\n *\n * @return {String} returns the previously persisted debug modes\n * @api private\n */\n\n\nfunction load() {\n var r;\n\n try {\n r = exports.storage.getItem('debug');\n } catch (error) {} // Swallow\n // XXX (@Qix-) should we be logging these?\n // If debug isn't set in LS, and we're in Electron, try to load $DEBUG\n\n\n if (!r && typeof process !== 'undefined' && 'env' in process) {\n r = process.env.DEBUG;\n }\n\n return r;\n}\n/**\n * Localstorage attempts to return the localstorage.\n *\n * This is necessary because safari throws\n * when a user disables cookies/localstorage\n * and you attempt to access it.\n *\n * @return {LocalStorage}\n * @api private\n */\n\n\nfunction localstorage() {\n try {\n // TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context\n // The Browser also has localStorage in the global context.\n return localStorage;\n } catch (error) {// Swallow\n // XXX (@Qix-) should we be logging these?\n }\n}\n\nmodule.exports = __webpack_require__(/*! ./common */ \"./node_modules/@kingsds/socket.io-client/node_modules/debug/src/common.js\")(exports);\nvar formatters = module.exports.formatters;\n/**\n * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.\n */\n\nformatters.j = function (v) {\n try {\n return JSON.stringify(v);\n } catch (error) {\n return '[UnexpectedJSONParseError]: ' + error.message;\n }\n};\n\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/node_modules/debug/src/browser.js?");
23
+
24
+ /***/ }),
25
+
26
+ /***/ "./node_modules/@kingsds/socket.io-client/node_modules/debug/src/common.js":
27
+ /*!*********************************************************************************!*\
28
+ !*** ./node_modules/@kingsds/socket.io-client/node_modules/debug/src/common.js ***!
29
+ \*********************************************************************************/
30
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
31
+
32
+ "use strict";
33
+ eval("\n\n/**\n * This is the common logic for both the Node.js and web browser\n * implementations of `debug()`.\n */\nfunction setup(env) {\n createDebug.debug = createDebug;\n createDebug.default = createDebug;\n createDebug.coerce = coerce;\n createDebug.disable = disable;\n createDebug.enable = enable;\n createDebug.enabled = enabled;\n createDebug.humanize = __webpack_require__(/*! ms */ \"./node_modules/ms/index.js\");\n Object.keys(env).forEach(function (key) {\n createDebug[key] = env[key];\n });\n /**\n * Active `debug` instances.\n */\n\n createDebug.instances = [];\n /**\n * The currently active debug mode names, and names to skip.\n */\n\n createDebug.names = [];\n createDebug.skips = [];\n /**\n * Map of special \"%n\" handling functions, for the debug \"format\" argument.\n *\n * Valid key names are a single, lower or upper-case letter, i.e. \"n\" and \"N\".\n */\n\n createDebug.formatters = {};\n /**\n * Selects a color for a debug namespace\n * @param {String} namespace The namespace string for the for the debug instance to be colored\n * @return {Number|String} An ANSI color code for the given namespace\n * @api private\n */\n\n function selectColor(namespace) {\n var hash = 0;\n\n for (var i = 0; i < namespace.length; i++) {\n hash = (hash << 5) - hash + namespace.charCodeAt(i);\n hash |= 0; // Convert to 32bit integer\n }\n\n return createDebug.colors[Math.abs(hash) % createDebug.colors.length];\n }\n\n createDebug.selectColor = selectColor;\n /**\n * Create a debugger with the given `namespace`.\n *\n * @param {String} namespace\n * @return {Function}\n * @api public\n */\n\n function createDebug(namespace) {\n var prevTime;\n\n function debug() {\n // Disabled?\n if (!debug.enabled) {\n return;\n }\n\n for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {\n args[_key] = arguments[_key];\n }\n\n var self = debug; // Set `diff` timestamp\n\n var curr = Number(new Date());\n var ms = curr - (prevTime || curr);\n self.diff = ms;\n self.prev = prevTime;\n self.curr = curr;\n prevTime = curr;\n args[0] = createDebug.coerce(args[0]);\n\n if (typeof args[0] !== 'string') {\n // Anything else let's inspect with %O\n args.unshift('%O');\n } // Apply any `formatters` transformations\n\n\n var index = 0;\n args[0] = args[0].replace(/%([a-zA-Z%])/g, function (match, format) {\n // If we encounter an escaped % then don't increase the array index\n if (match === '%%') {\n return match;\n }\n\n index++;\n var formatter = createDebug.formatters[format];\n\n if (typeof formatter === 'function') {\n var val = args[index];\n match = formatter.call(self, val); // Now we need to remove `args[index]` since it's inlined in the `format`\n\n args.splice(index, 1);\n index--;\n }\n\n return match;\n }); // Apply env-specific formatting (colors, etc.)\n\n createDebug.formatArgs.call(self, args);\n var logFn = self.log || createDebug.log;\n logFn.apply(self, args);\n }\n\n debug.namespace = namespace;\n debug.enabled = createDebug.enabled(namespace);\n debug.useColors = createDebug.useColors();\n debug.color = selectColor(namespace);\n debug.destroy = destroy;\n debug.extend = extend; // Debug.formatArgs = formatArgs;\n // debug.rawLog = rawLog;\n // env-specific initialization logic for debug instances\n\n if (typeof createDebug.init === 'function') {\n createDebug.init(debug);\n }\n\n createDebug.instances.push(debug);\n return debug;\n }\n\n function destroy() {\n var index = createDebug.instances.indexOf(this);\n\n if (index !== -1) {\n createDebug.instances.splice(index, 1);\n return true;\n }\n\n return false;\n }\n\n function extend(namespace, delimiter) {\n return createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);\n }\n /**\n * Enables a debug mode by namespaces. This can include modes\n * separated by a colon and wildcards.\n *\n * @param {String} namespaces\n * @api public\n */\n\n\n function enable(namespaces) {\n createDebug.save(namespaces);\n createDebug.names = [];\n createDebug.skips = [];\n var i;\n var split = (typeof namespaces === 'string' ? namespaces : '').split(/[\\s,]+/);\n var len = split.length;\n\n for (i = 0; i < len; i++) {\n if (!split[i]) {\n // ignore empty strings\n continue;\n }\n\n namespaces = split[i].replace(/\\*/g, '.*?');\n\n if (namespaces[0] === '-') {\n createDebug.skips.push(new RegExp('^' + namespaces.substr(1) + '$'));\n } else {\n createDebug.names.push(new RegExp('^' + namespaces + '$'));\n }\n }\n\n for (i = 0; i < createDebug.instances.length; i++) {\n var instance = createDebug.instances[i];\n instance.enabled = createDebug.enabled(instance.namespace);\n }\n }\n /**\n * Disable debug output.\n *\n * @api public\n */\n\n\n function disable() {\n createDebug.enable('');\n }\n /**\n * Returns true if the given mode name is enabled, false otherwise.\n *\n * @param {String} name\n * @return {Boolean}\n * @api public\n */\n\n\n function enabled(name) {\n if (name[name.length - 1] === '*') {\n return true;\n }\n\n var i;\n var len;\n\n for (i = 0, len = createDebug.skips.length; i < len; i++) {\n if (createDebug.skips[i].test(name)) {\n return false;\n }\n }\n\n for (i = 0, len = createDebug.names.length; i < len; i++) {\n if (createDebug.names[i].test(name)) {\n return true;\n }\n }\n\n return false;\n }\n /**\n * Coerce `val`.\n *\n * @param {Mixed} val\n * @return {Mixed}\n * @api private\n */\n\n\n function coerce(val) {\n if (val instanceof Error) {\n return val.stack || val.message;\n }\n\n return val;\n }\n\n createDebug.enable(createDebug.load());\n return createDebug;\n}\n\nmodule.exports = setup;\n\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/node_modules/debug/src/common.js?");
34
+
35
+ /***/ }),
36
+
15
37
  /***/ "./node_modules/@selderee/plugin-htmlparser2/node_modules/domelementtype/lib/index.js":
16
38
  /*!********************************************************************************************!*\
17
39
  !*** ./node_modules/@selderee/plugin-htmlparser2/node_modules/domelementtype/lib/index.js ***!
@@ -2340,6 +2362,50 @@ eval("\n\nvar undefined;\n\nvar $SyntaxError = SyntaxError;\nvar $Function = Fun
2340
2362
 
2341
2363
  /***/ }),
2342
2364
 
2365
+ /***/ "./node_modules/gopd/index.js":
2366
+ /*!************************************!*\
2367
+ !*** ./node_modules/gopd/index.js ***!
2368
+ \************************************/
2369
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
2370
+
2371
+ "use strict";
2372
+ eval("\n\nvar GetIntrinsic = __webpack_require__(/*! get-intrinsic */ \"./node_modules/gopd/node_modules/get-intrinsic/index.js\");\n\nvar $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true);\n\nif ($gOPD) {\n\ttry {\n\t\t$gOPD([], 'length');\n\t} catch (e) {\n\t\t// IE 8 has a broken gOPD\n\t\t$gOPD = null;\n\t}\n}\n\nmodule.exports = $gOPD;\n\n\n//# sourceURL=webpack://dcp/./node_modules/gopd/index.js?");
2373
+
2374
+ /***/ }),
2375
+
2376
+ /***/ "./node_modules/gopd/node_modules/get-intrinsic/index.js":
2377
+ /*!***************************************************************!*\
2378
+ !*** ./node_modules/gopd/node_modules/get-intrinsic/index.js ***!
2379
+ \***************************************************************/
2380
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
2381
+
2382
+ "use strict";
2383
+ eval("\n\nvar undefined;\n\nvar $SyntaxError = SyntaxError;\nvar $Function = Function;\nvar $TypeError = TypeError;\n\n// eslint-disable-next-line consistent-return\nvar getEvalledConstructor = function (expressionSyntax) {\n\ttry {\n\t\treturn $Function('\"use strict\"; return (' + expressionSyntax + ').constructor;')();\n\t} catch (e) {}\n};\n\nvar $gOPD = Object.getOwnPropertyDescriptor;\nif ($gOPD) {\n\ttry {\n\t\t$gOPD({}, '');\n\t} catch (e) {\n\t\t$gOPD = null; // this is IE 8, which has a broken gOPD\n\t}\n}\n\nvar throwTypeError = function () {\n\tthrow new $TypeError();\n};\nvar ThrowTypeError = $gOPD\n\t? (function () {\n\t\ttry {\n\t\t\t// eslint-disable-next-line no-unused-expressions, no-caller, no-restricted-properties\n\t\t\targuments.callee; // IE 8 does not throw here\n\t\t\treturn throwTypeError;\n\t\t} catch (calleeThrows) {\n\t\t\ttry {\n\t\t\t\t// IE 8 throws on Object.getOwnPropertyDescriptor(arguments, '')\n\t\t\t\treturn $gOPD(arguments, 'callee').get;\n\t\t\t} catch (gOPDthrows) {\n\t\t\t\treturn throwTypeError;\n\t\t\t}\n\t\t}\n\t}())\n\t: throwTypeError;\n\nvar hasSymbols = __webpack_require__(/*! has-symbols */ \"./node_modules/gopd/node_modules/has-symbols/index.js\")();\n\nvar getProto = Object.getPrototypeOf || function (x) { return x.__proto__; }; // eslint-disable-line no-proto\n\nvar needsEval = {};\n\nvar TypedArray = typeof Uint8Array === 'undefined' ? undefined : getProto(Uint8Array);\n\nvar INTRINSICS = {\n\t'%AggregateError%': typeof AggregateError === 'undefined' ? undefined : AggregateError,\n\t'%Array%': Array,\n\t'%ArrayBuffer%': typeof ArrayBuffer === 'undefined' ? undefined : ArrayBuffer,\n\t'%ArrayIteratorPrototype%': hasSymbols ? getProto([][Symbol.iterator]()) : undefined,\n\t'%AsyncFromSyncIteratorPrototype%': undefined,\n\t'%AsyncFunction%': needsEval,\n\t'%AsyncGenerator%': needsEval,\n\t'%AsyncGeneratorFunction%': needsEval,\n\t'%AsyncIteratorPrototype%': needsEval,\n\t'%Atomics%': typeof Atomics === 'undefined' ? undefined : Atomics,\n\t'%BigInt%': typeof BigInt === 'undefined' ? undefined : BigInt,\n\t'%BigInt64Array%': typeof BigInt64Array === 'undefined' ? undefined : BigInt64Array,\n\t'%BigUint64Array%': typeof BigUint64Array === 'undefined' ? undefined : BigUint64Array,\n\t'%Boolean%': Boolean,\n\t'%DataView%': typeof DataView === 'undefined' ? undefined : DataView,\n\t'%Date%': Date,\n\t'%decodeURI%': decodeURI,\n\t'%decodeURIComponent%': decodeURIComponent,\n\t'%encodeURI%': encodeURI,\n\t'%encodeURIComponent%': encodeURIComponent,\n\t'%Error%': Error,\n\t'%eval%': eval, // eslint-disable-line no-eval\n\t'%EvalError%': EvalError,\n\t'%Float32Array%': typeof Float32Array === 'undefined' ? undefined : Float32Array,\n\t'%Float64Array%': typeof Float64Array === 'undefined' ? undefined : Float64Array,\n\t'%FinalizationRegistry%': typeof FinalizationRegistry === 'undefined' ? undefined : FinalizationRegistry,\n\t'%Function%': $Function,\n\t'%GeneratorFunction%': needsEval,\n\t'%Int8Array%': typeof Int8Array === 'undefined' ? undefined : Int8Array,\n\t'%Int16Array%': typeof Int16Array === 'undefined' ? undefined : Int16Array,\n\t'%Int32Array%': typeof Int32Array === 'undefined' ? undefined : Int32Array,\n\t'%isFinite%': isFinite,\n\t'%isNaN%': isNaN,\n\t'%IteratorPrototype%': hasSymbols ? getProto(getProto([][Symbol.iterator]())) : undefined,\n\t'%JSON%': typeof JSON === 'object' ? JSON : undefined,\n\t'%Map%': typeof Map === 'undefined' ? undefined : Map,\n\t'%MapIteratorPrototype%': typeof Map === 'undefined' || !hasSymbols ? undefined : getProto(new Map()[Symbol.iterator]()),\n\t'%Math%': Math,\n\t'%Number%': Number,\n\t'%Object%': Object,\n\t'%parseFloat%': parseFloat,\n\t'%parseInt%': parseInt,\n\t'%Promise%': typeof Promise === 'undefined' ? undefined : Promise,\n\t'%Proxy%': typeof Proxy === 'undefined' ? undefined : Proxy,\n\t'%RangeError%': RangeError,\n\t'%ReferenceError%': ReferenceError,\n\t'%Reflect%': typeof Reflect === 'undefined' ? undefined : Reflect,\n\t'%RegExp%': RegExp,\n\t'%Set%': typeof Set === 'undefined' ? undefined : Set,\n\t'%SetIteratorPrototype%': typeof Set === 'undefined' || !hasSymbols ? undefined : getProto(new Set()[Symbol.iterator]()),\n\t'%SharedArrayBuffer%': typeof SharedArrayBuffer === 'undefined' ? undefined : SharedArrayBuffer,\n\t'%String%': String,\n\t'%StringIteratorPrototype%': hasSymbols ? getProto(''[Symbol.iterator]()) : undefined,\n\t'%Symbol%': hasSymbols ? Symbol : undefined,\n\t'%SyntaxError%': $SyntaxError,\n\t'%ThrowTypeError%': ThrowTypeError,\n\t'%TypedArray%': TypedArray,\n\t'%TypeError%': $TypeError,\n\t'%Uint8Array%': typeof Uint8Array === 'undefined' ? undefined : Uint8Array,\n\t'%Uint8ClampedArray%': typeof Uint8ClampedArray === 'undefined' ? undefined : Uint8ClampedArray,\n\t'%Uint16Array%': typeof Uint16Array === 'undefined' ? undefined : Uint16Array,\n\t'%Uint32Array%': typeof Uint32Array === 'undefined' ? undefined : Uint32Array,\n\t'%URIError%': URIError,\n\t'%WeakMap%': typeof WeakMap === 'undefined' ? undefined : WeakMap,\n\t'%WeakRef%': typeof WeakRef === 'undefined' ? undefined : WeakRef,\n\t'%WeakSet%': typeof WeakSet === 'undefined' ? undefined : WeakSet\n};\n\ntry {\n\tnull.error; // eslint-disable-line no-unused-expressions\n} catch (e) {\n\t// https://github.com/tc39/proposal-shadowrealm/pull/384#issuecomment-1364264229\n\tvar errorProto = getProto(getProto(e));\n\tINTRINSICS['%Error.prototype%'] = errorProto;\n}\n\nvar doEval = function doEval(name) {\n\tvar value;\n\tif (name === '%AsyncFunction%') {\n\t\tvalue = getEvalledConstructor('async function () {}');\n\t} else if (name === '%GeneratorFunction%') {\n\t\tvalue = getEvalledConstructor('function* () {}');\n\t} else if (name === '%AsyncGeneratorFunction%') {\n\t\tvalue = getEvalledConstructor('async function* () {}');\n\t} else if (name === '%AsyncGenerator%') {\n\t\tvar fn = doEval('%AsyncGeneratorFunction%');\n\t\tif (fn) {\n\t\t\tvalue = fn.prototype;\n\t\t}\n\t} else if (name === '%AsyncIteratorPrototype%') {\n\t\tvar gen = doEval('%AsyncGenerator%');\n\t\tif (gen) {\n\t\t\tvalue = getProto(gen.prototype);\n\t\t}\n\t}\n\n\tINTRINSICS[name] = value;\n\n\treturn value;\n};\n\nvar LEGACY_ALIASES = {\n\t'%ArrayBufferPrototype%': ['ArrayBuffer', 'prototype'],\n\t'%ArrayPrototype%': ['Array', 'prototype'],\n\t'%ArrayProto_entries%': ['Array', 'prototype', 'entries'],\n\t'%ArrayProto_forEach%': ['Array', 'prototype', 'forEach'],\n\t'%ArrayProto_keys%': ['Array', 'prototype', 'keys'],\n\t'%ArrayProto_values%': ['Array', 'prototype', 'values'],\n\t'%AsyncFunctionPrototype%': ['AsyncFunction', 'prototype'],\n\t'%AsyncGenerator%': ['AsyncGeneratorFunction', 'prototype'],\n\t'%AsyncGeneratorPrototype%': ['AsyncGeneratorFunction', 'prototype', 'prototype'],\n\t'%BooleanPrototype%': ['Boolean', 'prototype'],\n\t'%DataViewPrototype%': ['DataView', 'prototype'],\n\t'%DatePrototype%': ['Date', 'prototype'],\n\t'%ErrorPrototype%': ['Error', 'prototype'],\n\t'%EvalErrorPrototype%': ['EvalError', 'prototype'],\n\t'%Float32ArrayPrototype%': ['Float32Array', 'prototype'],\n\t'%Float64ArrayPrototype%': ['Float64Array', 'prototype'],\n\t'%FunctionPrototype%': ['Function', 'prototype'],\n\t'%Generator%': ['GeneratorFunction', 'prototype'],\n\t'%GeneratorPrototype%': ['GeneratorFunction', 'prototype', 'prototype'],\n\t'%Int8ArrayPrototype%': ['Int8Array', 'prototype'],\n\t'%Int16ArrayPrototype%': ['Int16Array', 'prototype'],\n\t'%Int32ArrayPrototype%': ['Int32Array', 'prototype'],\n\t'%JSONParse%': ['JSON', 'parse'],\n\t'%JSONStringify%': ['JSON', 'stringify'],\n\t'%MapPrototype%': ['Map', 'prototype'],\n\t'%NumberPrototype%': ['Number', 'prototype'],\n\t'%ObjectPrototype%': ['Object', 'prototype'],\n\t'%ObjProto_toString%': ['Object', 'prototype', 'toString'],\n\t'%ObjProto_valueOf%': ['Object', 'prototype', 'valueOf'],\n\t'%PromisePrototype%': ['Promise', 'prototype'],\n\t'%PromiseProto_then%': ['Promise', 'prototype', 'then'],\n\t'%Promise_all%': ['Promise', 'all'],\n\t'%Promise_reject%': ['Promise', 'reject'],\n\t'%Promise_resolve%': ['Promise', 'resolve'],\n\t'%RangeErrorPrototype%': ['RangeError', 'prototype'],\n\t'%ReferenceErrorPrototype%': ['ReferenceError', 'prototype'],\n\t'%RegExpPrototype%': ['RegExp', 'prototype'],\n\t'%SetPrototype%': ['Set', 'prototype'],\n\t'%SharedArrayBufferPrototype%': ['SharedArrayBuffer', 'prototype'],\n\t'%StringPrototype%': ['String', 'prototype'],\n\t'%SymbolPrototype%': ['Symbol', 'prototype'],\n\t'%SyntaxErrorPrototype%': ['SyntaxError', 'prototype'],\n\t'%TypedArrayPrototype%': ['TypedArray', 'prototype'],\n\t'%TypeErrorPrototype%': ['TypeError', 'prototype'],\n\t'%Uint8ArrayPrototype%': ['Uint8Array', 'prototype'],\n\t'%Uint8ClampedArrayPrototype%': ['Uint8ClampedArray', 'prototype'],\n\t'%Uint16ArrayPrototype%': ['Uint16Array', 'prototype'],\n\t'%Uint32ArrayPrototype%': ['Uint32Array', 'prototype'],\n\t'%URIErrorPrototype%': ['URIError', 'prototype'],\n\t'%WeakMapPrototype%': ['WeakMap', 'prototype'],\n\t'%WeakSetPrototype%': ['WeakSet', 'prototype']\n};\n\nvar bind = __webpack_require__(/*! function-bind */ \"./node_modules/function-bind/index.js\");\nvar hasOwn = __webpack_require__(/*! has */ \"./node_modules/has/src/index.js\");\nvar $concat = bind.call(Function.call, Array.prototype.concat);\nvar $spliceApply = bind.call(Function.apply, Array.prototype.splice);\nvar $replace = bind.call(Function.call, String.prototype.replace);\nvar $strSlice = bind.call(Function.call, String.prototype.slice);\nvar $exec = bind.call(Function.call, RegExp.prototype.exec);\n\n/* adapted from https://github.com/lodash/lodash/blob/4.17.15/dist/lodash.js#L6735-L6744 */\nvar rePropName = /[^%.[\\]]+|\\[(?:(-?\\d+(?:\\.\\d+)?)|([\"'])((?:(?!\\2)[^\\\\]|\\\\.)*?)\\2)\\]|(?=(?:\\.|\\[\\])(?:\\.|\\[\\]|%$))/g;\nvar reEscapeChar = /\\\\(\\\\)?/g; /** Used to match backslashes in property paths. */\nvar stringToPath = function stringToPath(string) {\n\tvar first = $strSlice(string, 0, 1);\n\tvar last = $strSlice(string, -1);\n\tif (first === '%' && last !== '%') {\n\t\tthrow new $SyntaxError('invalid intrinsic syntax, expected closing `%`');\n\t} else if (last === '%' && first !== '%') {\n\t\tthrow new $SyntaxError('invalid intrinsic syntax, expected opening `%`');\n\t}\n\tvar result = [];\n\t$replace(string, rePropName, function (match, number, quote, subString) {\n\t\tresult[result.length] = quote ? $replace(subString, reEscapeChar, '$1') : number || match;\n\t});\n\treturn result;\n};\n/* end adaptation */\n\nvar getBaseIntrinsic = function getBaseIntrinsic(name, allowMissing) {\n\tvar intrinsicName = name;\n\tvar alias;\n\tif (hasOwn(LEGACY_ALIASES, intrinsicName)) {\n\t\talias = LEGACY_ALIASES[intrinsicName];\n\t\tintrinsicName = '%' + alias[0] + '%';\n\t}\n\n\tif (hasOwn(INTRINSICS, intrinsicName)) {\n\t\tvar value = INTRINSICS[intrinsicName];\n\t\tif (value === needsEval) {\n\t\t\tvalue = doEval(intrinsicName);\n\t\t}\n\t\tif (typeof value === 'undefined' && !allowMissing) {\n\t\t\tthrow new $TypeError('intrinsic ' + name + ' exists, but is not available. Please file an issue!');\n\t\t}\n\n\t\treturn {\n\t\t\talias: alias,\n\t\t\tname: intrinsicName,\n\t\t\tvalue: value\n\t\t};\n\t}\n\n\tthrow new $SyntaxError('intrinsic ' + name + ' does not exist!');\n};\n\nmodule.exports = function GetIntrinsic(name, allowMissing) {\n\tif (typeof name !== 'string' || name.length === 0) {\n\t\tthrow new $TypeError('intrinsic name must be a non-empty string');\n\t}\n\tif (arguments.length > 1 && typeof allowMissing !== 'boolean') {\n\t\tthrow new $TypeError('\"allowMissing\" argument must be a boolean');\n\t}\n\n\tif ($exec(/^%?[^%]*%?$/, name) === null) {\n\t\tthrow new $SyntaxError('`%` may not be present anywhere but at the beginning and end of the intrinsic name');\n\t}\n\tvar parts = stringToPath(name);\n\tvar intrinsicBaseName = parts.length > 0 ? parts[0] : '';\n\n\tvar intrinsic = getBaseIntrinsic('%' + intrinsicBaseName + '%', allowMissing);\n\tvar intrinsicRealName = intrinsic.name;\n\tvar value = intrinsic.value;\n\tvar skipFurtherCaching = false;\n\n\tvar alias = intrinsic.alias;\n\tif (alias) {\n\t\tintrinsicBaseName = alias[0];\n\t\t$spliceApply(parts, $concat([0, 1], alias));\n\t}\n\n\tfor (var i = 1, isOwn = true; i < parts.length; i += 1) {\n\t\tvar part = parts[i];\n\t\tvar first = $strSlice(part, 0, 1);\n\t\tvar last = $strSlice(part, -1);\n\t\tif (\n\t\t\t(\n\t\t\t\t(first === '\"' || first === \"'\" || first === '`')\n\t\t\t\t|| (last === '\"' || last === \"'\" || last === '`')\n\t\t\t)\n\t\t\t&& first !== last\n\t\t) {\n\t\t\tthrow new $SyntaxError('property names with quotes must have matching quotes');\n\t\t}\n\t\tif (part === 'constructor' || !isOwn) {\n\t\t\tskipFurtherCaching = true;\n\t\t}\n\n\t\tintrinsicBaseName += '.' + part;\n\t\tintrinsicRealName = '%' + intrinsicBaseName + '%';\n\n\t\tif (hasOwn(INTRINSICS, intrinsicRealName)) {\n\t\t\tvalue = INTRINSICS[intrinsicRealName];\n\t\t} else if (value != null) {\n\t\t\tif (!(part in value)) {\n\t\t\t\tif (!allowMissing) {\n\t\t\t\t\tthrow new $TypeError('base intrinsic for ' + name + ' exists, but the property is not available.');\n\t\t\t\t}\n\t\t\t\treturn void undefined;\n\t\t\t}\n\t\t\tif ($gOPD && (i + 1) >= parts.length) {\n\t\t\t\tvar desc = $gOPD(value, part);\n\t\t\t\tisOwn = !!desc;\n\n\t\t\t\t// By convention, when a data property is converted to an accessor\n\t\t\t\t// property to emulate a data property that does not suffer from\n\t\t\t\t// the override mistake, that accessor's getter is marked with\n\t\t\t\t// an `originalValue` property. Here, when we detect this, we\n\t\t\t\t// uphold the illusion by pretending to see that original data\n\t\t\t\t// property, i.e., returning the value rather than the getter\n\t\t\t\t// itself.\n\t\t\t\tif (isOwn && 'get' in desc && !('originalValue' in desc.get)) {\n\t\t\t\t\tvalue = desc.get;\n\t\t\t\t} else {\n\t\t\t\t\tvalue = value[part];\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tisOwn = hasOwn(value, part);\n\t\t\t\tvalue = value[part];\n\t\t\t}\n\n\t\t\tif (isOwn && !skipFurtherCaching) {\n\t\t\t\tINTRINSICS[intrinsicRealName] = value;\n\t\t\t}\n\t\t}\n\t}\n\treturn value;\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/gopd/node_modules/get-intrinsic/index.js?");
2384
+
2385
+ /***/ }),
2386
+
2387
+ /***/ "./node_modules/gopd/node_modules/has-symbols/index.js":
2388
+ /*!*************************************************************!*\
2389
+ !*** ./node_modules/gopd/node_modules/has-symbols/index.js ***!
2390
+ \*************************************************************/
2391
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
2392
+
2393
+ "use strict";
2394
+ eval("\n\nvar origSymbol = typeof Symbol !== 'undefined' && Symbol;\nvar hasSymbolSham = __webpack_require__(/*! ./shams */ \"./node_modules/gopd/node_modules/has-symbols/shams.js\");\n\nmodule.exports = function hasNativeSymbols() {\n\tif (typeof origSymbol !== 'function') { return false; }\n\tif (typeof Symbol !== 'function') { return false; }\n\tif (typeof origSymbol('foo') !== 'symbol') { return false; }\n\tif (typeof Symbol('bar') !== 'symbol') { return false; }\n\n\treturn hasSymbolSham();\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/gopd/node_modules/has-symbols/index.js?");
2395
+
2396
+ /***/ }),
2397
+
2398
+ /***/ "./node_modules/gopd/node_modules/has-symbols/shams.js":
2399
+ /*!*************************************************************!*\
2400
+ !*** ./node_modules/gopd/node_modules/has-symbols/shams.js ***!
2401
+ \*************************************************************/
2402
+ /***/ ((module) => {
2403
+
2404
+ "use strict";
2405
+ eval("\n\n/* eslint complexity: [2, 18], max-statements: [2, 33] */\nmodule.exports = function hasSymbols() {\n\tif (typeof Symbol !== 'function' || typeof Object.getOwnPropertySymbols !== 'function') { return false; }\n\tif (typeof Symbol.iterator === 'symbol') { return true; }\n\n\tvar obj = {};\n\tvar sym = Symbol('test');\n\tvar symObj = Object(sym);\n\tif (typeof sym === 'string') { return false; }\n\n\tif (Object.prototype.toString.call(sym) !== '[object Symbol]') { return false; }\n\tif (Object.prototype.toString.call(symObj) !== '[object Symbol]') { return false; }\n\n\t// temp disabled per https://github.com/ljharb/object.assign/issues/17\n\t// if (sym instanceof Symbol) { return false; }\n\t// temp disabled per https://github.com/WebReflection/get-own-property-symbols/issues/4\n\t// if (!(symObj instanceof Symbol)) { return false; }\n\n\t// if (typeof Symbol.prototype.toString !== 'function') { return false; }\n\t// if (String(sym) !== Symbol.prototype.toString.call(sym)) { return false; }\n\n\tvar symVal = 42;\n\tobj[sym] = symVal;\n\tfor (sym in obj) { return false; } // eslint-disable-line no-restricted-syntax, no-unreachable-loop\n\tif (typeof Object.keys === 'function' && Object.keys(obj).length !== 0) { return false; }\n\n\tif (typeof Object.getOwnPropertyNames === 'function' && Object.getOwnPropertyNames(obj).length !== 0) { return false; }\n\n\tvar syms = Object.getOwnPropertySymbols(obj);\n\tif (syms.length !== 1 || syms[0] !== sym) { return false; }\n\n\tif (!Object.prototype.propertyIsEnumerable.call(obj, sym)) { return false; }\n\n\tif (typeof Object.getOwnPropertyDescriptor === 'function') {\n\t\tvar descriptor = Object.getOwnPropertyDescriptor(obj, sym);\n\t\tif (descriptor.value !== symVal || descriptor.enumerable !== true) { return false; }\n\t}\n\n\treturn true;\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/gopd/node_modules/has-symbols/shams.js?");
2406
+
2407
+ /***/ }),
2408
+
2343
2409
  /***/ "./node_modules/has-property-descriptors/index.js":
2344
2410
  /*!********************************************************!*\
2345
2411
  !*** ./node_modules/has-property-descriptors/index.js ***!
@@ -3830,26 +3896,6 @@ eval("var inherits = __webpack_require__(/*! inherits */ \"./node_modules/inheri
3830
3896
 
3831
3897
  /***/ }),
3832
3898
 
3833
- /***/ "./node_modules/socket.io-client/node_modules/debug/src/browser.js":
3834
- /*!*************************************************************************!*\
3835
- !*** ./node_modules/socket.io-client/node_modules/debug/src/browser.js ***!
3836
- \*************************************************************************/
3837
- /***/ ((module, exports, __webpack_require__) => {
3838
-
3839
- eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_modules/process/browser.js */ \"./node_modules/process/browser.js\");\n/* eslint-env browser */\n\n/**\n * This is the web browser implementation of `debug()`.\n */\n\nexports.formatArgs = formatArgs;\nexports.save = save;\nexports.load = load;\nexports.useColors = useColors;\nexports.storage = localstorage();\nexports.destroy = (() => {\n\tlet warned = false;\n\n\treturn () => {\n\t\tif (!warned) {\n\t\t\twarned = true;\n\t\t\tconsole.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');\n\t\t}\n\t};\n})();\n\n/**\n * Colors.\n */\n\nexports.colors = [\n\t'#0000CC',\n\t'#0000FF',\n\t'#0033CC',\n\t'#0033FF',\n\t'#0066CC',\n\t'#0066FF',\n\t'#0099CC',\n\t'#0099FF',\n\t'#00CC00',\n\t'#00CC33',\n\t'#00CC66',\n\t'#00CC99',\n\t'#00CCCC',\n\t'#00CCFF',\n\t'#3300CC',\n\t'#3300FF',\n\t'#3333CC',\n\t'#3333FF',\n\t'#3366CC',\n\t'#3366FF',\n\t'#3399CC',\n\t'#3399FF',\n\t'#33CC00',\n\t'#33CC33',\n\t'#33CC66',\n\t'#33CC99',\n\t'#33CCCC',\n\t'#33CCFF',\n\t'#6600CC',\n\t'#6600FF',\n\t'#6633CC',\n\t'#6633FF',\n\t'#66CC00',\n\t'#66CC33',\n\t'#9900CC',\n\t'#9900FF',\n\t'#9933CC',\n\t'#9933FF',\n\t'#99CC00',\n\t'#99CC33',\n\t'#CC0000',\n\t'#CC0033',\n\t'#CC0066',\n\t'#CC0099',\n\t'#CC00CC',\n\t'#CC00FF',\n\t'#CC3300',\n\t'#CC3333',\n\t'#CC3366',\n\t'#CC3399',\n\t'#CC33CC',\n\t'#CC33FF',\n\t'#CC6600',\n\t'#CC6633',\n\t'#CC9900',\n\t'#CC9933',\n\t'#CCCC00',\n\t'#CCCC33',\n\t'#FF0000',\n\t'#FF0033',\n\t'#FF0066',\n\t'#FF0099',\n\t'#FF00CC',\n\t'#FF00FF',\n\t'#FF3300',\n\t'#FF3333',\n\t'#FF3366',\n\t'#FF3399',\n\t'#FF33CC',\n\t'#FF33FF',\n\t'#FF6600',\n\t'#FF6633',\n\t'#FF9900',\n\t'#FF9933',\n\t'#FFCC00',\n\t'#FFCC33'\n];\n\n/**\n * Currently only WebKit-based Web Inspectors, Firefox >= v31,\n * and the Firebug extension (any Firefox version) are known\n * to support \"%c\" CSS customizations.\n *\n * TODO: add a `localStorage` variable to explicitly enable/disable colors\n */\n\n// eslint-disable-next-line complexity\nfunction useColors() {\n\t// NB: In an Electron preload script, document will be defined but not fully\n\t// initialized. Since we know we're in Chrome, we'll just detect this case\n\t// explicitly\n\tif (typeof window !== 'undefined' && window.process && (window.process.type === 'renderer' || window.process.__nwjs)) {\n\t\treturn true;\n\t}\n\n\t// Internet Explorer and Edge do not support colors.\n\tif (typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/(edge|trident)\\/(\\d+)/)) {\n\t\treturn false;\n\t}\n\n\t// Is webkit? http://stackoverflow.com/a/16459606/376773\n\t// document is undefined in react-native: https://github.com/facebook/react-native/pull/1632\n\treturn (typeof document !== 'undefined' && document.documentElement && document.documentElement.style && document.documentElement.style.WebkitAppearance) ||\n\t\t// Is firebug? http://stackoverflow.com/a/398120/376773\n\t\t(typeof window !== 'undefined' && window.console && (window.console.firebug || (window.console.exception && window.console.table))) ||\n\t\t// Is firefox >= v31?\n\t\t// https://developer.mozilla.org/en-US/docs/Tools/Web_Console#Styling_messages\n\t\t(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/firefox\\/(\\d+)/) && parseInt(RegExp.$1, 10) >= 31) ||\n\t\t// Double check webkit in userAgent just in case we are in a worker\n\t\t(typeof navigator !== 'undefined' && navigator.userAgent && navigator.userAgent.toLowerCase().match(/applewebkit\\/(\\d+)/));\n}\n\n/**\n * Colorize log arguments if enabled.\n *\n * @api public\n */\n\nfunction formatArgs(args) {\n\targs[0] = (this.useColors ? '%c' : '') +\n\t\tthis.namespace +\n\t\t(this.useColors ? ' %c' : ' ') +\n\t\targs[0] +\n\t\t(this.useColors ? '%c ' : ' ') +\n\t\t'+' + module.exports.humanize(this.diff);\n\n\tif (!this.useColors) {\n\t\treturn;\n\t}\n\n\tconst c = 'color: ' + this.color;\n\targs.splice(1, 0, c, 'color: inherit');\n\n\t// The final \"%c\" is somewhat tricky, because there could be other\n\t// arguments passed either before or after the %c, so we need to\n\t// figure out the correct index to insert the CSS into\n\tlet index = 0;\n\tlet lastC = 0;\n\targs[0].replace(/%[a-zA-Z%]/g, match => {\n\t\tif (match === '%%') {\n\t\t\treturn;\n\t\t}\n\t\tindex++;\n\t\tif (match === '%c') {\n\t\t\t// We only are interested in the *last* %c\n\t\t\t// (the user may have provided their own)\n\t\t\tlastC = index;\n\t\t}\n\t});\n\n\targs.splice(lastC, 0, c);\n}\n\n/**\n * Invokes `console.debug()` when available.\n * No-op when `console.debug` is not a \"function\".\n * If `console.debug` is not available, falls back\n * to `console.log`.\n *\n * @api public\n */\nexports.log = console.debug || console.log || (() => {});\n\n/**\n * Save `namespaces`.\n *\n * @param {String} namespaces\n * @api private\n */\nfunction save(namespaces) {\n\ttry {\n\t\tif (namespaces) {\n\t\t\texports.storage.setItem('debug', namespaces);\n\t\t} else {\n\t\t\texports.storage.removeItem('debug');\n\t\t}\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n}\n\n/**\n * Load `namespaces`.\n *\n * @return {String} returns the previously persisted debug modes\n * @api private\n */\nfunction load() {\n\tlet r;\n\ttry {\n\t\tr = exports.storage.getItem('debug');\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n\n\t// If debug isn't set in LS, and we're in Electron, try to load $DEBUG\n\tif (!r && typeof process !== 'undefined' && 'env' in process) {\n\t\tr = process.env.DEBUG;\n\t}\n\n\treturn r;\n}\n\n/**\n * Localstorage attempts to return the localstorage.\n *\n * This is necessary because safari throws\n * when a user disables cookies/localstorage\n * and you attempt to access it.\n *\n * @return {LocalStorage}\n * @api private\n */\n\nfunction localstorage() {\n\ttry {\n\t\t// TVMLKit (Apple TV JS Runtime) does not have a window object, just localStorage in the global context\n\t\t// The Browser also has localStorage in the global context.\n\t\treturn localStorage;\n\t} catch (error) {\n\t\t// Swallow\n\t\t// XXX (@Qix-) should we be logging these?\n\t}\n}\n\nmodule.exports = __webpack_require__(/*! ./common */ \"./node_modules/socket.io-client/node_modules/debug/src/common.js\")(exports);\n\nconst {formatters} = module.exports;\n\n/**\n * Map %j to `JSON.stringify()`, since no Web Inspectors do that by default.\n */\n\nformatters.j = function (v) {\n\ttry {\n\t\treturn JSON.stringify(v);\n\t} catch (error) {\n\t\treturn '[UnexpectedJSONParseError]: ' + error.message;\n\t}\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/node_modules/debug/src/browser.js?");
3840
-
3841
- /***/ }),
3842
-
3843
- /***/ "./node_modules/socket.io-client/node_modules/debug/src/common.js":
3844
- /*!************************************************************************!*\
3845
- !*** ./node_modules/socket.io-client/node_modules/debug/src/common.js ***!
3846
- \************************************************************************/
3847
- /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
3848
-
3849
- eval("\n/**\n * This is the common logic for both the Node.js and web browser\n * implementations of `debug()`.\n */\n\nfunction setup(env) {\n\tcreateDebug.debug = createDebug;\n\tcreateDebug.default = createDebug;\n\tcreateDebug.coerce = coerce;\n\tcreateDebug.disable = disable;\n\tcreateDebug.enable = enable;\n\tcreateDebug.enabled = enabled;\n\tcreateDebug.humanize = __webpack_require__(/*! ms */ \"./node_modules/ms/index.js\");\n\tcreateDebug.destroy = destroy;\n\n\tObject.keys(env).forEach(key => {\n\t\tcreateDebug[key] = env[key];\n\t});\n\n\t/**\n\t* The currently active debug mode names, and names to skip.\n\t*/\n\n\tcreateDebug.names = [];\n\tcreateDebug.skips = [];\n\n\t/**\n\t* Map of special \"%n\" handling functions, for the debug \"format\" argument.\n\t*\n\t* Valid key names are a single, lower or upper-case letter, i.e. \"n\" and \"N\".\n\t*/\n\tcreateDebug.formatters = {};\n\n\t/**\n\t* Selects a color for a debug namespace\n\t* @param {String} namespace The namespace string for the debug instance to be colored\n\t* @return {Number|String} An ANSI color code for the given namespace\n\t* @api private\n\t*/\n\tfunction selectColor(namespace) {\n\t\tlet hash = 0;\n\n\t\tfor (let i = 0; i < namespace.length; i++) {\n\t\t\thash = ((hash << 5) - hash) + namespace.charCodeAt(i);\n\t\t\thash |= 0; // Convert to 32bit integer\n\t\t}\n\n\t\treturn createDebug.colors[Math.abs(hash) % createDebug.colors.length];\n\t}\n\tcreateDebug.selectColor = selectColor;\n\n\t/**\n\t* Create a debugger with the given `namespace`.\n\t*\n\t* @param {String} namespace\n\t* @return {Function}\n\t* @api public\n\t*/\n\tfunction createDebug(namespace) {\n\t\tlet prevTime;\n\t\tlet enableOverride = null;\n\t\tlet namespacesCache;\n\t\tlet enabledCache;\n\n\t\tfunction debug(...args) {\n\t\t\t// Disabled?\n\t\t\tif (!debug.enabled) {\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst self = debug;\n\n\t\t\t// Set `diff` timestamp\n\t\t\tconst curr = Number(new Date());\n\t\t\tconst ms = curr - (prevTime || curr);\n\t\t\tself.diff = ms;\n\t\t\tself.prev = prevTime;\n\t\t\tself.curr = curr;\n\t\t\tprevTime = curr;\n\n\t\t\targs[0] = createDebug.coerce(args[0]);\n\n\t\t\tif (typeof args[0] !== 'string') {\n\t\t\t\t// Anything else let's inspect with %O\n\t\t\t\targs.unshift('%O');\n\t\t\t}\n\n\t\t\t// Apply any `formatters` transformations\n\t\t\tlet index = 0;\n\t\t\targs[0] = args[0].replace(/%([a-zA-Z%])/g, (match, format) => {\n\t\t\t\t// If we encounter an escaped % then don't increase the array index\n\t\t\t\tif (match === '%%') {\n\t\t\t\t\treturn '%';\n\t\t\t\t}\n\t\t\t\tindex++;\n\t\t\t\tconst formatter = createDebug.formatters[format];\n\t\t\t\tif (typeof formatter === 'function') {\n\t\t\t\t\tconst val = args[index];\n\t\t\t\t\tmatch = formatter.call(self, val);\n\n\t\t\t\t\t// Now we need to remove `args[index]` since it's inlined in the `format`\n\t\t\t\t\targs.splice(index, 1);\n\t\t\t\t\tindex--;\n\t\t\t\t}\n\t\t\t\treturn match;\n\t\t\t});\n\n\t\t\t// Apply env-specific formatting (colors, etc.)\n\t\t\tcreateDebug.formatArgs.call(self, args);\n\n\t\t\tconst logFn = self.log || createDebug.log;\n\t\t\tlogFn.apply(self, args);\n\t\t}\n\n\t\tdebug.namespace = namespace;\n\t\tdebug.useColors = createDebug.useColors();\n\t\tdebug.color = createDebug.selectColor(namespace);\n\t\tdebug.extend = extend;\n\t\tdebug.destroy = createDebug.destroy; // XXX Temporary. Will be removed in the next major release.\n\n\t\tObject.defineProperty(debug, 'enabled', {\n\t\t\tenumerable: true,\n\t\t\tconfigurable: false,\n\t\t\tget: () => {\n\t\t\t\tif (enableOverride !== null) {\n\t\t\t\t\treturn enableOverride;\n\t\t\t\t}\n\t\t\t\tif (namespacesCache !== createDebug.namespaces) {\n\t\t\t\t\tnamespacesCache = createDebug.namespaces;\n\t\t\t\t\tenabledCache = createDebug.enabled(namespace);\n\t\t\t\t}\n\n\t\t\t\treturn enabledCache;\n\t\t\t},\n\t\t\tset: v => {\n\t\t\t\tenableOverride = v;\n\t\t\t}\n\t\t});\n\n\t\t// Env-specific initialization logic for debug instances\n\t\tif (typeof createDebug.init === 'function') {\n\t\t\tcreateDebug.init(debug);\n\t\t}\n\n\t\treturn debug;\n\t}\n\n\tfunction extend(namespace, delimiter) {\n\t\tconst newDebug = createDebug(this.namespace + (typeof delimiter === 'undefined' ? ':' : delimiter) + namespace);\n\t\tnewDebug.log = this.log;\n\t\treturn newDebug;\n\t}\n\n\t/**\n\t* Enables a debug mode by namespaces. This can include modes\n\t* separated by a colon and wildcards.\n\t*\n\t* @param {String} namespaces\n\t* @api public\n\t*/\n\tfunction enable(namespaces) {\n\t\tcreateDebug.save(namespaces);\n\t\tcreateDebug.namespaces = namespaces;\n\n\t\tcreateDebug.names = [];\n\t\tcreateDebug.skips = [];\n\n\t\tlet i;\n\t\tconst split = (typeof namespaces === 'string' ? namespaces : '').split(/[\\s,]+/);\n\t\tconst len = split.length;\n\n\t\tfor (i = 0; i < len; i++) {\n\t\t\tif (!split[i]) {\n\t\t\t\t// ignore empty strings\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tnamespaces = split[i].replace(/\\*/g, '.*?');\n\n\t\t\tif (namespaces[0] === '-') {\n\t\t\t\tcreateDebug.skips.push(new RegExp('^' + namespaces.slice(1) + '$'));\n\t\t\t} else {\n\t\t\t\tcreateDebug.names.push(new RegExp('^' + namespaces + '$'));\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t* Disable debug output.\n\t*\n\t* @return {String} namespaces\n\t* @api public\n\t*/\n\tfunction disable() {\n\t\tconst namespaces = [\n\t\t\t...createDebug.names.map(toNamespace),\n\t\t\t...createDebug.skips.map(toNamespace).map(namespace => '-' + namespace)\n\t\t].join(',');\n\t\tcreateDebug.enable('');\n\t\treturn namespaces;\n\t}\n\n\t/**\n\t* Returns true if the given mode name is enabled, false otherwise.\n\t*\n\t* @param {String} name\n\t* @return {Boolean}\n\t* @api public\n\t*/\n\tfunction enabled(name) {\n\t\tif (name[name.length - 1] === '*') {\n\t\t\treturn true;\n\t\t}\n\n\t\tlet i;\n\t\tlet len;\n\n\t\tfor (i = 0, len = createDebug.skips.length; i < len; i++) {\n\t\t\tif (createDebug.skips[i].test(name)) {\n\t\t\t\treturn false;\n\t\t\t}\n\t\t}\n\n\t\tfor (i = 0, len = createDebug.names.length; i < len; i++) {\n\t\t\tif (createDebug.names[i].test(name)) {\n\t\t\t\treturn true;\n\t\t\t}\n\t\t}\n\n\t\treturn false;\n\t}\n\n\t/**\n\t* Convert regexp to namespace\n\t*\n\t* @param {RegExp} regxep\n\t* @return {String} namespace\n\t* @api private\n\t*/\n\tfunction toNamespace(regexp) {\n\t\treturn regexp.toString()\n\t\t\t.substring(2, regexp.toString().length - 2)\n\t\t\t.replace(/\\.\\*\\?$/, '*');\n\t}\n\n\t/**\n\t* Coerce `val`.\n\t*\n\t* @param {Mixed} val\n\t* @return {Mixed}\n\t* @api private\n\t*/\n\tfunction coerce(val) {\n\t\tif (val instanceof Error) {\n\t\t\treturn val.stack || val.message;\n\t\t}\n\t\treturn val;\n\t}\n\n\t/**\n\t* XXX DO NOT USE. This is a temporary stub function.\n\t* XXX It WILL be removed in the next major release.\n\t*/\n\tfunction destroy() {\n\t\tconsole.warn('Instance method `debug.destroy()` is deprecated and no longer does anything. It will be removed in the next major version of `debug`.');\n\t}\n\n\tcreateDebug.enable(createDebug.load());\n\n\treturn createDebug;\n}\n\nmodule.exports = setup;\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/node_modules/debug/src/common.js?");
3850
-
3851
- /***/ }),
3852
-
3853
3899
  /***/ "./node_modules/stream-browserify/index.js":
3854
3900
  /*!*************************************************!*\
3855
3901
  !*** ./node_modules/stream-browserify/index.js ***!
@@ -3867,7 +3913,7 @@ eval("// Copyright Joyent, Inc. and other Node contributors.\n//\n// Permission
3867
3913
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3868
3914
 
3869
3915
  "use strict";
3870
- eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Modal)\n/* harmony export */ });\n/**\n * A Small Modal Class\n * @module Modal\n */\n/* globals Event dcpConfig */\nclass Modal {\n constructor (title, message, callback = false, exitHandler = false, {\n continueLabel = 'Continue',\n cancelLabel = 'Cancel',\n cancelVisible = true\n } = {}) {\n const modal = document.createElement('div')\n modal.className = 'dcp-modal-container-old day'\n modal.innerHTML = `\n <dialog class=\"dcp-modal-content\">\n <div class=\"dcp-modal-header\">\n <h2>${title}<button type=\"button\" class=\"close\">&times;</button></h2>\n ${message ? '<p>' + message + '</p>' : ''}\n </div>\n <div class=\"dcp-modal-loading hidden\">\n <div class='loading'></div>\n </div>\n <form onsubmit='return false' method=\"dialog\">\n <div class=\"dcp-modal-body\"></div>\n <div class=\"dcp-modal-footer ${cancelVisible ? '' : 'centered'}\">\n <button type=\"submit\" class=\"continue green-modal-button\">${continueLabel}</button>\n <button type=\"button\" class=\"cancel green-modal-button\">${cancelLabel}</button>\n </div>\n </form>\n </dialog>`\n\n // To give a reference to do developer who wants to override the form submit.\n // May occur if they want to validate the information in the backend\n // without closing the modal prematurely.\n this.form = modal.querySelector('.dcp-modal-content form')\n this.continueButton = modal.querySelector('.dcp-modal-footer button.continue')\n this.cancelButton = modal.querySelector('.dcp-modal-footer button.cancel')\n this.closeButton = modal.querySelector('.dcp-modal-header .close')\n if (!cancelVisible) {\n this.cancelButton.style.display = 'none'\n }\n\n // To remove the event listener, the reference to the original function\n // added is required.\n this.formSubmitHandler = this.continue.bind(this)\n\n modal.addEventListener('keydown', function (event) {\n event.stopPropagation()\n // 27 is the keycode for the escape key.\n if (event.keyCode === 27) this.close()\n }.bind(this))\n\n this.container = modal\n this.callback = callback\n this.exitHandler = exitHandler\n document.body.appendChild(modal)\n }\n\n changeFormSubmitHandler (newFormSubmitHandler) {\n this.formSubmitHandler = newFormSubmitHandler\n }\n\n /**\n * Validates the form values in the modal and calls the modal's callback\n */\n async continue (event) {\n // To further prevent form submission from trying to redirect from the\n // current page.\n if (event instanceof Event) {\n event.preventDefault()\n }\n let fieldsAreValid = true\n let formElements = this.container.querySelectorAll('.dcp-modal-body select, .dcp-modal-body input, .dcp-modal-body textarea')\n\n const formValues = []\n if (typeof formElements.length === 'undefined') formElements = [formElements]\n // Separate into two loops to enable input validation requiring formValues\n // that come after it. e.g. Two password fields matching.\n for (let i = 0; i < formElements.length; i++) {\n switch (formElements[i].type) {\n case 'file':\n formValues.push(formElements[i])\n break\n case 'checkbox':\n formValues.push(formElements[i].checked)\n break\n default:\n formValues.push(formElements[i].value)\n break\n }\n }\n for (let i = 0; i < formElements.length; i++) {\n if (formElements[i].validation) {\n // Optional fields are allowed to be empty but still can't be wrong if not empty.\n if (!(formElements[i].value === '' && !formElements[i].required)) {\n if (typeof formElements[i].validation === 'function') {\n if (!formElements[i].validation(formValues)) {\n fieldsAreValid = false\n formElements[i].classList.add('is-invalid')\n }\n } else if (!formElements[i].validation.test(formElements[i].value)) {\n fieldsAreValid = false\n formElements[i].classList.add('is-invalid')\n }\n }\n }\n }\n\n if (!fieldsAreValid) return\n\n this.loading()\n if (typeof this.callback === 'function') {\n try {\n return this.callback(formValues)\n } catch (error) {\n console.error('Unexpected error in modal.continue:', error);\n return this.close(false)\n }\n }\n this.close(true)\n }\n\n loading () {\n this.container.querySelector('.dcp-modal-loading').classList.remove('hidden')\n this.container.querySelector('.dcp-modal-body').classList.add('hidden')\n this.container.querySelector('.dcp-modal-footer').classList.add('hidden')\n }\n\n open () {\n this.form.addEventListener('submit', async (event) => {\n const success = await this.formSubmitHandler(event)\n if (success === false) {\n return\n }\n this.close(true)\n })\n // When the user clicks on <span> (x), close the modal\n this.closeButton.addEventListener('click', this.close.bind(this))\n this.cancelButton.addEventListener('click', this.close.bind(this))\n\n // Prevent lingering outlines after clicking some form elements.\n this.container.querySelectorAll('.dcp-modal-body button, .dcp-modal-body input[type=\"checkbox\"]').forEach(element => {\n element.addEventListener('click', () => {\n element.blur()\n })\n })\n\n // Show the modal.\n this.container.style.display = 'block'\n\n const formElements = this.container.querySelectorAll('.dcp-modal-body select, .dcp-modal-body input')\n if (formElements.length) {\n formElements[0].focus()\n if (formElements[0].type === 'text') {\n formElements[0].select()\n }\n for (const el of formElements) {\n if (el.realType) {\n el.type = el.realType\n }\n }\n } else {\n // With no form elements to allow for form submission on enter, focus the\n // continue button.\n this.container.querySelector('.dcp-modal-footer button.continue').focus()\n }\n } // TODO: This should return a promise with the action resolving it\n\n /**\n * Shows the modal and returns a promise of the result of the modal (e.g. was\n * it closed, did its action succeed?)\n */\n showModal () {\n return new Promise((resolve, reject) => {\n this.form.addEventListener('submit', handleContinue.bind(this))\n this.cancelButton.addEventListener('click', handleCancel.bind(this))\n this.closeButton.addEventListener('click', handleCancel.bind(this))\n\n // Prevent lingering outlines after clicking some form elements.\n this.container.querySelectorAll('.dcp-modal-body button, .dcp-modal-body input[type=\"checkbox\"]').forEach(element => {\n element.addEventListener('click', () => {\n element.blur()\n })\n })\n\n // Show the modal.\n this.container.style.display = 'block'\n\n const formElements = this.container.querySelectorAll('.dcp-modal-body select, .dcp-modal-body input')\n if (formElements.length) {\n formElements[0].focus()\n if (formElements[0].type === 'text') {\n formElements[0].select()\n }\n for (const el of formElements) {\n if (el.realType) {\n el.type = el.realType\n }\n }\n } else {\n // With no form elements to allow for form submission on enter, focus the\n // continue button.\n this.continueButton.focus()\n }\n\n async function handleContinue (event) {\n let result\n try {\n result = await this.formSubmitHandler(event)\n } catch (error) {\n reject(error)\n }\n this.close(true)\n resolve(result)\n }\n\n async function handleCancel () {\n let result\n try {\n result = await this.close()\n } catch (error) {\n reject(error)\n }\n resolve(result)\n }\n })\n }\n\n close (success = false) {\n this.container.style.display = 'none'\n if (this.container.parentNode) {\n this.container.parentNode.removeChild(this.container)\n }\n\n // @todo this needs to remove eventlisteners to prevent memory leaks\n\n if ((success !== true) && typeof this.exitHandler === 'function') {\n return this.exitHandler(this)\n }\n }\n\n /**\n * Adds different form elements to the modal depending on the case.\n *\n * @param {*} elements - The properties of the form elements to add.\n * @returns {HTMLElement} The input form elements.\n */\n addFormElement (...elements) {\n const body = this.container.querySelector('.dcp-modal-body')\n const inputElements = []\n let label\n for (let i = 0; i < elements.length; i++) {\n let row = document.createElement('div')\n row.className = 'row'\n\n let col, input\n switch (elements[i].type) {\n case 'button':\n col = document.createElement('div')\n col.className = 'col-md-12'\n\n input = document.createElement('button')\n input.innerHTML = elements[i].label\n input.type = 'button'\n input.classList.add('green-modal-button')\n if (!elements[i].onclick) {\n throw new Error('A button in the modal body should have an on click event handler.')\n }\n input.addEventListener('click', elements[i].onclick)\n\n col.appendChild(input)\n row.appendChild(col)\n break\n case 'textarea':\n col = document.createElement('div')\n col.className = 'col-md-12'\n\n input = document.createElement('textarea')\n input.className = 'text-input-field form-control'\n if (elements[i].placeholder) input.placeholder = elements[i].placeholder\n\n col.appendChild(input)\n row.appendChild(col)\n break\n case 'text':\n case 'email':\n case 'number':\n case 'password': {\n const inputCol = document.createElement('div')\n\n input = document.createElement('input')\n input.type = elements[i].type\n input.validation = elements[i].validation\n input.autocomplete = elements[i].autocomplete || (elements[i].type === 'password' ? 'off' : 'on')\n input.className = 'text-input-field form-control'\n\n // Adding bootstraps custom feedback styles.\n let invalidFeedback = null\n if (elements[i].invalidFeedback) {\n invalidFeedback = document.createElement('div')\n invalidFeedback.className = 'invalid-feedback'\n invalidFeedback.innerText = elements[i].invalidFeedback\n }\n\n if (elements[i].type === 'password') {\n elements[i].realType = 'password'\n }\n\n if (elements[i].label) {\n const labelCol = document.createElement('div')\n label = document.createElement('label')\n label.innerText = elements[i].label\n const inputId = 'dcp-modal-input-' + this.container.querySelectorAll('input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"]').length\n label.setAttribute('for', inputId)\n input.id = inputId\n labelCol.classList.add('col-md-6', 'label-column')\n labelCol.appendChild(label)\n row.appendChild(labelCol)\n inputCol.className = 'col-md-6'\n } else {\n inputCol.className = 'col-md-12'\n }\n\n inputCol.appendChild(input)\n if (invalidFeedback !== null) {\n inputCol.appendChild(invalidFeedback)\n }\n row.appendChild(inputCol)\n break\n }\n case 'select':\n col = document.createElement('div')\n col.className = 'col-md-4'\n\n label = document.createElement('span')\n label.innerText = elements[i].label\n\n col.appendChild(label)\n row.appendChild(col)\n\n col = document.createElement('div')\n col.className = 'col-md-8'\n\n input = document.createElement('select')\n\n col.appendChild(input)\n row.appendChild(col)\n break\n case 'checkbox': {\n row.classList.add('checkbox-row')\n const checkboxLabelCol = document.createElement('div')\n checkboxLabelCol.classList.add('label-column', 'checkbox-label-column')\n\n label = document.createElement('label')\n label.innerText = elements[i].label\n label.for = 'dcp-checkbox-input-' + this.container.querySelectorAll('input[type=\"checkbox\"]').length\n label.setAttribute('for', label.for)\n label.className = 'checkbox-label'\n\n checkboxLabelCol.appendChild(label)\n\n const checkboxCol = document.createElement('div')\n checkboxCol.classList.add('checkbox-column')\n\n input = document.createElement('input')\n input.type = 'checkbox'\n input.id = label.for\n if (elements[i].checked) {\n input.checked = true\n }\n\n checkboxCol.appendChild(input)\n\n if (elements[i].labelToTheRightOfCheckbox) {\n checkboxCol.classList.add('col-md-5')\n row.appendChild(checkboxCol)\n checkboxLabelCol.classList.add('col-md-7')\n row.appendChild(checkboxLabelCol)\n } else {\n checkboxLabelCol.classList.add('col-md-6')\n checkboxCol.classList.add('col-md-6')\n row.appendChild(checkboxLabelCol)\n row.appendChild(checkboxCol)\n }\n break\n }\n case 'file':\n [input, row] = this.addFileInput(elements[i], input, row)\n break\n case 'label':\n row.classList.add('label-row')\n label = document.createElement('label')\n label.innerText = elements[i].label\n row.appendChild(label)\n break\n }\n\n // Copy other possibly specified element properties:\n const inputPropertyNames = ['title', 'inputmode', 'value', 'minLength', 'maxLength', 'size', 'required', 'pattern', 'min', 'max', 'step', 'placeholder', 'accept', 'multiple', 'id', 'onkeypress', 'oninput', 'for', 'readonly', 'autocomplete']\n for (const propertyName of inputPropertyNames) {\n if (Object.prototype.hasOwnProperty.call(elements[i], propertyName)) {\n if (propertyName === 'for' && !label.hasAttribute(propertyName)) {\n label.setAttribute(propertyName, elements[i][propertyName])\n }\n if (propertyName.startsWith('on')) {\n input.addEventListener(propertyName.slice(2), elements[i][propertyName])\n } else {\n input.setAttribute(propertyName, elements[i][propertyName])\n }\n }\n }\n\n inputElements.push(input)\n body.appendChild(row)\n }\n\n if (inputElements.length === 1) return inputElements[0]\n else return inputElements\n }\n\n /**\n * Adds a drag and drop file form element to the modal.\n *\n * @param {*} fileInputProperties - An object specifying some of the\n * properties of the file input element.\n * @param {*} fileInput - Placeholders to help create the file\n * input.\n * @param {HTMLDivElement} row - Placeholders to help create the file\n * input.\n */\n addFileInput (fileInputProperties, fileInput, row) {\n // Adding the upload label.\n const uploadLabel = document.createElement('label')\n uploadLabel.innerText = fileInputProperties.label\n row.appendChild(uploadLabel)\n const body = this.container.querySelector('.dcp-modal-body')\n body.appendChild(row)\n const fileSelectionRow = document.createElement('div')\n fileSelectionRow.id = 'file-selection-row'\n\n // Adding the drag and drop file upload input.\n const dropContainer = document.createElement('div')\n dropContainer.id = 'drop-container'\n\n // Adding an image of a wallet\n const imageContainer = document.createElement('div')\n imageContainer.id = 'image-container'\n const walletImage = document.createElement('span')\n walletImage.classList.add('fas', 'fa-wallet')\n imageContainer.appendChild(walletImage)\n\n // Adding some text prompts\n const dropMessage = document.createElement('span')\n dropMessage.innerText = 'Drop a keystore file here'\n const orMessage = document.createElement('span')\n orMessage.innerText = 'or'\n\n // Adding the manual file input element (hiding the default one)\n const fileInputContainer = document.createElement('div')\n const fileInputLabel = document.createElement('label')\n // Linking the label to the file input so that clicking on the label\n // activates the file input.\n fileInputLabel.setAttribute('for', 'file-input')\n fileInputLabel.innerText = 'Browse'\n fileInput = document.createElement('input')\n fileInput.type = fileInputProperties.type\n fileInput.id = 'file-input'\n // To remove the lingering outline after selecting the file.\n fileInput.addEventListener('click', () => {\n fileInput.blur()\n })\n fileInputContainer.append(fileInput, fileInputLabel)\n\n // Creating the final row element to append to the modal body.\n dropContainer.append(imageContainer, dropMessage, orMessage, fileInputContainer)\n fileSelectionRow.appendChild(dropContainer)\n\n // Adding functionality to the drag and drop file input.\n dropContainer.addEventListener('drop', selectDroppedFile.bind(this))\n dropContainer.addEventListener('drop', unhighlightDropArea)\n // Prevent file from being opened by the browser.\n dropContainer.ondragover = highlightDropArea\n dropContainer.ondragenter = highlightDropArea\n dropContainer.ondragleave = unhighlightDropArea\n\n fileInput.addEventListener('change', handleFileChange)\n\n const fileNamePlaceholder = document.createElement('center')\n fileNamePlaceholder.id = 'file-name-placeholder'\n fileNamePlaceholder.className = 'row'\n fileNamePlaceholder.innerText = ''\n fileSelectionRow.appendChild(fileNamePlaceholder)\n fileNamePlaceholder.classList.add('hidden')\n\n // Check if the continue button is invalid on the keystore upload modal and\n // click it if it should no longer be invalid.\n this.continueButton.addEventListener('invalid', () => {\n const fileFormElements = this.container.querySelectorAll('.dcp-modal-body input[type=\"file\"], .dcp-modal-body input[type=\"text\"]')\n const filledInFileFormElements = Array.from(fileFormElements).filter(fileFormElement => fileFormElement.value !== '')\n if (fileFormElements.length !== 0 && filledInFileFormElements.length !== 0) {\n this.continueButton.setCustomValidity('')\n // Clicking instead of dispatching a submit event to ensure other form validation is used before submitting the form.\n this.continueButton.click()\n }\n })\n\n return [fileInput, fileSelectionRow]\n\n /**\n * Checks that the dropped items contain only a single keystore file.\n * If valid, sets the file input's value to the dropped file.\n * @param {DragEvent} event - Contains the files dropped.\n */\n function selectDroppedFile (event) {\n // Prevent file from being opened.\n event.preventDefault()\n\n // Check if only one file was dropped.\n const wasOneFileDropped = event.dataTransfer.items.length === 1 ||\n event.dataTransfer.files.length === 1\n updateFileSelectionStatus(wasOneFileDropped)\n if (!wasOneFileDropped) {\n fileInput.setCustomValidity('Only one file can be uploaded.')\n fileInput.reportValidity()\n return\n } else {\n fileInput.setCustomValidity('')\n }\n\n // Now to use the DataTransfer interface to access the file(s), setting\n // the value of the file input.\n const file = event.dataTransfer.files[0]\n\n if (checkFileExtension(file)) {\n fileInput.files = event.dataTransfer.files\n fileInput.dispatchEvent(new Event('change'))\n }\n }\n\n function handleFileChange () {\n if (checkFileExtension(this.files[0]) && this.files.length === 1) {\n fileNamePlaceholder.innerText = `Selected File: ${this.files[0].name}`\n updateFileSelectionStatus(true)\n // Invoke a callback if additional functionality is required.\n if (typeof fileInputProperties.callback === 'function') {\n fileInputProperties.callback(this.files[0])\n }\n }\n }\n\n /**\n * Checks if the file extension on the inputted file is correct.\n * @param {File} file - The file to check\n * @returns {boolean} True if the file extension is valid, false otherwise.\n */\n function checkFileExtension (file) {\n // If there's no restriction, return true.\n if (!fileInputProperties.extension) {\n return true\n }\n const fileExtension = file.name.split('.').pop()\n const isValidExtension = fileExtension === fileInputProperties.extension\n updateFileSelectionStatus(isValidExtension)\n if (!isValidExtension) {\n fileInput.setCustomValidity(`Only a .${fileInputProperties.extension} file can be uploaded.`)\n fileInput.reportValidity()\n fileNamePlaceholder.classList.add('hidden')\n } else {\n fileInput.setCustomValidity('')\n }\n return isValidExtension\n }\n\n /**\n * Updates the file input to reflect the validity of the current file\n * selection.\n * @param {boolean} isValidFileSelection - True if a single .keystore file\n * was selected. False otherwise.\n */\n function updateFileSelectionStatus (isValidFileSelection) {\n imageContainer.innerHTML = ''\n const statusImage = document.createElement('span')\n statusImage.classList.add('fas', isValidFileSelection ? 'fa-check' : 'fa-times')\n statusImage.style.color = isValidFileSelection ? 'green' : 'red'\n imageContainer.appendChild(statusImage)\n\n if (!isValidFileSelection) {\n fileInput.value = null\n fileNamePlaceholder.classList.add('hidden')\n } else {\n fileNamePlaceholder.classList.remove('hidden')\n }\n\n // If the modal contains a password field for a keystore file, change its\n // visibility.\n const walletPasswordInputContainer = document.querySelector('.dcp-modal-body input[type=\"password\"]').parentElement.parentElement\n if (walletPasswordInputContainer) {\n if (isValidFileSelection) {\n walletPasswordInputContainer.classList.remove('hidden')\n const walletPasswordInput = document.querySelector('.dcp-modal-body input[type=\"password\"]')\n walletPasswordInput.focus()\n } else {\n walletPasswordInputContainer.classList.add('hidden')\n }\n }\n }\n\n function highlightDropArea (event) {\n event.preventDefault()\n this.classList.add('highlight')\n }\n\n function unhighlightDropArea (event) {\n event.preventDefault()\n this.classList.remove('highlight')\n }\n }\n\n /**\n * Sets up a custom tooltip to pop up when the passwords do not match, but are\n * valid otherwise.\n */\n addFormValidationForPasswordConfirmation () {\n const [newPassword, confirmPassword] = document.querySelectorAll('.dcp-modal-body input[type=\"password\"]')\n if (!newPassword || !confirmPassword) {\n throw Error('New Password field and Confirm Password fields not present.')\n }\n\n newPassword.addEventListener('input', checkMatchingPasswords)\n confirmPassword.addEventListener('input', checkMatchingPasswords)\n\n function checkMatchingPasswords () {\n if (newPassword.value !== confirmPassword.value &&\n newPassword.validity.valid &&\n confirmPassword.validity.valid) {\n newPassword.setCustomValidity('Both passwords must match.')\n } else if (newPassword.value === confirmPassword.value ||\n newPassword.validity.tooShort ||\n newPassword.validity.patternMismatch ||\n newPassword.validity.valueMissing ||\n confirmPassword.validity.tooShort ||\n confirmPassword.validity.patternMismatch ||\n confirmPassword.validity.valueMissing) {\n // If the passwords fields match or have become invalidated some other\n // way again, reset the custom message.\n newPassword.setCustomValidity('')\n }\n }\n }\n\n updateInvalidEmailMessage() {\n const email = document.querySelector('.dcp-modal-body input[id=\"email\"')\n if (!email){\n throw Error(\"Email field not present\")\n }\n email.addEventListener('input', checkValidEmail);\n function checkValidEmail() {\n if (!email.validity.patternMismatch &&\n !email.validity.valueMissing) {\n email.setCustomValidity('')\n } else {\n email.setCustomValidity(\"Enter a valid email address.\")\n }\n\n }\n }\n\n /**\n * Adds message(s) to the modal's body.\n * @param {string} messages - The message(s) to add to the modal's body.\n * @returns Paragraph element(s) containing the message(s) added to the\n * modal's body.\n */\n addMessage (...messages) {\n const elements = []\n const body = this.container.querySelector('.dcp-modal-body')\n for (let i = 0; i < messages.length; i++) {\n const row = document.createElement('div')\n row.className = 'row'\n\n const paragraph = document.createElement('p')\n paragraph.innerHTML = messages[i]\n paragraph.classList.add('message')\n row.appendChild(paragraph)\n body.appendChild(row)\n\n elements.push(paragraph)\n }\n\n if (elements.length === 1) return elements[0]\n else return elements\n }\n\n addHorizontalRule () {\n const body = this.container.querySelector('.dcp-modal-body')\n body.appendChild(document.createElement('hr'))\n }\n\n // Does what it says. Still ill advised to use unless you have to.\n addCustomHTML (htmlStr, browseCallback) {\n const elements = []\n const body = this.container.querySelector('.dcp-modal-body')\n body.innerHTML += htmlStr\n body.querySelector('#browse-button').addEventListener('click', browseCallback.bind(this, this))\n\n if (elements.length === 1) return elements[0]\n else return elements\n }\n\n addButton (...buttons) {\n const elements = []\n const body = this.container.querySelector('.dcp-modal-body')\n for (let i = 0; i < buttons.length; i++) {\n const row = document.createElement('div')\n row.className = 'row'\n\n let col = document.createElement('div')\n col.className = 'col-md-4'\n\n const description = document.createElement('span')\n description.innerText = buttons[i].description\n\n col.appendChild(description)\n row.appendChild(col)\n\n col = document.createElement('div')\n col.className = 'col-md-8'\n\n const button = document.createElement('button')\n button.innerText = buttons[i].label\n button.addEventListener('click', buttons[i].callback.bind(this, this))\n\n elements.push(button)\n\n col.appendChild(button)\n row.appendChild(col)\n\n body.appendChild(row)\n }\n\n if (elements.length === 1) return elements[0]\n else return elements\n }\n}\n\n\n// Inject our special stylesheet from dcp-client only if we're on the portal webpage.\nif (typeof window !== 'undefined' && typeof document !== 'undefined' && dcpConfig.portal.location.hostname === window.location.hostname) {\n // <link rel='stylesheet' href='/css/dashboard.css'>\n const stylesheet = document.createElement('link')\n stylesheet.rel = 'stylesheet'\n // Needed for the duplicate check done later.\n stylesheet.id = 'dcp-modal-styles'\n\n const dcpClientBundle = document.getElementById('_dcp_client_bundle')\n let src\n if (dcpClientBundle) {\n src = dcpClientBundle.src.replace('dcp-client-bundle.js', 'dcp-modal-style.css')\n } else {\n src = dcpConfig.portal.location.href + 'dcp-client/dist/dcp-modal-style.css'\n }\n\n stylesheet.href = src\n // If the style was injected before, don't inject it again.\n // Could occur when loading a file that imports Modal.js and loading\n // comput.min.js in the same HTML file.\n if (document.getElementById(stylesheet.id) === null) {\n document.getElementsByTagName('head')[0].appendChild(stylesheet)\n }\n\n if (typeof {\"version\":\"0bf54dbe88ee11e84b28e62b73cbd154d06967ea\",\"branch\":\"release\",\"dcpClient\":{\"version\":\"4.2.22\",\"from\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#prod-20221115\",\"resolved\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#295f18d25636449034433ddd3789070a5406c98e\"},\"built\":\"Mon Nov 21 2022 11:41:21 GMT-0500 (Eastern Standard Time)\",\"config\":{\"generated\":\"Mon 21 Nov 2022 11:41:19 AM EST by erose on lorge\",\"build\":\"debug\"},\"webpack\":\"5.74.0\",\"node\":\"v14.21.1\"} !== 'undefined' && typeof window.Modal === 'undefined') {\n window.Modal = Modal\n }\n}\n\n\n//# sourceURL=webpack://dcp/./portal/www/js/modal.js?");
3916
+ eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ \"default\": () => (/* binding */ Modal)\n/* harmony export */ });\n/**\n * A Small Modal Class\n * @module Modal\n */\n/* globals Event dcpConfig */\nclass Modal {\n constructor (title, message, callback = false, exitHandler = false, {\n continueLabel = 'Continue',\n cancelLabel = 'Cancel',\n cancelVisible = true\n } = {}) {\n const modal = document.createElement('div')\n modal.className = 'dcp-modal-container-old day'\n modal.innerHTML = `\n <dialog class=\"dcp-modal-content\">\n <div class=\"dcp-modal-header\">\n <h2>${title}<button type=\"button\" class=\"close\">&times;</button></h2>\n ${message ? '<p>' + message + '</p>' : ''}\n </div>\n <div class=\"dcp-modal-loading hidden\">\n <div class='loading'></div>\n </div>\n <form onsubmit='return false' method=\"dialog\">\n <div class=\"dcp-modal-body\"></div>\n <div class=\"dcp-modal-footer ${cancelVisible ? '' : 'centered'}\">\n <button type=\"submit\" class=\"continue green-modal-button\">${continueLabel}</button>\n <button type=\"button\" class=\"cancel green-modal-button\">${cancelLabel}</button>\n </div>\n </form>\n </dialog>`\n\n // To give a reference to do developer who wants to override the form submit.\n // May occur if they want to validate the information in the backend\n // without closing the modal prematurely.\n this.form = modal.querySelector('.dcp-modal-content form')\n this.continueButton = modal.querySelector('.dcp-modal-footer button.continue')\n this.cancelButton = modal.querySelector('.dcp-modal-footer button.cancel')\n this.closeButton = modal.querySelector('.dcp-modal-header .close')\n if (!cancelVisible) {\n this.cancelButton.style.display = 'none'\n }\n\n // To remove the event listener, the reference to the original function\n // added is required.\n this.formSubmitHandler = this.continue.bind(this)\n\n modal.addEventListener('keydown', function (event) {\n event.stopPropagation()\n // 27 is the keycode for the escape key.\n if (event.keyCode === 27) this.close()\n }.bind(this))\n\n this.container = modal\n this.callback = callback\n this.exitHandler = exitHandler\n document.body.appendChild(modal)\n }\n\n changeFormSubmitHandler (newFormSubmitHandler) {\n this.formSubmitHandler = newFormSubmitHandler\n }\n\n /**\n * Validates the form values in the modal and calls the modal's callback\n */\n async continue (event) {\n // To further prevent form submission from trying to redirect from the\n // current page.\n if (event instanceof Event) {\n event.preventDefault()\n }\n let fieldsAreValid = true\n let formElements = this.container.querySelectorAll('.dcp-modal-body select, .dcp-modal-body input, .dcp-modal-body textarea')\n\n const formValues = []\n if (typeof formElements.length === 'undefined') formElements = [formElements]\n // Separate into two loops to enable input validation requiring formValues\n // that come after it. e.g. Two password fields matching.\n for (let i = 0; i < formElements.length; i++) {\n switch (formElements[i].type) {\n case 'file':\n formValues.push(formElements[i])\n break\n case 'checkbox':\n formValues.push(formElements[i].checked)\n break\n default:\n formValues.push(formElements[i].value)\n break\n }\n }\n for (let i = 0; i < formElements.length; i++) {\n if (formElements[i].validation) {\n // Optional fields are allowed to be empty but still can't be wrong if not empty.\n if (!(formElements[i].value === '' && !formElements[i].required)) {\n if (typeof formElements[i].validation === 'function') {\n if (!formElements[i].validation(formValues)) {\n fieldsAreValid = false\n formElements[i].classList.add('is-invalid')\n }\n } else if (!formElements[i].validation.test(formElements[i].value)) {\n fieldsAreValid = false\n formElements[i].classList.add('is-invalid')\n }\n }\n }\n }\n\n if (!fieldsAreValid) return\n\n this.loading()\n if (typeof this.callback === 'function') {\n try {\n return this.callback(formValues)\n } catch (error) {\n console.error('Unexpected error in modal.continue:', error);\n return this.close(false)\n }\n }\n this.close(true)\n }\n\n loading () {\n this.container.querySelector('.dcp-modal-loading').classList.remove('hidden')\n this.container.querySelector('.dcp-modal-body').classList.add('hidden')\n this.container.querySelector('.dcp-modal-footer').classList.add('hidden')\n }\n\n open () {\n this.form.addEventListener('submit', async (event) => {\n const success = await this.formSubmitHandler(event)\n if (success === false) {\n return\n }\n this.close(true)\n })\n // When the user clicks on <span> (x), close the modal\n this.closeButton.addEventListener('click', this.close.bind(this))\n this.cancelButton.addEventListener('click', this.close.bind(this))\n\n // Prevent lingering outlines after clicking some form elements.\n this.container.querySelectorAll('.dcp-modal-body button, .dcp-modal-body input[type=\"checkbox\"]').forEach(element => {\n element.addEventListener('click', () => {\n element.blur()\n })\n })\n\n // Show the modal.\n this.container.style.display = 'block'\n\n const formElements = this.container.querySelectorAll('.dcp-modal-body select, .dcp-modal-body input')\n if (formElements.length) {\n formElements[0].focus()\n if (formElements[0].type === 'text') {\n formElements[0].select()\n }\n for (const el of formElements) {\n if (el.realType) {\n el.type = el.realType\n }\n }\n } else {\n // With no form elements to allow for form submission on enter, focus the\n // continue button.\n this.container.querySelector('.dcp-modal-footer button.continue').focus()\n }\n } // TODO: This should return a promise with the action resolving it\n\n /**\n * Shows the modal and returns a promise of the result of the modal (e.g. was\n * it closed, did its action succeed?)\n */\n showModal () {\n return new Promise((resolve, reject) => {\n this.form.addEventListener('submit', handleContinue.bind(this))\n this.cancelButton.addEventListener('click', handleCancel.bind(this))\n this.closeButton.addEventListener('click', handleCancel.bind(this))\n\n // Prevent lingering outlines after clicking some form elements.\n this.container.querySelectorAll('.dcp-modal-body button, .dcp-modal-body input[type=\"checkbox\"]').forEach(element => {\n element.addEventListener('click', () => {\n element.blur()\n })\n })\n\n // Show the modal.\n this.container.style.display = 'block'\n\n const formElements = this.container.querySelectorAll('.dcp-modal-body select, .dcp-modal-body input')\n if (formElements.length) {\n formElements[0].focus()\n if (formElements[0].type === 'text') {\n formElements[0].select()\n }\n for (const el of formElements) {\n if (el.realType) {\n el.type = el.realType\n }\n }\n } else {\n // With no form elements to allow for form submission on enter, focus the\n // continue button.\n this.continueButton.focus()\n }\n\n async function handleContinue (event) {\n let result\n try {\n result = await this.formSubmitHandler(event)\n } catch (error) {\n reject(error)\n }\n this.close(true)\n resolve(result)\n }\n\n async function handleCancel () {\n let result\n try {\n result = await this.close()\n } catch (error) {\n reject(error)\n }\n resolve(result)\n }\n })\n }\n\n close (success = false) {\n this.container.style.display = 'none'\n if (this.container.parentNode) {\n this.container.parentNode.removeChild(this.container)\n }\n\n // @todo this needs to remove eventlisteners to prevent memory leaks\n\n if ((success !== true) && typeof this.exitHandler === 'function') {\n return this.exitHandler(this)\n }\n }\n\n /**\n * Adds different form elements to the modal depending on the case.\n *\n * @param {*} elements - The properties of the form elements to add.\n * @returns {HTMLElement} The input form elements.\n */\n addFormElement (...elements) {\n const body = this.container.querySelector('.dcp-modal-body')\n const inputElements = []\n let label\n for (let i = 0; i < elements.length; i++) {\n let row = document.createElement('div')\n row.className = 'row'\n\n let col, input\n switch (elements[i].type) {\n case 'button':\n col = document.createElement('div')\n col.className = 'col-md-12'\n\n input = document.createElement('button')\n input.innerHTML = elements[i].label\n input.type = 'button'\n input.classList.add('green-modal-button')\n if (!elements[i].onclick) {\n throw new Error('A button in the modal body should have an on click event handler.')\n }\n input.addEventListener('click', elements[i].onclick)\n\n col.appendChild(input)\n row.appendChild(col)\n break\n case 'textarea':\n col = document.createElement('div')\n col.className = 'col-md-12'\n\n input = document.createElement('textarea')\n input.className = 'text-input-field form-control'\n if (elements[i].placeholder) input.placeholder = elements[i].placeholder\n\n col.appendChild(input)\n row.appendChild(col)\n break\n case 'text':\n case 'email':\n case 'number':\n case 'password': {\n const inputCol = document.createElement('div')\n\n input = document.createElement('input')\n input.type = elements[i].type\n input.validation = elements[i].validation\n input.autocomplete = elements[i].autocomplete || (elements[i].type === 'password' ? 'off' : 'on')\n input.className = 'text-input-field form-control'\n\n // Adding bootstraps custom feedback styles.\n let invalidFeedback = null\n if (elements[i].invalidFeedback) {\n invalidFeedback = document.createElement('div')\n invalidFeedback.className = 'invalid-feedback'\n invalidFeedback.innerText = elements[i].invalidFeedback\n }\n\n if (elements[i].type === 'password') {\n elements[i].realType = 'password'\n }\n\n if (elements[i].label) {\n const labelCol = document.createElement('div')\n label = document.createElement('label')\n label.innerText = elements[i].label\n const inputId = 'dcp-modal-input-' + this.container.querySelectorAll('input[type=\"text\"], input[type=\"email\"], input[type=\"number\"], input[type=\"password\"]').length\n label.setAttribute('for', inputId)\n input.id = inputId\n labelCol.classList.add('col-md-6', 'label-column')\n labelCol.appendChild(label)\n row.appendChild(labelCol)\n inputCol.className = 'col-md-6'\n } else {\n inputCol.className = 'col-md-12'\n }\n\n inputCol.appendChild(input)\n if (invalidFeedback !== null) {\n inputCol.appendChild(invalidFeedback)\n }\n row.appendChild(inputCol)\n break\n }\n case 'select':\n col = document.createElement('div')\n col.className = 'col-md-4'\n\n label = document.createElement('span')\n label.innerText = elements[i].label\n\n col.appendChild(label)\n row.appendChild(col)\n\n col = document.createElement('div')\n col.className = 'col-md-8'\n\n input = document.createElement('select')\n\n col.appendChild(input)\n row.appendChild(col)\n break\n case 'checkbox': {\n row.classList.add('checkbox-row')\n const checkboxLabelCol = document.createElement('div')\n checkboxLabelCol.classList.add('label-column', 'checkbox-label-column')\n\n label = document.createElement('label')\n label.innerText = elements[i].label\n label.for = 'dcp-checkbox-input-' + this.container.querySelectorAll('input[type=\"checkbox\"]').length\n label.setAttribute('for', label.for)\n label.className = 'checkbox-label'\n\n checkboxLabelCol.appendChild(label)\n\n const checkboxCol = document.createElement('div')\n checkboxCol.classList.add('checkbox-column')\n\n input = document.createElement('input')\n input.type = 'checkbox'\n input.id = label.for\n if (elements[i].checked) {\n input.checked = true\n }\n\n checkboxCol.appendChild(input)\n\n if (elements[i].labelToTheRightOfCheckbox) {\n checkboxCol.classList.add('col-md-5')\n row.appendChild(checkboxCol)\n checkboxLabelCol.classList.add('col-md-7')\n row.appendChild(checkboxLabelCol)\n } else {\n checkboxLabelCol.classList.add('col-md-6')\n checkboxCol.classList.add('col-md-6')\n row.appendChild(checkboxLabelCol)\n row.appendChild(checkboxCol)\n }\n break\n }\n case 'file':\n [input, row] = this.addFileInput(elements[i], input, row)\n break\n case 'label':\n row.classList.add('label-row')\n label = document.createElement('label')\n label.innerText = elements[i].label\n row.appendChild(label)\n break\n }\n\n // Copy other possibly specified element properties:\n const inputPropertyNames = ['title', 'inputmode', 'value', 'minLength', 'maxLength', 'size', 'required', 'pattern', 'min', 'max', 'step', 'placeholder', 'accept', 'multiple', 'id', 'onkeypress', 'oninput', 'for', 'readonly', 'autocomplete']\n for (const propertyName of inputPropertyNames) {\n if (Object.prototype.hasOwnProperty.call(elements[i], propertyName)) {\n if (propertyName === 'for' && !label.hasAttribute(propertyName)) {\n label.setAttribute(propertyName, elements[i][propertyName])\n }\n if (propertyName.startsWith('on')) {\n input.addEventListener(propertyName.slice(2), elements[i][propertyName])\n } else {\n input.setAttribute(propertyName, elements[i][propertyName])\n }\n }\n }\n\n inputElements.push(input)\n body.appendChild(row)\n }\n\n if (inputElements.length === 1) return inputElements[0]\n else return inputElements\n }\n\n /**\n * Adds a drag and drop file form element to the modal.\n *\n * @param {*} fileInputProperties - An object specifying some of the\n * properties of the file input element.\n * @param {*} fileInput - Placeholders to help create the file\n * input.\n * @param {HTMLDivElement} row - Placeholders to help create the file\n * input.\n */\n addFileInput (fileInputProperties, fileInput, row) {\n // Adding the upload label.\n const uploadLabel = document.createElement('label')\n uploadLabel.innerText = fileInputProperties.label\n row.appendChild(uploadLabel)\n const body = this.container.querySelector('.dcp-modal-body')\n body.appendChild(row)\n const fileSelectionRow = document.createElement('div')\n fileSelectionRow.id = 'file-selection-row'\n\n // Adding the drag and drop file upload input.\n const dropContainer = document.createElement('div')\n dropContainer.id = 'drop-container'\n\n // Adding an image of a wallet\n const imageContainer = document.createElement('div')\n imageContainer.id = 'image-container'\n const walletImage = document.createElement('span')\n walletImage.classList.add('fas', 'fa-wallet')\n imageContainer.appendChild(walletImage)\n\n // Adding some text prompts\n const dropMessage = document.createElement('span')\n dropMessage.innerText = 'Drop a keystore file here'\n const orMessage = document.createElement('span')\n orMessage.innerText = 'or'\n\n // Adding the manual file input element (hiding the default one)\n const fileInputContainer = document.createElement('div')\n const fileInputLabel = document.createElement('label')\n // Linking the label to the file input so that clicking on the label\n // activates the file input.\n fileInputLabel.setAttribute('for', 'file-input')\n fileInputLabel.innerText = 'Browse'\n fileInput = document.createElement('input')\n fileInput.type = fileInputProperties.type\n fileInput.id = 'file-input'\n // To remove the lingering outline after selecting the file.\n fileInput.addEventListener('click', () => {\n fileInput.blur()\n })\n fileInputContainer.append(fileInput, fileInputLabel)\n\n // Creating the final row element to append to the modal body.\n dropContainer.append(imageContainer, dropMessage, orMessage, fileInputContainer)\n fileSelectionRow.appendChild(dropContainer)\n\n // Adding functionality to the drag and drop file input.\n dropContainer.addEventListener('drop', selectDroppedFile.bind(this))\n dropContainer.addEventListener('drop', unhighlightDropArea)\n // Prevent file from being opened by the browser.\n dropContainer.ondragover = highlightDropArea\n dropContainer.ondragenter = highlightDropArea\n dropContainer.ondragleave = unhighlightDropArea\n\n fileInput.addEventListener('change', handleFileChange)\n\n const fileNamePlaceholder = document.createElement('center')\n fileNamePlaceholder.id = 'file-name-placeholder'\n fileNamePlaceholder.className = 'row'\n fileNamePlaceholder.innerText = ''\n fileSelectionRow.appendChild(fileNamePlaceholder)\n fileNamePlaceholder.classList.add('hidden')\n\n // Check if the continue button is invalid on the keystore upload modal and\n // click it if it should no longer be invalid.\n this.continueButton.addEventListener('invalid', () => {\n const fileFormElements = this.container.querySelectorAll('.dcp-modal-body input[type=\"file\"], .dcp-modal-body input[type=\"text\"]')\n const filledInFileFormElements = Array.from(fileFormElements).filter(fileFormElement => fileFormElement.value !== '')\n if (fileFormElements.length !== 0 && filledInFileFormElements.length !== 0) {\n this.continueButton.setCustomValidity('')\n // Clicking instead of dispatching a submit event to ensure other form validation is used before submitting the form.\n this.continueButton.click()\n }\n })\n\n return [fileInput, fileSelectionRow]\n\n /**\n * Checks that the dropped items contain only a single keystore file.\n * If valid, sets the file input's value to the dropped file.\n * @param {DragEvent} event - Contains the files dropped.\n */\n function selectDroppedFile (event) {\n // Prevent file from being opened.\n event.preventDefault()\n\n // Check if only one file was dropped.\n const wasOneFileDropped = event.dataTransfer.items.length === 1 ||\n event.dataTransfer.files.length === 1\n updateFileSelectionStatus(wasOneFileDropped)\n if (!wasOneFileDropped) {\n fileInput.setCustomValidity('Only one file can be uploaded.')\n fileInput.reportValidity()\n return\n } else {\n fileInput.setCustomValidity('')\n }\n\n // Now to use the DataTransfer interface to access the file(s), setting\n // the value of the file input.\n const file = event.dataTransfer.files[0]\n\n if (checkFileExtension(file)) {\n fileInput.files = event.dataTransfer.files\n fileInput.dispatchEvent(new Event('change'))\n }\n }\n\n function handleFileChange () {\n if (checkFileExtension(this.files[0]) && this.files.length === 1) {\n fileNamePlaceholder.innerText = `Selected File: ${this.files[0].name}`\n updateFileSelectionStatus(true)\n // Invoke a callback if additional functionality is required.\n if (typeof fileInputProperties.callback === 'function') {\n fileInputProperties.callback(this.files[0])\n }\n }\n }\n\n /**\n * Checks if the file extension on the inputted file is correct.\n * @param {File} file - The file to check\n * @returns {boolean} True if the file extension is valid, false otherwise.\n */\n function checkFileExtension (file) {\n // If there's no restriction, return true.\n if (!fileInputProperties.extension) {\n return true\n }\n const fileExtension = file.name.split('.').pop()\n const isValidExtension = fileExtension === fileInputProperties.extension\n updateFileSelectionStatus(isValidExtension)\n if (!isValidExtension) {\n fileInput.setCustomValidity(`Only a .${fileInputProperties.extension} file can be uploaded.`)\n fileInput.reportValidity()\n fileNamePlaceholder.classList.add('hidden')\n } else {\n fileInput.setCustomValidity('')\n }\n return isValidExtension\n }\n\n /**\n * Updates the file input to reflect the validity of the current file\n * selection.\n * @param {boolean} isValidFileSelection - True if a single .keystore file\n * was selected. False otherwise.\n */\n function updateFileSelectionStatus (isValidFileSelection) {\n imageContainer.innerHTML = ''\n const statusImage = document.createElement('span')\n statusImage.classList.add('fas', isValidFileSelection ? 'fa-check' : 'fa-times')\n statusImage.style.color = isValidFileSelection ? 'green' : 'red'\n imageContainer.appendChild(statusImage)\n\n if (!isValidFileSelection) {\n fileInput.value = null\n fileNamePlaceholder.classList.add('hidden')\n } else {\n fileNamePlaceholder.classList.remove('hidden')\n }\n\n // If the modal contains a password field for a keystore file, change its\n // visibility.\n const walletPasswordInputContainer = document.querySelector('.dcp-modal-body input[type=\"password\"]').parentElement.parentElement\n if (walletPasswordInputContainer) {\n if (isValidFileSelection) {\n walletPasswordInputContainer.classList.remove('hidden')\n const walletPasswordInput = document.querySelector('.dcp-modal-body input[type=\"password\"]')\n walletPasswordInput.focus()\n } else {\n walletPasswordInputContainer.classList.add('hidden')\n }\n }\n }\n\n function highlightDropArea (event) {\n event.preventDefault()\n this.classList.add('highlight')\n }\n\n function unhighlightDropArea (event) {\n event.preventDefault()\n this.classList.remove('highlight')\n }\n }\n\n /**\n * Sets up a custom tooltip to pop up when the passwords do not match, but are\n * valid otherwise.\n */\n addFormValidationForPasswordConfirmation () {\n const [newPassword, confirmPassword] = document.querySelectorAll('.dcp-modal-body input[type=\"password\"]')\n if (!newPassword || !confirmPassword) {\n throw Error('New Password field and Confirm Password fields not present.')\n }\n\n newPassword.addEventListener('input', checkMatchingPasswords)\n confirmPassword.addEventListener('input', checkMatchingPasswords)\n\n function checkMatchingPasswords () {\n if (newPassword.value !== confirmPassword.value &&\n newPassword.validity.valid &&\n confirmPassword.validity.valid) {\n newPassword.setCustomValidity('Both passwords must match.')\n } else if (newPassword.value === confirmPassword.value ||\n newPassword.validity.tooShort ||\n newPassword.validity.patternMismatch ||\n newPassword.validity.valueMissing ||\n confirmPassword.validity.tooShort ||\n confirmPassword.validity.patternMismatch ||\n confirmPassword.validity.valueMissing) {\n // If the passwords fields match or have become invalidated some other\n // way again, reset the custom message.\n newPassword.setCustomValidity('')\n }\n }\n }\n\n updateInvalidEmailMessage() {\n const email = document.querySelector('.dcp-modal-body input[id=\"email\"')\n if (!email){\n throw Error(\"Email field not present\")\n }\n email.addEventListener('input', checkValidEmail);\n function checkValidEmail() {\n if (!email.validity.patternMismatch &&\n !email.validity.valueMissing) {\n email.setCustomValidity('')\n } else {\n email.setCustomValidity(\"Enter a valid email address.\")\n }\n\n }\n }\n\n /**\n * Adds message(s) to the modal's body.\n * @param {string} messages - The message(s) to add to the modal's body.\n * @returns Paragraph element(s) containing the message(s) added to the\n * modal's body.\n */\n addMessage (...messages) {\n const elements = []\n const body = this.container.querySelector('.dcp-modal-body')\n for (let i = 0; i < messages.length; i++) {\n const row = document.createElement('div')\n row.className = 'row'\n\n const paragraph = document.createElement('p')\n paragraph.innerHTML = messages[i]\n paragraph.classList.add('message')\n row.appendChild(paragraph)\n body.appendChild(row)\n\n elements.push(paragraph)\n }\n\n if (elements.length === 1) return elements[0]\n else return elements\n }\n\n addHorizontalRule () {\n const body = this.container.querySelector('.dcp-modal-body')\n body.appendChild(document.createElement('hr'))\n }\n\n // Does what it says. Still ill advised to use unless you have to.\n addCustomHTML (htmlStr, browseCallback) {\n const elements = []\n const body = this.container.querySelector('.dcp-modal-body')\n body.innerHTML += htmlStr\n body.querySelector('#browse-button').addEventListener('click', browseCallback.bind(this, this))\n\n if (elements.length === 1) return elements[0]\n else return elements\n }\n\n addButton (...buttons) {\n const elements = []\n const body = this.container.querySelector('.dcp-modal-body')\n for (let i = 0; i < buttons.length; i++) {\n const row = document.createElement('div')\n row.className = 'row'\n\n let col = document.createElement('div')\n col.className = 'col-md-4'\n\n const description = document.createElement('span')\n description.innerText = buttons[i].description\n\n col.appendChild(description)\n row.appendChild(col)\n\n col = document.createElement('div')\n col.className = 'col-md-8'\n\n const button = document.createElement('button')\n button.innerText = buttons[i].label\n button.addEventListener('click', buttons[i].callback.bind(this, this))\n\n elements.push(button)\n\n col.appendChild(button)\n row.appendChild(col)\n\n body.appendChild(row)\n }\n\n if (elements.length === 1) return elements[0]\n else return elements\n }\n}\n\n\n// Inject our special stylesheet from dcp-client only if we're on the portal webpage.\nif (typeof window !== 'undefined' && typeof document !== 'undefined' && dcpConfig.portal.location.hostname === window.location.hostname) {\n // <link rel='stylesheet' href='/css/dashboard.css'>\n const stylesheet = document.createElement('link')\n stylesheet.rel = 'stylesheet'\n // Needed for the duplicate check done later.\n stylesheet.id = 'dcp-modal-styles'\n\n const dcpClientBundle = document.getElementById('_dcp_client_bundle')\n let src\n if (dcpClientBundle) {\n src = dcpClientBundle.src.replace('dcp-client-bundle.js', 'dcp-modal-style.css')\n } else {\n src = dcpConfig.portal.location.href + 'dcp-client/dist/dcp-modal-style.css'\n }\n\n stylesheet.href = src\n // If the style was injected before, don't inject it again.\n // Could occur when loading a file that imports Modal.js and loading\n // comput.min.js in the same HTML file.\n if (document.getElementById(stylesheet.id) === null) {\n document.getElementsByTagName('head')[0].appendChild(stylesheet)\n }\n\n if (typeof {\"version\":\"5895f3465816246196dbdb9c0cd5aa200a943c59\",\"branch\":\"release\",\"dcpClient\":{\"version\":\"4.2.24\",\"from\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#prod-20230111\",\"resolved\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#0b4bbf4f5e91b57e19c5bbb66c8160479dca32cd\"},\"built\":\"Mon Jan 23 2023 14:29:03 GMT-0500 (Eastern Standard Time)\",\"config\":{\"generated\":\"Mon 23 Jan 2023 02:29:01 PM EST by erose on lorge\",\"build\":\"debug\"},\"webpack\":\"5.74.0\",\"node\":\"v14.21.2\"} !== 'undefined' && typeof window.Modal === 'undefined') {\n window.Modal = Modal\n }\n}\n\n\n//# sourceURL=webpack://dcp/./portal/www/js/modal.js?");
3871
3917
 
3872
3918
  /***/ }),
3873
3919
 
@@ -3909,7 +3955,7 @@ eval("/** \n * Factory function which creates instances of the future function t
3909
3955
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
3910
3956
 
3911
3957
  "use strict";
3912
- eval("/**\n * @file dcp-assert.js\n * Simple assertion module for DCP. Assertions are only\n * evaluated for debug builds, except for the security-named\n * assertions.\n *\n * @author Wes Garland, wes@kingsds.network\n * @date Dec 2019\n */\n\n\nvar isDebugBuild = undefined;\n\nexports.assertTriggersDebugger = !!(__webpack_require__(/*! process */ \"./node_modules/process/browser.js\").env.DCP_ASSERT_TRIGGERS_DEBUGGER);\nexports.assertTestTriggersDebugger = !!(__webpack_require__(/*! process */ \"./node_modules/process/browser.js\").env.DCP_ASSERT_TEST_TRIGGERS_DEBUGGER);\n\n/** \n * Sets whether this assertion module is running in a debug mode or not. If not called\n * before the first assertion, then we figure this out by checking the module that DCP\n * was configured in with ./configure.sh. The difference is that non-security assertions\n * are ignored during production builds.\n *\n * @note this code test to run very early in the process due to eager module initialization,\n * especially while loading the webpack bundle, which might not have actually set\n * the correct dcpConfig.build yet, so it's stuck at \"bootstrap\", which we treat mostly\n * like a debug build.\n * \n * @param idb {boolean|undefined} falsey if this is release build; truey if this is a debug build; undefined to detect\n */\nexports.setDebugBuild = function dcpAssert$$setDebugBuild(idb)\n{\n if (typeof idb === 'undefined')\n idb = (typeof dcpConfig === 'object') && (dcpConfig.build !== 'release');\n \n /* In release mode, rewrite the non-security assertions as fast dummy functions */\n if (!idb)\n {\n let dummy = function dcpAssert$dummy(){return};\n for (let assertion of assertionList) {\n if (typeof exports[assertion] === 'function')\n exports[assertion] = dummy;\n }\n }\n else\n {\n for (let assertion of assertionList)\n exports[assertion] = exports.always[assertion];\n }\n\n if (typeof dcpConfig === 'undefined' || dcpConfig.build !== 'bootstrap')\n isDebugBuild = Boolean(idb); \n}\n \n/** Generic assertion mechanism. Throws if any argument is not true. */\nlet assert = exports.assert = function dcpAssert$$assert() {\n let e;\n\n if (typeof isDebugBuild === 'undefined')\n exports.setDebugBuild();\n \n if (exports.assertTestTriggersDebugger)\n debugger; // allow-debugger\n \n for (let value of arguments) {\n if (!value) {\n if (exports.assertTriggersDebugger)\n debugger; // allow-debugger\n\n try { /* this throws in ES5 strict mode and maybe future-ES */\n let loc = 2;\n if (Object.keys(exports).map((key) => exports[key]).includes(arguments.callee.caller))\n loc++;\n e = new Error('Assertion failure ' + new Error().stack.toString().split('\\n')[loc].trim());\n } catch(error) {\n e = new Error('Assertion failure');\n }\n e.code = 'EASSERT';\n throw e;\n }\n }\n}\n\n/** Evaluate an expression; assert if the result is not true */\nexports.assertEval = function dcpAssert$$assertEval(expr) {\n assert(eval(expr));\n}\n\n/** Assert to that two values are == equivalent */\nexports.assertEq2 = function dcpAssert$$assertEq2(lValue, rValue) {\n assert(lValue == rValue)\n}\n\n/**\n * Asserts that two values are the same in terms of strict equality (===).\n * Can pass an optional message describing the assertion being made.\n *\n * @param {any} expected The expected value to test for equality\n * @param {any} actual The actual value to compare teh expected value against\n * @param {string} [message=''] An message appended to the assertion error\n */\nexports.assertEq3 = function dcpAssert$$assertEq3(\n expected,\n actual,\n message = '',\n) {\n try {\n assert(expected === actual);\n } catch (e) {\n if (message) {\n e.message += `: ${message}`;\n }\n\n e.message += ` (${expected} !== ${actual})`;\n throw e;\n }\n};\n\n/** Assert to that two values are not == equivalent */\nexports.assertNeq2 = function dcpAssert$$assertNeq2(lValue, rValue) {\n assert(lValue != rValue);\n}\n\n/** Assert to that two values are not the same */\nexports.assertNeq3 = function dcpAssert$$assertNeq3(lValue, rValue) {\n assert(lValue !== rValue);\n}\n\n/**\n * Assertion that ensures a given statement will throw a given exception.\n * @param statement {function} function to invoke which is expected to throw\n * @param statement {string} source code of statement which is evaluated with direct-eval\n * and expected to throw\n * @param code [optional] {string} expected value of the exception's code property\n * @param ctor [optional] {function} function which is expected on the exceptions prototype chain\n * @returns true if expectations were met\n */\nexports.assertThrows = function dcpAssert$$assertThrows(statement, code, ctor) {\n var threw;\n \n if (typeof statement === 'string') {\n statement = function shouldThrow_statement() { eval(arguments[0]) };\n }\n if (arguments.length === 2 && typeof code === 'function') {\n ctor = code;\n code = undefined;\n }\n\n try {\n let result = statement();\n threw = false;\n } catch(e) {\n threw = true;\n if (code)\n assert(e.code === code);\n if (ctor)\n assert(e instanceof ctor);\n }\n\n assert(threw === true);\n}\n\n/**\n * Assertion that ensures a given collection contains a given element.\n *\n * @param {any} haystack The collection to search; must be a Set, Map, Array or Array-like object.\n * @param {any} needle The element to search for\n */\nexports.assertHas = function dcpAssert$$assertHas(haystack, needle) {\n if (Array.isArray(haystack))\n assert(haystack.indexOf(needle) !== -1);\n else if (needle instanceof Set || needle instanceof Map)\n assert(haystack.has(needle));\n else\n assert(Array.from(haystack).indexOf(needle) !== -1);\n}\n\n/**\n * Assertion that ensures a given value is of a given type.\n */\nexports.assertIsA = function dcpAssert$$assertIsA(value, type) {\n assert(typeof value === type);\n}\n\n/* *** All assertions must be defined above here *** */\nconst assertionList = Object.keys(exports);\n\n/** Add the security assertions (not disabled by debug build) */\nfor (let assertion of assertionList) {\n let securityAssertion = 'security' + assertion[0].toUpperCase() + assertion.slice(1);\n exports[securityAssertion] = exports[assertion];\n}\n\n/** Add the 'always' assertions (also not disabled by debug build) */\nexports.always = {};\nfor (let assertion of assertionList) {\n exports.always[assertion] = exports[assertion];\n}\n\n\n//# sourceURL=webpack://dcp/./src/common/dcp-assert.js?");
3958
+ eval("/**\n * @file dcp-assert.js\n * Simple assertion module for DCP. Assertions are only\n * evaluated for debug builds, except for the security-named\n * assertions.\n *\n * @author Wes Garland, wes@kingsds.network\n * @date Dec 2019\n */\n\n\nvar isDebugBuild = undefined;\n\nexports.assertTriggersDebugger = !!(__webpack_require__(/*! process */ \"./node_modules/process/browser.js\").env.DCP_ASSERT_TRIGGERS_DEBUGGER);\nexports.assertTestTriggersDebugger = !!(__webpack_require__(/*! process */ \"./node_modules/process/browser.js\").env.DCP_ASSERT_TEST_TRIGGERS_DEBUGGER);\n\n/** \n * Sets whether this assertion module is running in a debug mode or not. If not called\n * before the first assertion, then we figure this out by checking the module that DCP\n * was configured in with ./configure.sh. The difference is that non-security assertions\n * are ignored during production builds.\n *\n * @note this code test to run very early in the process due to eager module initialization,\n * especially while loading the webpack bundle, which might not have actually set\n * the correct dcpConfig.build yet, so it's stuck at \"bootstrap\", which we treat mostly\n * like a debug build.\n * \n * @param idb {boolean|undefined} falsey if this is release build; truey if this is a debug build; undefined to detect\n */\nexports.setDebugBuild = function dcpAssert$$setDebugBuild(idb)\n{\n if (typeof idb === 'undefined')\n idb = (typeof dcpConfig === 'object') && (dcpConfig.build !== 'release');\n \n /* In release mode, rewrite the non-security assertions as fast dummy functions */\n if (!idb)\n {\n let dummy = function dcpAssert$dummy(){ return false; /* allows us test for assert disabled */ };\n for (let assertion of assertionList) {\n if (typeof exports[assertion] === 'function')\n exports[assertion] = dummy;\n }\n }\n else\n {\n for (let assertion of assertionList)\n exports[assertion] = exports.always[assertion];\n }\n\n if (typeof dcpConfig === 'undefined' || dcpConfig.build !== 'bootstrap')\n isDebugBuild = Boolean(idb); \n}\n \n/** Generic assertion mechanism. Throws if any argument is not true. */\nlet assert = exports.assert = function dcpAssert$$assert() {\n let e;\n\n if (typeof isDebugBuild === 'undefined')\n exports.setDebugBuild();\n\n if (arguments.length === 0)\n return true; /* allow us to test for enabled asserts */\n\n if (exports.assertTestTriggersDebugger)\n debugger; // allow-debugger\n \n for (let value of arguments) {\n if (!value) {\n if (exports.assertTriggersDebugger)\n debugger; // allow-debugger\n\n try { /* this throws in ES5 strict mode and maybe future-ES */\n let loc = 2;\n if (Object.keys(exports).map((key) => exports[key]).includes(arguments.callee.caller))\n loc++;\n e = new Error('Assertion failure ' + new Error().stack.toString().split('\\n')[loc].trim());\n } catch(error) {\n e = new Error('Assertion failure');\n }\n e.code = 'EASSERT';\n throw e;\n }\n }\n}\n\n/** Evaluate an expression; assert if the result is not true */\nexports.assertEval = function dcpAssert$$assertEval(expr) {\n assert(eval(expr));\n}\n\n/** Assert to that two values are == equivalent */\nexports.assertEq2 = function dcpAssert$$assertEq2(lValue, rValue) {\n assert(lValue == rValue)\n}\n\n/**\n * Asserts that two values are the same in terms of strict equality (===).\n * Can pass an optional message describing the assertion being made.\n *\n * @param {any} expected The expected value to test for equality\n * @param {any} actual The actual value to compare teh expected value against\n * @param {string} [message=''] An message appended to the assertion error\n */\nexports.assertEq3 = function dcpAssert$$assertEq3(\n expected,\n actual,\n message = '',\n) {\n try {\n assert(expected === actual);\n } catch (e) {\n if (message) {\n e.message += `: ${message}`;\n }\n\n e.message += ` (${expected} !== ${actual})`;\n throw e;\n }\n};\n\n/** Assert to that two values are not == equivalent */\nexports.assertNeq2 = function dcpAssert$$assertNeq2(lValue, rValue) {\n assert(lValue != rValue);\n}\n\n/** Assert to that two values are not the same */\nexports.assertNeq3 = function dcpAssert$$assertNeq3(lValue, rValue) {\n assert(lValue !== rValue);\n}\n\n/**\n * Assertion that ensures a given statement will throw a given exception.\n * @param statement {function} function to invoke which is expected to throw\n * @param statement {string} source code of statement which is evaluated with direct-eval\n * and expected to throw\n * @param code [optional] {string} expected value of the exception's code property\n * @param ctor [optional] {function} function which is expected on the exceptions prototype chain\n * @returns true if expectations were met\n */\nexports.assertThrows = function dcpAssert$$assertThrows(statement, code, ctor) {\n var threw;\n \n if (typeof statement === 'string') {\n statement = function shouldThrow_statement() { eval(arguments[0]) };\n }\n if (arguments.length === 2 && typeof code === 'function') {\n ctor = code;\n code = undefined;\n }\n\n try {\n let result = statement();\n threw = false;\n } catch(e) {\n threw = true;\n if (code)\n assert(e.code === code);\n if (ctor)\n assert(e instanceof ctor);\n }\n\n assert(threw === true);\n}\n\n/**\n * Assertion that ensures a given collection contains a given element.\n *\n * @param {any} haystack The collection to search; must be a Set, Map, Array or Array-like object.\n * @param {any} needle The element to search for\n */\nexports.assertHas = function dcpAssert$$assertHas(haystack, needle) {\n if (Array.isArray(haystack))\n assert(haystack.indexOf(needle) !== -1);\n else if (needle instanceof Set || needle instanceof Map)\n assert(haystack.has(needle));\n else\n assert(Array.from(haystack).indexOf(needle) !== -1);\n}\n\n/**\n * Assertion that ensures a given value is of a given type.\n */\nexports.assertIsA = function dcpAssert$$assertIsA(value, type) {\n assert(typeof value === type);\n}\n\n/* *** All assertions must be defined above here *** */\nconst assertionList = Object.keys(exports);\n\n/** Add the security assertions (not disabled by debug build) */\nfor (let assertion of assertionList) {\n let securityAssertion = 'security' + assertion[0].toUpperCase() + assertion.slice(1);\n exports[securityAssertion] = exports[assertion];\n}\n\n/** Add the 'always' assertions (also not disabled by debug build) */\nexports.always = {};\nfor (let assertion of assertionList) {\n exports.always[assertion] = exports[assertion];\n}\n\n\n//# sourceURL=webpack://dcp/./src/common/dcp-assert.js?");
3913
3959
 
3914
3960
  /***/ }),
3915
3961
 
@@ -4042,7 +4088,7 @@ eval("/**\n * @file dcp-url.js Module for working with URLs;
4042
4088
  \*******************************/
4043
4089
  /***/ ((module, exports, __webpack_require__) => {
4044
4090
 
4045
- eval("/* module decorator */ module = __webpack_require__.nmd(module);\n/**\n * @file dcp-xhr.js XMLHttpRequest library for DCP, implemented via\n * the xmlhttprequest-ssl NPM package, that transparently\n * enables KeepAlive (for http and https) in a way that\n * that is call-compatible with the browser code.\n *\n * If this module is invoked in an environment where there\n * is a self.XMLHttpRequest already defined (eg browser),\n * it is assumed that that implementation behaves correctly\n * and this module's export is replaced by that function.\n *\n * @module dcp-xhr\n * @author Wes Garland, wes@kingsds.network\n * @date June 2019\n */\n\nconst { requireNative, process } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\")\nconst debug = process.env.DCP_XHR_DEBUG\n\nvar vlog \nif (debug && debug.match(/\\bverbose\\b/)) {\n /** Verbose logging function: first argument should be an id number */\n vlog = function vlog() {\n var argv = Array.from(arguments)\n argv.unshift('DCP-XHR')\n if (typeof argv[1] === 'number')\n argv[1] += '>'\n else\n argv[0] += '>'\n console.log.apply(null, argv)\n }\n}\nelse\n vlog = Function()\n\n/** Return a node Agent (or workalike) object that matches the given URL.\n * Agents are cached per-protocol so that xmlhttprequest-ssl can actually\n * implement HTTP KeepAlive\n */\nfunction getAgent(url) {\n let prot, agent\n let urlo = requireNative('url').parse(url)\n let pxHost = process.env.DCP_XHR_PROXY || (dcpConfig.network && dcpConfig.network.proxy)\n\n if (!getAgent.cache)\n getAgent.cache = {} /* one agent per protocol */\n\n prot = urlo.protocol.replace(/:/,'')\n if (prot != 'http' && prot != 'https')\n throw new Error('Protocol must be http: or https:!')\n\n if (getAgent.cache[prot])\n return getAgent.cache[prot]\n\n if (pxHost) {\n /* px support experimental! /wg jul 2019 */\n let pxAgent = requireNative(prot + '-proxy-agent')\n if (debug)\n console.log('* XHR using proxy', pxHost)\n agent = new pxAgent(pxHost)\n } else {\n agent = new (requireNative(prot).Agent)({\n keepAlive: true,\n keepAliveMsecs: (dcpConfig.network && dcpConfig.network.httpKeepAliveMsecs) || 180000\n });\n }\n\n getAgent.cache[prot] = agent\n return agent\n}\n\n/** \n * XMLHttpRequest constructor. Instanciates a Proxy which memoizes function calls and\n * property writes before the open() method is invoked. Property accesses before open() \n * return values from a dummy instance. \n *\n * When open() is invoked, we examine the protocol in the URL and set up an instance of\n * require('xmlhttprequest-ssl').XMLHttpRequest. We look at the pending call/writes memo,\n * applying pending method calls and property writes to this object, and then we invoke\n * its own open() method.\n *\n * After open() is invoked, we proxy all property accesses to the 'real' xhr.\n *\n * The reason for these gymnastics is a design error in the xmlhttprequest-ssl module; it\n * requires knowledge of the Agent type, which depends on the protocol, in the constructor,\n * which is contrary to the spec.; the protocol is only officially known once the URL is\n * known.\n *\n * @constructor\n * @see xmlhttprequest-ssl\n * @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest\n * @returns {object} instance of Proxy\n */\nexports.XMLHttpRequest = function dcpXhr$$XMLHttpRequest() {\n var thisId = exports.XMLHttpRequest.id = (exports.XMLHttpRequest.id || 0) + 1\n\n vlog(thisId, `Constructor`, new Error('VLOG XHR').stack.split('\\n')[2])\n const XMLHttpRequest = requireNative('xmlhttprequest-ssl').XMLHttpRequest\n let dummy = new XMLHttpRequest() /* dummy request, used for type-testing */\n let pending = [] /* list of pending function calls or property assignments, memoized before open() */\n let xhr /* actual XMLHttpRequest object; instanciated during open() */\n\n function debugLabel(obj, prop) {\n if (typeof obj[prop] === 'function')\n return '[pxFunction' + (obj === xhr ? \" \" : \" dummy \") + (obj[prop].name || prop) + ']'\n return obj[prop]\n }\n\n /* Apply the pending call/writes memo, then set up so that proxy will operate directly on xhr */\n let open = (function dcpXhr$$XMLHttpRequest$open(requestType, url) {\n var referer = exports.getReferer();\n\n vlog(thisId, `open`, arguments)\n xhr = new XMLHttpRequest({agent: getAgent(url)})\n pending.forEach((capture) => {\n if (capture.type === 'invoke') {\n xhr[capture.prop].apply(xhr, capture.arguments)\n } else {\n xhr[capture.prop] = capture.value\n }\n })\n\n dummy = pending = undefined\n xhr.open.apply(xhr, arguments)\n xhr.setDisableHeaderCheck(true)\n if (debug) console.log(' * XHR:', url)\n if (referer)\n xhr.setRequestHeader('Referer', referer);\n }).bind(this)\n\n return new Proxy({} /**< target */, {\n /* before .open() - return properties of dummy object\n * after .open() - return properties of real object\n */\n get: function dcpXhr$$XMLHttpRequest$pxGetter(target, prop) {\n if (xhr) {\n const p = typeof prop === 'string' ? prop : '{Symbol}';\n vlog(thisId, `get ${p}=${debugLabel(xhr, p)}`)\n if (typeof xhr[prop] === 'function')\n return (function pxFunctionWrapper() {\n vlog(thisId, `invoke`, prop, arguments)\n return xhr[prop].apply(xhr, arguments)\n }).bind(xhr)\n return xhr[prop]\n }\n vlog(thisId, `get dummy ${prop}=${debugLabel(dummy, prop)}`)\n\n if (prop === 'open')\n return open\n if (typeof dummy[prop] === 'function') {\n return function dcpXhr$$XMLHttpRequest$captureFunctionCall() {\n if (xhr)\n xhr[prop] /* don't memoize if .open() happened since property retrieval */\n else\n pending.push({type: 'invoke', prop: prop, arguments: Array.from(arguments)})\n }\n }\n else\n return dummy[prop]\n },\n\n /* memoize assignments or pass through to xhr if we're past open() */\n set: function dcpXhr$$XMLHttpRequest$pxSetter(target, prop, value) {\n if (xhr) {\n let old = debugLabel(xhr, prop)\n xhr[prop] = value\n vlog(thisId, `set ${prop} ${old} -> ${value}`);\n }\n else {\n pending.push({type: 'assignment', prop: prop, value: value})\n vlog(thisId, `memoize ${prop}=${value}`)\n }\n return true\n },\n\n has: function dcpXhr$$XMLHttpRequest$pxHas(target, key) {\n return key in (xhr || dummy)\n },\n\n apply: function dcpXhr$$XMLHttpRequest$pxApply(target, thisArg, argumentsList) {\n return (xhr || dummy).apply(target, thisArg, argumentsList);\n },\n\n ownKeys: function dcpXhr$$XMLHttpRequest$pxOwnKeys(target) {\n return Object.getOwnPropertyNames(xhr || dummy).concat(Object.getOwnPropertySymbols(xhr || dummy))\n },\n\n getOwnPropertyDescriptor: function dcpXhr$$XMLHttpRequest$pxGetOwnPropertyDescriptor(target, prop) {\n return Object.getOwnPropertyDescriptor(xhr || dummy, prop);\n },\n })\n}\n\nvar _referer;\nexports.getReferer = function dcpXhr$$getReferer() {\n _referer = process.env.DCP_XHR_REFERER || (dcpConfig.network && dcpConfig.network['http-referer']);\n\n if (!_referer) {\n if (module && module.parent && module.parent.filename) {\n _referer = requireNative('path').basename(module.parent.filename) + '/dcp-xhr'\n } else {\n /* webpack, probably */\n _referer = String(new Error().stack.split('\\n')[1].match(/\\(.*\\)/)).replace(/^\\(/,'').replace(/\\)$/,'').replace(/\\?.*/,'')\n _referer = _referer.substring(_referer.lastIndexOf(requireNative('path').sep) + 1);\n _referer = _referer.substring(0, _referer.indexOf(':'));\n }\n }\n}\n\nexports.setReferer = function dcpXhr$$setReferer(referer) {\n _referer = referer;\n}\n\n\n//# sourceURL=webpack://dcp/./src/common/dcp-xhr.js?");
4091
+ eval("/* module decorator */ module = __webpack_require__.nmd(module);\n/**\n * @file dcp-xhr.js XMLHttpRequest library for DCP, implemented via\n * the xmlhttprequest-ssl NPM package, that transparently\n * enables KeepAlive (for http and https) in a way that\n * that is call-compatible with the browser code.\n *\n * If this module is invoked in an environment where there\n * is a self.XMLHttpRequest already defined (eg browser),\n * it is assumed that that implementation behaves correctly\n * and this module's export is replaced by that function.\n *\n * @module dcp-xhr\n * @author Wes Garland, wes@kingsds.network\n * @date June 2019\n */\n\nconst { requireNative, process } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\")\nconst debug = process.env.DCP_XHR_DEBUG\n\nvar vlog \nif (debug && debug.match(/\\bverbose\\b/)) {\n /** Verbose logging function: first argument should be an id number */\n vlog = function vlog() {\n var argv = Array.from(arguments)\n argv.unshift('DCP-XHR')\n if (typeof argv[1] === 'number')\n argv[1] += '>'\n else\n argv[0] += '>'\n console.log.apply(null, argv)\n }\n}\nelse\n vlog = Function()\n\n/** Return a node Agent (or workalike) object that matches the given URL.\n * Agents are cached per-protocol so that xmlhttprequest-ssl can actually\n * implement HTTP KeepAlive\n */\nfunction getAgent(url) {\n let prot, agent\n let urlo = requireNative('url').parse(url)\n let pxHost = process.env.DCP_XHR_PROXY || (dcpConfig.network && dcpConfig.network.proxy)\n\n if (!getAgent.cache)\n getAgent.cache = {} /* one agent per protocol */\n\n prot = urlo.protocol.replace(/:/,'')\n if (prot != 'http' && prot != 'https')\n throw new Error('Protocol must be http: or https:!')\n\n if (getAgent.cache[prot])\n return getAgent.cache[prot]\n\n if (pxHost) {\n /* px support experimental! /wg jul 2019 */\n let pxAgent = requireNative(prot + '-proxy-agent')\n if (debug)\n console.log('* XHR using proxy', pxHost)\n agent = new pxAgent(pxHost)\n } else {\n agent = new (requireNative(prot).Agent)({\n keepAlive: true,\n keepAliveMsecs: (dcpConfig.network && dcpConfig.network.httpKeepAliveMsecs) || 180000\n });\n }\n\n getAgent.cache[prot] = agent\n return agent\n}\n\n/** \n * XMLHttpRequest constructor. Instanciates a Proxy which memoizes function calls and\n * property writes before the open() method is invoked. Property accesses before open() \n * return values from a dummy instance. \n *\n * When open() is invoked, we examine the protocol in the URL and set up an instance of\n * require('xmlhttprequest-ssl').XMLHttpRequest. We look at the pending call/writes memo,\n * applying pending method calls and property writes to this object, and then we invoke\n * its own open() method.\n *\n * After open() is invoked, we proxy all property accesses to the 'real' xhr.\n *\n * The reason for these gymnastics is a design error in the xmlhttprequest-ssl module; it\n * requires knowledge of the Agent type, which depends on the protocol, in the constructor,\n * which is contrary to the spec.; the protocol is only officially known once the URL is\n * known.\n *\n * @constructor\n * @see xmlhttprequest-ssl\n * @see https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest\n * @returns {object} instance of Proxy\n */\nexports.XMLHttpRequest = function dcpXhr$$XMLHttpRequest() {\n var thisId = exports.XMLHttpRequest.id = (exports.XMLHttpRequest.id || 0) + 1\n\n vlog(thisId, `Constructor`, new Error('VLOG XHR').stack.split('\\n')[2])\n const XMLHttpRequest = requireNative('@kingsds/xmlhttprequest-ssl').XMLHttpRequest\n let dummy = new XMLHttpRequest() /* dummy request, used for type-testing */\n let pending = [] /* list of pending function calls or property assignments, memoized before open() */\n let xhr /* actual XMLHttpRequest object; instanciated during open() */\n\n function debugLabel(obj, prop) {\n if (typeof obj[prop] === 'function')\n return '[pxFunction' + (obj === xhr ? \" \" : \" dummy \") + (obj[prop].name || prop) + ']'\n return obj[prop]\n }\n\n /* Apply the pending call/writes memo, then set up so that proxy will operate directly on xhr */\n let open = (function dcpXhr$$XMLHttpRequest$open(requestType, url) {\n var referer = exports.getReferer();\n\n vlog(thisId, `open`, arguments)\n xhr = new XMLHttpRequest({agent: getAgent(url)})\n pending.forEach((capture) => {\n if (capture.type === 'invoke') {\n xhr[capture.prop].apply(xhr, capture.arguments)\n } else {\n xhr[capture.prop] = capture.value\n }\n })\n\n dummy = pending = undefined\n xhr.open.apply(xhr, arguments)\n xhr.setDisableHeaderCheck(true)\n if (debug) console.log(' * XHR:', url)\n if (referer)\n xhr.setRequestHeader('Referer', referer);\n }).bind(this)\n\n return new Proxy({} /**< target */, {\n /* before .open() - return properties of dummy object\n * after .open() - return properties of real object\n */\n get: function dcpXhr$$XMLHttpRequest$pxGetter(target, prop) {\n if (xhr) {\n const p = typeof prop === 'string' ? prop : '{Symbol}';\n vlog(thisId, `get ${p}=${debugLabel(xhr, p)}`)\n if (typeof xhr[prop] === 'function')\n return (function pxFunctionWrapper() {\n vlog(thisId, `invoke`, prop, arguments)\n return xhr[prop].apply(xhr, arguments)\n }).bind(xhr)\n return xhr[prop]\n }\n vlog(thisId, `get dummy ${prop}=${debugLabel(dummy, prop)}`)\n\n if (prop === 'open')\n return open\n if (typeof dummy[prop] === 'function') {\n return function dcpXhr$$XMLHttpRequest$captureFunctionCall() {\n if (xhr)\n xhr[prop] /* don't memoize if .open() happened since property retrieval */\n else\n pending.push({type: 'invoke', prop: prop, arguments: Array.from(arguments)})\n }\n }\n else\n return dummy[prop]\n },\n\n /* memoize assignments or pass through to xhr if we're past open() */\n set: function dcpXhr$$XMLHttpRequest$pxSetter(target, prop, value) {\n if (xhr) {\n let old = debugLabel(xhr, prop)\n xhr[prop] = value\n vlog(thisId, `set ${prop} ${old} -> ${value}`);\n }\n else {\n pending.push({type: 'assignment', prop: prop, value: value})\n vlog(thisId, `memoize ${prop}=${value}`)\n }\n return true\n },\n\n has: function dcpXhr$$XMLHttpRequest$pxHas(target, key) {\n return key in (xhr || dummy)\n },\n\n apply: function dcpXhr$$XMLHttpRequest$pxApply(target, thisArg, argumentsList) {\n return (xhr || dummy).apply(target, thisArg, argumentsList);\n },\n\n ownKeys: function dcpXhr$$XMLHttpRequest$pxOwnKeys(target) {\n return Object.getOwnPropertyNames(xhr || dummy).concat(Object.getOwnPropertySymbols(xhr || dummy))\n },\n\n getOwnPropertyDescriptor: function dcpXhr$$XMLHttpRequest$pxGetOwnPropertyDescriptor(target, prop) {\n return Object.getOwnPropertyDescriptor(xhr || dummy, prop);\n },\n })\n}\n\nvar _referer;\nexports.getReferer = function dcpXhr$$getReferer() {\n _referer = process.env.DCP_XHR_REFERER || (dcpConfig.network && dcpConfig.network['http-referer']);\n\n if (!_referer) {\n if (module && module.parent && module.parent.filename) {\n _referer = requireNative('path').basename(module.parent.filename) + '/dcp-xhr'\n } else {\n /* webpack, probably */\n _referer = String(new Error().stack.split('\\n')[1].match(/\\(.*\\)/)).replace(/^\\(/,'').replace(/\\)$/,'').replace(/\\?.*/,'')\n _referer = _referer.substring(_referer.lastIndexOf(requireNative('path').sep) + 1);\n _referer = _referer.substring(0, _referer.indexOf(':'));\n }\n }\n}\n\nexports.setReferer = function dcpXhr$$setReferer(referer) {\n _referer = referer;\n}\n\n\n//# sourceURL=webpack://dcp/./src/common/dcp-xhr.js?");
4046
4092
 
4047
4093
  /***/ }),
4048
4094
 
@@ -4133,7 +4179,7 @@ eval("/**\n * @file client-modal/index.js\n * @author KC Erb\n * @date Mar 2020\
4133
4179
  \*****************************************************/
4134
4180
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4135
4181
 
4136
- eval("/**\n * @file keystoreFile.js\n * Modal providing a way to upload a keystore file and cache it (on request) in local/session storage.\n * \n * @author KC Erb - kcerb@kingsds.network\n * @author Nazila Kharazian - nazila@kingsds.network\n * @date Apr 2020\n */\nconst utils = __webpack_require__(/*! ./utils */ \"./src/dcp-client/client-modal/utils.js\");\nconst { confirm } = __webpack_require__(/*! ./confirm */ \"./src/dcp-client/client-modal/confirm.js\");\nconst passwordModule = __webpack_require__(/*! ./passwordEntry */ \"./src/dcp-client/client-modal/passwordEntry.js\");\n\nexports.config = {\n id: 'dcp-modal-keystore-file',\n required: ['dcp-modal-keystore-file-form', 'dcp-modal-keystore-file-prompt', 'dcp-modal-keystore-file-input', 'dcp-modal-keystore-file-checkbox'],\n path: \"./templates/keystore-file-modal.html\",\n eagerLoad: [passwordModule.config]\n};\n\n/**\n * @param {Object} options From `wallet.get_internal`\n * @param {string} options.name - The name of the keystore type (wallet.get => 'default' and wallet.getId => 'id').\n * @param {string} options.contextId - An optional, user-defined identifier used for caching keystores.\n * See `job.contextId` in the compute-api spec.\n * @param {string} options.jobName - A user-defined job property provided to identify this job.\n * @param {string} options.msg - Optional string denoting the purpose for fetching the keystore\n * @param {function} [onClose] Override default onClose behavior. An instance of `modal` is handed to the function.\n * @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n * @returns {string} contents of Keystore file\n * @memberof module:dcp/client-modal\n * @access public\n * @async\n * @alias getKeystoreFile\n */\nexports.keystoreFile = async function (options, onClose=null) {\n const storageKey = `dcp-keystore-${options.name}-${options.contextId}`;\n \n let jobName = options.jobName;\n if (jobName) {\n jobName = `\"${jobName}\"`;\n } else {\n jobName = 'this job';\n }\n\n let keystoreType;\n switch (options.name) {\n case 'id':\n keystoreType = \"DCP identity\";\n break;\n case 'default':\n keystoreType = \"DCP Bank Account\";\n default:\n keystoreType = options.name;\n break;\n };\n\n let keystoreMsg = options.msg;\n\n let keystoreFile = await getKeystoreFromStorage(storageKey, jobName, onClose);\n if (!keystoreFile) {\n keystoreFile = await getKeystoreFromUser(keystoreType, jobName, storageKey, onClose, keystoreMsg);\n }\n\n return keystoreFile;\n};\n\n/**\n * Alert user that a key from storage will be used, give them a chance to ditch it.\n * @param {string} storageKey key in local or session storage where the file is stored\n * @param {string} jobName helps user know which key is retrieved from storage\n * @returns {string} contents of Keystore file\n */\nasync function getKeystoreFromStorage (storageKey, jobName) {\n let keystoreFile = sessionStorage.getItem(storageKey);\n if (keystoreFile) return keystoreFile;\n \n keystoreFile = localStorage.getItem(storageKey);\n if (keystoreFile) {\n let ks = JSON.parse(keystoreFile);\n let label = ks.label || ks.address;\n let prompt;\n // alert is not necessarily for a job, specify jobname only if there is an actual job\n if(jobName)\n prompt = `You have keystore \"${label}\" saved for ${jobName}. Do you want to use it?`;\n else\n prompt = `You have keystore \"${label}\". Do you want to use it?`;\n let opts = { positive: \"Yes\", negative: \"No\", title: 'Keystore File' };\n let confirmation = await confirm(prompt, opts);\n if (confirmation) {\n sessionStorage.setItem(storageKey, keystoreFile)\n return keystoreFile;\n }\n localStorage.removeItem(storageKey);\n return false;\n };\n};\n\n/**\n * Bring up file prompt modal in browser, give user option to save the file locally for easier access.\n * @param {string} keystoreType Help user know whether to upload an identity or bank or whatever keystore.\n * @param {string} jobName Help user identify the job this key will be used on.\n * @param {string} storageKey Store the key in local/session storage under this key.\n * @param {string} msg Custom message for the modal that specifies the purpose for providing a keystore\n * @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n */\nasync function getKeystoreFromUser (keystoreType, jobName, storageKey, onClose, msg) {\n const [elements, formPromise] = await utils.initModal(exports.config, onClose);\n const [modal, form, prompt, fileInput, rememberInput] = elements;\n \n // default modal text that explains what a keystore is for.\n prompt.innerText = `This application is requesting a keystore file. Keystore files are used to identify ownership or stewardship of bank accounts, users, and other DCP resources. If you upload a keystore file which has been encrypted with a passphrase, the application will not be able to use it until it prompts for a passphrase and you enter it.`;\n if (msg)\n {\n prompt.innerText = msg;\n }\n\n fileInput.focus();\n fileInput.onchange = async function () {\n let files = fileInput.files;\n if (!files || files.length === 0) return;\n const keystoreFile = await fileToString(files[0]);\n try {\n JSON.parse(keystoreFile);\n } catch (error) {\n fileInput.setCustomValidity(\"File could not be parsed as JSON. Please upload a JSON keystore file.\");\n return;\n };\n fileInput.setCustomValidity(\"\");\n };\n\n await formPromise;\n let files = fileInput.files;\n const keystoreFile = await fileToString(files[0]);\n if (rememberInput.checked) {\n window.sessionStorage.setItem(storageKey, keystoreFile);\n window.localStorage.setItem(storageKey, keystoreFile);\n };\n \n utils.MicroModal.close(modal.id);\n return keystoreFile;\n};\n\n/**\n * Helper for reading in an uploaded file and returning the stringified version. \n * @param {File} inputFile Element of FileList\n * @returns {string} Contents of file.\n */\nconst fileToString = function (inputFile) {\n return new Promise((resolve, reject) => {\n const fileReader = new FileReader();\n\n fileReader.onerror = function () {\n reject(new DOMException('Problem parsing input file.'));\n };\n\n fileReader.onload = function () {\n resolve(this.result);\n };\n\n fileReader.readAsText(inputFile);\n });\n};\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/keystoreFile.js?");
4182
+ eval("/**\n * @file keystoreFile.js\n * Modal providing a way to upload a keystore file and cache it (on request) in local/session storage.\n * \n * @author KC Erb - kcerb@kingsds.network\n * @author Nazila Kharazian - nazila@kingsds.network\n * @date Apr 2020\n */\nconst utils = __webpack_require__(/*! ./utils */ \"./src/dcp-client/client-modal/utils.js\");\nconst { confirm } = __webpack_require__(/*! ./confirm */ \"./src/dcp-client/client-modal/confirm.js\");\nconst passwordModule = __webpack_require__(/*! ./passwordEntry */ \"./src/dcp-client/client-modal/passwordEntry.js\");\n\nexports.config = {\n id: 'dcp-modal-keystore-file',\n required: ['dcp-modal-keystore-file-form', 'dcp-modal-keystore-file-prompt', 'dcp-modal-keystore-file-input'],\n path: \"./templates/keystore-file-modal.html\",\n eagerLoad: [passwordModule.config]\n};\n\n/**\n * @param {Object} options From `wallet.get_internal`\n * @param {string} options.name - The name of the keystore type (wallet.get => 'default' and wallet.getId => 'id').\n * @param {string} options.contextId - An optional, user-defined identifier used for caching keystores.\n * See `job.contextId` in the compute-api spec.\n * @param {string} options.jobName - A user-defined job property provided to identify this job.\n * @param {string} options.msg - Optional string denoting the purpose for fetching the keystore\n * @param {function} [onClose] Override default onClose behavior. An instance of `modal` is handed to the function.\n * @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n * @returns {string} contents of Keystore file\n * @memberof module:dcp/client-modal\n * @access public\n * @async\n * @alias getKeystoreFile\n */\nexports.keystoreFile = async function (options, onClose=null) {\n const storageKey = `dcp-keystore-${options.name}-${options.contextId}`;\n \n let jobName = options.jobName;\n if (jobName) {\n jobName = `\"${jobName}\"`;\n } else {\n jobName = 'this job';\n }\n\n let keystoreType;\n switch (options.name) {\n case 'id':\n keystoreType = \"DCP identity\";\n break;\n case 'default':\n keystoreType = \"DCP Bank Account\";\n default:\n keystoreType = options.name;\n break;\n };\n\n let keystoreMsg = options.msg;\n\n let keystoreFile = await getKeystoreFromStorage(storageKey, jobName, onClose);\n if (!keystoreFile) {\n keystoreFile = await getKeystoreFromUser(keystoreType, jobName, storageKey, onClose, keystoreMsg);\n }\n\n return keystoreFile;\n};\n\n/**\n * Alert user that a key from storage will be used, give them a chance to ditch it.\n * @param {string} storageKey key in local or session storage where the file is stored\n * @param {string} jobName helps user know which key is retrieved from storage\n * @returns {string} contents of Keystore file\n */\nasync function getKeystoreFromStorage (storageKey, jobName) {\n let keystoreFile = sessionStorage.getItem(storageKey);\n if (keystoreFile) return keystoreFile;\n \n keystoreFile = localStorage.getItem(storageKey);\n if (keystoreFile) {\n let ks = JSON.parse(keystoreFile);\n let label = ks.label || ks.address;\n let prompt;\n // alert is not necessarily for a job, specify jobname only if there is an actual job\n if(jobName)\n prompt = `You have keystore \"${label}\" saved for ${jobName}. Do you want to use it?`;\n else\n prompt = `You have keystore \"${label}\". Do you want to use it?`;\n let opts = { positive: \"Yes\", negative: \"No\", title: 'Keystore File' };\n let confirmation = await confirm(prompt, opts);\n if (confirmation) {\n sessionStorage.setItem(storageKey, keystoreFile)\n return keystoreFile;\n }\n localStorage.removeItem(storageKey);\n return false;\n };\n};\n\n/**\n * Bring up file prompt modal in browser, give user option to save the file locally for easier access.\n * @param {string} keystoreType Help user know whether to upload an identity or bank or whatever keystore.\n * @param {string} jobName Help user identify the job this key will be used on.\n * @param {string} storageKey Store the key in local/session storage under this key.\n * @param {string} msg Custom message for the modal that specifies the purpose for providing a keystore\n * @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n */\nasync function getKeystoreFromUser (keystoreType, jobName, storageKey, onClose, msg) {\n const [elements, formPromise] = await utils.initModal(exports.config, onClose);\n const [modal, form, prompt, fileInput] = elements;\n \n // default modal text that explains what a keystore is for.\n prompt.innerText = `This application is requesting a keystore file. Keystore files are used to identify ownership or stewardship of bank accounts, users, and other DCP resources. If you upload a keystore file which has been encrypted with a passphrase, the application will not be able to use it until it prompts for a passphrase and you enter it.`;\n if (msg)\n {\n prompt.innerHTML = msg;\n }\n \n document.getElementById('dcp-modal-keystore-file-upload-button').onclick = function (e) {\n fileInput.click();\n }\n\n fileInput.focus();\n fileInput.onchange = async function () {\n let files = fileInput.files;\n if (!files || files.length === 0) return;\n const keystoreFile = await fileToString(files[0]);\n try {\n JSON.parse(keystoreFile);\n } catch (error) {\n fileInput.setCustomValidity(\"File could not be parsed as JSON. Please upload a JSON keystore file.\");\n return;\n };\n fileInput.setCustomValidity(\"\");\n document.getElementById('dcp-modal-keystore-file-submit').click();\n };\n\n await formPromise;\n let files = fileInput.files;\n const keystoreFile = await fileToString(files[0]);\n \n utils.MicroModal.close(modal.id);\n return keystoreFile;\n};\n\n/**\n * Helper for reading in an uploaded file and returning the stringified version. \n * @param {File} inputFile Element of FileList\n * @returns {string} Contents of file.\n */\nconst fileToString = function (inputFile) {\n return new Promise((resolve, reject) => {\n const fileReader = new FileReader();\n\n fileReader.onerror = function () {\n reject(new DOMException('Problem parsing input file.'));\n };\n\n fileReader.onload = function () {\n resolve(this.result);\n };\n\n fileReader.readAsText(inputFile);\n });\n};\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/keystoreFile.js?");
4137
4183
 
4138
4184
  /***/ }),
4139
4185
 
@@ -4143,7 +4189,7 @@ eval("/**\n * @file keystoreFile.js\n * Modal providing a wa
4143
4189
  \***************************************************/
4144
4190
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4145
4191
 
4146
- eval("/**\n * @file oauthLogin.js\n * Modal providing a way to retrieve a keystore from the oauth login service\n * Originally based on keystoreFile.js\n * \n * @author Alex Huctwith - alex@kingsds.network\n * @date Feb 2022\n */\nconst utils = __webpack_require__(/*! ./utils */ \"./src/dcp-client/client-modal/utils.js\");\nconst { confirm } = __webpack_require__(/*! ./confirm */ \"./src/dcp-client/client-modal/confirm.js\");\nconst passwordModule = __webpack_require__(/*! ./passwordEntry */ \"./src/dcp-client/client-modal/passwordEntry.js\");\n\nexports.config = {\n id: 'dcp-modal-oauth-login',\n required: ['dcp-modal-oauth-login-form', 'dcp-modal-oauth-login-checkbox'],\n path: \"./templates/oauth-login-modal.html\",\n eagerLoad: [passwordModule.config]\n};\n\n/**\n* @param {Object} options From `wallet.get_internal`\n* @param {string} options.name - The name of the keystore type (wallet.get => 'default' and wallet.getId => 'id').\n* @param {string} options.contextId - An optional, user-defined identifier used for caching keystores.\n* See `job.contextId` in the compute-api spec.\n* @param {string} options.jobName - A user-defined job property provided to identify this job.\n* @param {string} options.msg - Optional string denoting the purpose for fetching the keystore\n* @param {function} [onClose] Override default onClose behavior. An instance of `modal` is handed to the function.\n* @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n* @returns {string} contents of Keystore file\n* @memberof module:dcp/client-modal\n* @access public\n* @async\n* @alias getKeystoreFile\n*/\nexports.oauthLogin = async function (options, onClose=null) {\n const storageKey = `dcp-keystore-${options.name}-${options.contextId}`;\n \n let jobName = options.jobName;\n if (jobName) {\n jobName = `\"${jobName}\"`;\n } else {\n jobName = 'this job';\n }\n\n const keystoreType = \"DCP Bank Account\";\n\n let keystoreMsg = options.msg;\n\n let keystoreFile = await getKeystoreFromStorage(storageKey, jobName, onClose);\n if (!keystoreFile) {\n // currently only anonymous oauth access is implemented in this version\n keystoreFile = await anonGetKeystoreFromPopup(keystoreType, jobName, storageKey, onClose, keystoreMsg);\n }\n\n return keystoreFile;\n};\n\n/**\n* Alert user that a key from storage will be used, give them a chance to ditch it.\n* @param {string} storageKey key in local or session storage where the file is stored\n* @param {string} jobName helps user know which key is retrieved from storage\n* @returns {string} contents of Keystore file\n*/\nasync function getKeystoreFromStorage (storageKey, jobName) {\n let keystoreFile = sessionStorage.getItem(storageKey);\n if (keystoreFile) return keystoreFile;\n \n keystoreFile = localStorage.getItem(storageKey);\n if (keystoreFile) {\n let ks = JSON.parse(keystoreFile);\n let label = ks.label || ks.address;\n let prompt;\n // alert is not necessarily for a job, specify jobname only if there is an actual job\n if(jobName)\n prompt = `You have keystore \"${label}\" saved for ${jobName}. Do you want to use it?`;\n else\n prompt = `You have keystore \"${label}\". Do you want to use it?`;\n let opts = { positive: \"Yes\", negative: \"No\", title: 'Keystore File' };\n let confirmation = await confirm(prompt, opts);\n if (confirmation) {\n sessionStorage.setItem(storageKey, keystoreFile)\n return keystoreFile;\n }\n localStorage.removeItem(storageKey);\n return false;\n };\n};\n\n/**\n* Bring up anonymous (unregistered, default permissions) login prompt modal in browser\n* @param {string} keystoreType Help user know whether to upload an identity or bank or whatever keystore.\n* @param {string} jobName Help user identify the job this key will be used on.\n* @param {string} storageKey Store the key in local/session storage under this key.\n* @param {string} msg Custom message for the modal that specifies the purpose for providing a keystore\n* @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n*/\nasync function anonGetKeystoreFromPopup (keystoreType, jobName, storageKey, onClose, msg) {\n if (!onClose) {\n onClose = ()=>{};\n }\n\n const [elements, formPromise] = await utils.initModal(exports.config, onClose);\n const [modal, form, prompt, fileInput, rememberInput] = elements;\n \n // default modal text that explains what a keystore is for.\n prompt.innerText = `This application requires that you log in to DCP`;\n if (msg)\n {\n prompt.innerText = msg;\n }\n \n const loginBtn = document.getElementById('dcp-modal-oauth-login-button');\n // make button open a popup with the configured oauth server's reflector page\n loginBtn.onclick = () => window.open(dcpConfig.oAuth.location.resolve('/reflector'), 'DCP Login', 'popup=yes,left=100,top=100,width=800,height=600');\n \n let loginks = 'none';\n \n // wait for a message back from the popup with the bank account keystore\n await new Promise((resolve) => {\n window.addEventListener('message', (event) => {\n // make sure it is coming from the reflector window\n if (dcpConfig.oAuth.location.sameOrigin(event.origin)) {\n loginks = event.data;\n resolve();\n } else {\n return;\n }\n });\n });\n \n const keystoreFile = loginks;\n \n utils.MicroModal.close(modal.id);\n return keystoreFile;\n};\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/oauthLogin.js?");
4192
+ eval("/**\n * @file oauthLogin.js\n * Modal providing a way to retrieve a keystore from the oauth login service\n * Originally based on keystoreFile.js\n * \n * @author Alex Huctwith - alex@kingsds.network\n * @date Feb 2022\n */\nconst utils = __webpack_require__(/*! ./utils */ \"./src/dcp-client/client-modal/utils.js\");\nconst { confirm } = __webpack_require__(/*! ./confirm */ \"./src/dcp-client/client-modal/confirm.js\");\nconst passwordModule = __webpack_require__(/*! ./passwordEntry */ \"./src/dcp-client/client-modal/passwordEntry.js\");\n\nexports.config = {\n id: 'dcp-modal-oauth-login',\n required: ['dcp-modal-oauth-login-form'],\n path: \"./templates/oauth-login-modal.html\",\n eagerLoad: [passwordModule.config]\n};\n\n/**\n* @param {Object} options From `wallet.get_internal`\n* @param {string} options.name - The name of the keystore type (wallet.get => 'default' and wallet.getId => 'id').\n* @param {string} options.contextId - An optional, user-defined identifier used for caching keystores.\n* See `job.contextId` in the compute-api spec.\n* @param {string} options.jobName - A user-defined job property provided to identify this job.\n* @param {string} options.msg - Optional string denoting the purpose for fetching the keystore\n* @param {function} [onClose] Override default onClose behavior. An instance of `modal` is handed to the function.\n* @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n* @returns {string} contents of Keystore file\n* @memberof module:dcp/client-modal\n* @access public\n* @async\n* @alias getKeystoreFile\n*/\nexports.oauthLogin = async function (options, onClose=null) {\n const storageKey = `dcp-keystore-${options.name}-${options.contextId}`;\n \n let jobName = options.jobName;\n if (jobName) {\n jobName = `\"${jobName}\"`;\n } else {\n jobName = 'this job';\n }\n\n const keystoreType = \"DCP Bank Account\";\n\n let keystoreMsg = options.msg;\n\n let keystoreFile = await getKeystoreFromStorage(storageKey, jobName, onClose);\n if (!keystoreFile) {\n // currently only anonymous oauth access is implemented in this version\n keystoreFile = await anonGetKeystoreFromPopup(keystoreType, jobName, storageKey, onClose, keystoreMsg);\n }\n\n return keystoreFile;\n};\n\n/**\n* Alert user that a key from storage will be used, give them a chance to ditch it.\n* @param {string} storageKey key in local or session storage where the file is stored\n* @param {string} jobName helps user know which key is retrieved from storage\n* @returns {string} contents of Keystore file\n*/\nasync function getKeystoreFromStorage (storageKey, jobName) {\n let keystoreFile = sessionStorage.getItem(storageKey);\n if (keystoreFile) return keystoreFile;\n \n keystoreFile = localStorage.getItem(storageKey);\n if (keystoreFile) {\n let ks = JSON.parse(keystoreFile);\n let label = ks.label || ks.address;\n let prompt;\n // alert is not necessarily for a job, specify jobname only if there is an actual job\n if(jobName)\n prompt = `You have keystore \"${label}\" saved for ${jobName}. Do you want to use it?`;\n else\n prompt = `You have keystore \"${label}\". Do you want to use it?`;\n let opts = { positive: \"Yes\", negative: \"No\", title: 'Keystore File' };\n let confirmation = await confirm(prompt, opts);\n if (confirmation) {\n sessionStorage.setItem(storageKey, keystoreFile)\n return keystoreFile;\n }\n localStorage.removeItem(storageKey);\n return false;\n };\n};\n\n/**\n* Bring up anonymous (unregistered, default permissions) login prompt modal in browser\n* @param {string} keystoreType Help user know whether to upload an identity or bank or whatever keystore.\n* @param {string} jobName Help user identify the job this key will be used on.\n* @param {string} storageKey Store the key in local/session storage under this key.\n* @param {string} msg Custom message for the modal that specifies the purpose for providing a keystore\n* @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n*/\nasync function anonGetKeystoreFromPopup (keystoreType, jobName, storageKey, onClose, msg) {\n if (!onClose) {\n onClose = ()=>{};\n }\n\n const [elements, formPromise] = await utils.initModal(exports.config, onClose);\n const [modal, form, prompt, fileInput] = elements;\n \n const loginBtn = document.getElementById('dcp-modal-oauth-login-button');\n // make button open a popup with the configured oauth server's reflector page\n loginBtn.onclick = () => window.open(dcpConfig.oAuth.location.resolve('/reflector'), 'DCP Login', 'popup=yes,left=200,top=200,width=400,height=360');\n \n let loginks = 'none';\n \n // wait for a message back from the popup with the bank account keystore\n await new Promise((resolve) => {\n window.addEventListener('message', (event) => {\n // make sure it is coming from the reflector window\n if (dcpConfig.oAuth.location.sameOrigin(event.origin)) {\n loginks = event.data;\n resolve();\n } else {\n return;\n }\n });\n });\n \n const keystoreFile = loginks;\n \n utils.MicroModal.close(modal.id);\n return keystoreFile;\n};\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/oauthLogin.js?");
4147
4193
 
4148
4194
  /***/ }),
4149
4195
 
@@ -4163,7 +4209,7 @@ eval("/**\n * @file passwordCreation.js\n * Modal providing
4163
4209
  \******************************************************/
4164
4210
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4165
4211
 
4166
- eval("/**\n * @file password.js\n * Modal providing a way to enter a password.\n * \n * @author KC Erb - kcerb@kingsds.network\n * @author Nazila Kharazian - nazila@kingsds.network\n * @author Andrew Fryer - andrewf@kingds.network\n * @date Apr 2020\n */\nconst utils = __webpack_require__(/*! ./utils */ \"./src/dcp-client/client-modal/utils.js\");\n\nexports.config = {\n id: 'dcp-modal-password',\n required: ['dcp-modal-password-form', 'dcp-modal-password-hidden-username', 'dcp-modal-password-prompt', 'dcp-modal-password-input'],\n path: \"./templates/password-entry-modal.html\",\n};\n\n/**\n * @param {string} username The hidden username provided to give a hint to password managers.\n * @param {string} label We expect and use the label property to identify which thing this password is associated to.\n * @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n */\nexports.passwordEntry = async function (username, label, maxTries, tryPassphrase, onClose=null)\n{\n const [elements, formPromise] = await utils.initModal(exports.config, onClose);\n let [modal, form, hiddenUsername, prompt, passwordInput] = elements;\n\n hiddenUsername.value = username;\n prompt.innerText = `Please enter the passphrase for \"${label}\".`;\n\n const submitHandler = function(e)\n {\n setTimeout(function(){\n history.replaceState(null, document.title); // This should signal to the browser that the login was successful.\n }, 1);\n };\n\n // Don't overwrite the existing event handler!\n let existingSubmitHandler = form.onsubmit;\n form.onsubmit = async function(e)\n {\n // waiting for password validation promise to resolve\n const passwordIsValid = await tryPassphrase(passwordInput.value);\n if (passwordIsValid)\n {\n submitHandler(e);\n existingSubmitHandler(e);\n }\n else\n {\n prompt.innerText = `Incorrect passphrase for \"${label}\". Please try again.`;\n passwordInput.focus();\n }\n form.reset();\n };\n \n passwordInput.focus();\n await formPromise;\n\n // At this point, form validation has ensured that the last pasword entered was correct,\n // and this password was passed out of the modal via tryPassphrase.\n\n utils.MicroModal.close(modal.id);\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/passwordEntry.js?");
4212
+ eval("/**\n * @file password.js\n * Modal providing a way to enter a password.\n * \n * @author KC Erb - kcerb@kingsds.network\n * @author Nazila Kharazian - nazila@kingsds.network\n * @author Andrew Fryer - andrewf@kingds.network\n * @date Apr 2020\n */\nconst utils = __webpack_require__(/*! ./utils */ \"./src/dcp-client/client-modal/utils.js\");\n\nexports.config = {\n id: 'dcp-modal-password',\n required: ['dcp-modal-password-form', 'dcp-modal-password-hidden-username', 'dcp-modal-password-prompt', 'dcp-modal-password-input'],\n path: \"./templates/password-entry-modal.html\",\n};\n\n/**\n * @param {string} username The hidden username provided to give a hint to password managers.\n * @param {string} label We expect and use the label property to identify which thing this password is associated to.\n * @throws If modal is closed without the form being submitted (submitting the form closes the modal).\n */\nexports.passwordEntry = async function (username, label, maxTries, tryPassphrase, onClose=null)\n{\n const [elements, formPromise] = await utils.initModal(exports.config, onClose);\n let [modal, form, hiddenUsername, prompt, passwordInput] = elements;\n\n hiddenUsername.value = username;\n prompt.innerHTML = `Enter passphrase for <span style=\"color:#1aa473\"><i>${label}</i></span><br>`;\n\n const submitHandler = function(e)\n {\n setTimeout(function(){\n history.replaceState(null, document.title); // This should signal to the browser that the login was successful.\n }, 1);\n };\n\n // Don't overwrite the existing event handler!\n let existingSubmitHandler = form.onsubmit;\n form.onsubmit = async function(e)\n {\n // waiting for password validation promise to resolve\n const passwordIsValid = await tryPassphrase(passwordInput.value);\n if (passwordIsValid)\n {\n submitHandler(e);\n existingSubmitHandler(e);\n }\n else\n {\n prompt.innerHTML = `Incorrect passphrase for <span style=\"color:#1aa473\"><i>${label}</i></span><br><span style=\"color:#b83b33;text-align:center;\">Please try again.</span>`;\n passwordInput.focus();\n }\n form.reset();\n };\n \n passwordInput.focus();\n await formPromise;\n\n // At this point, form validation has ensured that the last pasword entered was correct,\n // and this password was passed out of the modal via tryPassphrase.\n\n utils.MicroModal.close(modal.id);\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/passwordEntry.js?");
4167
4213
 
4168
4214
  /***/ }),
4169
4215
 
@@ -4173,7 +4219,7 @@ eval("/**\n * @file password.js\n * Modal providing a way to
4173
4219
  \**********************************************/
4174
4220
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4175
4221
 
4176
- eval("/**\n * @file client-modal/utils.js\n * @author KC Erb\n * @date Mar 2020\n * \n * All shared functions among the modals.\n */\nconst { fetchRelative } = __webpack_require__(/*! ./fetch-relative */ \"./src/dcp-client/client-modal/fetch-relative.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nexports.OnCloseErrorCode = 'DCP_CM:CANCELX';\n\nif (DCP_ENV.isBrowserPlatform) {\n // Provide as export for the convenience of `utils.MicroModal` instead of a separate require.\n exports.MicroModal = __webpack_require__(/*! micromodal */ \"./node_modules/micromodal/dist/micromodal.es.js\")[\"default\"];\n}\n\n/**\n * Return a unique string, formatted as a GET parameter, that changes often enough to\n * always force the browser to fetch the latest version of our resource.\n *\n * @note Currently always returns the Date-based poison due to webpack. \n */\nfunction cachePoison() {\n if (true)\n return '?ucp=0bf54dbe88ee11e84b28e62b73cbd154d06967ea'; /* installer token */\n return '?ucp=' + Date.now();\n}\n \n/* Detect load type - on webpack, load dynamic content relative to webpack bundle;\n * otherwise load relative to the current scheduler's configured portal.\n */\nexports.myScript = (typeof document !== 'undefined') && document.currentScript;\nexports.corsProxyHref = undefined;\nif (exports.myScript && exports.myScript === (__webpack_require__(/*! ./fetch-relative */ \"./src/dcp-client/client-modal/fetch-relative.js\").myScript)) {\n let url = new ((__webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\").DcpURL))(exports.myScript.src);\n exports.corsProxyHref = url.resolve('../cors-proxy.html');\n}\n\n/**\n * Look for modal id and required ids on page based on config, if not found, provide from dcp-client.\n * The first id in the required array must be the id of the modal's form element.\n * @param {Object} modalConfig Modal configuration object\n * @param {string} modalConfig.id Id of parent modal element\n * @param {string[]} modalConfig.required Array of required ids in parent modal element\n * @param {string[]} [modalConfig.optional] Array of optional ids in parent modal element\n * @param {string} modalConfig.path Relative path to modal html in dcp-client\n * @returns {DOMElement[]} Array of modal elements on page [config.id, ...config.required]\n */\nexports.initModal = async function (modalConfig, onClose) {\n exports.corsProxyHref = exports.corsProxyHref || dcpConfig.portal.location.resolve('dcp-client/cors-proxy.html');\n\n // Call ensure modal on any eager-loaded modals.\n if (modalConfig.eagerLoad) {\n Promise.all(\n modalConfig.eagerLoad.map(config => ensureModal(config))\n )\n };\n\n const [elements, optionalElements] = await ensureModal(modalConfig);\n\n // Wire up form to prevent default, resolve on submission, reject+reset when closed (or call onClose when closed)\n const [modal, form] = elements;\n form.reset(); // ensure that form is fresh\n let formResolve, formReject;\n let formPromise = new Promise( function(res, rej) {\n formResolve = res;\n formReject = rej;\n });\n form.onsubmit = function (submitEvent) {\n submitEvent.preventDefault();\n modal.setAttribute(\"data-state\", \"submitted\");\n formResolve(submitEvent);\n }\n\n exports.MicroModal.show(modalConfig.id, { \n disableFocus: true, \n onClose: onClose || getDefaultOnClose(formReject)\n });\n return [elements, formPromise, optionalElements];\n};\n\n// Ensure all required modal elements are on page according to modalConfig\nasync function ensureModal(modalConfig) {\n let allRequiredIds = [modalConfig.id, ...modalConfig.required];\n let missing = allRequiredIds.filter( id => !document.getElementById(id) );\n if (missing.length > 0) {\n if (missing.length !== allRequiredIds.length)\n console.warn(`Some of the ids needed to replace the default DCP-modal were found, but not all. So the default DCP-Modal will be used. Missing ids are: [${missing}].`);\n let contents = await fetchRelative(exports.corsProxyHref, modalConfig.path + cachePoison());\n const container = document.createElement('div');\n container.innerHTML = contents;\n document.body.appendChild(container);\n }\n\n const elements = allRequiredIds.map(id => document.getElementById(id));\n const optionalElements = (modalConfig.optional || []).map(id => document.getElementById(id));\n return [elements, optionalElements];\n};\n\n// This onClose is called by MicroModal and thus has the modal passed to it.\nfunction getDefaultOnClose (formReject) {\n return (modal) => {\n modal.offsetLeft; // forces style recalc\n const origState = modal.dataset.state;\n // reset form including data-state\n modal.setAttribute(\"data-state\", \"new\");\n // reject if closed without submitting form.\n if (origState !== \"submitted\") {\n const err = new DCPError(\"Modal was closed but modal's form was not submitted.\", exports.OnCloseErrorCode);\n formReject(err);\n }\n }\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/utils.js?");
4222
+ eval("/**\n * @file client-modal/utils.js\n * @author KC Erb\n * @date Mar 2020\n * \n * All shared functions among the modals.\n */\nconst { fetchRelative } = __webpack_require__(/*! ./fetch-relative */ \"./src/dcp-client/client-modal/fetch-relative.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nexports.OnCloseErrorCode = 'DCP_CM:CANCELX';\n\nif (DCP_ENV.isBrowserPlatform) {\n // Provide as export for the convenience of `utils.MicroModal` instead of a separate require.\n exports.MicroModal = __webpack_require__(/*! micromodal */ \"./node_modules/micromodal/dist/micromodal.es.js\")[\"default\"];\n}\n\n/**\n * Return a unique string, formatted as a GET parameter, that changes often enough to\n * always force the browser to fetch the latest version of our resource.\n *\n * @note Currently always returns the Date-based poison due to webpack. \n */\nfunction cachePoison() {\n if (true)\n return '?ucp=5895f3465816246196dbdb9c0cd5aa200a943c59'; /* installer token */\n return '?ucp=' + Date.now();\n}\n \n/* Detect load type - on webpack, load dynamic content relative to webpack bundle;\n * otherwise load relative to the current scheduler's configured portal.\n */\nexports.myScript = (typeof document !== 'undefined') && document.currentScript;\nexports.corsProxyHref = undefined;\nif (exports.myScript && exports.myScript === (__webpack_require__(/*! ./fetch-relative */ \"./src/dcp-client/client-modal/fetch-relative.js\").myScript)) {\n let url = new ((__webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\").DcpURL))(exports.myScript.src);\n exports.corsProxyHref = url.resolve('../cors-proxy.html');\n}\n\n/**\n * Look for modal id and required ids on page based on config, if not found, provide from dcp-client.\n * The first id in the required array must be the id of the modal's form element.\n * @param {Object} modalConfig Modal configuration object\n * @param {string} modalConfig.id Id of parent modal element\n * @param {string[]} modalConfig.required Array of required ids in parent modal element\n * @param {string[]} [modalConfig.optional] Array of optional ids in parent modal element\n * @param {string} modalConfig.path Relative path to modal html in dcp-client\n * @returns {DOMElement[]} Array of modal elements on page [config.id, ...config.required]\n */\nexports.initModal = async function (modalConfig, onClose) {\n exports.corsProxyHref = exports.corsProxyHref || dcpConfig.portal.location.resolve('dcp-client/cors-proxy.html');\n\n // Call ensure modal on any eager-loaded modals.\n if (modalConfig.eagerLoad) {\n Promise.all(\n modalConfig.eagerLoad.map(config => ensureModal(config))\n )\n };\n\n const [elements, optionalElements] = await ensureModal(modalConfig);\n\n // Wire up form to prevent default, resolve on submission, reject+reset when closed (or call onClose when closed)\n const [modal, form] = elements;\n form.reset(); // ensure that form is fresh\n let formResolve, formReject;\n let formPromise = new Promise( function(res, rej) {\n formResolve = res;\n formReject = rej;\n });\n form.onsubmit = function (submitEvent) {\n submitEvent.preventDefault();\n modal.setAttribute(\"data-state\", \"submitted\");\n formResolve(submitEvent);\n }\n\n exports.MicroModal.show(modalConfig.id, { \n disableFocus: true, \n onClose: onClose || getDefaultOnClose(formReject)\n });\n return [elements, formPromise, optionalElements];\n};\n\n// Ensure all required modal elements are on page according to modalConfig\nasync function ensureModal(modalConfig) {\n let allRequiredIds = [modalConfig.id, ...modalConfig.required];\n let missing = allRequiredIds.filter( id => !document.getElementById(id) );\n if (missing.length > 0) {\n if (missing.length !== allRequiredIds.length)\n console.warn(`Some of the ids needed to replace the default DCP-modal were found, but not all. So the default DCP-Modal will be used. Missing ids are: [${missing}].`);\n let contents = await fetchRelative(exports.corsProxyHref, modalConfig.path + cachePoison());\n const container = document.createElement('div');\n container.innerHTML = contents;\n document.body.appendChild(container);\n }\n\n const elements = allRequiredIds.map(id => document.getElementById(id));\n const optionalElements = (modalConfig.optional || []).map(id => document.getElementById(id));\n return [elements, optionalElements];\n};\n\n// This onClose is called by MicroModal and thus has the modal passed to it.\nfunction getDefaultOnClose (formReject) {\n return (modal) => {\n modal.offsetLeft; // forces style recalc\n const origState = modal.dataset.state;\n // reset form including data-state\n modal.setAttribute(\"data-state\", \"new\");\n // reject if closed without submitting form.\n if (origState !== \"submitted\") {\n const err = new DCPError(\"Modal was closed but modal's form was not submitted.\", exports.OnCloseErrorCode);\n formReject(err);\n }\n }\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/client-modal/utils.js?");
4177
4223
 
4178
4224
  /***/ }),
4179
4225
 
@@ -4184,7 +4230,7 @@ eval("/**\n * @file client-modal/utils.js\n * @author KC Erb\n * @date Mar 2020\
4184
4230
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4185
4231
 
4186
4232
  "use strict";
4187
- eval("/**\n * @file Client facing module that implements Compute Groups API\n * @module dcp/compute-groups\n * @access public\n * @author Kayra E-A <kayra@kingsds.network>\n * Wes Garland <wes@kingsds.network>\n * Paul <paul@kingsds.network>\n * @date Sept 2020\n * February 2022\n * May 2022\n */\n\n\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst wallet = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst hash = __webpack_require__(/*! ../../common/hash */ \"./src/common/hash.js\");\nconst { DCPError } = __webpack_require__(/*! ../../common/dcp-error */ \"./src/common/dcp-error.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('scheduler');\nconst { Address } = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst constants = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { clientError, reconstructServiceError } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n\n/** @typedef {import('dcp/utils').apiServiceType} apiServiceType */\n/** @typedef {import('dcp/utils').apiClientType} apiClientType */\n/** @typedef {string} opaqueId */\n\n/**\n * @typedef {object} cgAccessType\n * @property {opaqueId} [id]\n * @property {string} [joinKey]\n */\n\n/**\n * @typedef {object} cgClientJoinType\n * @property {opaqueId} [id]\n * @property {Address} [joinAddress]\n * @property {string} [joinKey]\n * @property {string} [joinSecret]\n * @property {string} [joinHash]\n */\n\n/**\n * @typedef {object} cgServiceJoinType\n * @property {opaqueId} [id]\n * @property {Address} [joinAddress]\n * @property {string} [joinKey]\n * @property {string} [joinHashHash]\n */\n\n/**\n * Establishes the client connection to the computeGroups microservice if it does not exist already from the default config.\n * \n * @returns {protocolV4.Connection}\n * @access public\n * @example\n * const result = await exports.serviceConnection.send('createGroup', {\n name: name,\n description: description,\n });\n */\n\nexports.serviceConnection = null;\n\n//\n// Reference counting pattern:\n// For every time addRef is called,\n// closeServiceConnection must eventually be called.\n// Reference counting allows multiple execs in a Promise.all .\n//\nvar refCount = 0;\nexports.addRef = function addRef() {\n refCount++;\n}\n\nconst openAndConnectServiceConn = async function openAndConnectServiceConn()\n{\n exports.serviceConnection = new protocolV4.Connection(dcpConfig.scheduler.services.computeGroups);\n exports.serviceConnection.on('close', openAndConnectServiceConn);\n await exports.serviceConnection.connect();\n refCount = 0; // Help with sanity.\n}\n\n/**\n * Resets the client connection to the computeGroups microservice.\n */\nexports.closeServiceConnection = async function closeServiceConnection() {\n if (refCount > 0) refCount--;\n if (exports.serviceConnection && refCount < 1)\n {\n exports.serviceConnection.off('close', openAndConnectServiceConn);\n exports.serviceConnection.close(null, true);\n refCount = 0; // Help with sanity.\n exports.serviceConnection = null;\n }\n};\n\n/**\n * (Used in jobs/index.js)\n * KeepAlive for the service connection to compute groups.\n */\nexports.keepAlive = async function keepAlive() {\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n exports.serviceConnection.keepalive().catch(err => console.error('Warning: keepalive failed for compute groups service', err));\n}\n\n/**\n * Checks whether descriptor corresponds to the public compute group from the scheduler constants.\n */\nexports.isPublicComputeGroup = function isPublicComputeGroup(descriptor) {\n return descriptor.id === constants.computeGroups.public.id\n && descriptor.opaqueId === constants.computeGroups.public.opaqueId;\n};\n\n/**\n * Returns a compute group identification snippet for diagnostic messages,\n * @param {object} descriptor - Must have one of the properties joinKey, id (id:=opaqueId). Specifically\n * descriptor = { joinKey: 'dcpDemo' } or descriptor = { id: 'bYcYGQ3NOpFnP4FKs6IBQd' },\n * where the corresponding row in table computeGroups have attributes\n * joinKey:='dcpDemo' or opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd' .\n * @returns {string}\n */\nfunction cgId(descriptor) {\n return (descriptor.joinKey) ? `joinKey ${descriptor.joinKey}` : `id ${descriptor.id}`;\n}\n\n/**\n * Verify sufficient information in descriptor to access a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgAccessType} descriptor \n * @param {string} methodName \n */\nfunction validateCGDescriptor(descriptor, methodName) {\n for (const prop in descriptor) {\n if ([ 'id', 'joinKey' ].includes(prop)) continue;\n if ([ 'joinAddress', 'joinHash', 'joinSecret' ].includes(prop))\n console.warn(`It is not necessary to specify '${prop}' in the descriptor ${JSON.stringify(descriptor)} when calling ${methodName}`);\n else\n console.error(`Do not specify '${prop}' in the descriptor ${JSON.stringify(descriptor)} when calling ${methodName}`);\n }\n}\n\n/**\n * Verify sufficient information in descriptor to authorize a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgClientJoinType} joinDescriptor \n * @param {string} methodName \n */\nfunction validateCGJoinDescriptor(joinDescriptor, methodName) {\n for (const prop in joinDescriptor) {\n if ([ 'id', 'joinKey', 'joinSecret', 'joinHash', 'joinAddress' ].includes(prop)) continue;\n console.error(`Do not specify '${prop}' in the descriptor ${JSON.stringify(joinDescriptor)} when calling ${methodName}`);\n }\n}\n\n/**\n * Build message to go across the wire.\n * Verify sufficient information in descriptor to access a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgAccessType} descriptor\n * @param {string} methodName\n * @returns {cgAccessType}\n */\nfunction buildCGMessage(descriptor, methodName)\n{\n if (exports.isPublicComputeGroup(descriptor)) return descriptor;\n\n const message = {};\n // Construct message.joinKey xor message.id .\n if (descriptor.joinKey) message.joinKey = descriptor.joinKey;\n else if (descriptor.id) message.id = descriptor.id; // id:=opaqueId\n\n debugging('computeGroups') && console.debug(`${methodName}:buildCGMessage: descriptor`, descriptor, 'message', message);\n\n validateCGDescriptor(descriptor, methodName);\n\n return message;\n}\n\n/**\n * Build message so that joinHash, joinSecret, opaqueId do not go across the wire.\n * Verify sufficient information in descriptor to authorize a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgClientJoinType} descriptor\n * @param {string} methodName\n * @returns {cgServiceJoinType}\n */\nfunction buildCGJoinMessage(descriptor, methodName)\n{\n if (exports.isPublicComputeGroup(descriptor)) return descriptor;\n\n const message = {};\n // Construct message.joinKey xor message.id .\n if (descriptor.joinKey) message.joinKey = descriptor.joinKey;\n else if (descriptor.id) message.id = descriptor.id; // id:=opaqueId\n // Construct message.joinAddress .\n if (descriptor.joinAddress) message.joinAddress = descriptor.joinAddress;\n\n debugging('computeGroups') && console.debug(`${methodName}:buildCGJoinMessage: descriptor`, descriptor, 'message', message);\n\n validateCGJoinDescriptor(descriptor, methodName);\n\n // Construct message.joinHashHash .\n if (descriptor.joinSecret) message.joinHashHash = hash.calculate(hash.eh1, exports.calculateJoinHash(descriptor), exports.serviceConnection.dcpsid);\n if (descriptor.joinHash) message.joinHashHash = hash.calculate(hash.eh1, descriptor.joinHash, exports.serviceConnection.dcpsid);\n\n return message;\n}\n\nfunction hasSufficientJoinInfo(joinDescriptor) {\n // Verify joinDescriptor has sufficient information to authorize a compute group (not guarenteed).\n return (joinDescriptor.joinKey && (joinDescriptor.joinSecret || joinDescriptor.joinHash))\n || (joinDescriptor.id && joinDescriptor.joinAddress)\n || exports.isPublicComputeGroup(joinDescriptor);\n}\n\nconst newCGPrototype = { type: 'object',\n parameters: {\n // name: { type: 'string', default: undefined }, /* name of group (length <= 255) */\n // description: { type: 'string', default: undefined }, /* description of group (length <= 255) */\n // id: { type: 'string', default: undefined }, /* opaqueId, the unique identifier of the compute group; nanoid (length === 22) */\n // joinKey: { type: 'string', default: undefined }, /* basically the login (length <= 255) */\n // joinSecret: { type: 'string', default: undefined }, /* basically the password (length <= 255) */\n // joinHash: { type: 'string', default: undefined }, /* basically the password, the joinSecret seeded & hashed */\n // joinAddress: { type: Address, default: undefined }, /* signature gives alternative to login/password */\n\n commissionRate: { type: 'BigNumber', default: undefined }, /* commission, see DCP-1889 */\n deployFee: { type: 'BigNumber', default: undefined }, /* number of DCC to take for every deployment */\n deployAccess: { type: 'string', default: undefined }, /* can be \"owner\"|\"join\" (dcp-1910) */\n addJobFee: { type: 'BigNumber', default: undefined }, /* fee required each time a job joins a compute group */\n maxTotalPayment: { type: 'BigNumber', default: undefined }, /* limit on maximum job payment, NULL => Infinity */\n\n /* Administrative limits on group. NULL => Infinity: Should all be integers or undefined. */\n maxConcurrentJobs: { type: 'number', default: undefined },\n maxConcurrentWorkers: { type: 'number', default: undefined },\n maxConcurrentSandboxes: { type: 'number', default: undefined },\n maxConcurrentCPUs: { type: 'number', default: undefined },\n maxConcurrentGPUs: { type: 'number', default: undefined },\n maxConcurrentEscrow: { type: 'BigNumber', default: undefined },\n },\n};\n\n/**\n * Async function that creates a new Compute Group.\n *\n * The joinDescriptor is of the form { joinKey, joinSecret }, { joinKey, joinHash } or { id, joinAddress }.\n * where id will correspond to the attribute opaqueId in the new row in the computeGroups table.\n *\n * This function can only be called with ADMIN permission.\n * Properties not appearing in newCGPrototype.parameters are not allowed in otherProperties.\n *\n * @param {cgClientJoinType} joinDescriptor - Must have properly defined { joinKey, joinSecret }, { joinKey, joinHash }\n * or { id, joinAddress }, where id will correspond to the attribute opaqueId\n * in the new row in the computeGroups table.\n * @param {string} [name] - The name of the compute group.\n * @param {string} [description] - The description of the compute group.\n * @param {object} [otherProperties] - The 5 attributes of table computeGroup related to commissions and fees.\n * commissionRate: notNull(zFinNum),// commission, see DCP-1889\n * deployFee: notNull(zFinNum),// number of DCC to take for every deployment\n * deployAccess: string, // can be \"owner\"|\"join\" (dcp-1910)\n * addJobFee: notNull(zFinNum),// fee required each time a job joins a compute group\n * maxTotalPayment: finNum, // limit on maximum job payment, NULL => Infinity\n * And the 6 attributes of table computeGroup related to limits.\n * maxConcurrentJobs: integer,\n * maxConcurrentWorkers: integer,\n * maxConcurrentSandboxes: integer,\n * maxConcurrentCPUs: integer,\n * maxConcurrentGPUs: integer,\n * maxConcurrentEscrow: finNum,\n * @returns {Promise<apiClientType>} - { success, payload: computeGroup.id }\n * @access public\n * @example\n * await computeGroup.createGroup({ joinKey: 'dcpDemo', joinSecret: 'theSecret' }, 'myCGName', 'myCGDescription', { deployFee: 0.00015 });\n * await computeGroup.createGroup({ joinKey: 'dcpDemo2', joinHash: 'eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2' });\n * await computeGroup.createGroup({ id: 'bYcYGQ3NOpFnP4FKs6IBQd', joinAddress: 'c15053fc30d4bdf91e2e0bba79578f8b649e55ea' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: joinKey:='dcpDemo2', joinHash:='eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2'\n * row3: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd', joinAddress:='c15053fc30d4bdf91e2e0bba79578f8b649e55ea' .\n */\nexports.createGroup = async function createGroup(joinDescriptor, name, description, otherProperties)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n validateCGJoinDescriptor(joinDescriptor, 'createGroup');\n\n // Verify joinDescriptor has sufficient information to authorize a compute group (not guarenteed).\n if (!hasSufficientJoinInfo(joinDescriptor))\n return clientError(`createGroup: Insufficient information to authorize compute group: ${JSON.stringify(joinDescriptor)}.`);\n\n // Validate the properties in otherProperties.\n for (const methodName in otherProperties) {\n if (!Object.keys(newCGPrototype.parameters).includes(methodName))\n return clientError(`createGroup: Property ${methodName} cannot be speicfied in otherProperties. Can only specify ${JSON.stringify(Object.keys(newCGPrototype.parameters))}`);\n }\n\n // Translate joinSecret to joinHash.\n if (joinDescriptor.joinSecret) {\n joinDescriptor.joinHash = exports.calculateJoinHash(joinDescriptor);\n delete joinDescriptor.joinSecret;\n }\n\n if (otherProperties && (otherProperties.commissionRate < 0 || otherProperties.commissionRate >= 1))\n return clientError(`client-createGroup: commissionRate ${otherProperties.commissionRate} must be between 0 and 1 (0 <= commissionRate < 1).`);\n\n debugging('computeGroups') && console.debug('client-createGroup: input:', joinDescriptor, name, description, otherProperties);\n\n const { success, payload } = await exports.serviceConnection.send('createGroup', { joinDescriptor, name, description, otherProperties });\n\n if (!success) return clientError(`Cannot create new compute group, with ${cgId(joinDescriptor)}.`);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n debugging('computeGroups') && console.debug('client-createGroup: payload', payload);\n\n return payload;\n};\n\nconst changeCGPrototype = { type: 'object',\n parameters: {\n name: { type: 'string', default: undefined }, /* name of group (length <= 255) */\n description: { type: 'string', default: undefined }, /* description of group (length <= 255) */\n joinHash: { type: 'string', default: undefined }, /* basically the password, seeded & hashed (length <= 255) */\n joinAddress: { type: Address, default: undefined }, /* signature gives alternative to login/password */\n\n commissionRate: { type: 'BigNumber', default: undefined }, /* commission, see DCP-1889 */\n deployFee: { type: 'BigNumber', default: undefined }, /* number of DCC to take for every deployment */\n deployAccess: { type: 'string', default: undefined }, /* can be \"owner\"|\"join\" (dcp-1910) */\n addJobFee: { type: 'BigNumber', default: undefined }, /* fee required each time a job joins a compute group */\n maxTotalPayment: { type: 'BigNumber', default: undefined }, /* limit on maximum job payment, NULL => Infinity */\n\n /* Administrative limits on group. NULL => Infinity: Should all be integers or undefined. */\n maxConcurrentJobs: { type: 'number', default: undefined },\n maxConcurrentWorkers: { type: 'number', default: undefined },\n maxConcurrentSandboxes: { type: 'number', default: undefined },\n maxConcurrentCPUs: { type: 'number', default: undefined },\n maxConcurrentGPUs: { type: 'number', default: undefined },\n maxConcurrentEscrow: { type: 'BigNumber', default: undefined },\n },\n};\n\n/**\n * Async function that changes a new Compute Group.\n * \n * The parameter newDescriptor contains the new property values,\n * and the properties that are allowed to be changed appear in changeCGPrototype.parameters.\n * \n * The descriptor must have joinKey or id, where id:=opaqueId.\n * Must own the compute group or be ADMIN to use changeGroup.\n * \n * @param {cgAccessType} descriptor - Must have joinkey or id, where id:=opaqueId.\n * @param {object} newDescriptor - Properties not appearing in changeCGPrototype.parameters are not allowed.\n * @returns {Promise<apiClientType>}\n * await computeGroup.changeGroup({ joinKey: 'dcpDemo' }, { joinSecret: 'myNewPasswrd' });\n * await computeGroup.changeGroup({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' }, { name: 'myNewName', deployFee: 0.0001 });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.changeGroup = async function changeGroup(descriptor, newDescriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`changeGroup: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n // Validate the properties in newDescriptor.\n for (const methodName in newDescriptor) {\n if (!Object.keys(changeCGPrototype.parameters).includes(methodName))\n return clientError(`changeGroup: Not allowed to change property ${methodName}. Can only change ${JSON.stringify(Object.keys(changeCGPrototype.parameters))}`);\n }\n\n // Translate joinSecret to joinHash.\n if (newDescriptor.joinSecret) {\n newDescriptor.joinHash = exports.calculateJoinHash(newDescriptor);\n delete newDescriptor.joinSecret;\n }\n\n descriptor = buildCGMessage(descriptor, 'changeGroup');\n debugging('computeGroups') && console.debug('change compute group client:', descriptor, newDescriptor);\n const { success, payload } = await exports.serviceConnection.send('changeGroup', { descriptor, newDescriptor });\n\n if (!success) throw new DCPError(`Cannot change compute group with ${cgId(descriptor)}:`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that deletes a compute group.\n * \n * The descriptor must have joinkey or id, where id:=opaqueId.\n * \n * Must either own the group or be ADMIN.\n * If not ADMIN, then the following config must be true:\n * dcpConfig.scheduler.services.computeGroups.usersCanDeleteGroups\n * \n * @param {cgAccessType} descriptor - Must contain joinKey or id (id:=opaqueId) \n * @returns {Promise<apiClientType>}\n * await computeGroup.deleteGroup({ joinKey: 'dcpDemo' });\n * await computeGroup.deleteGroup({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.deleteGroup = async function deleteGroup(descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`deleteGroup: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'deleteGroup');\n debugging('computeGroups') && console.debug('delete compute group client:', descriptor);\n const { success, payload } = await exports.serviceConnection.send('deleteGroup', { descriptor });\n\n if (!success) throw new DCPError(`Cannot delete compute group with ${cgId(descriptor)}:`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that adds a job to a specified compute group. \n * \n * Must be the owner of the job.\n *\n * Useful feedback is provided from this function, as it\n * will make its way back to the application developer, *after* they have made the\n * deployment fee micropayment.\n *\n * On the client side the access model in place is that if you know the (user/password)\n * joinKey+joinSecret/joinKey+joinHash/joinKey+joinHashHash/id+joinAddress,\n * you can add the job to the compute groups, where id:=opaqueId from table computeGroups.\n * On the service side the corresponding access model is\n * joinKey+joinHashHash/id+joinAddress .\n * Access is also allowed if the compute group owner is the connection peerAddress.\n * \n * Unless the compute group owner is the connection peerAddress, element of the descriptor array must contain\n * { joinKey, joinSecret }, { joinKey, joinHash } or { id, joinAddress }\n * where the value of id in { id, joinAddress } is the opaqueId attribute of the row in table computeGroups.\n *\n * @param {Address} job The address of the Job that will be added to the Compute Group.\n * @param {Array} computeGroups Array of descriptor objects for the compute groups. This descriptor\n * needs to contain enough information to authorize access to the\n * compute group. Properties may include:\n * - id (id:=opaqueId)\n * - joinKey\n * - joinSecret\n * - joinHash\n * - joinAddress\n * \n * Additional, either the joinKey or id MUST be specified so\n * that we can identify the compute group in question.\n *\n * All compute groups can have jobs submitted to them, provided either the joinKey\n * or the id are specified, and the message contains valid join permission and the \n * job is owned by the caller of addJobToGroups.\n *\n * FUTURE - after DCP-1910\n * keystore A keystore used to grant access to job deployment within this compute group.\n * This can be either the ownerKeystore or the joinAddress keystore when the\n * compute group is in deployAccessType='join' mode.\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * await computeGroup.addJobToGroups('P+Y4IApeFQLrYS2W7MkVg7', \n * [ { joinKey: 'dcpDemo', joinSecret: 'theSecret' },\n * { joinKey: 'dcpDemo2', joinHash: 'eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2' }, \n * { id: 'bYcYGQ3NOpFnP4FKs6IBQd', joinAddress: 'c15053fc30d4bdf91e2e0bba79578f8b649e55ea' } ]);\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: joinKey:='dcpDemo2', joinHash:='eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2'\n * row3: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd', joinAddress:='c15053fc30d4bdf91e2e0bba79578f8b649e55ea' .\n */\nexports.addJobToGroups = async function addJobToGroups(job, computeGroups)\n{\n // ensure the service connection is hot before calling buildCGJoinMessage:\n await exports.keepAlive();\n\n const cgArray = [];\n for (const joinDescriptor of computeGroups)\n {\n // Verify joinDescriptor has sufficient information to authorize a compute group (not guarenteed).\n if (!hasSufficientJoinInfo(joinDescriptor))\n {\n console.error(`addJobToGroups: Insufficient information to authorize compute group: ${JSON.stringify(joinDescriptor)}.`);\n continue;\n }\n\n // Translate so that neither joinHash nor joinSecret goes across the wire.\n const message = buildCGJoinMessage(joinDescriptor, 'addJobToGroups');\n debugging('computeGroups') && console.debug(`addJobToGroups client: job ${job}, message`, message);\n\n cgArray.push(message);\n }\n\n const { success, payload } = await exports.serviceConnection.send('addJobToGroups', { job, cgArray });\n\n debugging('computeGroups') && console.debug('addJobToGroups payload', payload);\n\n if (!success) throw new DCPError(`Cannot add job ${job} to compute groups.`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n // If the server reported success but did not return a list of CGs (eg. v.4.2.5 server),\n // assume (and inform the client) we added all groups successfully\n return payload || computeGroups;\n};\n\n/**\n * Async function that lists all the Jobs in a Compute Group.\n * \n * The descriptor must have one of the properties joinkey, id (id:=opaqueId).\n * Must be the owner of the Compute Group to list jobs from it.\n * The job does not need to be owned.\n * \n * The descriptor is of the form { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }.\n * where 'bYcYGQ3NOpFnP4FKs6IBQd' is the opaqueId of the Compute Group.\n *\n * @param {cgAccessType} descriptor - Must have one of the properties joinKey, id (id:=opaqueId). Specifically\n * descriptor = { joinKey: 'dcpDemo' } or descriptor = { id: opaqueId }\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * let listOfJobs1 = await computeGroup.listJobs({ joinKey: 'dcpDemo' });\n * let listOfJobs2 = await computeGroup.listJobs({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo'\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.listJobs = async function listJobs(descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`listJobs: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'listJobs');\n debugging('computeGroups') && console.debug('listJob client: descriptor', descriptor);\n const { success, payload } = await exports.serviceConnection.send('listJobs', { descriptor });\n\n if (!success) throw new DCPError(`Cannot list jobs for compute group with ${cgId(descriptor)}`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that removes a job from a Compute Group.\n * \n * The descriptor must have one of the properties joinkey, id (id:=opaqueId).\n * Must be the owner of the Compute Group to remove a job from it.\n * The job does not need to be owned.\n * \n * The descriptor is of the form { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }.\n * where 'bYcYGQ3NOpFnP4FKs6IBQd' is the opaqueId of the Compute Group.\n *\n * @param {Address} job - The address of the Job that will be added to the Compute Group.\n * @param {cgAccessType} descriptor - { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * await computeGroup.removeJob( 'P+Y4IApeFQLrYS2W7MkVg7', { joinKey: 'dcpDemo' });\n * await computeGroup.removeJob( 'P+Y4IApeFQLrYS2W7MkVg7', { id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo'\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.removeJob = async function removeJob(job, descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`removeJob: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'removeJob');\n debugging('computeGroups') && console.debug(`removeJob client: job ${job}, descriptor`, descriptor);\n const { success, payload } = await exports.serviceConnection.send('removeJob', { job, descriptor });\n\n if (!success) throw new DCPError(`Cannot remove job ${job} from compute group with ${cgId(descriptor)}`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that removes all jobs from a Compute Group.\n * \n * The descriptor must have one of the properties joinkey, id (id:=opaqueId).\n * Must be the owner of the Compute Group to remove jobs from it.\n * \n * The descriptor is of the form { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }.\n * where 'bYcYGQ3NOpFnP4FKs6IBQd' is the opaqueId of the Compute Group.\n *\n * @param {cgAccessType} descriptor - { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * await computeGroup.removeAllJobs({ joinKey: 'dcpDemo' });\n * await computeGroup.removeAllJobs({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo'\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.removeAllJobs = async function removeAllJobs(descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`removeAllJobs: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'removeAllJobs');\n debugging('computeGroups') && console.debug('removeAllJobs client: descriptor', descriptor);\n const { success, payload } = await exports.serviceConnection.send('removeAllJobs', { descriptor });\n\n if (!success) throw new DCPError(`Cannot remove all jobs from compute group with ${cgId(descriptor)}:`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Calculate a joinHash for a compute group. This is an eh1- hash of the cg salt and \n * joinSecret components of a compute group description.\n *\n * @param {object} details an object containing the cg salt, which is\n * the joinKey if the compute group uses one;\n * otherwise it is the joinAddress. This object\n * may also contain the joinSecret.\n * @param {string} [joinSecret] the join secret -- plain text -- that is\n * the \"password\" for the compute group. If not\n * specified, we use details.joinSecret.\n */\nexports.calculateJoinHash = function computeGroups$calculateJoinHash(details, joinSecret)\n{\n if (typeof joinSecret === 'undefined')\n joinSecret = details.joinSecret;\n\n return hash.calculate(hash.eh1, `${details.joinKey || details.joinAddress} ${joinSecret}`);\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/compute-groups/index.js?");
4233
+ eval("/**\n * @file Client facing module that implements Compute Groups API\n * @module dcp/compute-groups\n * @access public\n * @author Kayra E-A <kayra@kingsds.network>\n * Wes Garland <wes@kingsds.network>\n * Paul <paul@kingsds.network>\n * @date Sept 2020\n * February 2022\n * May 2022\n */\n\n\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst wallet = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst hash = __webpack_require__(/*! ../../common/hash */ \"./src/common/hash.js\");\nconst { DCPError } = __webpack_require__(/*! ../../common/dcp-error */ \"./src/common/dcp-error.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('scheduler');\nconst { Address } = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst constants = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { clientError, reconstructServiceError } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n\n/** @typedef {import('dcp/utils').apiServiceType} apiServiceType */\n/** @typedef {import('dcp/utils').apiClientType} apiClientType */\n/** @typedef {string} opaqueId */\n\n/**\n * @typedef {object} cgAccessType\n * @property {opaqueId} [id]\n * @property {string} [joinKey]\n */\n\n/**\n * @typedef {object} cgClientJoinType\n * @property {opaqueId} [id]\n * @property {Address} [joinAddress]\n * @property {string} [joinKey]\n * @property {string} [joinSecret]\n * @property {string} [joinHash]\n */\n\n/**\n * @typedef {object} cgServiceJoinType\n * @property {opaqueId} [id]\n * @property {Address} [joinAddress]\n * @property {string} [joinKey]\n * @property {string} [joinHashHash]\n */\n\n/**\n * Establishes the client connection to the computeGroups microservice if it does not exist already from the default config.\n * \n * @returns {protocolV4.Connection}\n * @access public\n * @example\n * const result = await exports.serviceConnection.send('createGroup', {\n name: name,\n description: description,\n });\n */\n\nexports.serviceConnection = null;\n\n//\n// Reference counting pattern:\n// For every time addRef is called,\n// closeServiceConnection must eventually be called.\n// Reference counting allows multiple execs in a Promise.all .\n//\nvar refCount = 0;\nexports.addRef = function addRef() {\n refCount++;\n}\n\nconst openAndConnectServiceConn = async function openAndConnectServiceConn()\n{\n exports.serviceConnection = new protocolV4.Connection(dcpConfig.scheduler.services.computeGroups);\n exports.serviceConnection.on('close', openAndConnectServiceConn);\n await exports.serviceConnection.connect();\n refCount = 0; // Help with sanity.\n}\n\n/**\n * Resets the client connection to the computeGroups microservice.\n */\nexports.closeServiceConnection = async function closeServiceConnection() {\n if (refCount > 0) refCount--;\n if (exports.serviceConnection && refCount < 1)\n {\n exports.serviceConnection.off('close', openAndConnectServiceConn);\n exports.serviceConnection.close(null, true);\n refCount = 0; // Help with sanity.\n exports.serviceConnection = null;\n }\n};\n\n/**\n * (Used in jobs/index.js)\n * KeepAlive for the service connection to compute groups.\n */\nexports.keepAlive = async function keepAlive() {\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n exports.serviceConnection.keepalive().catch(err => console.error('Warning: keepalive failed for compute groups service', err));\n}\n\n/**\n * Checks whether descriptor corresponds to the public compute group\n * defined in scheduler constants.\n */\nexports.isPublicComputeGroup = function isPublicComputeGroup(descriptor) {\n const publicGroup = constants.computeGroups.public;\n return descriptor.id === publicGroup.id\n || descriptor.opaqueId === publicGroup.opaqueId\n || descriptor.joinKey === publicGroup.joinKey;\n};\n\n/**\n * Returns a compute group identification snippet for diagnostic messages,\n * @param {object} descriptor - Must have one of the properties joinKey, id (id:=opaqueId). Specifically\n * descriptor = { joinKey: 'dcpDemo' } or descriptor = { id: 'bYcYGQ3NOpFnP4FKs6IBQd' },\n * where the corresponding row in table computeGroups have attributes\n * joinKey:='dcpDemo' or opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd' .\n * @returns {string}\n */\nfunction cgId(descriptor) {\n return (descriptor.joinKey) ? `joinKey ${descriptor.joinKey}` : `id ${descriptor.id}`;\n}\n\n/**\n * Verify sufficient information in descriptor to access a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgAccessType} descriptor \n * @param {string} methodName \n */\nfunction validateCGDescriptor(descriptor, methodName) {\n for (const prop in descriptor) {\n if ([ 'id', 'joinKey' ].includes(prop)) continue;\n if ([ 'joinAddress', 'joinHash', 'joinSecret' ].includes(prop))\n console.warn(`It is not necessary to specify '${prop}' in the descriptor ${JSON.stringify(descriptor)} when calling ${methodName}`);\n else\n console.error(`Do not specify '${prop}' in the descriptor ${JSON.stringify(descriptor)} when calling ${methodName}`);\n }\n}\n\n/**\n * Verify sufficient information in descriptor to authorize a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgClientJoinType} joinDescriptor \n * @param {string} methodName \n */\nfunction validateCGJoinDescriptor(joinDescriptor, methodName) {\n for (const prop in joinDescriptor) {\n if ([ 'id', 'joinKey', 'joinSecret', 'joinHash', 'joinAddress' ].includes(prop)) continue;\n console.error(`Do not specify '${prop}' in the descriptor ${JSON.stringify(joinDescriptor)} when calling ${methodName}`);\n }\n}\n\n/**\n * Build message to go across the wire.\n * Verify sufficient information in descriptor to access a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgAccessType} descriptor\n * @param {string} methodName\n * @returns {cgAccessType}\n */\nfunction buildCGMessage(descriptor, methodName)\n{\n if (exports.isPublicComputeGroup(descriptor)) return descriptor;\n\n const message = {};\n // Construct message.joinKey xor message.id .\n if (descriptor.joinKey) message.joinKey = descriptor.joinKey;\n else if (descriptor.id) message.id = descriptor.id; // id:=opaqueId\n\n debugging('computeGroups') && console.debug(`${methodName}:buildCGMessage: descriptor`, descriptor, 'message', message);\n\n validateCGDescriptor(descriptor, methodName);\n\n return message;\n}\n\n/**\n * Build message so that joinHash, joinSecret, opaqueId do not go across the wire.\n * Verify sufficient information in descriptor to authorize a compute group.\n * Emit diagnostics about unnecessary information.\n * @param {cgClientJoinType} descriptor\n * @param {string} methodName\n * @returns {cgServiceJoinType}\n */\nfunction buildCGJoinMessage(descriptor, methodName)\n{\n if (exports.isPublicComputeGroup(descriptor)) return descriptor;\n\n const message = {};\n // Construct message.joinKey xor message.id .\n if (descriptor.joinKey) message.joinKey = descriptor.joinKey;\n else if (descriptor.id) message.id = descriptor.id; // id:=opaqueId\n // Construct message.joinAddress .\n if (descriptor.joinAddress) message.joinAddress = descriptor.joinAddress;\n\n debugging('computeGroups') && console.debug(`${methodName}:buildCGJoinMessage: descriptor`, descriptor, 'message', message);\n\n validateCGJoinDescriptor(descriptor, methodName);\n\n // Construct message.joinHashHash .\n if (descriptor.joinSecret) message.joinHashHash = hash.calculate(hash.eh1, exports.calculateJoinHash(descriptor), exports.serviceConnection.dcpsid);\n if (descriptor.joinHash) message.joinHashHash = hash.calculate(hash.eh1, descriptor.joinHash, exports.serviceConnection.dcpsid);\n\n return message;\n}\n\nfunction hasSufficientJoinInfo(joinDescriptor) {\n // Verify joinDescriptor has sufficient information to authorize a compute group (not guarenteed).\n return (joinDescriptor.joinKey && (joinDescriptor.joinSecret || joinDescriptor.joinHash))\n || (joinDescriptor.id && joinDescriptor.joinAddress)\n || exports.isPublicComputeGroup(joinDescriptor);\n}\n\nconst newCGPrototype = { type: 'object',\n parameters: {\n // name: { type: 'string', default: undefined }, /* name of group (length <= 255) */\n // description: { type: 'string', default: undefined }, /* description of group (length <= 255) */\n // id: { type: 'string', default: undefined }, /* opaqueId, the unique identifier of the compute group; nanoid (length === 22) */\n // joinKey: { type: 'string', default: undefined }, /* basically the login (length <= 255) */\n // joinSecret: { type: 'string', default: undefined }, /* basically the password (length <= 255) */\n // joinHash: { type: 'string', default: undefined }, /* basically the password, the joinSecret seeded & hashed */\n // joinAddress: { type: Address, default: undefined }, /* signature gives alternative to login/password */\n\n commissionRate: { type: 'BigNumber', default: undefined }, /* commission, see DCP-1889 */\n deployFee: { type: 'BigNumber', default: undefined }, /* number of DCC to take for every deployment */\n deployAccess: { type: 'string', default: undefined }, /* can be \"owner\"|\"join\" (dcp-1910) */\n addJobFee: { type: 'BigNumber', default: undefined }, /* fee required each time a job joins a compute group */\n maxTotalPayment: { type: 'BigNumber', default: undefined }, /* limit on maximum job payment, NULL => Infinity */\n\n /* Administrative limits on group. NULL => Infinity: Should all be integers or undefined. */\n maxConcurrentJobs: { type: 'number', default: undefined },\n maxConcurrentWorkers: { type: 'number', default: undefined },\n maxConcurrentSandboxes: { type: 'number', default: undefined },\n maxConcurrentCPUs: { type: 'number', default: undefined },\n maxConcurrentGPUs: { type: 'number', default: undefined },\n maxConcurrentEscrow: { type: 'BigNumber', default: undefined },\n },\n};\n\n/**\n * Async function that creates a new Compute Group.\n *\n * The joinDescriptor is of the form { joinKey, joinSecret }, { joinKey, joinHash } or { id, joinAddress }.\n * where id will correspond to the attribute opaqueId in the new row in the computeGroups table.\n *\n * This function can only be called with ADMIN permission.\n * Properties not appearing in newCGPrototype.parameters are not allowed in otherProperties.\n *\n * @param {cgClientJoinType} joinDescriptor - Must have properly defined { joinKey, joinSecret }, { joinKey, joinHash }\n * or { id, joinAddress }, where id will correspond to the attribute opaqueId\n * in the new row in the computeGroups table.\n * @param {string} [name] - The name of the compute group.\n * @param {string} [description] - The description of the compute group.\n * @param {object} [otherProperties] - The 5 attributes of table computeGroup related to commissions and fees.\n * commissionRate: notNull(zFinNum),// commission, see DCP-1889\n * deployFee: notNull(zFinNum),// number of DCC to take for every deployment\n * deployAccess: string, // can be \"owner\"|\"join\" (dcp-1910)\n * addJobFee: notNull(zFinNum),// fee required each time a job joins a compute group\n * maxTotalPayment: finNum, // limit on maximum job payment, NULL => Infinity\n * And the 6 attributes of table computeGroup related to limits.\n * maxConcurrentJobs: integer,\n * maxConcurrentWorkers: integer,\n * maxConcurrentSandboxes: integer,\n * maxConcurrentCPUs: integer,\n * maxConcurrentGPUs: integer,\n * maxConcurrentEscrow: finNum,\n * @returns {Promise<apiClientType>} - { success, payload: computeGroup.id }\n * @access public\n * @example\n * await computeGroup.createGroup({ joinKey: 'dcpDemo', joinSecret: 'theSecret' }, 'myCGName', 'myCGDescription', { deployFee: 0.00015 });\n * await computeGroup.createGroup({ joinKey: 'dcpDemo2', joinHash: 'eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2' });\n * await computeGroup.createGroup({ id: 'bYcYGQ3NOpFnP4FKs6IBQd', joinAddress: 'c15053fc30d4bdf91e2e0bba79578f8b649e55ea' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: joinKey:='dcpDemo2', joinHash:='eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2'\n * row3: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd', joinAddress:='c15053fc30d4bdf91e2e0bba79578f8b649e55ea' .\n */\nexports.createGroup = async function createGroup(joinDescriptor, name, description, otherProperties)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n validateCGJoinDescriptor(joinDescriptor, 'createGroup');\n\n // Verify joinDescriptor has sufficient information to authorize a compute group (not guarenteed).\n if (!hasSufficientJoinInfo(joinDescriptor))\n return clientError(`createGroup: Insufficient information to authorize compute group: ${JSON.stringify(joinDescriptor)}.`);\n\n // Validate the properties in otherProperties.\n for (const methodName in otherProperties) {\n if (!Object.keys(newCGPrototype.parameters).includes(methodName))\n return clientError(`createGroup: Property ${methodName} cannot be speicfied in otherProperties. Can only specify ${JSON.stringify(Object.keys(newCGPrototype.parameters))}`);\n }\n\n // Translate joinSecret to joinHash.\n if (joinDescriptor.joinSecret) {\n joinDescriptor.joinHash = exports.calculateJoinHash(joinDescriptor);\n delete joinDescriptor.joinSecret;\n }\n\n if (otherProperties && (otherProperties.commissionRate < 0 || otherProperties.commissionRate >= 1))\n return clientError(`client-createGroup: commissionRate ${otherProperties.commissionRate} must be between 0 and 1 (0 <= commissionRate < 1).`);\n\n debugging('computeGroups') && console.debug('client-createGroup: input:', joinDescriptor, name, description, otherProperties);\n\n const { success, payload } = await exports.serviceConnection.send('createGroup', { joinDescriptor, name, description, otherProperties });\n\n if (!success) return clientError(`Cannot create new compute group, with ${cgId(joinDescriptor)}.`);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n debugging('computeGroups') && console.debug('client-createGroup: payload', payload);\n\n return payload;\n};\n\nconst changeCGPrototype = { type: 'object',\n parameters: {\n name: { type: 'string', default: undefined }, /* name of group (length <= 255) */\n description: { type: 'string', default: undefined }, /* description of group (length <= 255) */\n joinHash: { type: 'string', default: undefined }, /* basically the password, seeded & hashed (length <= 255) */\n joinAddress: { type: Address, default: undefined }, /* signature gives alternative to login/password */\n\n commissionRate: { type: 'BigNumber', default: undefined }, /* commission, see DCP-1889 */\n deployFee: { type: 'BigNumber', default: undefined }, /* number of DCC to take for every deployment */\n deployAccess: { type: 'string', default: undefined }, /* can be \"owner\"|\"join\" (dcp-1910) */\n addJobFee: { type: 'BigNumber', default: undefined }, /* fee required each time a job joins a compute group */\n maxTotalPayment: { type: 'BigNumber', default: undefined }, /* limit on maximum job payment, NULL => Infinity */\n\n /* Administrative limits on group. NULL => Infinity: Should all be integers or undefined. */\n maxConcurrentJobs: { type: 'number', default: undefined },\n maxConcurrentWorkers: { type: 'number', default: undefined },\n maxConcurrentSandboxes: { type: 'number', default: undefined },\n maxConcurrentCPUs: { type: 'number', default: undefined },\n maxConcurrentGPUs: { type: 'number', default: undefined },\n maxConcurrentEscrow: { type: 'BigNumber', default: undefined },\n },\n};\n\n/**\n * Async function that changes a new Compute Group.\n * \n * The parameter newDescriptor contains the new property values,\n * and the properties that are allowed to be changed appear in changeCGPrototype.parameters.\n * \n * The descriptor must have joinKey or id, where id:=opaqueId.\n * Must own the compute group or be ADMIN to use changeGroup.\n * \n * @param {cgAccessType} descriptor - Must have joinkey or id, where id:=opaqueId.\n * @param {object} newDescriptor - Properties not appearing in changeCGPrototype.parameters are not allowed.\n * @returns {Promise<apiClientType>}\n * await computeGroup.changeGroup({ joinKey: 'dcpDemo' }, { joinSecret: 'myNewPasswrd' });\n * await computeGroup.changeGroup({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' }, { name: 'myNewName', deployFee: 0.0001 });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.changeGroup = async function changeGroup(descriptor, newDescriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`changeGroup: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n // Validate the properties in newDescriptor.\n for (const methodName in newDescriptor) {\n if (!Object.keys(changeCGPrototype.parameters).includes(methodName))\n return clientError(`changeGroup: Not allowed to change property ${methodName}. Can only change ${JSON.stringify(Object.keys(changeCGPrototype.parameters))}`);\n }\n\n // Translate joinSecret to joinHash.\n if (newDescriptor.joinSecret) {\n newDescriptor.joinHash = exports.calculateJoinHash(newDescriptor);\n delete newDescriptor.joinSecret;\n }\n\n descriptor = buildCGMessage(descriptor, 'changeGroup');\n debugging('computeGroups') && console.debug('change compute group client:', descriptor, newDescriptor);\n const { success, payload } = await exports.serviceConnection.send('changeGroup', { descriptor, newDescriptor });\n\n if (!success) throw new DCPError(`Cannot change compute group with ${cgId(descriptor)}:`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that deletes a compute group.\n * \n * The descriptor must have joinkey or id, where id:=opaqueId.\n * \n * Must either own the group or be ADMIN.\n * If not ADMIN, then the following config must be true:\n * dcpConfig.scheduler.services.computeGroups.usersCanDeleteGroups\n * \n * @param {cgAccessType} descriptor - Must contain joinKey or id (id:=opaqueId) \n * @returns {Promise<apiClientType>}\n * await computeGroup.deleteGroup({ joinKey: 'dcpDemo' });\n * await computeGroup.deleteGroup({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.deleteGroup = async function deleteGroup(descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`deleteGroup: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'deleteGroup');\n debugging('computeGroups') && console.debug('delete compute group client:', descriptor);\n const { success, payload } = await exports.serviceConnection.send('deleteGroup', { descriptor });\n\n if (!success) throw new DCPError(`Cannot delete compute group with ${cgId(descriptor)}:`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that adds a job to a specified compute group. \n * \n * Must be the owner of the job.\n *\n * Useful feedback is provided from this function, as it\n * will make its way back to the application developer, *after* they have made the\n * deployment fee micropayment.\n *\n * On the client side the access model in place is that if you know the (user/password)\n * joinKey+joinSecret/joinKey+joinHash/joinKey+joinHashHash/id+joinAddress,\n * you can add the job to the compute groups, where id:=opaqueId from table computeGroups.\n * On the service side the corresponding access model is\n * joinKey+joinHashHash/id+joinAddress .\n * Access is also allowed if the compute group owner is the connection peerAddress.\n * \n * Unless the compute group owner is the connection peerAddress, element of the descriptor array must contain\n * { joinKey, joinSecret }, { joinKey, joinHash } or { id, joinAddress }\n * where the value of id in { id, joinAddress } is the opaqueId attribute of the row in table computeGroups.\n *\n * @param {Address} job The address of the Job that will be added to the Compute Group.\n * @param {Array} computeGroups Array of descriptor objects for the compute groups. This descriptor\n * needs to contain enough information to authorize access to the\n * compute group. Properties may include:\n * - id (id:=opaqueId)\n * - joinKey\n * - joinSecret\n * - joinHash\n * - joinAddress\n * \n * Additional, either the joinKey or id MUST be specified so\n * that we can identify the compute group in question.\n *\n * All compute groups can have jobs submitted to them, provided either the joinKey\n * or the id are specified, and the message contains valid join permission and the \n * job is owned by the caller of addJobToGroups.\n *\n * FUTURE - after DCP-1910\n * keystore A keystore used to grant access to job deployment within this compute group.\n * This can be either the ownerKeystore or the joinAddress keystore when the\n * compute group is in deployAccessType='join' mode.\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * await computeGroup.addJobToGroups('P+Y4IApeFQLrYS2W7MkVg7', \n * [ { joinKey: 'dcpDemo', joinSecret: 'theSecret' },\n * { joinKey: 'dcpDemo2', joinHash: 'eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2' }, \n * { id: 'bYcYGQ3NOpFnP4FKs6IBQd', joinAddress: 'c15053fc30d4bdf91e2e0bba79578f8b649e55ea' } ]);\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo',\n * row2: joinKey:='dcpDemo2', joinHash:='eh1-e063976b20a92da97a27b9873465c6f2c9d6e4370befa86c8c1dd312c78befc2'\n * row3: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd', joinAddress:='c15053fc30d4bdf91e2e0bba79578f8b649e55ea' .\n */\nexports.addJobToGroups = async function addJobToGroups(job, computeGroups)\n{\n // ensure the service connection is hot before calling buildCGJoinMessage:\n await exports.keepAlive();\n\n const cgArray = [];\n for (const joinDescriptor of computeGroups)\n {\n // Verify joinDescriptor has sufficient information to authorize a compute group (not guarenteed).\n if (!hasSufficientJoinInfo(joinDescriptor))\n {\n console.error(`addJobToGroups: Insufficient information to authorize compute group: ${JSON.stringify(joinDescriptor)}.`);\n continue;\n }\n\n // Translate so that neither joinHash nor joinSecret goes across the wire.\n const message = buildCGJoinMessage(joinDescriptor, 'addJobToGroups');\n debugging('computeGroups') && console.debug(`addJobToGroups client: job ${job}, message`, message);\n\n cgArray.push(message);\n }\n\n const { success, payload } = await exports.serviceConnection.send('addJobToGroups', { job, cgArray });\n\n debugging('computeGroups') && console.debug('addJobToGroups payload', payload);\n\n if (!success) throw new DCPError(`Cannot add job ${job} to compute groups.`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n // If the server reported success but did not return a list of CGs (eg. v.4.2.5 server),\n // assume (and inform the client) we added all groups successfully\n return payload || computeGroups;\n};\n\n/**\n * Async function that lists all the Jobs in a Compute Group.\n * \n * The descriptor must have one of the properties joinkey, id (id:=opaqueId).\n * Must be the owner of the Compute Group to list jobs from it.\n * The job does not need to be owned.\n * \n * The descriptor is of the form { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }.\n * where 'bYcYGQ3NOpFnP4FKs6IBQd' is the opaqueId of the Compute Group.\n *\n * @param {cgAccessType} descriptor - Must have one of the properties joinKey, id (id:=opaqueId). Specifically\n * descriptor = { joinKey: 'dcpDemo' } or descriptor = { id: opaqueId }\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * let listOfJobs1 = await computeGroup.listJobs({ joinKey: 'dcpDemo' });\n * let listOfJobs2 = await computeGroup.listJobs({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo'\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.listJobs = async function listJobs(descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`listJobs: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'listJobs');\n debugging('computeGroups') && console.debug('listJob client: descriptor', descriptor);\n const { success, payload } = await exports.serviceConnection.send('listJobs', { descriptor });\n\n if (!success) throw new DCPError(`Cannot list jobs for compute group with ${cgId(descriptor)}`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that removes a job from a Compute Group.\n * \n * The descriptor must have one of the properties joinkey, id (id:=opaqueId).\n * Must be the owner of the Compute Group to remove a job from it.\n * The job does not need to be owned.\n * \n * The descriptor is of the form { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }.\n * where 'bYcYGQ3NOpFnP4FKs6IBQd' is the opaqueId of the Compute Group.\n *\n * @param {Address} job - The address of the Job that will be added to the Compute Group.\n * @param {cgAccessType} descriptor - { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * await computeGroup.removeJob( 'P+Y4IApeFQLrYS2W7MkVg7', { joinKey: 'dcpDemo' });\n * await computeGroup.removeJob( 'P+Y4IApeFQLrYS2W7MkVg7', { id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo'\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.removeJob = async function removeJob(job, descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`removeJob: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'removeJob');\n debugging('computeGroups') && console.debug(`removeJob client: job ${job}, descriptor`, descriptor);\n const { success, payload } = await exports.serviceConnection.send('removeJob', { job, descriptor });\n\n if (!success) throw new DCPError(`Cannot remove job ${job} from compute group with ${cgId(descriptor)}`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Async function that removes all jobs from a Compute Group.\n * \n * The descriptor must have one of the properties joinkey, id (id:=opaqueId).\n * Must be the owner of the Compute Group to remove jobs from it.\n * \n * The descriptor is of the form { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }.\n * where 'bYcYGQ3NOpFnP4FKs6IBQd' is the opaqueId of the Compute Group.\n *\n * @param {cgAccessType} descriptor - { joinKey: 'dcpDemo' } or { id: 'bYcYGQ3NOpFnP4FKs6IBQd' }\n * @returns {Promise<apiClientType>}\n * @access public\n * @example\n * await computeGroup.removeAllJobs({ joinKey: 'dcpDemo' });\n * await computeGroup.removeAllJobs({ id: 'bYcYGQ3NOpFnP4FKs6IBQd' });\n * where the corresponding rows in table computeGroups have attributes\n * row1: joinKey:='dcpDemo'\n * row2: opaqueId:='bYcYGQ3NOpFnP4FKs6IBQd'\n */\nexports.removeAllJobs = async function removeAllJobs(descriptor)\n{\n if (!exports.serviceConnection)\n await openAndConnectServiceConn();\n\n // Verify descriptor has sufficient information to access a compute group (not guarenteed).\n if (!descriptor.joinKey && !descriptor.id)\n return clientError(`removeAllJobs: Insufficient information to identify compute group: ${JSON.stringify(descriptor)}.`);\n\n descriptor = buildCGMessage(descriptor, 'removeAllJobs');\n debugging('computeGroups') && console.debug('removeAllJobs client: descriptor', descriptor);\n const { success, payload } = await exports.serviceConnection.send('removeAllJobs', { descriptor });\n\n if (!success) throw new DCPError(`Cannot remove all jobs from compute group with ${cgId(descriptor)}:`, payload);\n if (payload && !payload.success) return reconstructServiceError(payload);\n\n return payload;\n};\n\n/**\n * Calculate a joinHash for a compute group. This is an eh1- hash of the cg salt and \n * joinSecret components of a compute group description.\n *\n * @param {object} details an object containing the cg salt, which is\n * the joinKey if the compute group uses one;\n * otherwise it is the joinAddress. This object\n * may also contain the joinSecret.\n * @param {string} [joinSecret] the join secret -- plain text -- that is\n * the \"password\" for the compute group. If not\n * specified, we use details.joinSecret.\n */\nexports.calculateJoinHash = function computeGroups$calculateJoinHash(details, joinSecret)\n{\n if (typeof joinSecret === 'undefined')\n joinSecret = details.joinSecret;\n\n return hash.calculate(hash.eh1, `${details.joinKey || details.joinAddress} ${joinSecret}`);\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/compute-groups/index.js?");
4188
4234
 
4189
4235
  /***/ }),
4190
4236
 
@@ -4204,7 +4250,7 @@ eval("/**\n * @file Module that implements Compute API\n * @module dcp/comput
4204
4250
  \*********************************/
4205
4251
  /***/ ((module, exports, __webpack_require__) => {
4206
4252
 
4207
- eval("/* module decorator */ module = __webpack_require__.nmd(module);\n/**\n * @file dcp-client-bundle-src.js\n * Top-level file which gets webpacked into the bundle consumed by dcp-client 2.5\n * @author Wes Garland, wes@kingsds.network\n * @date July 2019\n */\n\n{\n let thisScript = typeof document !== 'undefined' ? (typeof document.currentScript !== 'undefined' && document.currentScript) || document.getElementById('_dcp_client_bundle') : {}\n let realModuleDeclare\n\n if ( false || typeof module.declare === 'undefined') {\n realModuleDeclare = ( true) ? module.declare : 0\n if (false) {}\n module.declare = function moduleUnWrapper (deps, factory) {\n factory(null, module.exports, module)\n return module.exports\n }\n }\n\n let _debugging = () => false\n dcpConfig.future = (__webpack_require__(/*! ../common/config-future.js */ \"./src/common/config-future.js\").futureFactory)(_debugging, dcpConfig);\n\n /* These modules are official API and must be part of DCP Client */\n let officialApi = {\n 'protocol': __webpack_require__(/*! ../protocol-v4 */ \"./src/protocol-v4/index.js\"),\n 'compute': (__webpack_require__(/*! ./compute */ \"./src/dcp-client/compute.js\").compute),\n 'worker': __webpack_require__(/*! ./worker */ \"./src/dcp-client/worker/index.js\"),\n 'wallet': __webpack_require__(/*! ./wallet */ \"./src/dcp-client/wallet/index.js\"),\n };\n\n /* Allow client programs to use modules which happen to be in the bundle anyhow */\n let conveniencePeers = {\n 'ethereumjs-wallet': (__webpack_require__(/*! ./wallet/keystore */ \"./src/dcp-client/wallet/keystore.js\")._internalEth.wallet),\n 'ethereumjs-util': (__webpack_require__(/*! ./wallet/keystore */ \"./src/dcp-client/wallet/keystore.js\")._internalEth.util),\n 'socket.io-client': __webpack_require__(/*! socket.io-client */ \"./node_modules/socket.io-client/build/cjs/index.js\"),\n 'bignumber.js': __webpack_require__(/*! bignumber.js */ \"./node_modules/bignumber.js/bignumber.js\"),\n 'semver': __webpack_require__(/*! semver */ \"./node_modules/semver/semver.js\"),\n };\n\n /* Some of these modules are API-track. Some of them need to be published to be\n * available for top-level resolution by DCP internals. Those (mostly) should have\n * been written using relative module paths.....\n */\n let modules = Object.assign({\n 'dcp-build': {\"version\":\"0bf54dbe88ee11e84b28e62b73cbd154d06967ea\",\"branch\":\"release\",\"dcpClient\":{\"version\":\"4.2.22\",\"from\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#prod-20221115\",\"resolved\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#295f18d25636449034433ddd3789070a5406c98e\"},\"built\":\"Mon Nov 21 2022 11:41:21 GMT-0500 (Eastern Standard Time)\",\"config\":{\"generated\":\"Mon 21 Nov 2022 11:41:19 AM EST by erose on lorge\",\"build\":\"debug\"},\"webpack\":\"5.74.0\",\"node\":\"v14.21.1\"},\n 'dcp-xhr': __webpack_require__(/*! ../common/dcp-xhr */ \"./src/common/dcp-xhr.js\"),\n 'dcp-env': __webpack_require__(/*! ../common/dcp-env */ \"./src/common/dcp-env.js\"),\n 'dcp-url': __webpack_require__(/*! ../common/dcp-url */ \"./src/common/dcp-url.js\"),\n 'cli': __webpack_require__(/*! ../common/cli */ \"./src/common/cli.js\"),\n 'dcp-timers': __webpack_require__(/*! ../common/dcp-timers */ \"./src/common/dcp-timers.js\"),\n 'dcp-dot-dir': __webpack_require__(/*! ../common/dcp-dot-dir */ \"./src/common/dcp-dot-dir.js\"),\n 'dcp-assert': __webpack_require__(/*! ../common/dcp-assert */ \"./src/common/dcp-assert.js\"),\n 'dcp-events': __webpack_require__(/*! ../common/dcp-events */ \"./src/common/dcp-events/index.js\"),\n 'utils': __webpack_require__(/*! ../utils */ \"./src/utils/index.js\"),\n 'debugging': __webpack_require__(/*! ../debugging */ \"./src/debugging.js\"),\n 'publish': __webpack_require__(/*! ../common/dcp-publish */ \"./src/common/dcp-publish.js\"),\n 'compute-groups': {\n ...__webpack_require__(/*! ./compute-groups */ \"./src/dcp-client/compute-groups/index.js\"),\n publicGroupOpaqueId: (__webpack_require__(/*! ../common/scheduler-constants */ \"./src/common/scheduler-constants.js\").computeGroups[\"public\"].opaqueId),\n },\n 'bank-util': __webpack_require__(/*! ./bank-util */ \"./src/dcp-client/bank-util.js\"),\n 'protocol-v4': __webpack_require__(/*! ../protocol-v4 */ \"./src/protocol-v4/index.js\"), /* deprecated */\n 'client-modal': __webpack_require__(/*! ./client-modal */ \"./src/dcp-client/client-modal/index.js\"),\n 'legacy-modal': (__webpack_require__(/*! ../../portal/www/js/modal */ \"./portal/www/js/modal.js\").Modal),\n 'eth': __webpack_require__(/*! ./wallet/eth */ \"./src/dcp-client/wallet/eth.js\"),\n 'serialize': __webpack_require__(/*! ../utils/serialize */ \"./src/utils/serialize.js\"),\n 'job': __webpack_require__(/*! ./job */ \"./src/dcp-client/job/index.js\"),\n 'range-object': __webpack_require__(/*! ./range-object */ \"./src/dcp-client/range-object.js\"),\n 'stats-ranges': __webpack_require__(/*! ./stats-ranges */ \"./src/dcp-client/stats-ranges.js\"),\n 'job-values': __webpack_require__(/*! ./job-values */ \"./src/dcp-client/job-values.js\"),\n 'signal-handler': __webpack_require__(/*! ../node-libs/signal-handler */ \"./src/node-libs/signal-handler.js\"),\n 'standard-objects': {}\n }, conveniencePeers, officialApi);\n\n /* Export the JS Standard Classes (etc) from the global object of the bundle evaluation context,\n * in case we have code somewhere that needs to use these for instanceof checks.\n */\n ;[ Object, Function, Boolean, Symbol,\n Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError,\n Number, Math, Date,\n String, RegExp,\n Array, Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array,\n Map, Set, WeakMap, WeakSet,\n ArrayBuffer, DataView, JSON,\n Promise, \n Reflect, Proxy, Intl, WebAssembly, __webpack_require__\n ].forEach(function (obj) {\n if (obj.name && (typeof obj === 'function' || typeof obj === 'object'))\n modules['standard-objects'][obj.name] = obj\n })\n\n if (typeof BigInt !== 'undefined')\n modules['standard-objects']['BigInt'] === BigInt;\n if (typeof BigInt64Array !== 'undefined')\n modules['standard-objects']['BigInt64Array'] === BigInt64Array;\n if (typeof BigInt64Array !== 'undefined')\n modules['standard-objects']['BigUint64Array'] === BigUint64Array;\n\n module.declare([], function(require, exports, module) {\n Object.assign(exports, modules)\n exports['dcp-config'] = dcpConfig\n })\n if (realModuleDeclare)\n module.declare = realModuleDeclare\n\n bundleExports = thisScript.exports = exports; /* must be last expression evaluated! */\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/index.js?");
4253
+ eval("/* module decorator */ module = __webpack_require__.nmd(module);\n/**\n * @file dcp-client-bundle-src.js\n * Top-level file which gets webpacked into the bundle consumed by dcp-client 2.5\n * @author Wes Garland, wes@kingsds.network\n * @date July 2019\n */\n\n{\n let thisScript = typeof document !== 'undefined' ? (typeof document.currentScript !== 'undefined' && document.currentScript) || document.getElementById('_dcp_client_bundle') : {}\n let realModuleDeclare\n\n if ( false || typeof module.declare === 'undefined') {\n realModuleDeclare = ( true) ? module.declare : 0\n if (false) {}\n module.declare = function moduleUnWrapper (deps, factory) {\n factory(null, module.exports, module)\n return module.exports\n }\n }\n\n let _debugging = () => false\n dcpConfig.future = (__webpack_require__(/*! ../common/config-future.js */ \"./src/common/config-future.js\").futureFactory)(_debugging, dcpConfig);\n\n /* These modules are official API and must be part of DCP Client */\n let officialApi = {\n 'protocol': __webpack_require__(/*! ../protocol-v4 */ \"./src/protocol-v4/index.js\"),\n 'compute': (__webpack_require__(/*! ./compute */ \"./src/dcp-client/compute.js\").compute),\n 'worker': __webpack_require__(/*! ./worker */ \"./src/dcp-client/worker/index.js\"),\n 'wallet': __webpack_require__(/*! ./wallet */ \"./src/dcp-client/wallet/index.js\"),\n };\n\n /* Allow client programs to use modules which happen to be in the bundle anyhow */\n let conveniencePeers = {\n 'ethereumjs-wallet': (__webpack_require__(/*! ./wallet/keystore */ \"./src/dcp-client/wallet/keystore.js\")._internalEth.wallet),\n 'ethereumjs-util': (__webpack_require__(/*! ./wallet/keystore */ \"./src/dcp-client/wallet/keystore.js\")._internalEth.util),\n 'socket.io-client': __webpack_require__(/*! @kingsds/socket.io-client */ \"./node_modules/@kingsds/socket.io-client/build/cjs/index.js\"),\n '@kingsds/socket.io-client': __webpack_require__(/*! @kingsds/socket.io-client */ \"./node_modules/@kingsds/socket.io-client/build/cjs/index.js\"),\n 'bignumber.js': __webpack_require__(/*! bignumber.js */ \"./node_modules/bignumber.js/bignumber.js\"),\n 'semver': __webpack_require__(/*! semver */ \"./node_modules/semver/semver.js\"),\n };\n\n /* Some of these modules are API-track. Some of them need to be published to be\n * available for top-level resolution by DCP internals. Those (mostly) should have\n * been written using relative module paths.....\n */\n let modules = Object.assign({\n 'dcp-build': {\"version\":\"5895f3465816246196dbdb9c0cd5aa200a943c59\",\"branch\":\"release\",\"dcpClient\":{\"version\":\"4.2.24\",\"from\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#prod-20230111\",\"resolved\":\"git+ssh://git@gitlab.com/Distributed-Compute-Protocol/dcp-client.git#0b4bbf4f5e91b57e19c5bbb66c8160479dca32cd\"},\"built\":\"Mon Jan 23 2023 14:29:03 GMT-0500 (Eastern Standard Time)\",\"config\":{\"generated\":\"Mon 23 Jan 2023 02:29:01 PM EST by erose on lorge\",\"build\":\"debug\"},\"webpack\":\"5.74.0\",\"node\":\"v14.21.2\"},\n 'dcp-xhr': __webpack_require__(/*! ../common/dcp-xhr */ \"./src/common/dcp-xhr.js\"),\n 'dcp-env': __webpack_require__(/*! ../common/dcp-env */ \"./src/common/dcp-env.js\"),\n 'dcp-url': __webpack_require__(/*! ../common/dcp-url */ \"./src/common/dcp-url.js\"),\n 'cli': __webpack_require__(/*! ../common/cli */ \"./src/common/cli.js\"),\n 'dcp-timers': __webpack_require__(/*! ../common/dcp-timers */ \"./src/common/dcp-timers.js\"),\n 'dcp-dot-dir': __webpack_require__(/*! ../common/dcp-dot-dir */ \"./src/common/dcp-dot-dir.js\"),\n 'dcp-assert': __webpack_require__(/*! ../common/dcp-assert */ \"./src/common/dcp-assert.js\"),\n 'dcp-events': __webpack_require__(/*! ../common/dcp-events */ \"./src/common/dcp-events/index.js\"),\n 'utils': __webpack_require__(/*! ../utils */ \"./src/utils/index.js\"),\n 'debugging': __webpack_require__(/*! ../debugging */ \"./src/debugging.js\"),\n 'publish': __webpack_require__(/*! ../common/dcp-publish */ \"./src/common/dcp-publish.js\"),\n 'compute-groups': {\n ...__webpack_require__(/*! ./compute-groups */ \"./src/dcp-client/compute-groups/index.js\"),\n publicGroupOpaqueId: (__webpack_require__(/*! ../common/scheduler-constants */ \"./src/common/scheduler-constants.js\").computeGroups[\"public\"].opaqueId),\n },\n 'bank-util': __webpack_require__(/*! ./bank-util */ \"./src/dcp-client/bank-util.js\"),\n 'protocol-v4': __webpack_require__(/*! ../protocol-v4 */ \"./src/protocol-v4/index.js\"), /* deprecated */\n 'client-modal': __webpack_require__(/*! ./client-modal */ \"./src/dcp-client/client-modal/index.js\"),\n 'legacy-modal': (__webpack_require__(/*! ../../portal/www/js/modal */ \"./portal/www/js/modal.js\").Modal),\n 'eth': __webpack_require__(/*! ./wallet/eth */ \"./src/dcp-client/wallet/eth.js\"),\n 'serialize': __webpack_require__(/*! ../utils/serialize */ \"./src/utils/serialize.js\"),\n 'job': __webpack_require__(/*! ./job */ \"./src/dcp-client/job/index.js\"),\n 'range-object': __webpack_require__(/*! ./range-object */ \"./src/dcp-client/range-object.js\"),\n 'stats-ranges': __webpack_require__(/*! ./stats-ranges */ \"./src/dcp-client/stats-ranges.js\"),\n 'job-values': __webpack_require__(/*! ./job-values */ \"./src/dcp-client/job-values.js\"),\n 'signal-handler': __webpack_require__(/*! ../node-libs/signal-handler */ \"./src/node-libs/signal-handler.js\"),\n 'standard-objects': {}\n }, conveniencePeers, officialApi);\n\n /* Export the JS Standard Classes (etc) from the global object of the bundle evaluation context,\n * in case we have code somewhere that needs to use these for instanceof checks.\n */\n ;[ Object, Function, Boolean, Symbol,\n Error, EvalError, RangeError, ReferenceError, SyntaxError, TypeError, URIError,\n Number, Math, Date,\n String, RegExp,\n Array, Int8Array, Uint8Array, Uint8ClampedArray, Int16Array, Uint16Array, Int32Array, Uint32Array, Float32Array, Float64Array,\n Map, Set, WeakMap, WeakSet,\n ArrayBuffer, DataView, JSON,\n Promise, \n Reflect, Proxy, Intl, WebAssembly, __webpack_require__\n ].forEach(function (obj) {\n if (obj.name && (typeof obj === 'function' || typeof obj === 'object'))\n modules['standard-objects'][obj.name] = obj\n })\n\n if (typeof BigInt !== 'undefined')\n modules['standard-objects']['BigInt'] === BigInt;\n if (typeof BigInt64Array !== 'undefined')\n modules['standard-objects']['BigInt64Array'] === BigInt64Array;\n if (typeof BigInt64Array !== 'undefined')\n modules['standard-objects']['BigUint64Array'] === BigUint64Array;\n\n module.declare([], function(require, exports, module) {\n Object.assign(exports, modules)\n exports['dcp-config'] = dcpConfig\n })\n if (realModuleDeclare)\n module.declare = realModuleDeclare\n\n bundleExports = thisScript.exports = exports; /* must be last expression evaluated! */\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/index.js?");
4208
4254
 
4209
4255
  /***/ }),
4210
4256
 
@@ -4225,7 +4271,7 @@ eval("/**\n * @file job-values.js\n * Utility code related t
4225
4271
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4226
4272
 
4227
4273
  "use strict";
4228
- eval("/**\n * @file job/index.js\n * @author Eddie Roosenmaallen, eddie@kingsds.network\n * Matthew Palma, mpalma@kingsds.network\n * Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * Ryan Saweczko, ryansaweczko@kingsds.network\n * @date November 2018\n * November 2018\n * February 2022\n * May 2022\n * Jun 2022\n *\n * This module implements the Compute API's Job Handle\n *\n */\n\n\nconst { BigNumber } = __webpack_require__(/*! bignumber.js */ \"./node_modules/bignumber.js/bignumber.js\");\nconst { v4: uuidv4 } = __webpack_require__(/*! uuid */ \"./node_modules/uuid/dist/esm-browser/index.js\");\nconst { EventEmitter, PropagatingEventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { RangeObject, MultiRangeObject, DistributionRange, SuperRangeObject, SparseRangeObject } = __webpack_require__(/*! dcp/dcp-client/range-object */ \"./src/dcp-client/range-object.js\");\nconst { fetchURI, encodeDataURI, createTempFile } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { serializeJobValue, RemoteValue } = __webpack_require__(/*! dcp/dcp-client/job-values */ \"./src/dcp-client/job-values.js\");\nconst wallet = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst ClientModal = __webpack_require__(/*! dcp/dcp-client/client-modal */ \"./src/dcp-client/client-modal/index.js\");\nconst { Worker } = __webpack_require__(/*! dcp/dcp-client/worker */ \"./src/dcp-client/worker/index.js\");\nconst { RemoteDataSet } = __webpack_require__(/*! dcp/dcp-client/remote-data-set */ \"./src/dcp-client/remote-data-set.js\");\nconst { RemoteDataPattern } = __webpack_require__(/*! dcp/dcp-client/remote-data-pattern */ \"./src/dcp-client/remote-data-pattern.js\");\nconst { ResultHandle } = __webpack_require__(/*! ./result-handle */ \"./src/dcp-client/job/result-handle.js\");\nconst { SlicePaymentOffer } = __webpack_require__(/*! ./slice-payment-offer */ \"./src/dcp-client/job/slice-payment-offer.js\");\nconst { addSlices } = __webpack_require__(/*! ./upload-slices */ \"./src/dcp-client/job/upload-slices.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst computeGroups = __webpack_require__(/*! dcp/dcp-client/compute-groups */ \"./src/dcp-client/compute-groups/index.js\");\nconst schedulerConstants = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { DEFAULT_REQUIREMENTS, removeBadRequirements } = __webpack_require__(/*! dcp/common/job-requirements-defaults */ \"./src/common/job-requirements-defaults.js\");\nconst { sliceStatus, jobValueKind } = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { jobStatus } = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst bankUtil = __webpack_require__(/*! dcp/dcp-client/bank-util */ \"./src/dcp-client/bank-util.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('dcp-client');\nconst kvin = __webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\");\nlet tunedKvin;\n\nconst log = (...args) => {\n if (debugging('job')) {\n console.debug('dcp-client:job', ...args);\n }\n};\n\nconst ON_BROWSER = DCP_ENV.isBrowserPlatform;\nconst sideloaderModuleIdentifier = 'sideloader-v1';\n\n\n/** @typedef {import('dcp/dcp-client/wallet/keystore').Keystore} Keystore */\n/** @typedef {import('dcp/dcp-client/range-object').RangeLike} RangeLike */\n/** @typedef {import('scheduler-v4/libexec/job-submit/operations/submit').MarshaledInputData} MarshaledInputData */\n\n/**\n * Ensure input data is an appropriate format\n * @param {RangeObject | DistributionRange | RemoteDataSet | Array | Iterable}\n * inputData - A URI-shaped string, a [Multi]RangeObject-constructing value, or\n * an array of slice data\n * @return {RangeObject | RangeLike | DistributionRange | RemoteDataSet | Array}\n * The coerced input in an appropriate format ([Multi]RangeObject,\n * DistributionRange, RemoteDataSet, or array)\n */\n const wrangleData = (inputData) => {\n\n if (RangeObject.isRangelike(inputData)) { return inputData }\n if (RangeObject.isRangeObject(inputData)) { return inputData }\n if (DistributionRange.isDistribution(inputData)) { return inputData }\n if (inputData instanceof SparseRangeObject) { return inputData }\n if (inputData instanceof MultiRangeObject) { return inputData }\n if (MultiRangeObject.isProtoMultiRangelike(inputData)) { return new MultiRangeObject(inputData) }\n if (RangeObject.isProtoRangelike(inputData)) { return new RangeObject(inputData) }\n if (DistributionRange.isProtoDistribution(inputData)) { return new DistributionRange(inputData) }\n if (RemoteDataSet.isRemoteDataSet(inputData)) { return inputData }\n if (RemoteDataPattern.isRemoteDataPattern(inputData)) { return inputData }\n\n return Array.isArray(inputData) ? inputData : [inputData];\n};\n\n/**\n * @classdesc The Compute API's Job Handle (see {@link https://docs.dcp.dev/specs/compute-api.html#job-handles|Compute API spec})\n * Job handles are objects which correspond to jobs. \n * They are created by some exports of the compute module, such as {@link module:dcp/compute.do|compute.do} and {@link module:dcp/compute.for|compute.for}.\n * @extends module:dcp/dcp-events.PropagatingEventEmitter\n * @hideconstructor\n * @access public\n */\nclass Job extends PropagatingEventEmitter\n{\n /**\n * Fired when the job is accepted by the scheduler on deploy.\n * \n * @event Job#accepted\n * @access public\n * @type {object}\n *//**\n * Fired when the job is cancelled.\n * \n * @event Job#cancel\n * @access public\n *//**\n * Fired when a result is returned.\n * \n * @event Job#result\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {string} task ID of the task (slice) the result came from\n * @property {number} sort The index of the slice\n * @property {object} result\n * @property {string} result.request\n * @property {*} result.result The value returned from the work function\n *//**\n * Fired when the result handle is modified, either when a new `result` event is fired or when the results are populated with `results.fetch()`\n * \n * @event Job#resultsUpdated\n * @access public\n *//**\n * Fired when the job has been completed.\n * \n * @event Job#complete\n * @access public\n * @type {ResultHandle}\n *//**\n * Fired when the job's status changes.\n * \n * @event Job#status\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} total Total number of slices in the job\n * @property {number} distributed Number of slices that have been distributed\n * @property {number} computed Number of slices that have completed execution (returned a result)\n * @property {string} runStatus Current runStatus of the job\n *//**\n * Fired when a slice throws an error.\n * \n * @event Job#error\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} sliceIndex Index of the slice that threw the error\n * @property {string} message The error message\n * @property {string} stack The error stacktrace\n * @property {string} name The error type name\n *//**\n * Fired when a slice uses one of the console log functions.\n * \n * @event Job#console\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} sliceIndex The index of the slice that produced this event\n * @property {string} level The log level, one of `debug`, `info`, `log`, `warn`, or `error`\n * @property {string} message The console log message\n *//**\n * Fired when a slice is stopped for not calling progress. Contains information about how long the slice ran for, and about the last reported progress calls.\n * \n * @event Job#noProgress\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} sliceIndex The index of the slice that failed due to no progress\n * @property {number} timestamp How long the slice ran before failing\n * @property {object} progressReports\n * @property {object} progressReports.last The last progress report received from the worker\n * @property {number} progressReports.last.timestamp Time since the start of the slice\n * @property {number} progressReports.last.progress Progress value reported\n * @property {*} progressReports.last.value The last value that was passed to the progress function\n * @property {number} progressReports.last.throttledReports Number of calls to progress that were throttled since the last report\n * @property {object} progressReports.lastUpdate The last determinate (update to the progress param) progress report received from the worker\n * @property {number} progressReports.lastUpdate.timestamp\n * @property {number} progressReports.lastUpdate.progress\n * @property {*} progressReports.lastUpdate.value\n * @property {number} progressReports.lastUpdate.throttledReports\n *//**\n @todo: is this in the spec? is there a no progress data? should there be?\n * Identical to `noProgress`, except that it also contains the data that the slice was executed with.\n * \n * @event Job#noProgressData\n * @access public\n * @type {object}\n * @property {*} data The data that the slice was executed with\n *//**\n * Fired when the job is paused due to running out of funds. The job can be resumed by escrowing more funds then resuming the job.\n * @todo: is this a thing, should it be a thing (the payload)\n * Event payload is the estimated funds required to complete the job\n * \n * @event Job#nofunds\n * @access public\n * @type {BigNumber}\n *//**\n * Fired when the job cannot be deployed due to no bank account / not enough balance to deploy the job\n * \n * @event Job#ENOFUNDS\n * @access public\n *//**\n * Fired when the job is cancelled due to the work function not calling the `progress` method frequently enough.\n * \n * @event Job#ENOPROGRESS\n * @access public\n *//**\n * The job was cancelled because scheduler has determined that individual tasks in this job exceed the maximum allowable execution time.\n * \n * @event Job#ESLICETOOSLOW\n * @access public\n *//**\n * Fired when the job is cancelled because too many work functions are terminating with uncaught exceptions.\n * \n * @event Job#ETOOMANYERRORS\n * @access public\n */\n\n /**\n * @form1 new Job('application_worker_address'[, data[, arguments]])\n * @form2a new Job('worker source'[, data[, arguments]])\n * @form2b new Job(worker_function[, data[, arguments]])\n */\n constructor ()\n {\n super('Job');\n if (typeof arguments[0] === 'function')\n arguments[0] = arguments[0].toString();\n\n if (typeof arguments[0] === 'string')\n {\n const { encodeDataURI } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n this.workFunctionURI = encodeDataURI(arguments[0], 'application/javascript');\n } \n else if (DcpURL.isURL(arguments[0]))\n this.workFunctionURI = arguments[0].href;\n\n this.jobInputData = wrangleData(arguments[1] || []);\n this.jobArguments = wrangleData(arguments[2] || []);\n \n log('num wrangledInputData:', this.jobInputData.length);\n log('num wrangledArguments:', this.jobArguments.length);\n\n this.initEventSystems();\n\n /**\n * An object describing the cost the user believes each the average slice will incur, in terms of CPU/GPU and I/O.\n * If defined, this object is used to provide initial scheduling hints and to calculate escrow amounts.\n *\n * @type {object}\n * @access public\n */\n this.initialSliceProfile = undefined;\n \n // The max value that the client is willing to spend to deploy\n // (list on the scheduler, doesn't include compute payment)\n // maxDeployPayment is the max the user is willing to pay to DCP (as a\n // Hold), in addition to the per-slice offer and associated scrape.\n // Currently calculated as `deployCost = costPerKB *\n // (JSON.stringify(job).length / 1024) // 1e-9 per kb`\n // @todo: figure this out / er nov 2018\n this.maxDeployPayment = 1;\n\n /**\n * An object describing the requirements that workers must have to be eligible for this job. The default values are set in the job-submitter, and only the client specified\n * requirements are sent over the wire. See {@link https://docs.dcp.dev/specs/compute-api.html#requirements-objects|Requirements Objects}.\n *\n * @type {object}\n * @access public\n */\n this.requirements = {};\n\n /**\n * A place to store public-facing attributes of the job. Anything stored on this object will be available inside the work \n * function (see {@link module:dcp/compute~sandboxEnv.work}). The properties documented here may be used by workers to display what jobs are currently being \n * worked on.\n * @access public\n * @property {string} name Public-facing name of this job.\n * @property {string} description Public-facing description for this job.\n * @property {string} link Public-facing link to external resource about this job.\n */\n this.public = {\n name: null,\n description: null,\n link: null,\n };\n \n /**\n * A cryptographic receipt indicating deployment of the job on the scheduler\n * @type {object}\n * @access public\n */\n this.receipt = null;\n \n /**\n * a SliceProfile object which contains the average costs for the slices which have been computed to date.\n * Until the first result is returned, this property is undefined.\n * @type {object}\n * @access public\n */\n this.meanSliceProfile = null;\n \n /**\n * A number (can be null, undefined, or infinity) describing the estimationSlicesRemaining in the jpd (dcp-2593)\n * @type {number}\n * @access public\n */\n this.estimationSlices = undefined;\n /**\n * When true, allows a job in estimation to have requestTask return multiple estimation slices.\n * This flag applies independent of infinite estimation, viz., this.estimationSlices === null .\n * @type {boolean}\n * @access public\n */\n this.greedyEstimation = false;\n /**\n * tunable parameters per job\n * @access public\n * @param {object} tuning \n * @param {string} tuning.kvin Encode the TypedArray into a string, trying multiple methods to determine optimum \n * size/performance. The this.tune variable affects the behavior of this code this:\n * @param {boolean} speed If true, only do naive encoding: floats get represented as byte-per-digit strings\n * @param {boolean} size If true, try the naive, ab8, and ab16 encodings; pick the smallest\n * If both are false try the naive encoding if under typedArrayPackThreshold and use if smaller\n * than ab8; otherwise, use ab8\n */\n this.tuning = {\n kvin: {\n size: false,\n speed: false,\n },\n }\n /* For API interface to end-users only */\n Object.defineProperty(this, 'id', {\n get: () => this.address,\n set: (id) => { this.address = id }\n });\n \n this.uuid = uuidv4(); /** @see {@link https://kingsds.atlassian.net/browse/DCP-1475?atlOrigin=eyJpIjoiNzg3NmEzOWE0OWI4NGZkNmI5NjU0MWNmZGY2OTYzZDUiLCJwIjoiaiJ9|Jira Issue} */\n this.dependencies = []; /* dependencies of the work function */\n this.requirePath = []; /* require path for dependencies */\n this.modulePath = []; /* path to module that invoked .exec() for job */\n this.connected = false; /* true when exec or resume called */\n this.results = new ResultHandle(this); /* result handle */\n this.collateResults = true; /* option to receive results as they are computed & ensure all are received on finish */\n this.contextId = null; /* optional string which is used to indicate to caching mechanisms different keystores with same name */ \n this.force100pctCPUDensity = false; /* tell scheduler to assume this job uses 100% cpu density */\n this.workerConsole = false; /* tell workers to log more information about failures in the evaluator this job causes */\n this.address = null; /* job address, created by scheduler during exec call. */\n this.paymentAccountKeystore = null; /* keystore for payment for job to come from */\n this.status = { /* job status details */\n runStatus: null,\n total: null,\n distributed: null,\n computed: null\n };\n \n // service locations\n this.scheduler = dcpConfig.scheduler.services.jobSubmit.location;\n this.bank = dcpConfig.bank.services.bankTeller.location;\n \n // Compute groups. Add to public compute group by default\n this.computeGroups = [ Object.assign({}, schedulerConstants.computeGroups.public) ];\n \n // Update the ready state as we go through job deployment\n this.readyState = sliceStatus.new;\n const that = this;\n this.readyStateChange = function job$$readyStateChange (readyState)\n {\n that.readyState = readyState;\n that.emit('readyStateChange', that.readyState);\n }\n }\n \n /**\n * Initialize the various event systems the job handle requires. These include:\n * - an internal event emitter (this.ee)\n * - an event emitter for any events emitted on `work.emit` within work functions (this.work)\n * - an event subscriber to subscribe (to receive) events from the scheduler (this.eventSubscriber)\n */\n initEventSystems ()\n {\n // Handle the various event-related things required in the constructor\n\n // Internal event emitter for events within job handle\n this.ee = new EventEmitter('Job Internal');\n\n /**\n * An EventEmitter for custom events dispatched by the work function.\n * @type {module:dcp/dcp-events.EventEmitter}\n * @access public\n * @example\n * // in work function\n * work.emit('myEventName', 1, [2], \"three\");\n * // client-side\n * job.work.on('myEventName', (num, arr, string) => { });\n */\n this.work = new EventEmitter('job.work');\n this.listenForCustomEvents = false;\n\n // Initialize the eventSubscriber so each job has unique eventSubscriber\n this.eventSubscriber = new ((__webpack_require__(/*! dcp/events/event-subscriber */ \"./src/events/event-subscriber.js\").EventSubscriber))(this);\n\n // Some events from the event subscriber can't be emitted immediately upon receipt without having \n // weird/wrong output due to things like serialization. We allow interceptors in the event subscriber\n // to handle this.\n const that = this\n var lastConsoleEv;\n var sameCounter = 1;\n const parseConsole = function deserializeConsoleMessage(ev) {\n if (tunedKvin)\n ev.message = tunedKvin.unmarshal(ev.message);\n else \n ev.message = kvin.unmarshal(ev.message);\n \n if (lastConsoleEv && ev.message[0] === lastConsoleEv.message[0] && ev.sliceNumber === lastConsoleEv.sliceNumber && ev.level === lastConsoleEv.level)\n ev.same = ++sameCounter;\n else\n sameCounter = 1;\n lastConsoleEv = ev;\n \n /* if we have the same message being logged (same sliceNumber, message, log level), the console event object will have the sole property same, nothing else */\n if (ev.same > 1)\n that.emit('console', { same: ev.same });\n else\n {\n delete ev.same;\n that.emit('console', ev);\n }\n }\n\n this.eventIntercepts = {\n result: (ev) => this.handleResult(ev),\n status: (ev) => this.handleStatus(ev),\n cancel: (ev) => this.ee.emit('stopped', ev),\n custom: (ev) => this.work.emit(ev.customEvent, ev),\n console: parseConsole,\n };\n \n this.eventTypes = (__webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\").eventTypes);\n\n this.work.on('newListener', (evt) => {\n this.listenForCustomEvents = true;\n });\n this.desiredEvents = []\n this.on('newListener', (evt) => {\n if (!this.connected && evt !== 'newListener')\n this.desiredEvents.push(evt);\n if (evt === 'cancel')\n this.listeningForCancel = true;\n });\n }\n \n /** \n * Cancel the job\n * @access public\n * @param {string} reason If provided, will be sent to client\n */\n async cancel (reason = undefined)\n {\n const response = await this.useDeployConnection('cancelJob', {\n job: this.address,\n owner: this.paymentAccountKeystore.address,\n reason,\n }, this.paymentAccountKeystore);\n\n return response.payload;\n }\n\n /** \n * Resume this job\n * @access public\n */\n async resume ()\n {\n // Careful -- this.schedulerConnection does not exist.\n const response = await this.schedulerConnection.send('resumeJob', {\n job: this.address,\n owner: this.paymentAccountKeystore.address,\n }, this.paymentAccountKeystore);\n\n return response.payload;\n }\n\n /**\n * Helper function for retrieving info about the job. The job must have already been deployed.\n * An alias for {@link module:dcp/compute.getJobInfo}.\n * @access public\n */\n getJobInfo ()\n {\n return (__webpack_require__(/*! ../compute */ \"./src/dcp-client/compute.js\").compute.getJobInfo)(this.address);\n }\n \n /**\n * Helper function for retrieving info about the job's slices. The job must have already been deployed.\n * An alias for {@link module:dcp/compute.getSliceInfo}.\n * @access public\n */\n getSliceInfo ()\n {\n return (__webpack_require__(/*! ../compute */ \"./src/dcp-client/compute.js\").compute.getSliceInfo)(this.address);\n }\n \n /** Escrow additional funds for this job\n * @access public\n * @param {number|BigNumber} fundsRequired - A number or BigNumber instance representing the funds to escrow for this job\n */\n async escrow (fundsRequired)\n {\n if ((typeof fundsRequired !== 'number' && !BigNumber.isBigNumber(fundsRequired))\n || fundsRequired <= 0 || !Number.isFinite(fundsRequired) || Number.isNaN(fundsRequired))\n throw new Error(`Job.escrow: fundsRequired must be a number greater than zero. (not ${fundsRequired})`);\n\n const bankConnection = new protocolV4.Connection(dcpConfig.bank.services.bankTeller);\n\n /*\n * escrow has been broken for an unknown amount of time. `feeStructureId` is not defined anywhere in the job class, and hasn't\n * for a period of time. When fixed, `this[INTERNAL_SYMBOL].payloadDetails.feeStructureId` will likely become just `this.feeStructureId`,\n * but it's being left alone until someone spends the time to fix escrow. / rs Jul 2022\n */\n const response = await bankConnection.send('embiggenFeeStructure', {\n feeStructureAddress: this[INTERNAL_SYMBOL].payloadDetails.feeStructureId,\n additionalEscrow: new BigNumber(fundsRequired),\n fromAddress: this.paymentAccountKeystore.address,\n }, this.paymentAccountKeystore);\n\n bankConnection.close();\n const escrowReceipt = response.payload;\n return escrowReceipt;\n }\n\n /**\n * create bundles for local dependencies\n */\n _pack ()\n {\n var retval = (__webpack_require__(/*! ./node-modules */ \"./src/dcp-client/job/node-modules.js\").createModuleBundle)(this.dependencies);\n return retval;\n }\n\n /** \n * Collect all of the dependencies together, throw them into a BravoJS\n * module which sideloads them as a side effect of declaration, and transmit\n * them to the package manager. Then we return the package descriptor object,\n * which is guaranteed to have only one file in it.\n *\n * @returns {object} with properties name and files[0]\n */\n async _publishLocalModules()\n {\n const dcpPublish = __webpack_require__(/*! dcp/common/dcp-publish */ \"./src/common/dcp-publish.js\");\n \n const { tempFile, hash, unresolved } = await this._pack();\n\n if (!tempFile) {\n return { unresolved };\n }\n\n const sideloaderFilename = tempFile.filename;\n const pkg = {\n name: `dcp-pkg-v1-localhost-${hash.toString('hex')}`,\n version: '1.0.0',\n files: {\n [sideloaderFilename]: `${sideloaderModuleIdentifier}.js`,\n },\n }\n\n await dcpPublish.publish(pkg);\n tempFile.remove();\n\n return { pkg, unresolved };\n }\n \n /**\n * This function specifies a module dependency (when the argument is a string)\n * or a list of dependencies (when the argument is an array) of the work\n * function. This function can be invoked multiple times before deployment.\n * @param {string | string[]} modulePaths - A string or array describing one\n * or more dependencies of the job.\n * @access public\n */\n requires (modulePaths)\n {\n if (typeof modulePaths !== 'string' && (!Array.isArray(modulePaths) || modulePaths.some((modulePath) => typeof modulePath !== 'string')))\n throw new TypeError('The argument to dependencies is not a string or an array of strings');\n else if (modulePaths.length === 0)\n throw new RangeError('The argument to dependencies cannot be an empty string or array');\n else if (Array.isArray(modulePaths) && modulePaths.some((modulePath) => modulePath.length === 0))\n throw new RangeError('The argument to dependencies cannot be an array containing an empty string');\n\n if (!Array.isArray(modulePaths))\n modulePaths = [modulePaths];\n\n for (const modulePath of modulePaths)\n {\n if (modulePath[0] !== '.' && modulePath.indexOf('/') !== -1)\n {\n const modulePrefixRegEx = /^(.*)\\/.*?$/;\n const [, modulePrefix] = modulePath.match(modulePrefixRegEx);\n if (modulePrefix && this.requirePath.indexOf(modulePrefix) === -1)\n this.requirePath.push(modulePrefix);\n }\n this.dependencies.push(modulePath);\n }\n }\n \n /** Set the account upon which funds will be drawn to pay for the job.\n * @param {module:dcp/wallet.AuthKeystore} keystore A keystore that representa a bank account.\n * @access public\n */\n setPaymentAccountKeystore (keystore)\n {\n if (this.address)\n {\n if (!keystore.address.eq(this.paymentAccountKeystore))\n {\n let message = 'Cannot change payment account after job has been deployed';\n this.emit('EPERM', message);\n throw new Error(`EPERM: ${message}`);\n }\n }\n \n if (!(keystore instanceof wallet.Keystore))\n throw new Error('Not an instance of Keystore: ' + keystore.toString());\n this.paymentAccountKeystore = keystore;\n }\n \n /** Set the slice payment offer. This is equivalent to the first argument to exec.\n * @param {number} slicePaymentOffer - The number of DCC the user is willing to pay to compute one slice of this job\n */\n setSlicePaymentOffer (slicePaymentOffer)\n {\n this.slicePaymentOffer = new SlicePaymentOffer(slicePaymentOffer);\n }\n \n \n /**\n * @param {URL|DcpURL} locationUrl - A URL object\n * @param {object} postParams - An object with any parameters that a user would like to be passed to a \n * remote result location. This object is capable of carry API keys for S3, \n * DropBox, etc. These parameters are passed as parameters in an \n * application/x-www-form-urlencoded request.\n */\n setResultStorage (locationUrl, postParams)\n {\n if (locationUrl instanceof URL || locationUrl instanceof DcpURL)\n this.resultStorageDetails = locationUrl;\n else\n throw new Error('Not an instance of a DCP URL: ' + locationUrl);\n \n\n // resultStorageParams contains any post params required for off-prem storage\n if (typeof postParams !== 'undefined' && typeof postParams === 'object' )\n this.resultStorageParams = postParams;\n else\n throw new Error('Not an instance of a object: ' + postParams);\n\n // Some type of object here\n this.resultStorageType = 'pattern';\n }\n \n /**\n * This function is identical to exec, except that the job is executed locally\n * in the client.\n * @async\n * @param {number} cores - the number of local cores in which to execute the job.\n * @param {...any} args - The remaining arguments are identical to the arguments of exec\n * @return {Promise<ResultHandle>} - resolves with the results of the job, rejects on an error\n * @access public\n */\n async localExec (cores = 1, ...args)\n {\n this.inLocalExec = true;\n this.estimationSlices = 0;\n this.greedyEstimation = false;\n this.isCI = false;\n\n let worker;\n this.on('accepted', () => {\n // Start a worker for this job\n worker = new Worker({\n localExec: true,\n jobAddresses: [this.address],\n allowedOrigins: this.localExecAllowedFiles,\n paymentAddress: this.paymentAccountKeystore.address,\n identity: this.identityKeystore,\n maxWorkingSandboxes: cores,\n sandboxOptions: {\n ignoreNoProgress: true,\n SandboxConstructor: (DCP_ENV.platform === 'nodejs'\n && (__webpack_require__(/*! ../worker/evaluators */ \"./src/dcp-client/worker/evaluators/index.js\").nodeEvaluatorFactory)())\n },\n });\n \n worker.on('error', (error) => {\n console.error('Worker Error:', error);\n });\n \n worker.on('warning', (warning) => {\n console.warn('Worker Warning:', warning);\n });\n\n worker.start().catch((e) => {\n console.error('Failed to start worker for localExec:');\n console.error(e.message);\n });\n });\n \n if (DCP_ENV.platform === 'nodejs')\n {\n this.localExecAllowedFiles =\n {\n any: [],\n fetchData: [],\n fetchWorkFunctions: [],\n fetchArguments: [],\n sendResults: [],\n };\n \n // Determine type of input data\n const { dataRange, dataValues, dataPattern, sliceCount } = marshalInputData(this.jobInputData);\n \n const inputSetFiles = [];\n \n let inputSetURIs = [];\n let dataSet;\n \n if (dataValues)\n {\n for (let i = 0; i < dataValues.length; i++)\n {\n if (!(dataValues[i] instanceof URL))\n {\n let marshaledInputValue = kvinMarshal(dataValues[i]);\n let inputDataFile = createTempFile('dcp-localExec-sliceData-XXXXXXXXX', 'kvin');\n inputDataFile.writeSync(JSON.stringify(marshaledInputValue));\n inputSetFiles.push(inputDataFile);\n inputSetURIs.push(new URL('file://' + inputDataFile.filename));\n }\n else\n {\n inputSetURIs.push(dataValues[i]);\n if (this.localExecAllowedFiles['fetchData'].indexOf(dataValues[i].origin) === -1)\n this.localExecAllowedFiles['fetchData'].push(dataValues[i].origin);\n }\n }\n dataSet = new RemoteDataSet(inputSetURIs);\n if (dataSet.length > 0)\n this.marshaledDataValues = dataSet;\n }\n if (dataRange)\n {\n inputSetFiles.push(createTempFile('dcp-localExec-sliceData-XXXXXXXXX', 'json'));\n let marshaledInputSet = JSON.stringify(dataRange);\n inputSetFiles[0].writeSync(marshaledInputSet)\n inputSetURIs.push(new URL('file://' + inputSetFiles[0].filename));\n dataSet = new RemoteDataSet(inputSetURIs);\n this.marshaledDataRange = dataSet;\n this.rangeLength = dataRange.length;\n }\n \n if (dataPattern)\n {\n let uri = dataPattern;\n for (let i = 0; i < sliceCount; i++)\n {\n let sliceNum = i+1;\n let newURI = new URL(uri.replace('{slice}', sliceNum.toString()));\n this.localExecAllowedFiles['fetchData'].push(newURI.origin);\n }\n }\n \n // For allowed origins of the localexec worker. Only allow the origins (files in this case) in this list.\n for (let i = 0; i < inputSetFiles.length; i++)\n this.localExecAllowedFiles['fetchData'].push(inputSetFiles[i].filename);\n \n // Save work function to disk if work function starts with data (ie not remote)\n if (this.workFunctionURI.startsWith('data:'))\n {\n const workFunctionFile = createTempFile('dcp-localExec-workFunction-XXXXXXXXX', 'js');\n const workFunction = await fetchURI(this.workFunctionURI);\n workFunctionFile.writeSync(workFunction);\n \n const workFunctionFileURL = new URL('file://' + workFunctionFile);\n this.workFunctionURI = workFunctionFileURL.href;\n this.localExecAllowedFiles['fetchWorkFunctions'].push(workFunctionFile.filename);\n }\n else\n this.localExecAllowedFiles['fetchWorkFunctions'].push(new URL(this.workFunctionURI).origin);\n \n let encodedJobArgumentUris = [];\n if (this.jobArguments)\n {\n if (this.jobArguments instanceof RemoteDataPattern) /* Not supported */\n throw new DCPError('Cannot use RemoteDataPattern as work function arguments', 'EBADARG')\n if (this.jobArguments instanceof RemoteDataSet) /* Entire set is RemoteDataSet */\n {\n this.jobArguments.forEach((e) =>\n {\n this.localExecAllowedFiles['fetchArguments'].push(new URL(e).origin);\n encodedJobArgumentUris.push(new URL(e));\n });\n }\n else\n {\n for (let i = 0; i < this.jobArguments.length; i++)\n {\n if (this.jobArguments[i] instanceof URL)\n {\n if (this.localExecAllowedFiles['fetchArguments'].indexOf(this.jobArguments[i].origin) === -1)\n this.localExecAllowedFiles['fetchArguments'].push(this.jobArguments[i].origin);\n encodedJobArgumentUris.push(this.jobArguments[i]);\n }\n else\n {\n if (this.jobArguments[i] instanceof RemoteDataSet) /* Member of set is RemoteDataSet */\n {\n this.jobArguments[i].forEach((e) =>\n {\n this.localExecAllowedFiles['fetchArguments'].push(new URL(e).origin);\n encodedJobArgumentUris.push(new URL(e));\n });\n }\n else /* Actual Value */\n {\n const localArgFile = createTempFile(`dcp-localExec-argument-${i}-XXXXXXXXX`, 'kvin');\n localArgFile.writeSync(JSON.stringify(kvinMarshal(this.jobArguments[i])));\n this.localExecAllowedFiles['fetchArguments'].push(localArgFile.filename);\n encodedJobArgumentUris.push(new URL('file://' + localArgFile.filename));\n }\n }\n } \n }\n }\n this.marshaledArguments = kvinMarshal(encodedJobArgumentUris);\n\n // Support for remote results with localExec.\n if (this.resultStorageDetails)\n {\n this.localExecAllowedFiles['sendResults'].push(this.resultStorageDetails.origin);\n }\n }\n \n return this.exec(...args).finally(() => {\n if (worker) {\n setTimeout(() => {\n // stop the worker\n worker.stop(true);\n }, 3000);\n }\n });\n }\n\n /**\n * Deploys the job to the scheduler.\n * @param {number | object} [slicePaymentOffer=compute.marketValue] - Amount\n * in DCC that the user is willing to pay per slice.\n * @param {Keystore} [paymentAccountKeystore=wallet.get] - An instance of the\n * Wallet API Keystore that's used as the payment account when executing the\n * job.\n * @param {object} [initialSliceProfile] - An object describing the cost the\n * user believes the average slice will incur.\n * @access public\n * @emits Job#accepted\n */\n async exec (slicePaymentOffer = (__webpack_require__(/*! ../compute */ \"./src/dcp-client/compute.js\").compute.marketValue), paymentAccountKeystore, initialSliceProfile)\n {\n if (this.connected)\n throw new Error('Exec called twice on the same job handle.');\n \n if (this.estimationSlices === Infinity)\n this.estimationSlices = null;\n else if (this.estimationSlices < 0)\n throw new Error('Incorrect value for estimationSlices; it can be an integer or Infinity!');\n \n if (this.tuning.kvin.speed || this.tuning.kvin.size)\n {\n tunedKvin = new kvin.KVIN();\n tunedKvin.tune = 'size';\n if(this.tuning.kvin.speed)\n tunedKvin.tune = 'speed';\n // If both size and speed are true, kvin will optimize based on speed\n if(this.tuning.kvin.speed && this.tuning.kvin.size)\n console.log('Slices and arguments are being uploaded with speed optimization.');\n }\n \n /* slight optimization to ensure we don't send requirements that will be ignored in the job submitter. Make a copy of the client specified requirements for this so that we dont magically override something they manually set */\n const _DEFAULT_REQUIREMENTS = JSON.parse(JSON.stringify(DEFAULT_REQUIREMENTS));\n removeBadRequirements(this.requirements, _DEFAULT_REQUIREMENTS);\n \n this.readyStateChange('exec');\n if ((typeof slicePaymentOffer === 'number') || (typeof slicePaymentOffer === 'object')\n || ((this.slicePaymentOffer === null || this.slicePaymentOffer === undefined) && typeof slicePaymentOffer === 'function'))\n this.setSlicePaymentOffer(slicePaymentOffer);\n if (typeof initialSliceProfile !== 'undefined')\n this.initialSliceProfile = initialSliceProfile;\n \n if (typeof paymentAccountKeystore !== 'undefined')\n {\n /** XXX @todo deprecate use of ethereum wallet objects */\n if (typeof paymentAccountKeystore === 'object' && paymentAccountKeystore.hasOwnProperty('_privKey'))\n {\n console.warn('* deprecated API * - job.exec invoked with ethereum wallet object as paymentAccountKeystore') /* /wg oct 2019 */\n paymentAccountKeystore = paymentAccountKeystore._privKey\n }\n /** XXX @todo deprecate use of private keys */\n if (wallet.isPrivateKey(paymentAccountKeystore))\n {\n console.warn('* deprecated API * - job.exec invoked with private key as paymentAccountKeystore') /* /wg dec 2019 */\n paymentAccountKeystore = await new wallet.Keystore(paymentAccountKeystore, '');\n }\n\n this.setPaymentAccountKeystore(paymentAccountKeystore)\n }\n \n if (this.paymentAccountKeystore)\n // Throws if they fail to unlock, we allow this since the keystore was set programmatically. \n await this.paymentAccountKeystore.unlock(undefined, parseFloat(dcpConfig.job.maxDeployTime));\n else\n {\n // If not set programmatically, we keep trying to get an unlocked keystore ... forever.\n let locked = true;\n let safety = 0; // no while loop shall go unguarded\n let ks;\n do\n {\n ks = null;\n // custom message for the browser modal to denote the purpose of keystore submission\n let msg = `This application is requesting a keystore file to execute ${this.public.description || this.public.name || 'this job'}. Please upload the corresponding keystore file. If you upload a keystore file which has been encrypted with a passphrase, the application will not be able to use it until it prompts for a passphrase and you enter it.`;\n try\n {\n ks = await wallet.get({ contextId: this.contextId, jobName: this.public.name, msg});\n }\n catch (e)\n {\n if (e.code !== ClientModal.CancelErrorCode) throw e;\n };\n if (ks)\n {\n try\n {\n await ks.unlock(undefined, parseFloat(dcpConfig.job.maxDeployTime));\n locked = false;\n }\n catch (e)\n {\n // prompt user again if user enters password incorrectly, exit modal otherwise\n if (e.code !== wallet.unlockFailErrorCode) throw e;\n }\n }\n if (safety++ > 1000) throw new Error('EINFINITY: job.exec tried wallet.get more than 1000 times.')\n } while (locked);\n this.setPaymentAccountKeystore(ks)\n }\n \n // We either have a valid keystore + password or we have rejected by this point.\n if (!this.slicePaymentOffer)\n throw new Error('A payment profile must be assigned before executing the job');\n else\n this.feeStructure = this.slicePaymentOffer.toFeeStructure(this.jobInputData.length);\n\n if (!this.address)\n {\n try\n {\n this.readyStateChange('init');\n await this.deployJob();\n const listenersPromise = this.addInitialEvents();\n const computeGroupsPromise = this.joinComputeGroups();\n let uploadSlicePromise;\n // if job data is by value then upload data to the scheduler in a staggered fashion\n if (Array.isArray(this.dataValues) && !this.marshaledDataValues)\n {\n this.readyStateChange('uploading');\n uploadSlicePromise = addSlices(this.dataValues, this.address, tunedKvin)\n .then(() => {\n debugging('slice-upload') && console.info(`970: slice data uploaded, closing job...`);\n return this.close();\n });\n }\n \n // await all promises for operations that can be done after the job is deployed\n await Promise.all([listenersPromise, computeGroupsPromise, uploadSlicePromise]);\n \n this.readyStateChange('deployed');\n this.emit('accepted', { job: this });\n }\n catch (error)\n {\n if (ON_BROWSER)\n await ClientModal.alert(error, { title: 'Failed to deploy job!' });\n throw error;\n }\n }\n else\n {\n // reconnecting to an old job\n await this.addInitialEvents();\n this.readyStateChange('reconnected');\n }\n\n this.connected = true;\n \n // start a timer to keep the node process alive until the results come back. Keep refreshing this timer until the results promise is resovled or rejected.\n let pendingResultsTimer;\n if (DCP_ENV.platform === 'nodejs')\n {\n pendingResultsTimer = setTimeout(() => {\n pendingResultsTimer.refresh();\n }, 1000 * 60 * 60) /* hour long timer */\n }\n\n return new Promise((resolve, reject) => {\n this.startEventBandaidWatchdog();\n \n const onComplete = () => {\n if (DCP_ENV.platform === 'nodejs')\n clearTimeout(pendingResultsTimer);\n this.stopEventBandaidWatchdog();\n resolve(this.results); \n };\n\n const onCancel = (event) => {\n /**\n * FIXME(DCP-1150): Remove this since normal cancel event is noisy\n * enough to not need stopped event too.\n */\n if (ON_BROWSER && !this.listeningForCancel)\n ClientModal.alert('More details in console...', { title: 'Job Canceled' });\n this.emit('cancel', event);\n\n let errorMsg = event.reason;\n if (event.error && event.error !== 'undefined')\n errorMsg = errorMsg +`\\n Recent error message: ${event.error.message}`\n \n if (DCP_ENV.platform === 'nodejs')\n clearTimeout(pendingResultsTimer); \n\n this.stopEventBandaidWatchdog();\n\n reject(new DCPError(errorMsg, event.code));\n };\n\n this.ee.once('stopped', async (stopEvent) => {\n // There is a chance the result submitter will emit finished > 1 time. Only handle it once.\n if (this.receivedStop)\n return;\n this.receivedStop = true;\n this.emit('stopped', stopEvent.runStatus);\n switch (stopEvent.runStatus) {\n case jobStatus.finished:\n if (this.collateResults)\n {\n let report = await this.getJobInfo();\n let allSliceNumbers = Array.from(Array(report.totalSlices)).map((e,i)=>i+1);\n let remainSliceNumbers = allSliceNumbers.filter((e) => !this.results.isAvailable(e));\n\n if (remainSliceNumbers.length)\n {\n const promises = remainSliceNumbers.map(sliceNumber => this.results.fetch([sliceNumber], true));\n await Promise.all(promises);\n }\n }\n\n this.emit('complete', this.results);\n onComplete();\n break;\n case jobStatus.cancelled:\n onCancel(stopEvent);\n break;\n default:\n /**\n * Asserting that we should never be able to reach here. The only\n * scheduler events that should trigger the Job's 'stopped' event\n * are jobStatus.cancelled, jobStatus.finished, and sliceStatus.paused.\n */\n reject(new Error(`Unknown event \"${stopEvent.runStatus}\" caused the job to be stopped.`));\n break;\n }\n });\n\n }).finally(() => {\n const handleErr = (e) => {\n console.error('Error while closing job connection:');\n console.error(e);\n }\n\n // Create an async IIFE to not block the promise chain\n (async () => {\n // delay to let last few events to be received\n await new Promise((resolve) => setTimeout(resolve, 1000));\n \n // close all of the connections so that we don't cause node processes to hang.\n this.closeDeployConnection();\n await this.eventSubscriber.close().catch(handleErr);\n await computeGroups.closeServiceConnection().catch((err) => {\n console.error('Warning: could not close compute groups service connection', err);\n });\n })();\n });\n }\n\n /**\n * Start the event-bandaid watchdog. Every [configurable] seconds, fetch\n * the job report and do appropriate things if the job is stopped.\n */\n startEventBandaidWatchdog()\n {\n if (this.eventBandaidHandle)\n this.stopEventBandaidWatchdog();\n\n this.eventBandaidHandle = setInterval(() => {\n this.eventBandaid();\n }, 60 * 1000);\n\n // if we are in a nodelike environment with unreffable timers, unref the\n // timer so it doesn't hold the process open. \n // \n // (n.b. while I understand that we _want_ the process to stay open while\n // the job lives, but this is Not The Way; the whole eventBandaid is a\n // temporary hack which will go away in time ~ER 2022-11-02)\n if (this.eventBandaidHandle.unref)\n this.eventBandaidHandle.unref();\n }\n\n /**\n * Stop and clean up the even-bandaid watchdog.\n */\n stopEventBandaidWatchdog()\n {\n if (this.eventBandaidHandle)\n clearInterval(this.eventBandaidHandle);\n\n this.eventBandaidHandle = false;\n }\n\n /**\n * Called on an interval, started by .exec and stopped by\n * onComplete/onCancel. Fetches the jobReport and verifies the job is\n * not in a terminal state; if the job is finished or cancelled, we\n * emit a synthetic internal stop event to trigger regular cleanup\n * (result retrieval, etc) and finalize the job handle.\n */\n eventBandaid()\n {\n this.getJobInfo()\n .then(jobInfo => {\n if ([jobStatus.finished, jobStatus.cancelled].includes(jobInfo.status))\n this.ee.emit('stopped', { runStatus: jobInfo.status });\n })\n .catch(error => {\n\n });\n }\n \n /**\n * job.addListeners(): Private function used to set up event listeners to the scheduler\n * before deploying the job.\n */\n async addInitialEvents ()\n {\n this.readyStateChange('listeners');\n\n // This is important: We need to flush the task queue before adding listeners\n // because we queue pending listeners by listening to the newListener event (in the constructor).\n // If we don't flush here, then the newListener events may fire after this function has run,\n // and the events won't be properly set up.\n await new Promise(resolve => setTimeout(resolve, 0));\n\n // @todo: Listen for an estimated cost, probably emit an \"estimated\" event when it comes in?\n // also @todo: Do the estimation task(s) on the scheduler and send an \"estimated\" event\n\n // Always listen to the stop event. It will resolve the work function promise, so is always needed.\n this.on('stop', (ev) => {this.ee.emit('stopped', ev)});\n\n // Connect listeners that were set up before exec\n if (this.desiredEvents.includes('result'))\n this.listeningForResults = true;\n await this.subscribeNewEvents(this.desiredEvents);\n\n // Connect listeners that are set up after exec\n this.on('newListener', (evt) => {\n if (evt === 'newListener' || this.desiredEvents.includes(evt))\n return;\n this.subscribeNewEvents([evt]);\n });\n \n // automatically add a listener for results if collateResults is on\n if (this.collateResults && !this.listeningForResults)\n this.on('result', () => {});\n\n debugging('dcp-client') && console.debug('subscribedEvents', this.desiredEvents);\n\n // If we have listeners for job.work, subscribe to custom events\n if (this.listenForCustomEvents)\n await this.subscribeCustomEvents();\n // Connect work event listeners that are set up after exec\n else\n this.work.on('newListener', () => this.subscribeCustomEvents());\n }\n \n /**\n * Subscribes to either reliable events or optional events. It is assumed that\n * any call to this function will include only new events.\n * @param {string[]} events \n */\n async subscribeNewEvents (events)\n {\n const reliableEvents = [];\n const optionalEvents = [];\n for (let eventName of events)\n {\n eventName = eventName.toLowerCase();\n if (this.eventTypes[eventName] && this.eventTypes[eventName].reliable)\n reliableEvents.push(eventName);\n else if (this.eventTypes[eventName] && !this.eventTypes[eventName].reliable)\n optionalEvents.push(eventName);\n else\n debugging('dcp-client') && console.debug(`Job handler has listener ${eventName} which isn't an event-router event.`);\n }\n if (debugging('dcp-client'))\n {\n console.debug('reliableEvents', reliableEvents);\n console.debug('optionalEvents', optionalEvents);\n }\n await this.eventSubscriber.subscribeManyEvents(reliableEvents, optionalEvents, { filter: { job: this.address } });\n }\n \n /**\n * Establishes listeners for worker events when requested by the client\n */\n async subscribeCustomEvents ()\n {\n if (!this.listeningForCustomEvents)\n await this.eventSubscriber.subscribeManyEvents([], ['custom'], { filter: { job: this.address } });\n this.listeningForCustomEvents = true\n }\n \n async joinComputeGroups ()\n {\n // localExec jobs are not entered in any compute group.\n if (!this.inLocalExec && this.computeGroups && this.computeGroups.length > 0)\n {\n this.readyStateChange('compute-groups');\n computeGroups.addRef(); // Just in case we're doing a Promise.all on multiple execs.\n\n // Add this job to its currently-defined compute groups (as well as public group, if included)\n let success;\n \n if (!Array.isArray(this.computeGroups)) \n throw new DCPError('Compute groups must be wrapped in an Array', 'DCPL-1101');\n\n for (let i = 0; i < this.computeGroups.length; i++)\n {\n let value = this.computeGroups[i];\n \n if (typeof value !== 'object')\n throw new DCPError(`This compute group: ${value[i]} must be an object`, 'DCPL-1102');\n \n if (value.joinKey && typeof value.joinKey !== 'string' && !(value.joinKey instanceof String))\n throw new DCPError(`This join key: ${value.joinKey} must be a string or a string literal`, 'DCPL-1103');\n else if (value.joinKeystore && !(value.joinKeystore instanceof wallet.Keystore))\n throw new DCPError(`This join Keystore: ${value.joinKeystore} must be an instance of wallet.Keystore`, 'DCPL-1104');\n else if (!value.joinKey && !value.joinKeystore)\n throw new DCPError('Compute group must contain a joinKey or a joinKeystore', 'DCPL-1105');\n }\n \n try\n {\n const cgPayload = await computeGroups.addJobToGroups(this.address, this.computeGroups);\n success = true; // To support older version of CG service where addJobToGroups had void/undefined return.\n if (cgPayload) success = cgPayload.success;\n debugging('dcp-client') && console.debug('job/index: addJobToGroups cgPayload:', cgPayload ? cgPayload : 'cgPayload is not defined; probably from legacy CG service.');\n }\n catch (e)\n {\n debugging('dcp-client') && console.debug('job/index: addJobToGroups threw exception:', e);\n success = false;\n }\n\n computeGroups.closeServiceConnection().catch((err) => {\n console.error('Warning: could not close compute groups service connection', err)\n });\n\n /* Could not put the job in any compute group, even though the user wanted it to run. Cancel the job. */\n if (!success)\n {\n await this.cancel('compute-groups::Unable to join any compute groups');\n throw new DCPError(`Access Denied::Failed to add job ${this.address} to any of the desired compute groups`, 'DCPL-1100');\n }\n }\n }\n \n /**\n * Takes result events as input, stores the result and fires off\n * events on the job handle as required. (result, duplicate-result)\n *\n * @param {object} ev - the event recieved from protocol.listen('/results/0xThisGenAdr')\n */\n async handleResult (ev)\n {\n if (this.results === null)\n // This should never happen - the onResult event should only be established/called\n // in addListeners which should also initialize the internal results array\n throw new Error('Job.onResult was invoked before initializing internal results');\n \n const { result: _result, time } = ev.result;\n debugging('dcp-client') && console.debug('handleResult', _result);\n let result = await fetchURI(_result);\n\n if (this.results.isAvailable(ev.sliceNumber))\n {\n const changed = JSON.stringify(this.results[ev.sliceNumber]) !== JSON.stringify(result);\n this.emit('duplicate-result', { sliceNumber: ev.sliceNumber, changed });\n }\n\n this.results.newResult(result, ev.sliceNumber);\n }\n \n /**\n * Receives status events from the scheduler, updates the local status object\n * and emits a 'status' event\n *\n * @param {object} ev - the status event received from\n * protocol.listen('/status/0xThisGenAdr')\n * @param {boolean} emitStatus - value indicating whether or not the status\n * event should be emitted\n */\n handleStatus ({ runStatus, total, distributed, computed }, emitStatus = true)\n {\n Object.assign(this.status, {\n runStatus,\n total,\n distributed,\n computed,\n });\n\n if (emitStatus)\n this.emit('status', { ...this.status, job: this.address });\n }\n \n /**\n * Sends a request to the scheduler to deploy the job.\n */\n async deployJob ()\n {\n var moduleDependencies; \n \n /* Send sideloader bundle to the package server */\n if (DCP_ENV.platform === 'nodejs' && this.dependencies.length)\n {\n try\n {\n let { pkg, unresolved } = await this._publishLocalModules();\n\n moduleDependencies = unresolved;\n if (pkg)\n moduleDependencies.push(pkg.name + '/' + sideloaderModuleIdentifier); \n }\n catch(error)\n {\n throw new DCPError(`Error trying to communicate with package manager server: ${error}`);\n }\n }\n else\n moduleDependencies = this.dependencies;\n \n this.readyStateChange('preauth');\n\n const adhocId = this.uuid.slice(this.uuid.length - 6, this.uuid.length);\n const schedId = await dcpConfig.scheduler.identity;\n // The following check is needed for when using dcp-rtlink and loading the config through source, instead of using the dcp-client bundle\n let schedIdAddress = schedId;\n if(schedId.address)\n schedIdAddress = schedId.address;\n this.identityKeystore = await wallet.getId();\n const preauthToken = await bankUtil.preAuthorizePayment(schedIdAddress, this.maxDeployPayment, this.paymentAccountKeystore);\n const { dataRange, dataValues, dataPattern, sliceCount } = marshalInputData(this.jobInputData);\n if(dataValues)\n this.dataValues = dataValues;\n\n this.readyStateChange('deploying');\n\n /* Payload format is documented in scheduler-v4/libexec/job-submit/operations/submit.js */\n const submitPayload = {\n owner: this.identityKeystore.address,\n paymentAccount: this.paymentAccountKeystore.address,\n priority: 0, // @nyi\n\n workFunctionURI: this.workFunctionURI,\n uuid: this.uuid,\n mvMultSlicePayment: Number(this.feeStructure.marketValue) || 0, // @todo: improve feeStructure internals to better reflect v4\n absoluteSlicePayment: Number(this.feeStructure.maxPerRequest) || 0,\n requirePath: this.requirePath,\n dependencies: moduleDependencies,\n requirements: this.requirements, /* capex */\n localExec: this.inLocalExec,\n force100pctCPUDensity: this.force100pctCPUDensity,\n estimationSlices: this.estimationSlices,\n greedyEstimation: this.greedyEstimation,\n workerConsole: this.workerConsole,\n isCI: this.isCI,\n\n description: this.public.description || 'Discreetly making the world smarter',\n name: this.public.name || 'Ad-Hoc Job' + adhocId,\n link: this.public.link || '',\n\n preauthToken, // XXXwg/er @todo: validate this after fleshing out the stub(s)\n\n resultStorageType: this.resultStorageType, // @todo: implement other result types\n resultStorageDetails: this.resultStorageDetails, // Content depends on resultStorageType\n resultStorageParams: this.resultStorageParams, // Post params for off-prem storage\n dataRange,\n dataPattern,\n sliceCount,\n marshaledDataValues: this.marshaledDataValues,\n rangeLength: this.rangeLength\n };\n\n // Check if dataRange or dataPattern input is already marshaled\n if (this.marshaledDataRange)\n submitPayload.dataRange = this.marshaledDataRange;\n\n /* Determine composition of argument set and build payload */\n if (this.marshaledArguments)\n submitPayload.marshaledArguments = this.marshaledArguments;\n else if (this.jobArguments)\n {\n if (this.jobArguments instanceof RemoteDataPattern) /* Not supported */\n throw new DCPError('Cannot use RemoteDataPattern as work function arguments', 'EBADARG')\n if (this.jobArguments instanceof RemoteDataSet) /* Entire set is RemoteDataSet */\n this.jobArguments = this.jobArguments.map((e) => new URL(e));\n\n submitPayload.marshaledArguments = kvin.marshal(encodeJobValueList(this.jobArguments, 'jobArguments'));\n }\n\n // XXXpfr Excellent tracing.\n if (debugging('dcp-client'))\n {\n const { dumpObject } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n dumpObject(submitPayload, 'Submit: Job Index: submitPayload', 256);\n }\n debugging('dcp-client') && console.debug('submitPayload.marshaledArguments', JSON.stringify(submitPayload.marshaledArguments).length, JSON.stringify(submitPayload).length);\n\n // Deploy the job! If we get an error, try again a few times until threshold of errors is reached, then actually throw it\n let deployed\n let deployAttempts = 0;\n while (deployAttempts++ < (dcpConfig.job.deployAttempts || 10))\n {\n try\n {\n deployed = await this.useDeployConnection('submit', submitPayload, this.identityKeystore);\n break;\n }\n catch (e)\n {\n if (deployAttempts < 10)\n debugging('dcp-client') && console.debug('Error when trying to deploy job, trying again', e);\n else\n throw e;\n }\n }\n\n if (!deployed.success)\n {\n // close all of the connections so that we don't cause node processes to hang.\n const handleErr = (e) => {\n console.error('Error while closing job connection:');\n console.error(e);\n };\n \n this.closeDeployConnection();\n this.eventSubscriber.close().catch(handleErr);\n computeGroups.closeServiceConnection().catch(handleErr);\n \n // Yes, it is possible for deployed or deployed.payload to be undefined.\n if (deployed.payload)\n {\n if (deployed.payload.code === 'ENOTFOUND')\n throw new DCPError(`Failed to submit job to scheduler. Account: ${submitPayload.paymentAccount} was not found or does not have sufficient balance (${deployed.payload.info.deployCost} DCCs needed to deploy this job)`, deployed.payload); \n throw new DCPError('Failed to submit job to scheduler', deployed.payload);\n }\n throw new DCPError('Failed to submit job to scheduler (no payload)', deployed ? deployed : '');\n }\n\n debugging('dcp-client') && console.debug('After Deploy', JSON.stringify(deployed));\n\n this.address = deployed.payload.job;\n this.deployCost = deployed.payload.deployCost;\n\n if (!this.status)\n this.status = {\n runStatus: null,\n total: 0,\n computed: 0,\n distributed: 0,\n };\n \n this.status.runStatus = deployed.payload.status;\n this.status.total = deployed.payload.lastSliceNumber;\n this.running = true;\n }\n \n /** close an open job to indicate we are done adding data so it is okay to finish\n * the job at the appropriate time\n */\n close ()\n {\n return this.useDeployConnection('closeJob', {\n job: this.id,\n });\n }\n \n /** Use the connection to job submit service. Will open a new connection if one does not exist,\n * and close the connection if it is idle for more than 10 seconds (tuneable).\n */\n useDeployConnection(...args)\n {\n if (!this.useDeployConnection.uses)\n this.useDeployConnection.uses = 0;\n this.useDeployConnection.uses++;\n if (!this.deployConnection)\n {\n debugging('deploy-connection') && console.info(`1453: making a new deployConnection...`)\n this.deployConnection = new protocolV4.Connection(dcpConfig.scheduler.services.jobSubmit); \n this.deployConnection.on('close', () => { this.deployConnection = null; });\n }\n if (this.deployConnectionTimeout)\n clearTimeout(this.deployConnectionTimeout);\n\n debugging('deploy-connection') && console.info(`1460: sending ${args[0]} request...`);\n const deployPromise = this.deployConnection.send(...args);\n \n deployPromise.finally(() => {\n this.useDeployConnection.uses--;\n\n debugging('deploy-connection') && console.info(`1462: deployConnection done ${args[0]} request, connection uses is ${this.useDeployConnection.uses}`)\n\n this.deployConnectionTimeout = setTimeout(() => {\n if (this.useDeployConnection.uses === 0 && this.deployConnection)\n {\n debugging('deploy-connection') && console.info(`1469: done with deployConn, closing...`);\n // if we're done w/ the connection, then remove its cleanup\n // function, close it, and clean up manually to make room for a\n // new conn when needed (ie, don't leave the closing conn where\n // someone could accidentally pick it up)\n this.deployConnection.removeAllListeners('close');\n this.deployConnection.close();\n this.deployConnection = null;\n }\n }, (dcpConfig.job.deployCloseTimeout || 10 * 1000));\n if (!ON_BROWSER)\n this.deployConnectionTimeout.unref();\n }); \n \n return deployPromise;\n }\n \n /**\n * Close the connection to the job submit (if it exists), and clear the close timeout (if needed).\n */\n closeDeployConnection()\n {\n if (this.deployConnection)\n this.deployConnection.close();\n if (this.deployConnectionTimeout)\n clearTimeout(this.deployConnectionTimeout);\n }\n}\n\n/** \n * Encode a value list for transmission to the job-submit daemon. This could be either job arguments\n * or the slice input set, if the input set was an Array-like object.\n *\n * @param {ArrayLike} valueList - The list of values to encode\n * @returns {Array<URL|{string: string, method:string, MIMEType: string }>} - URL or object with 3 properties:\n * string - serialization of value\n * method - how value was serialized (e.g. 'kvin', 'json')\n * MIMEType - MIME information corresponding to method (e.g. 'application/kvin', 'application/json')\n */\nfunction encodeJobValueList(valueList, valueKind)\n{\n var list = [];\n\n /*\n * We need to handle several different styles of datasets, and create the output array accordingly.\n *\n * 1. instance of RemoteDataSet or RemoteDataPattern => apply new URI(?)\n * 2. an Array-like objects => apply serializeJobValue\n * Values sent to the scheduler in payload are either URL or the result of calling serializeJobValue.\n */\n\n if (typeof valueList === 'undefined' || (typeof valueList === 'object' && valueList.length === 0))\n return list; /* empty set */\n\n if (typeof valueList !== 'object' || !valueList.hasOwnProperty('length'))\n throw new Error('value list must be an Array-like object');\n\n for (let i = 0; i < valueList.length; i++) /* Set is composed of values from potentially varying sources */\n {\n let value = valueList[i];\n if (value instanceof RemoteDataSet)\n value.forEach((el) => list.push(new URL(el)));\n else if (value instanceof RemoteDataPattern)\n {\n if (valueKind === jobValueKind.jobArguments)\n throw new DCPError('Cannot use RemoteDataPattern as work function arguments', 'EBADARG');\n else\n {\n let uri = valueList['pattern'];\n for (let sliceNum = 1; sliceNum <= valueList['sliceCount']; sliceNum++)\n list.push(new URL(uri.replace('{slice}', sliceNum)))\n }\n }\n else if (value instanceof RemoteValue)\n list.push(serializeJobValue(value.href));\n else\n list.push(serializeJobValue(value));\n }\n\n return list;\n} \n\n/**\n * Depending on the shape of the job's data, resolve it into a RangeObject, a\n * Pattern, or a values array, and return it in the appropriate property.\n *\n * @param {any} data Job's input data\n * @return {MarshaledInputData} An object with one of the following properties set:\n * - dataValues: job input is an array of arbitrary values \n * - dataPattern: job input is a URI pattern \n * - dataRange: job input is a RangeObject (and/or friends)\n */\nfunction marshalInputData (data)\n{\n if (!(data instanceof Object || data instanceof SuperRangeObject))\n throw new TypeError(`Invalid job data type: ${typeof data}`);\n\n /**\n * @type {MarshaledInputData}\n */\n const marshalledInputData = {};\n\n // TODO(wesgarland): Make this more robust.\n if (data instanceof SuperRangeObject ||\n (data.hasOwnProperty('ranges') && data.ranges instanceof MultiRangeObject) ||\n (data.hasOwnProperty('start') && data.hasOwnProperty('end')))\n marshalledInputData.dataRange = data;\n else if (Array.isArray(data))\n marshalledInputData.dataValues = data;\n else if (data instanceof URL || data instanceof DcpURL)\n marshalledInputData.dataPattern = String(data);\n else if(data instanceof RemoteDataSet)\n marshalledInputData.dataValues = data.map(e => new URL(e));\n else if(data instanceof RemoteDataPattern)\n {\n marshalledInputData.dataPattern = data['pattern'];\n marshalledInputData.sliceCount = data['sliceCount'];\n }\n\n debugging('job') && console.debug('marshalledInputData:', marshalledInputData);\n return marshalledInputData;\n}\n\n/**\n * marshal the value using kvin or instance of the kvin (tunedKvin)\n * tunedKvin is defined if job.tuning.kvin is specified.\n *\n * @param {any} value \n * @return {object} A marshaled object\n * \n */\nfunction kvinMarshal (value) {\n if (tunedKvin)\n return tunedKvin.marshal(value);\n\n return kvin.marshal(value);\n}\n\n\n\nexports.Job = Job;\nexports.SlicePaymentOffer = SlicePaymentOffer;\nexports.ResultHandle = ResultHandle;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/job/index.js?");
4274
+ eval("/**\n * @file job/index.js\n * @author Eddie Roosenmaallen, eddie@kingsds.network\n * Matthew Palma, mpalma@kingsds.network\n * Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * Ryan Saweczko, ryansaweczko@kingsds.network\n * @date November 2018\n * November 2018\n * February 2022\n * May 2022\n * Jun 2022\n *\n * This module implements the Compute API's Job Handle\n *\n */\n\n\nconst { BigNumber } = __webpack_require__(/*! bignumber.js */ \"./node_modules/bignumber.js/bignumber.js\");\nconst { v4: uuidv4 } = __webpack_require__(/*! uuid */ \"./node_modules/uuid/dist/esm-browser/index.js\");\nconst { EventEmitter, PropagatingEventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { RangeObject, MultiRangeObject, DistributionRange, SuperRangeObject, SparseRangeObject } = __webpack_require__(/*! dcp/dcp-client/range-object */ \"./src/dcp-client/range-object.js\");\nconst { fetchURI, encodeDataURI, createTempFile } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { serializeJobValue, RemoteValue } = __webpack_require__(/*! dcp/dcp-client/job-values */ \"./src/dcp-client/job-values.js\");\nconst wallet = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst ClientModal = __webpack_require__(/*! dcp/dcp-client/client-modal */ \"./src/dcp-client/client-modal/index.js\");\nconst { Worker } = __webpack_require__(/*! dcp/dcp-client/worker */ \"./src/dcp-client/worker/index.js\");\nconst { RemoteDataSet } = __webpack_require__(/*! dcp/dcp-client/remote-data-set */ \"./src/dcp-client/remote-data-set.js\");\nconst { RemoteDataPattern } = __webpack_require__(/*! dcp/dcp-client/remote-data-pattern */ \"./src/dcp-client/remote-data-pattern.js\");\nconst { ResultHandle } = __webpack_require__(/*! ./result-handle */ \"./src/dcp-client/job/result-handle.js\");\nconst { SlicePaymentOffer } = __webpack_require__(/*! ./slice-payment-offer */ \"./src/dcp-client/job/slice-payment-offer.js\");\nconst { addSlices } = __webpack_require__(/*! ./upload-slices */ \"./src/dcp-client/job/upload-slices.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst computeGroups = __webpack_require__(/*! dcp/dcp-client/compute-groups */ \"./src/dcp-client/compute-groups/index.js\");\nconst schedulerConstants = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { DEFAULT_REQUIREMENTS, removeBadRequirements } = __webpack_require__(/*! dcp/common/job-requirements-defaults */ \"./src/common/job-requirements-defaults.js\");\nconst { sliceStatus, jobValueKind } = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { jobStatus } = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst bankUtil = __webpack_require__(/*! dcp/dcp-client/bank-util */ \"./src/dcp-client/bank-util.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('dcp-client');\nconst kvin = __webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\");\nlet tunedKvin;\n\nconst log = (...args) => {\n if (debugging('job')) {\n console.debug('dcp-client:job', ...args);\n }\n};\n\nconst ON_BROWSER = DCP_ENV.isBrowserPlatform;\nconst sideloaderModuleIdentifier = 'sideloader-v1';\n\n\n/** @typedef {import('dcp/dcp-client/wallet/keystore').Keystore} Keystore */\n/** @typedef {import('dcp/dcp-client/range-object').RangeLike} RangeLike */\n/** @typedef {import('scheduler-v4/libexec/job-submit/operations/submit').MarshaledInputData} MarshaledInputData */\n\n/**\n * Ensure input data is an appropriate format\n * @param {RangeObject | DistributionRange | RemoteDataSet | Array | Iterable}\n * inputData - A URI-shaped string, a [Multi]RangeObject-constructing value, or\n * an array of slice data\n * @return {RangeObject | RangeLike | DistributionRange | RemoteDataSet | Array}\n * The coerced input in an appropriate format ([Multi]RangeObject,\n * DistributionRange, RemoteDataSet, or array)\n */\n const wrangleData = (inputData) => {\n\n if (RangeObject.isRangelike(inputData)) { return inputData }\n if (RangeObject.isRangeObject(inputData)) { return inputData }\n if (DistributionRange.isDistribution(inputData)) { return inputData }\n if (inputData instanceof SparseRangeObject) { return inputData }\n if (inputData instanceof MultiRangeObject) { return inputData }\n if (MultiRangeObject.isProtoMultiRangelike(inputData)) { return new MultiRangeObject(inputData) }\n if (RangeObject.isProtoRangelike(inputData)) { return new RangeObject(inputData) }\n if (DistributionRange.isProtoDistribution(inputData)) { return new DistributionRange(inputData) }\n if (RemoteDataSet.isRemoteDataSet(inputData)) { return inputData }\n if (RemoteDataPattern.isRemoteDataPattern(inputData)) { return inputData }\n\n return Array.isArray(inputData) ? inputData : [inputData];\n};\n\n/**\n * @classdesc The Compute API's Job Handle (see {@link https://docs.dcp.dev/specs/compute-api.html#job-handles|Compute API spec})\n * Job handles are objects which correspond to jobs. \n * They are created by some exports of the compute module, such as {@link module:dcp/compute.do|compute.do} and {@link module:dcp/compute.for|compute.for}.\n * @extends module:dcp/dcp-events.PropagatingEventEmitter\n * @hideconstructor\n * @access public\n */\nclass Job extends PropagatingEventEmitter\n{\n /**\n * Fired when the job is accepted by the scheduler on deploy.\n * \n * @event Job#accepted\n * @access public\n * @type {object}\n *//**\n * Fired when the job is cancelled.\n * \n * @event Job#cancel\n * @access public\n *//**\n * Fired when a result is returned.\n * \n * @event Job#result\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {string} task ID of the task (slice) the result came from\n * @property {number} sort The index of the slice\n * @property {object} result\n * @property {string} result.request\n * @property {*} result.result The value returned from the work function\n *//**\n * Fired when the result handle is modified, either when a new `result` event is fired or when the results are populated with `results.fetch()`\n * \n * @event Job#resultsUpdated\n * @access public\n *//**\n * Fired when the job has been completed.\n * \n * @event Job#complete\n * @access public\n * @type {ResultHandle}\n *//**\n * Fired when the job's status changes.\n * \n * @event Job#status\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} total Total number of slices in the job\n * @property {number} distributed Number of slices that have been distributed\n * @property {number} computed Number of slices that have completed execution (returned a result)\n * @property {string} runStatus Current runStatus of the job\n *//**\n * Fired when a slice throws an error.\n * \n * @event Job#error\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} sliceIndex Index of the slice that threw the error\n * @property {string} message The error message\n * @property {string} stack The error stacktrace\n * @property {string} name The error type name\n *//**\n * Fired when a slice uses one of the console log functions.\n * \n * @event Job#console\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} sliceIndex The index of the slice that produced this event\n * @property {string} level The log level, one of `debug`, `info`, `log`, `warn`, or `error`\n * @property {string} message The console log message\n *//**\n * Fired when a slice is stopped for not calling progress. Contains information about how long the slice ran for, and about the last reported progress calls.\n * \n * @event Job#noProgress\n * @access public\n * @type {object}\n * @property {string} jobAddress Address of the job\n * @property {number} sliceIndex The index of the slice that failed due to no progress\n * @property {number} timestamp How long the slice ran before failing\n * @property {object} progressReports\n * @property {object} progressReports.last The last progress report received from the worker\n * @property {number} progressReports.last.timestamp Time since the start of the slice\n * @property {number} progressReports.last.progress Progress value reported\n * @property {*} progressReports.last.value The last value that was passed to the progress function\n * @property {number} progressReports.last.throttledReports Number of calls to progress that were throttled since the last report\n * @property {object} progressReports.lastUpdate The last determinate (update to the progress param) progress report received from the worker\n * @property {number} progressReports.lastUpdate.timestamp\n * @property {number} progressReports.lastUpdate.progress\n * @property {*} progressReports.lastUpdate.value\n * @property {number} progressReports.lastUpdate.throttledReports\n *//**\n @todo: is this in the spec? is there a no progress data? should there be?\n * Identical to `noProgress`, except that it also contains the data that the slice was executed with.\n * \n * @event Job#noProgressData\n * @access public\n * @type {object}\n * @property {*} data The data that the slice was executed with\n *//**\n * Fired when the job is paused due to running out of funds. The job can be resumed by escrowing more funds then resuming the job.\n * @todo: is this a thing, should it be a thing (the payload)\n * Event payload is the estimated funds required to complete the job\n * \n * @event Job#nofunds\n * @access public\n * @type {BigNumber}\n *//**\n * Fired when the job cannot be deployed due to no bank account / not enough balance to deploy the job\n * \n * @event Job#ENOFUNDS\n * @access public\n *//**\n * Fired when the job is cancelled due to the work function not calling the `progress` method frequently enough.\n * \n * @event Job#ENOPROGRESS\n * @access public\n *//**\n * The job was cancelled because scheduler has determined that individual tasks in this job exceed the maximum allowable execution time.\n * \n * @event Job#ESLICETOOSLOW\n * @access public\n *//**\n * Fired when the job is cancelled because too many work functions are terminating with uncaught exceptions.\n * \n * @event Job#ETOOMANYERRORS\n * @access public\n */\n\n /**\n * @form1 new Job('application_worker_address'[, data[, arguments]])\n * @form2a new Job('worker source'[, data[, arguments]])\n * @form2b new Job(worker_function[, data[, arguments]])\n */\n constructor ()\n {\n super('Job');\n if (typeof arguments[0] === 'function')\n arguments[0] = arguments[0].toString();\n\n if (typeof arguments[0] === 'string')\n {\n const { encodeDataURI } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n this.workFunctionURI = encodeDataURI(arguments[0], 'application/javascript');\n } \n else if (DcpURL.isURL(arguments[0]))\n this.workFunctionURI = arguments[0].href;\n\n this.jobInputData = wrangleData(arguments[1] || []);\n this.jobArguments = wrangleData(arguments[2] || []);\n \n log('num wrangledInputData:', this.jobInputData.length);\n log('num wrangledArguments:', this.jobArguments.length);\n\n this.initEventSystems();\n\n /**\n * An object describing the cost the user believes each the average slice will incur, in terms of CPU/GPU and I/O.\n * If defined, this object is used to provide initial scheduling hints and to calculate escrow amounts.\n *\n * @type {object}\n * @access public\n */\n this.initialSliceProfile = undefined;\n \n // The max value that the client is willing to spend to deploy\n // (list on the scheduler, doesn't include compute payment)\n // maxDeployPayment is the max the user is willing to pay to DCP (as a\n // Hold), in addition to the per-slice offer and associated scrape.\n // Currently calculated as `deployCost = costPerKB *\n // (JSON.stringify(job).length / 1024) // 1e-9 per kb`\n // @todo: figure this out / er nov 2018\n this.maxDeployPayment = 1;\n\n /**\n * An object describing the requirements that workers must have to be eligible for this job. The default values are set in the job-submitter, and only the client specified\n * requirements are sent over the wire. See {@link https://docs.dcp.dev/specs/compute-api.html#requirements-objects|Requirements Objects}.\n *\n * @type {object}\n * @access public\n */\n this.requirements = {};\n\n /**\n * A place to store public-facing attributes of the job. Anything stored on this object will be available inside the work \n * function (see {@link module:dcp/compute~sandboxEnv.work}). The properties documented here may be used by workers to display what jobs are currently being \n * worked on.\n * @access public\n * @property {string} name Public-facing name of this job.\n * @property {string} description Public-facing description for this job.\n * @property {string} link Public-facing link to external resource about this job.\n */\n this.public = {\n name: null,\n description: null,\n link: null,\n };\n \n /**\n * A cryptographic receipt indicating deployment of the job on the scheduler\n * @type {object}\n * @access public\n */\n this.receipt = null;\n \n /**\n * a SliceProfile object which contains the average costs for the slices which have been computed to date.\n * Until the first result is returned, this property is undefined.\n * @type {object}\n * @access public\n */\n this.meanSliceProfile = null;\n \n /**\n * A number (can be null, undefined, or infinity) describing the estimationSlicesRemaining in the jpd (dcp-2593)\n * @type {number}\n * @access public\n */\n this.estimationSlices = undefined;\n /**\n * When true, allows a job in estimation to have requestTask return multiple estimation slices.\n * This flag applies independent of infinite estimation, viz., this.estimationSlices === null .\n * @type {boolean}\n * @access public\n */\n this.greedyEstimation = false;\n /**\n * tunable parameters per job\n * @access public\n * @param {object} tuning \n * @param {string} tuning.kvin Encode the TypedArray into a string, trying multiple methods to determine optimum \n * size/performance. The this.tune variable affects the behavior of this code this:\n * @param {boolean} speed If true, only do naive encoding: floats get represented as byte-per-digit strings\n * @param {boolean} size If true, try the naive, ab8, and ab16 encodings; pick the smallest\n * If both are false try the naive encoding if under typedArrayPackThreshold and use if smaller\n * than ab8; otherwise, use ab8\n */\n this.tuning = {\n kvin: {\n size: false,\n speed: false,\n },\n }\n /* For API interface to end-users only */\n Object.defineProperty(this, 'id', {\n get: () => this.address,\n set: (id) => { this.address = id }\n });\n \n this.uuid = uuidv4(); /** @see {@link https://kingsds.atlassian.net/browse/DCP-1475?atlOrigin=eyJpIjoiNzg3NmEzOWE0OWI4NGZkNmI5NjU0MWNmZGY2OTYzZDUiLCJwIjoiaiJ9|Jira Issue} */\n this.dependencies = []; /* dependencies of the work function */\n this.requirePath = []; /* require path for dependencies */\n this.modulePath = []; /* path to module that invoked .exec() for job */\n this.connected = false; /* true when exec or resume called */\n this.results = new ResultHandle(this); /* result handle */\n this.collateResults = true; /* option to receive results as they are computed & ensure all are received on finish */\n this.contextId = null; /* optional string which is used to indicate to caching mechanisms different keystores with same name */ \n this.force100pctCPUDensity = false; /* tell scheduler to assume this job uses 100% cpu density */\n this.workerConsole = false; /* tell workers to log more information about failures in the evaluator this job causes */\n this.address = null; /* job address, created by scheduler during exec call. */\n this.paymentAccountKeystore = null; /* keystore for payment for job to come from */\n this.status = { /* job status details */\n runStatus: null,\n total: null,\n distributed: null,\n computed: null\n };\n \n // service locations\n this.scheduler = dcpConfig.scheduler.services.jobSubmit.location;\n this.bank = dcpConfig.bank.services.bankTeller.location;\n \n // Compute groups. Add to public compute group by default\n this.computeGroups = [ Object.assign({}, schedulerConstants.computeGroups.public) ];\n \n // Update the ready state as we go through job deployment\n this.readyState = sliceStatus.new;\n const that = this;\n this.readyStateChange = function job$$readyStateChange (readyState)\n {\n that.readyState = readyState;\n that.emit('readyStateChange', that.readyState);\n }\n }\n \n /**\n * Initialize the various event systems the job handle requires. These include:\n * - an internal event emitter (this.ee)\n * - an event emitter for any events emitted on `work.emit` within work functions (this.work)\n * - an event subscriber to subscribe (to receive) events from the scheduler (this.eventSubscriber)\n */\n initEventSystems ()\n {\n // Handle the various event-related things required in the constructor\n\n // Internal event emitter for events within job handle\n this.ee = new EventEmitter('Job Internal');\n\n /**\n * An EventEmitter for custom events dispatched by the work function.\n * @type {module:dcp/dcp-events.EventEmitter}\n * @access public\n * @example\n * // in work function\n * work.emit('myEventName', 1, [2], \"three\");\n * // client-side\n * job.work.on('myEventName', (num, arr, string) => { });\n */\n this.work = new EventEmitter('job.work');\n this.listenForCustomEvents = false;\n\n // Initialize the eventSubscriber so each job has unique eventSubscriber\n this.eventSubscriber = new ((__webpack_require__(/*! dcp/events/event-subscriber */ \"./src/events/event-subscriber.js\").EventSubscriber))(this);\n\n // Some events from the event subscriber can't be emitted immediately upon receipt without having \n // weird/wrong output due to things like serialization. We allow interceptors in the event subscriber\n // to handle this.\n const that = this\n var lastConsoleEv;\n var sameCounter = 1;\n const parseConsole = function deserializeConsoleMessage(ev) {\n if (tunedKvin)\n ev.message = tunedKvin.unmarshal(ev.message);\n else \n ev.message = kvin.unmarshal(ev.message);\n \n if (lastConsoleEv && ev.message[0] === lastConsoleEv.message[0] && ev.sliceNumber === lastConsoleEv.sliceNumber && ev.level === lastConsoleEv.level)\n ev.same = ++sameCounter;\n else\n sameCounter = 1;\n lastConsoleEv = ev;\n \n /* if we have the same message being logged (same sliceNumber, message, log level), the console event object will have the sole property same, nothing else */\n if (ev.same > 1)\n that.emit('console', { same: ev.same });\n else\n {\n delete ev.same;\n that.emit('console', ev);\n }\n }\n\n this.eventIntercepts = {\n result: (ev) => this.handleResult(ev),\n status: (ev) => this.handleStatus(ev),\n cancel: (ev) => this.ee.emit('stopped', ev),\n custom: (ev) => this.work.emit(ev.customEvent, ev),\n console: parseConsole,\n };\n \n this.eventTypes = (__webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\").eventTypes);\n\n this.work.on('newListener', (evt) => {\n this.listenForCustomEvents = true;\n });\n this.desiredEvents = []\n this.on('newListener', (evt) => {\n if (!this.connected && evt !== 'newListener')\n this.desiredEvents.push(evt);\n if (evt === 'cancel')\n this.listeningForCancel = true;\n });\n }\n \n /** \n * Cancel the job\n * @access public\n * @param {string} reason If provided, will be sent to client\n */\n async cancel (reason = undefined)\n {\n const response = await this.useDeployConnection('cancelJob', {\n job: this.address,\n owner: this.paymentAccountKeystore.address,\n reason,\n }, this.paymentAccountKeystore);\n\n return response.payload;\n }\n\n /** \n * Resume this job\n * @access public\n */\n async resume ()\n {\n // Careful -- this.schedulerConnection does not exist.\n const response = await this.schedulerConnection.send('resumeJob', {\n job: this.address,\n owner: this.paymentAccountKeystore.address,\n }, this.paymentAccountKeystore);\n\n return response.payload;\n }\n\n /**\n * Helper function for retrieving info about the job. The job must have already been deployed.\n * An alias for {@link module:dcp/compute.getJobInfo}.\n * @access public\n */\n getJobInfo ()\n {\n return (__webpack_require__(/*! ../compute */ \"./src/dcp-client/compute.js\").compute.getJobInfo)(this.address);\n }\n \n /**\n * Helper function for retrieving info about the job's slices. The job must have already been deployed.\n * An alias for {@link module:dcp/compute.getSliceInfo}.\n * @access public\n */\n getSliceInfo ()\n {\n return (__webpack_require__(/*! ../compute */ \"./src/dcp-client/compute.js\").compute.getSliceInfo)(this.address);\n }\n \n /** Escrow additional funds for this job\n * @access public\n * @param {number|BigNumber} fundsRequired - A number or BigNumber instance representing the funds to escrow for this job\n */\n async escrow (fundsRequired)\n {\n if ((typeof fundsRequired !== 'number' && !BigNumber.isBigNumber(fundsRequired))\n || fundsRequired <= 0 || !Number.isFinite(fundsRequired) || Number.isNaN(fundsRequired))\n throw new Error(`Job.escrow: fundsRequired must be a number greater than zero. (not ${fundsRequired})`);\n\n const bankConnection = new protocolV4.Connection(dcpConfig.bank.services.bankTeller);\n\n /*\n * escrow has been broken for an unknown amount of time. `feeStructureId` is not defined anywhere in the job class, and hasn't\n * for a period of time. When fixed, `this[INTERNAL_SYMBOL].payloadDetails.feeStructureId` will likely become just `this.feeStructureId`,\n * but it's being left alone until someone spends the time to fix escrow. / rs Jul 2022\n */\n const response = await bankConnection.send('embiggenFeeStructure', {\n feeStructureAddress: this[INTERNAL_SYMBOL].payloadDetails.feeStructureId,\n additionalEscrow: new BigNumber(fundsRequired),\n fromAddress: this.paymentAccountKeystore.address,\n }, this.paymentAccountKeystore);\n\n bankConnection.close();\n const escrowReceipt = response.payload;\n return escrowReceipt;\n }\n\n /**\n * create bundles for local dependencies\n */\n _pack ()\n {\n var retval = (__webpack_require__(/*! ./node-modules */ \"./src/dcp-client/job/node-modules.js\").createModuleBundle)(this.dependencies);\n return retval;\n }\n\n /** \n * Collect all of the dependencies together, throw them into a BravoJS\n * module which sideloads them as a side effect of declaration, and transmit\n * them to the package manager. Then we return the package descriptor object,\n * which is guaranteed to have only one file in it.\n *\n * @returns {object} with properties name and files[0]\n */\n async _publishLocalModules()\n {\n const dcpPublish = __webpack_require__(/*! dcp/common/dcp-publish */ \"./src/common/dcp-publish.js\");\n \n const { tempFile, hash, unresolved } = await this._pack();\n\n if (!tempFile) {\n return { unresolved };\n }\n\n const sideloaderFilename = tempFile.filename;\n const pkg = {\n name: `dcp-pkg-v1-localhost-${hash.toString('hex')}`,\n version: '1.0.0',\n files: {\n [sideloaderFilename]: `${sideloaderModuleIdentifier}.js`,\n },\n }\n\n await dcpPublish.publish(pkg);\n tempFile.remove();\n\n return { pkg, unresolved };\n }\n \n /**\n * This function specifies a module dependency (when the argument is a string)\n * or a list of dependencies (when the argument is an array) of the work\n * function. This function can be invoked multiple times before deployment.\n * @param {string | string[]} modulePaths - A string or array describing one\n * or more dependencies of the job.\n * @access public\n */\n requires (modulePaths)\n {\n if (typeof modulePaths !== 'string' && (!Array.isArray(modulePaths) || modulePaths.some((modulePath) => typeof modulePath !== 'string')))\n throw new TypeError('The argument to dependencies is not a string or an array of strings');\n else if (modulePaths.length === 0)\n throw new RangeError('The argument to dependencies cannot be an empty string or array');\n else if (Array.isArray(modulePaths) && modulePaths.some((modulePath) => modulePath.length === 0))\n throw new RangeError('The argument to dependencies cannot be an array containing an empty string');\n\n if (!Array.isArray(modulePaths))\n modulePaths = [modulePaths];\n\n for (const modulePath of modulePaths)\n {\n if (modulePath[0] !== '.' && modulePath.indexOf('/') !== -1)\n {\n const modulePrefixRegEx = /^(.*)\\/.*?$/;\n const [, modulePrefix] = modulePath.match(modulePrefixRegEx);\n if (modulePrefix && this.requirePath.indexOf(modulePrefix) === -1)\n this.requirePath.push(modulePrefix);\n }\n this.dependencies.push(modulePath);\n }\n }\n \n /** Set the account upon which funds will be drawn to pay for the job.\n * @param {module:dcp/wallet.AuthKeystore} keystore A keystore that representa a bank account.\n * @access public\n */\n setPaymentAccountKeystore (keystore)\n {\n if (this.address)\n {\n if (!keystore.address.eq(this.paymentAccountKeystore))\n {\n let message = 'Cannot change payment account after job has been deployed';\n this.emit('EPERM', message);\n throw new Error(`EPERM: ${message}`);\n }\n }\n \n if (!(keystore instanceof wallet.Keystore))\n throw new Error('Not an instance of Keystore: ' + keystore.toString());\n this.paymentAccountKeystore = keystore;\n }\n \n /** Set the slice payment offer. This is equivalent to the first argument to exec.\n * @param {number} slicePaymentOffer - The number of DCC the user is willing to pay to compute one slice of this job\n */\n setSlicePaymentOffer (slicePaymentOffer)\n {\n this.slicePaymentOffer = new SlicePaymentOffer(slicePaymentOffer);\n }\n \n \n /**\n * @param {URL|DcpURL} locationUrl - A URL object\n * @param {object} postParams - An object with any parameters that a user would like to be passed to a \n * remote result location. This object is capable of carry API keys for S3, \n * DropBox, etc. These parameters are passed as parameters in an \n * application/x-www-form-urlencoded request.\n */\n setResultStorage (locationUrl, postParams)\n {\n if (locationUrl instanceof URL || locationUrl instanceof DcpURL)\n this.resultStorageDetails = locationUrl;\n else\n throw new Error('Not an instance of a DCP URL: ' + locationUrl);\n \n\n // resultStorageParams contains any post params required for off-prem storage\n if (typeof postParams !== 'undefined' && typeof postParams === 'object' )\n this.resultStorageParams = postParams;\n else\n throw new Error('Not an instance of a object: ' + postParams);\n\n // Some type of object here\n this.resultStorageType = 'pattern';\n }\n \n /**\n * This function is identical to exec, except that the job is executed locally\n * in the client.\n * @async\n * @param {number} cores - the number of local cores in which to execute the job.\n * @param {...any} args - The remaining arguments are identical to the arguments of exec\n * @return {Promise<ResultHandle>} - resolves with the results of the job, rejects on an error\n * @access public\n */\n async localExec (cores = 1, ...args)\n {\n this.inLocalExec = true;\n this.estimationSlices = 0;\n this.greedyEstimation = false;\n this.isCI = false;\n\n let worker;\n this.on('accepted', () => {\n // Start a worker for this job\n worker = new Worker({\n localExec: true,\n jobAddresses: [this.address],\n allowedOrigins: this.localExecAllowedFiles,\n paymentAddress: this.paymentAccountKeystore.address,\n identity: this.identityKeystore,\n maxWorkingSandboxes: cores,\n sandboxOptions: {\n ignoreNoProgress: true,\n SandboxConstructor: (DCP_ENV.platform === 'nodejs'\n && (__webpack_require__(/*! ../worker/evaluators */ \"./src/dcp-client/worker/evaluators/index.js\").nodeEvaluatorFactory)())\n },\n });\n \n worker.on('error', (error) => {\n console.error('LocalExec Error:', error);\n });\n \n worker.on('warning', (warning) => {\n console.warn('LocalExec Warning:', warning);\n });\n\n worker.start().catch((e) => {\n console.error('Failed to start worker for localExec:');\n console.error(e.message);\n });\n });\n \n if (DCP_ENV.platform === 'nodejs')\n {\n this.localExecAllowedFiles =\n {\n any: [],\n fetchData: [],\n fetchWorkFunctions: [],\n fetchArguments: [],\n sendResults: [],\n };\n \n // Determine type of input data\n const { dataRange, dataValues, dataPattern, sliceCount } = marshalInputData(this.jobInputData);\n \n const inputSetFiles = [];\n \n let inputSetURIs = [];\n let dataSet;\n \n if (dataValues)\n {\n for (let i = 0; i < dataValues.length; i++)\n {\n if (!(dataValues[i] instanceof URL))\n {\n let marshaledInputValue = kvinMarshal(dataValues[i]);\n let inputDataFile = createTempFile('dcp-localExec-sliceData-XXXXXXXXX', 'kvin');\n inputDataFile.writeSync(JSON.stringify(marshaledInputValue));\n inputSetFiles.push(inputDataFile);\n inputSetURIs.push(new URL('file://' + inputDataFile.filename));\n }\n else\n {\n inputSetURIs.push(dataValues[i]);\n if (this.localExecAllowedFiles['fetchData'].indexOf(dataValues[i].origin) === -1)\n this.localExecAllowedFiles['fetchData'].push(dataValues[i].origin);\n }\n }\n dataSet = new RemoteDataSet(inputSetURIs);\n if (dataSet.length > 0)\n this.marshaledDataValues = dataSet;\n }\n if (dataRange)\n {\n inputSetFiles.push(createTempFile('dcp-localExec-sliceData-XXXXXXXXX', 'json'));\n let marshaledInputSet = JSON.stringify(dataRange);\n inputSetFiles[0].writeSync(marshaledInputSet)\n inputSetURIs.push(new URL('file://' + inputSetFiles[0].filename));\n dataSet = new RemoteDataSet(inputSetURIs);\n this.marshaledDataRange = dataSet;\n this.rangeLength = dataRange.length;\n }\n \n if (dataPattern)\n {\n let uri = dataPattern;\n for (let i = 0; i < sliceCount; i++)\n {\n let sliceNum = i+1;\n let newURI = new URL(uri.replace('{slice}', sliceNum.toString()));\n this.localExecAllowedFiles['fetchData'].push(newURI.origin);\n }\n }\n \n // For allowed origins of the localexec worker. Only allow the origins (files in this case) in this list.\n for (let i = 0; i < inputSetFiles.length; i++)\n {\n // Coerce into a URI to handle OS specific path separators.\n const inputFileURI = new URL(`file://${inputSetFiles[i].filename}`);\n this.localExecAllowedFiles['fetchData'].push(inputFileURI.pathname);\n }\n \n // Save work function to disk if work function starts with data (ie not remote)\n if (this.workFunctionURI.startsWith('data:'))\n {\n const workFunctionFile = createTempFile('dcp-localExec-workFunction-XXXXXXXXX', 'js');\n const workFunction = await fetchURI(this.workFunctionURI);\n workFunctionFile.writeSync(workFunction);\n \n const workFunctionFileURL = new URL('file://' + workFunctionFile);\n this.workFunctionURI = workFunctionFileURL.href;\n this.localExecAllowedFiles['fetchWorkFunctions'].push(workFunctionFileURL.pathname);\n }\n else\n this.localExecAllowedFiles['fetchWorkFunctions'].push(new URL(this.workFunctionURI).origin);\n \n let encodedJobArgumentUris = [];\n if (this.jobArguments)\n {\n if (this.jobArguments instanceof RemoteDataPattern) /* Not supported */\n throw new DCPError('Cannot use RemoteDataPattern as work function arguments', 'EBADARG')\n if (this.jobArguments instanceof RemoteDataSet) /* Entire set is RemoteDataSet */\n {\n this.jobArguments.forEach((e) =>\n {\n this.localExecAllowedFiles['fetchArguments'].push(new URL(e).origin);\n encodedJobArgumentUris.push(new URL(e));\n });\n }\n else\n {\n for (let i = 0; i < this.jobArguments.length; i++)\n {\n if (this.jobArguments[i] instanceof URL)\n {\n if (this.localExecAllowedFiles['fetchArguments'].indexOf(this.jobArguments[i].origin) === -1)\n this.localExecAllowedFiles['fetchArguments'].push(this.jobArguments[i].origin);\n encodedJobArgumentUris.push(this.jobArguments[i]);\n }\n else\n {\n if (this.jobArguments[i] instanceof RemoteDataSet) /* Member of set is RemoteDataSet */\n {\n this.jobArguments[i].forEach((e) =>\n {\n this.localExecAllowedFiles['fetchArguments'].push(new URL(e).origin);\n encodedJobArgumentUris.push(new URL(e));\n });\n }\n else /* Actual Value */\n {\n const localArgFile = createTempFile(`dcp-localExec-argument-${i}-XXXXXXXXX`, 'kvin');\n const localArgFileURI = new URL(`file://${localArgFile.filename}`);\n\n localArgFile.writeSync(JSON.stringify(kvinMarshal(this.jobArguments[i])));\n this.localExecAllowedFiles['fetchArguments'].push(localArgFileURI.pathname);\n encodedJobArgumentUris.push(localArgFileURI);\n }\n }\n } \n }\n }\n this.marshaledArguments = kvinMarshal(encodedJobArgumentUris);\n\n // Support for remote results with localExec.\n if (this.resultStorageDetails)\n {\n this.localExecAllowedFiles['sendResults'].push(this.resultStorageDetails.origin);\n }\n }\n \n return this.exec(...args).finally(() => {\n if (worker) {\n setTimeout(() => {\n // stop the worker\n worker.stop(true);\n }, 3000);\n }\n });\n }\n\n /**\n * Deploys the job to the scheduler.\n * @param {number | object} [slicePaymentOffer=compute.marketValue] - Amount\n * in DCC that the user is willing to pay per slice.\n * @param {Keystore} [paymentAccountKeystore=wallet.get] - An instance of the\n * Wallet API Keystore that's used as the payment account when executing the\n * job.\n * @param {object} [initialSliceProfile] - An object describing the cost the\n * user believes the average slice will incur.\n * @access public\n * @emits Job#accepted\n */\n async exec (slicePaymentOffer = (__webpack_require__(/*! ../compute */ \"./src/dcp-client/compute.js\").compute.marketValue), paymentAccountKeystore, initialSliceProfile)\n {\n if (this.connected)\n throw new Error('Exec called twice on the same job handle.');\n \n if (this.estimationSlices === Infinity)\n this.estimationSlices = null;\n else if (this.estimationSlices < 0)\n throw new Error('Incorrect value for estimationSlices; it can be an integer or Infinity!');\n \n if (this.tuning.kvin.speed || this.tuning.kvin.size)\n {\n tunedKvin = new kvin.KVIN();\n tunedKvin.tune = 'size';\n if(this.tuning.kvin.speed)\n tunedKvin.tune = 'speed';\n // If both size and speed are true, kvin will optimize based on speed\n if(this.tuning.kvin.speed && this.tuning.kvin.size)\n console.log('Slices and arguments are being uploaded with speed optimization.');\n }\n \n /* slight optimization to ensure we don't send requirements that will be ignored in the job submitter. Make a copy of the client specified requirements for this so that we dont magically override something they manually set */\n const _DEFAULT_REQUIREMENTS = JSON.parse(JSON.stringify(DEFAULT_REQUIREMENTS));\n removeBadRequirements(this.requirements, _DEFAULT_REQUIREMENTS);\n \n this.readyStateChange('exec');\n if ((typeof slicePaymentOffer === 'number') || (typeof slicePaymentOffer === 'object')\n || ((this.slicePaymentOffer === null || this.slicePaymentOffer === undefined) && typeof slicePaymentOffer === 'function'))\n this.setSlicePaymentOffer(slicePaymentOffer);\n if (typeof initialSliceProfile !== 'undefined')\n this.initialSliceProfile = initialSliceProfile;\n \n if (typeof paymentAccountKeystore !== 'undefined')\n {\n /** XXX @todo deprecate use of ethereum wallet objects */\n if (typeof paymentAccountKeystore === 'object' && paymentAccountKeystore.hasOwnProperty('_privKey'))\n {\n console.warn('* deprecated API * - job.exec invoked with ethereum wallet object as paymentAccountKeystore') /* /wg oct 2019 */\n paymentAccountKeystore = paymentAccountKeystore._privKey\n }\n /** XXX @todo deprecate use of private keys */\n if (wallet.isPrivateKey(paymentAccountKeystore))\n {\n console.warn('* deprecated API * - job.exec invoked with private key as paymentAccountKeystore') /* /wg dec 2019 */\n paymentAccountKeystore = await new wallet.Keystore(paymentAccountKeystore, '');\n }\n\n this.setPaymentAccountKeystore(paymentAccountKeystore)\n }\n \n if (this.paymentAccountKeystore)\n // Throws if they fail to unlock, we allow this since the keystore was set programmatically. \n await this.paymentAccountKeystore.unlock(undefined, parseFloat(dcpConfig.job.maxDeployTime));\n else\n {\n // If not set programmatically, we keep trying to get an unlocked keystore ... forever.\n let locked = true;\n let safety = 0; // no while loop shall go unguarded\n let ks;\n do\n {\n ks = null;\n // custom message for the browser modal to denote the purpose of keystore submission\n // let msg = `This application is requesting a keystore file to execute ${this.public.description || this.public.name || 'this job'}.<br><br> Please upload the corresponding keystore file. If you upload a keystore file which has been encrypted with a passphrase, the application will not be able to use it until it prompts for a passphrase and you enter it.`;\n let msg = `Upload a DCP bank account keystore<br> containing compute credits.`;\n try\n {\n ks = await wallet.get({ contextId: this.contextId, jobName: this.public.name, msg});\n }\n catch (e)\n {\n if (e.code !== ClientModal.CancelErrorCode) throw e;\n };\n if (ks)\n {\n try\n {\n await ks.unlock(undefined, parseFloat(dcpConfig.job.maxDeployTime));\n locked = false;\n }\n catch (e)\n {\n // prompt user again if user enters password incorrectly, exit modal otherwise\n if (e.code !== wallet.unlockFailErrorCode) throw e;\n }\n }\n if (safety++ > 1000) throw new Error('EINFINITY: job.exec tried wallet.get more than 1000 times.')\n } while (locked);\n this.setPaymentAccountKeystore(ks)\n }\n \n // We either have a valid keystore + password or we have rejected by this point.\n if (!this.slicePaymentOffer)\n throw new Error('A payment profile must be assigned before executing the job');\n else\n this.feeStructure = this.slicePaymentOffer.toFeeStructure(this.jobInputData.length);\n\n if (!this.address)\n {\n try\n {\n this.readyStateChange('init');\n await this.deployJob();\n const listenersPromise = this.addInitialEvents();\n const computeGroupsPromise = this.joinComputeGroups();\n let uploadSlicePromise;\n // if job data is by value then upload data to the scheduler in a staggered fashion\n if (Array.isArray(this.dataValues) && !this.marshaledDataValues)\n {\n this.readyStateChange('uploading');\n uploadSlicePromise = addSlices(this.dataValues, this.address, tunedKvin)\n .then(() => {\n debugging('slice-upload') && console.info(`970: slice data uploaded, closing job...`);\n return this.close();\n });\n }\n \n // await all promises for operations that can be done after the job is deployed\n await Promise.all([listenersPromise, computeGroupsPromise, uploadSlicePromise]);\n \n this.readyStateChange('deployed');\n this.emit('accepted', { job: this });\n }\n catch (error)\n {\n if (ON_BROWSER)\n await ClientModal.alert(error, { title: 'Failed to deploy job!' });\n throw error;\n }\n }\n else\n {\n // reconnecting to an old job\n await this.addInitialEvents();\n this.readyStateChange('reconnected');\n }\n\n this.connected = true;\n \n // start a timer to keep the node process alive until the results come back. Keep refreshing this timer until the results promise is resovled or rejected.\n let pendingResultsTimer;\n if (DCP_ENV.platform === 'nodejs')\n {\n pendingResultsTimer = setTimeout(() => {\n pendingResultsTimer.refresh();\n }, 1000 * 60 * 60) /* hour long timer */\n }\n\n return new Promise((resolve, reject) => {\n this.startEventBandaidWatchdog();\n \n const onComplete = () => {\n if (DCP_ENV.platform === 'nodejs')\n clearTimeout(pendingResultsTimer);\n this.stopEventBandaidWatchdog();\n resolve(this.results); \n };\n\n const onCancel = (event) => {\n /**\n * FIXME(DCP-1150): Remove this since normal cancel event is noisy\n * enough to not need stopped event too.\n */\n if (ON_BROWSER && !this.listeningForCancel)\n ClientModal.alert('More details in console...', { title: 'Job Canceled' });\n this.emit('cancel', event);\n\n let errorMsg = event.reason;\n if (event.error && event.error !== 'undefined')\n errorMsg = errorMsg +`\\n Recent error message: ${event.error.message}`\n \n if (DCP_ENV.platform === 'nodejs')\n clearTimeout(pendingResultsTimer); \n\n this.stopEventBandaidWatchdog();\n\n reject(new DCPError(errorMsg, event.code));\n };\n\n this.ee.once('stopped', async (stopEvent) => {\n // There is a chance the result submitter will emit finished > 1 time. Only handle it once.\n if (this.receivedStop)\n return;\n this.receivedStop = true;\n this.emit('stopped', stopEvent.runStatus);\n switch (stopEvent.runStatus) {\n case jobStatus.finished:\n if (this.collateResults)\n {\n let report = await this.getJobInfo();\n let allSliceNumbers = Array.from(Array(report.totalSlices)).map((e,i)=>i+1);\n let remainSliceNumbers = allSliceNumbers.filter((e) => !this.results.isAvailable(e));\n\n if (remainSliceNumbers.length)\n {\n const promises = remainSliceNumbers.map(sliceNumber => this.results.fetch([sliceNumber], true));\n await Promise.all(promises);\n }\n }\n\n this.emit('complete', this.results);\n onComplete();\n break;\n case jobStatus.cancelled:\n onCancel(stopEvent);\n break;\n default:\n /**\n * Asserting that we should never be able to reach here. The only\n * scheduler events that should trigger the Job's 'stopped' event\n * are jobStatus.cancelled, jobStatus.finished, and sliceStatus.paused.\n */\n reject(new Error(`Unknown event \"${stopEvent.runStatus}\" caused the job to be stopped.`));\n break;\n }\n });\n\n }).finally(() => {\n const handleErr = (e) => {\n console.error('Error while closing job connection:');\n console.error(e);\n }\n\n // Create an async IIFE to not block the promise chain\n (async () => {\n // delay to let last few events to be received\n await new Promise((resolve) => setTimeout(resolve, 1000));\n \n // close all of the connections so that we don't cause node processes to hang.\n this.closeDeployConnection();\n await this.eventSubscriber.close().catch(handleErr);\n await computeGroups.closeServiceConnection().catch((err) => {\n console.error('Warning: could not close compute groups service connection', err);\n });\n })();\n });\n }\n\n /**\n * Start the event-bandaid watchdog. Every [configurable] seconds, fetch\n * the job report and do appropriate things if the job is stopped.\n */\n startEventBandaidWatchdog()\n {\n if (this.eventBandaidHandle)\n this.stopEventBandaidWatchdog();\n\n this.eventBandaidHandle = setInterval(() => {\n this.eventBandaid();\n }, 60 * 1000);\n\n // if we are in a nodelike environment with unreffable timers, unref the\n // timer so it doesn't hold the process open. \n // \n // (n.b. while I understand that we _want_ the process to stay open while\n // the job lives, but this is Not The Way; the whole eventBandaid is a\n // temporary hack which will go away in time ~ER 2022-11-02)\n if (this.eventBandaidHandle.unref)\n this.eventBandaidHandle.unref();\n }\n\n /**\n * Stop and clean up the even-bandaid watchdog.\n */\n stopEventBandaidWatchdog()\n {\n if (this.eventBandaidHandle)\n clearInterval(this.eventBandaidHandle);\n\n this.eventBandaidHandle = false;\n }\n\n /**\n * Called on an interval, started by .exec and stopped by\n * onComplete/onCancel. Fetches the jobReport and verifies the job is\n * not in a terminal state; if the job is finished or cancelled, we\n * emit a synthetic internal stop event to trigger regular cleanup\n * (result retrieval, etc) and finalize the job handle.\n */\n eventBandaid()\n {\n this.getJobInfo()\n .then(jobInfo => {\n if ([jobStatus.finished, jobStatus.cancelled].includes(jobInfo.status))\n this.ee.emit('stopped', { runStatus: jobInfo.status });\n })\n .catch(error => {\n\n });\n }\n \n /**\n * job.addListeners(): Private function used to set up event listeners to the scheduler\n * before deploying the job.\n */\n async addInitialEvents ()\n {\n this.readyStateChange('listeners');\n\n // This is important: We need to flush the task queue before adding listeners\n // because we queue pending listeners by listening to the newListener event (in the constructor).\n // If we don't flush here, then the newListener events may fire after this function has run,\n // and the events won't be properly set up.\n await new Promise(resolve => setTimeout(resolve, 0));\n\n // @todo: Listen for an estimated cost, probably emit an \"estimated\" event when it comes in?\n // also @todo: Do the estimation task(s) on the scheduler and send an \"estimated\" event\n\n // Always listen to the stop event. It will resolve the work function promise, so is always needed.\n this.on('stop', (ev) => {this.ee.emit('stopped', ev)});\n\n // Connect listeners that were set up before exec\n if (this.desiredEvents.includes('result'))\n this.listeningForResults = true;\n await this.subscribeNewEvents(this.desiredEvents);\n\n // Connect listeners that are set up after exec\n this.on('newListener', (evt) => {\n if (evt === 'newListener' || this.desiredEvents.includes(evt))\n return;\n this.subscribeNewEvents([evt]);\n });\n \n // automatically add a listener for results if collateResults is on\n if (this.collateResults && !this.listeningForResults)\n this.on('result', () => {});\n\n debugging('dcp-client') && console.debug('subscribedEvents', this.desiredEvents);\n\n // If we have listeners for job.work, subscribe to custom events\n if (this.listenForCustomEvents)\n await this.subscribeCustomEvents();\n // Connect work event listeners that are set up after exec\n else\n this.work.on('newListener', () => this.subscribeCustomEvents());\n }\n \n /**\n * Subscribes to either reliable events or optional events. It is assumed that\n * any call to this function will include only new events.\n * @param {string[]} events \n */\n async subscribeNewEvents (events)\n {\n const reliableEvents = [];\n const optionalEvents = [];\n for (let eventName of events)\n {\n eventName = eventName.toLowerCase();\n if (this.eventTypes[eventName] && this.eventTypes[eventName].reliable)\n reliableEvents.push(eventName);\n else if (this.eventTypes[eventName] && !this.eventTypes[eventName].reliable)\n optionalEvents.push(eventName);\n else\n debugging('dcp-client') && console.debug(`Job handler has listener ${eventName} which isn't an event-router event.`);\n }\n if (debugging('dcp-client'))\n {\n console.debug('reliableEvents', reliableEvents);\n console.debug('optionalEvents', optionalEvents);\n }\n await this.eventSubscriber.subscribeManyEvents(reliableEvents, optionalEvents, { filter: { job: this.address } });\n }\n \n /**\n * Establishes listeners for worker events when requested by the client\n */\n async subscribeCustomEvents ()\n {\n if (!this.listeningForCustomEvents)\n await this.eventSubscriber.subscribeManyEvents([], ['custom'], { filter: { job: this.address } });\n this.listeningForCustomEvents = true\n }\n \n async joinComputeGroups ()\n {\n // localExec jobs are not entered in any compute group.\n if (!this.inLocalExec && this.computeGroups && this.computeGroups.length > 0)\n {\n this.readyStateChange('compute-groups');\n computeGroups.addRef(); // Just in case we're doing a Promise.all on multiple execs.\n\n // Add this job to its currently-defined compute groups (as well as public group, if included)\n let success;\n \n if (!Array.isArray(this.computeGroups)) \n throw new DCPError('Compute groups must be wrapped in an Array', 'DCPL-1101');\n\n for (let i = 0; i < this.computeGroups.length; i++)\n {\n let value = this.computeGroups[i];\n \n if (typeof value !== 'object')\n throw new DCPError(`This compute group: ${value[i]} must be an object`, 'DCPL-1102');\n \n if (value.joinKey && typeof value.joinKey !== 'string' && !(value.joinKey instanceof String))\n throw new DCPError(`This join key: ${value.joinKey} must be a string or a string literal`, 'DCPL-1103');\n else if (value.joinKeystore && !(value.joinKeystore instanceof wallet.Keystore))\n throw new DCPError(`This join Keystore: ${value.joinKeystore} must be an instance of wallet.Keystore`, 'DCPL-1104');\n else if (!value.joinKey && !value.joinKeystore)\n throw new DCPError('Compute group must contain a joinKey or a joinKeystore', 'DCPL-1105');\n }\n \n try\n {\n const cgPayload = await computeGroups.addJobToGroups(this.address, this.computeGroups);\n success = true; // To support older version of CG service where addJobToGroups had void/undefined return.\n if (cgPayload) success = cgPayload.success;\n debugging('dcp-client') && console.debug('job/index: addJobToGroups cgPayload:', cgPayload ? cgPayload : 'cgPayload is not defined; probably from legacy CG service.');\n }\n catch (e)\n {\n debugging('dcp-client') && console.debug('job/index: addJobToGroups threw exception:', e);\n success = false;\n }\n\n computeGroups.closeServiceConnection().catch((err) => {\n console.error('Warning: could not close compute groups service connection', err)\n });\n\n /* Could not put the job in any compute group, even though the user wanted it to run. Cancel the job. */\n if (!success)\n {\n await this.cancel('compute-groups::Unable to join any compute groups');\n throw new DCPError(`Access Denied::Failed to add job ${this.address} to any of the desired compute groups`, 'DCPL-1100');\n }\n }\n }\n \n /**\n * Takes result events as input, stores the result and fires off\n * events on the job handle as required. (result, duplicate-result)\n *\n * @param {object} ev - the event recieved from protocol.listen('/results/0xThisGenAdr')\n */\n async handleResult (ev)\n {\n if (this.results === null)\n // This should never happen - the onResult event should only be established/called\n // in addListeners which should also initialize the internal results array\n throw new Error('Job.onResult was invoked before initializing internal results');\n \n const { result: _result, time } = ev.result;\n debugging('dcp-client') && console.debug('handleResult', _result);\n let result = await fetchURI(_result);\n\n if (this.results.isAvailable(ev.sliceNumber))\n {\n const changed = JSON.stringify(this.results[ev.sliceNumber]) !== JSON.stringify(result);\n this.emit('duplicate-result', { sliceNumber: ev.sliceNumber, changed });\n }\n \n this.results.newResult(result, ev.sliceNumber - 1);\n }\n \n /**\n * Receives status events from the scheduler, updates the local status object\n * and emits a 'status' event\n *\n * @param {object} ev - the status event received from\n * protocol.listen('/status/0xThisGenAdr')\n * @param {boolean} emitStatus - value indicating whether or not the status\n * event should be emitted\n */\n handleStatus ({ runStatus, total, distributed, computed }, emitStatus = true)\n {\n Object.assign(this.status, {\n runStatus,\n total,\n distributed,\n computed,\n });\n\n if (emitStatus)\n this.emit('status', { ...this.status, job: this.address });\n }\n \n /**\n * Sends a request to the scheduler to deploy the job.\n */\n async deployJob ()\n {\n var moduleDependencies; \n \n /* Send sideloader bundle to the package server */\n if (DCP_ENV.platform === 'nodejs' && this.dependencies.length)\n {\n try\n {\n let { pkg, unresolved } = await this._publishLocalModules();\n\n moduleDependencies = unresolved;\n if (pkg)\n moduleDependencies.push(pkg.name + '/' + sideloaderModuleIdentifier); \n }\n catch(error)\n {\n throw new DCPError(`Error trying to communicate with package manager server: ${error}`);\n }\n }\n else\n moduleDependencies = this.dependencies;\n \n this.readyStateChange('preauth');\n\n const adhocId = this.uuid.slice(this.uuid.length - 6, this.uuid.length);\n const schedId = await dcpConfig.scheduler.identity;\n // The following check is needed for when using dcp-rtlink and loading the config through source, instead of using the dcp-client bundle\n let schedIdAddress = schedId;\n if(schedId.address)\n schedIdAddress = schedId.address;\n this.identityKeystore = await wallet.getId();\n const preauthToken = await bankUtil.preAuthorizePayment(schedIdAddress, this.maxDeployPayment, this.paymentAccountKeystore);\n const { dataRange, dataValues, dataPattern, sliceCount } = marshalInputData(this.jobInputData);\n if(dataValues)\n this.dataValues = dataValues;\n\n this.readyStateChange('deploying');\n\n /* Payload format is documented in scheduler-v4/libexec/job-submit/operations/submit.js */\n const submitPayload = {\n owner: this.identityKeystore.address,\n paymentAccount: this.paymentAccountKeystore.address,\n priority: 0, // @nyi\n\n workFunctionURI: this.workFunctionURI,\n uuid: this.uuid,\n mvMultSlicePayment: Number(this.feeStructure.marketValue) || 0, // @todo: improve feeStructure internals to better reflect v4\n absoluteSlicePayment: Number(this.feeStructure.maxPerRequest) || 0,\n requirePath: this.requirePath,\n dependencies: moduleDependencies,\n requirements: this.requirements, /* capex */\n localExec: this.inLocalExec,\n force100pctCPUDensity: this.force100pctCPUDensity,\n estimationSlices: this.estimationSlices,\n greedyEstimation: this.greedyEstimation,\n workerConsole: this.workerConsole,\n isCI: this.isCI,\n\n description: this.public.description || 'Discreetly making the world smarter',\n name: this.public.name || 'Ad-Hoc Job' + adhocId,\n link: this.public.link || '',\n\n preauthToken, // XXXwg/er @todo: validate this after fleshing out the stub(s)\n\n resultStorageType: this.resultStorageType, // @todo: implement other result types\n resultStorageDetails: this.resultStorageDetails, // Content depends on resultStorageType\n resultStorageParams: this.resultStorageParams, // Post params for off-prem storage\n dataRange,\n dataPattern,\n sliceCount,\n marshaledDataValues: this.marshaledDataValues,\n rangeLength: this.rangeLength\n };\n\n // Check if dataRange or dataPattern input is already marshaled\n if (this.marshaledDataRange)\n submitPayload.dataRange = this.marshaledDataRange;\n\n /* Determine composition of argument set and build payload */\n if (this.marshaledArguments)\n submitPayload.marshaledArguments = this.marshaledArguments;\n else if (this.jobArguments)\n {\n if (this.jobArguments instanceof RemoteDataPattern) /* Not supported */\n throw new DCPError('Cannot use RemoteDataPattern as work function arguments', 'EBADARG')\n if (this.jobArguments instanceof RemoteDataSet) /* Entire set is RemoteDataSet */\n this.jobArguments = this.jobArguments.map((e) => new URL(e));\n\n submitPayload.marshaledArguments = kvin.marshal(encodeJobValueList(this.jobArguments, 'jobArguments'));\n }\n\n // XXXpfr Excellent tracing.\n if (debugging('dcp-client'))\n {\n const { dumpObject } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n dumpObject(submitPayload, 'Submit: Job Index: submitPayload', 256);\n }\n debugging('dcp-client') && console.debug('submitPayload.marshaledArguments', JSON.stringify(submitPayload.marshaledArguments).length, JSON.stringify(submitPayload).length);\n\n // Deploy the job! If we get an error, try again a few times until threshold of errors is reached, then actually throw it\n let deployed\n let deployAttempts = 0;\n while (deployAttempts++ < (dcpConfig.job.deployAttempts || 10))\n {\n try\n {\n deployed = await this.useDeployConnection('submit', submitPayload, this.identityKeystore);\n break;\n }\n catch (e)\n {\n if (deployAttempts < 10)\n debugging('dcp-client') && console.debug('Error when trying to deploy job, trying again', e);\n else\n throw e;\n }\n }\n\n if (!deployed.success)\n {\n // close all of the connections so that we don't cause node processes to hang.\n const handleErr = (e) => {\n console.error('Error while closing job connection:');\n console.error(e);\n };\n \n this.closeDeployConnection();\n this.eventSubscriber.close().catch(handleErr);\n computeGroups.closeServiceConnection().catch(handleErr);\n \n // Yes, it is possible for deployed or deployed.payload to be undefined.\n if (deployed.payload)\n {\n if (deployed.payload.code === 'ENOTFOUND')\n throw new DCPError(`Failed to submit job to scheduler. Account: ${submitPayload.paymentAccount} was not found or does not have sufficient balance (${deployed.payload.info.deployCost} DCCs needed to deploy this job)`, deployed.payload); \n throw new DCPError('Failed to submit job to scheduler', deployed.payload);\n }\n throw new DCPError('Failed to submit job to scheduler (no payload)', deployed ? deployed : '');\n }\n\n debugging('dcp-client') && console.debug('After Deploy', JSON.stringify(deployed));\n\n this.address = deployed.payload.job;\n this.deployCost = deployed.payload.deployCost;\n\n if (!this.status)\n this.status = {\n runStatus: null,\n total: 0,\n computed: 0,\n distributed: 0,\n };\n \n this.status.runStatus = deployed.payload.status;\n this.status.total = deployed.payload.lastSliceNumber;\n this.running = true;\n }\n \n /** close an open job to indicate we are done adding data so it is okay to finish\n * the job at the appropriate time\n */\n close ()\n {\n return this.useDeployConnection('closeJob', {\n job: this.id,\n });\n }\n \n /** Use the connection to job submit service. Will open a new connection if one does not exist,\n * and close the connection if it is idle for more than 10 seconds (tuneable).\n */\n useDeployConnection(...args)\n {\n if (!this.useDeployConnection.uses)\n this.useDeployConnection.uses = 0;\n this.useDeployConnection.uses++;\n if (!this.deployConnection)\n {\n debugging('deploy-connection') && console.info(`1453: making a new deployConnection...`)\n this.deployConnection = new protocolV4.Connection(dcpConfig.scheduler.services.jobSubmit); \n this.deployConnection.on('close', () => { this.deployConnection = null; });\n }\n if (this.deployConnectionTimeout)\n clearTimeout(this.deployConnectionTimeout);\n\n debugging('deploy-connection') && console.info(`1460: sending ${args[0]} request...`);\n const deployPromise = this.deployConnection.send(...args);\n \n deployPromise.finally(() => {\n this.useDeployConnection.uses--;\n\n debugging('deploy-connection') && console.info(`1462: deployConnection done ${args[0]} request, connection uses is ${this.useDeployConnection.uses}`)\n\n this.deployConnectionTimeout = setTimeout(() => {\n if (this.useDeployConnection.uses === 0 && this.deployConnection)\n {\n debugging('deploy-connection') && console.info(`1469: done with deployConn, closing...`);\n // if we're done w/ the connection, then remove its cleanup\n // function, close it, and clean up manually to make room for a\n // new conn when needed (ie, don't leave the closing conn where\n // someone could accidentally pick it up)\n this.deployConnection.removeAllListeners('close');\n this.deployConnection.close();\n this.deployConnection = null;\n }\n }, (dcpConfig.job.deployCloseTimeout || 10 * 1000));\n if (!ON_BROWSER)\n this.deployConnectionTimeout.unref();\n }); \n \n return deployPromise;\n }\n \n /**\n * Close the connection to the job submit (if it exists), and clear the close timeout (if needed).\n */\n closeDeployConnection()\n {\n if (this.deployConnection)\n this.deployConnection.close();\n if (this.deployConnectionTimeout)\n clearTimeout(this.deployConnectionTimeout);\n }\n}\n\n/** \n * Encode a value list for transmission to the job-submit daemon. This could be either job arguments\n * or the slice input set, if the input set was an Array-like object.\n *\n * @param {ArrayLike} valueList - The list of values to encode\n * @returns {Array<URL|{string: string, method:string, MIMEType: string }>} - URL or object with 3 properties:\n * string - serialization of value\n * method - how value was serialized (e.g. 'kvin', 'json')\n * MIMEType - MIME information corresponding to method (e.g. 'application/kvin', 'application/json')\n */\nfunction encodeJobValueList(valueList, valueKind)\n{\n var list = [];\n\n /*\n * We need to handle several different styles of datasets, and create the output array accordingly.\n *\n * 1. instance of RemoteDataSet or RemoteDataPattern => apply new URI(?)\n * 2. an Array-like objects => apply serializeJobValue\n * Values sent to the scheduler in payload are either URL or the result of calling serializeJobValue.\n */\n\n if (typeof valueList === 'undefined' || (typeof valueList === 'object' && valueList.length === 0))\n return list; /* empty set */\n\n if (typeof valueList !== 'object' || !valueList.hasOwnProperty('length'))\n throw new Error('value list must be an Array-like object');\n\n for (let i = 0; i < valueList.length; i++) /* Set is composed of values from potentially varying sources */\n {\n let value = valueList[i];\n if (value instanceof RemoteDataSet)\n value.forEach((el) => list.push(new URL(el)));\n else if (value instanceof RemoteDataPattern)\n {\n if (valueKind === jobValueKind.jobArguments)\n throw new DCPError('Cannot use RemoteDataPattern as work function arguments', 'EBADARG');\n else\n {\n let uri = valueList['pattern'];\n for (let sliceNum = 1; sliceNum <= valueList['sliceCount']; sliceNum++)\n list.push(new URL(uri.replace('{slice}', sliceNum)))\n }\n }\n else if (value instanceof RemoteValue)\n list.push(serializeJobValue(value.href));\n else\n list.push(serializeJobValue(value));\n }\n\n return list;\n} \n\n/**\n * Depending on the shape of the job's data, resolve it into a RangeObject, a\n * Pattern, or a values array, and return it in the appropriate property.\n *\n * @param {any} data Job's input data\n * @return {MarshaledInputData} An object with one of the following properties set:\n * - dataValues: job input is an array of arbitrary values \n * - dataPattern: job input is a URI pattern \n * - dataRange: job input is a RangeObject (and/or friends)\n */\nfunction marshalInputData (data)\n{\n if (!(data instanceof Object || data instanceof SuperRangeObject))\n throw new TypeError(`Invalid job data type: ${typeof data}`);\n\n /**\n * @type {MarshaledInputData}\n */\n const marshalledInputData = {};\n\n // TODO(wesgarland): Make this more robust.\n if (data instanceof SuperRangeObject ||\n (data.hasOwnProperty('ranges') && data.ranges instanceof MultiRangeObject) ||\n (data.hasOwnProperty('start') && data.hasOwnProperty('end')))\n marshalledInputData.dataRange = data;\n else if (Array.isArray(data))\n marshalledInputData.dataValues = data;\n else if (data instanceof URL || data instanceof DcpURL)\n marshalledInputData.dataPattern = String(data);\n else if(data instanceof RemoteDataSet)\n marshalledInputData.dataValues = data.map(e => new URL(e));\n else if(data instanceof RemoteDataPattern)\n {\n marshalledInputData.dataPattern = data['pattern'];\n marshalledInputData.sliceCount = data['sliceCount'];\n }\n\n debugging('job') && console.debug('marshalledInputData:', marshalledInputData);\n return marshalledInputData;\n}\n\n/**\n * marshal the value using kvin or instance of the kvin (tunedKvin)\n * tunedKvin is defined if job.tuning.kvin is specified.\n *\n * @param {any} value \n * @return {object} A marshaled object\n * \n */\nfunction kvinMarshal (value) {\n if (tunedKvin)\n return tunedKvin.marshal(value);\n\n return kvin.marshal(value);\n}\n\n\n\nexports.Job = Job;\nexports.SlicePaymentOffer = SlicePaymentOffer;\nexports.ResultHandle = ResultHandle;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/job/index.js?");
4229
4275
 
4230
4276
  /***/ }),
4231
4277
 
@@ -4246,7 +4292,7 @@ eval("/**\n * @file node-modules.js Node-specific support for sen
4246
4292
  \*********************************************/
4247
4293
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4248
4294
 
4249
- eval("/**\n * @file job/result-handle.js\n * @author Ryan Rossiter, ryan@kingsds.network\n * Robert Mirandola, <robert@kingsds.network>\n * @date June 2020\n * July 2022\n *\n * The ResultHandle acts as a proxy for a job's results, querying\n * internal results when available or the scheduler when the results\n * are not available locally.\n */\n\nconst { rehydrateRange, RangeObject, SparseRangeObject } = __webpack_require__(/*! ../range-object */ \"./src/dcp-client/range-object.js\");\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst wallet = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst { fetchURI } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst customInspectSymbol = Symbol.for('nodejs.util.inspect.custom');\n\n// Array methods that don't modify the array, will be applied to the ResultHandle's values object\nconst RH_ARRAY_METHODS = [\n 'slice', 'filter', 'concat', 'find', 'findIndex', 'indexOf', 'map', 'reduce', 'includes', 'toString', 'forEach'\n];\n\n/**\n * This class represents a handle on a job's results. It can be used for accessing the job's results, or for querying the scheduler to fetch results.\n * In addition to the properties and methods of this class, the following standard array methods are also available for accessing the available results: `slice`, `filter`, `concat`, `find`, `findIndex`, `indexOf`, `map`, `reduce`, `includes`, `toString`, and `forEach`.\n * The results can also be accessed by index (`results[i]`), but an error will be thrown if the result for that index is not yet available.\n * @access public\n */\nclass ResultHandle extends Array\n{\n constructor(job)\n {\n super();\n\n const { Job } = __webpack_require__(/*! . */ \"./src/dcp-client/job/index.js\");\n if (!(job instanceof Job))\n throw new TypeError('ResultHandle must be constructed from a Job object');\n\n /**\n * The length of the available results array.\n * @type {number}\n * @access public\n */\n this.length = 0; // overridden by the proxy, here for JSDoc purposes\n\n this.job = job;\n this.realKeys = job.jobInputData;\n this.realValues = [];\n this.valuesAvailable = [];\n\n return new Proxy(this, {\n get: (target, name) => {\n if ((typeof name === 'string' || typeof name === 'number') && Number.isInteger(parseFloat(name)))\n {\n let i = parseFloat(name);\n if (target.isAvailable(i))\n return target.realValues[i];\n else\n throw new Error(`Result ${i} is not available. It has either not been computed or you need to fetch it.`);\n } \n else if (name === 'length')\n return target.getLength();\n else if (name === 'constructor')\n return Array.constructor;\n else if (RH_ARRAY_METHODS.includes(name))\n {\n let values = target.values();\n return values[name].bind(values);\n }\n else\n {\n // only return methods on this class, don't allow access\n // to array methods other than the whitelisted ones\n let p = target.__proto__[name];\n if (typeof p === 'function') {\n return p.bind(target);\n } else {\n return p;\n }\n }\n }\n });\n }\n\n toJSON ()\n {\n return JSON.stringify(this.values());\n }\n\n isAvailable(index)\n {\n return this.valuesAvailable[index];\n }\n\n reset() {\n // quickly empty the realValues array\n this.realValues.length = this.valuesAvailable.length = 0;\n this.realValues.length = this.valuesAvailable.length =this.realKeys.length;\n }\n\n /**\n * Returns an array of input values. Will only return input values that have a completed result available.\n * @access public\n * @returns {Array<*>}\n */\n keys() {\n // Keys can be a RangeObject, faster to iterate over valuesAvailable\n return this.valuesAvailable.reduce((keysList, valueAvailable, sliceNumber) => {\n if (valueAvailable) keysList.push(this.realKeys[sliceNumber - 1]);\n return keysList;\n }, []);\n }\n\n /**\n * Returns an array of results. Will only return results that have been received from the scheduler, if only one result is complete the array will contain one value.\n * @access public\n * @returns {Array<*>}\n */\n values() {\n return this.realValues.filter((v, i) => this.isAvailable(i));\n }\n\n // Aliased as length in proxy, can't use getter because Array.length is not configurable\n getLength() {\n return this.valuesAvailable.reduce((n, v) => (n + (v ? 1 : 0)), 0);\n }\n \n [customInspectSymbol]() /* overrides the output when console.log(result) is called */\n { \n return this.values(); \n }\n\n [Symbol.iterator]() {\n let index = 0;\n let values = this.values(); // use available values\n\n return {\n next: () => ({\n value: values[index++],\n done: index > values.length\n })\n };\n }\n\n /** \n * Returns an array of [input, output] pairs, in sliceNumber order.\n * Return value is undefined if the input is not an ES5 primitive.\n * @access public\n * @returns {Array<*>}\n */\n entries() {\n return this.valuesAvailable.reduce((keyValuePairs, valueAvailable, sliceNumber) => {\n if (valueAvailable) keyValuePairs.push([\n String(this.realKeys[sliceNumber - 1]),\n this.realValues[sliceNumber],\n ]);\n return keyValuePairs;\n }, []);\n }\n\n /** \n * Returns an Object associating input and output values where the inputs are ES5 primitive types.\n * Return value is undefined if the input is not an ES5 primitive.\n * @access public\n * @returns {object}\n */\n fromEntries() {\n return this.entries().reduce((o, [k, v]) => {\n o[k] = v; return o;\n }, {});\n }\n\n /** \n * Return the nth input value/input vector \n * @access public\n * @param {number} n Index in the input set to return the value for.\n * @returns {*} Input set value\n */\n key(n) {\n return this.realKeys[n];\n }\n\n /** \n * Return the value corresponding to the provided key \n * @access public\n * @param {*} key Corresponds to a value in the job's input set.\n * @returns {*} Result corresponding to the input value\n */\n lookupValue(key) {\n // use keys instead of _keys so we only lookup on available results\n let ind = this.keys().indexOf(key);\n if (ind === -1)\n throw new DCPError('Argument passed into the function was not found in input set', 'ERANGE');\n\n return this.values()[ind];\n }\n\n /**\n * Sends request to scheduler to fetch results, the retrieved results will be populated on this object.\n * @param {RangeObject} [rangeObject] - range object to query results\n * @param {string} [emitEvents] - if truthy, emits a `result` event for new results as they are added to the handle, and if set to 'all' emits 'resultUpdated' for duplicate results \n * @access public\n * @emits Job#resultsUpdated\n */\n async fetch(rangeObject, emitEvents) {\n const range = rangeObject && rehydrateRange(rangeObject);\n\n const job = this.job;\n \n // Fetch any existing results\n let ks = await wallet.getId();\n const conn = new protocolV4.Connection(dcpConfig.scheduler.services.resultSubmitter.location, ks);\n\n const { success, payload } = await conn.send('fetchResult', {\n job: job.id,\n owner: job.paymentAccountKeystore.address,\n range,\n }, job.paymentAccountKeystore);\n\n // Unpack results, using fetchURI to decode/fetch result URIs\n await Promise.all(payload.map(async r => {\n if (!r) {\n console.warn(`ResultHandle.fetch: Received result was not defined (${r}), ignoring...`);\n return;\n }\n\n const decodedValue = decodeURIComponent(r.value)\n try\n {\n this.realValues[r.slice] = await fetchURI(decodedValue, dcpConfig.scheduler.location.origin); \n } catch (error)\n {\n // ensure the error is an actual URI parsing error and not due actual problems\n if (error.code === 'ERR_INVALID_URL')\n this.realValues[r.slice] = decodedValue;\n else\n return console.warn('Error in ResultHandle.fetch():', error.message);\n }\n\n if (emitEvents)\n {\n if (!this.valuesAvailable[r.slice])\n this.job.emit('result', { sliceNumber: r.slice, result: this.realValues[r.slice] });\n else if (emitEvents === 'all')\n {\n this.job.emit('resultsUpdated', { sliceNumber: r.slice }); \n this.job.emit('result', { sliceNumber: r.slice, result: this.realValues[r.slice] });\n }\n }\n this.valuesAvailable[r.slice] = true;\n }));\n await conn.close(null, true);\n }\n \n /**\n * Handle adding a new result\n */\n newResult(result, index)\n {\n if (this.valuesAvailable[index])\n {\n this.realValues[index] = result;\n this.job.emit('resultsUpdated', { sliceNumber: index }); \n }\n else\n {\n this.valuesAvailable[index] = true;\n this.realValues[index] = result;\n this.job.emit('result', { job: this.job.address, sliceNumber: index, result });\n }\n }\n \n list(rangeObject) {\n let range = rangeObject && rehydrateRange(rangeObject);\n if (!range) range = new RangeObject(0, this.valuesAvailable.length);\n let sparseObjects = [];\n let min = range.start + range.step;\n let goodRange = false;\n let step = range.step;\n for (let i = range.start; i <= range.end; i+= step)\n {\n if (!goodRange && this.isAvailable(i))\n {\n min = i\n goodRange = true;\n }\n if (goodRange && !this.isAvailable(i))\n {\n if (min !== i-step)\n sparseObjects.push(new RangeObject(min, i-step, step))\n else\n sparseObjects.push(new RangeObject(min, i-step))\n goodRange = false;\n }\n }\n if (goodRange)\n {\n if (min !== range.end)\n sparseObjects.push(new RangeObject(min, range.end, step))\n else\n sparseObjects.push(new RangeObject(min, range.end))\n }\n\n try\n {\n return new SparseRangeObject(...sparseObjects);\n }\n catch\n {\n return [];\n }\n\n }\n\n async delete(rangeObject) {\n var range;\n try\n {\n range = rehydrateRange(rangeObject);\n }\n catch\n {\n range = 'all';\n }\n \n const job = this.job;\n let ks = await wallet.getId();\n const conn = new protocolV4.Connection(dcpConfig.scheduler.services.resultSubmitter.location, ks);\n\n const { success, payload } = await conn.send('deleteResult', {\n job: job.id,\n owner: job.paymentAccountKeystore.address,\n range,\n }, job.paymentAccountKeystore);\n \n if (!success)\n throw new Error('ResultHandle.delete: Could not delete slices from scheduler');\n \n await Promise.all(payload.map(r => {\n if (!r) {\n console.warn(`ResultHandle.delete: Received result was not defined (${r}), ignoring...`);\n return;\n }\n this.valuesAvailable[r] = false;\n delete this.realValues[r];\n }));\n \n await conn.close(null, true);\n return payload;\n }\n\n async stat(rangeObject) {\n const range = rehydrateRange(rangeObject);\n throw new Error(\"ResultHandle.stat not implemented\");\n }\n}\n\nObject.assign(exports, {\n ResultHandle,\n});\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/job/result-handle.js?");
4295
+ eval("/**\n * @file job/result-handle.js\n * @author Ryan Rossiter, ryan@kingsds.network\n * Robert Mirandola, <robert@kingsds.network>\n * @date June 2020\n * July 2022\n *\n * The ResultHandle acts as a proxy for a job's results, querying\n * internal results when available or the scheduler when the results\n * are not available locally.\n */\n\nconst { rehydrateRange, RangeObject, SparseRangeObject } = __webpack_require__(/*! ../range-object */ \"./src/dcp-client/range-object.js\");\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst wallet = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst { fetchURI } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst customInspectSymbol = Symbol.for('nodejs.util.inspect.custom');\n\n// Array methods that don't modify the array, will be applied to the ResultHandle's values object\nconst RH_ARRAY_METHODS = [\n 'slice', 'filter', 'concat', 'find', 'findIndex', 'indexOf', 'map', 'reduce', 'includes', 'toString', 'forEach'\n];\n\n/**\n * This class represents a handle on a job's results. It can be used for accessing the job's results, or for querying the scheduler to fetch results.\n * In addition to the properties and methods of this class, the following standard array methods are also available for accessing the available results: `slice`, `filter`, `concat`, `find`, `findIndex`, `indexOf`, `map`, `reduce`, `includes`, `toString`, and `forEach`.\n * The results can also be accessed by index (`results[i]`), but an error will be thrown if the result for that index is not yet available.\n * @access public\n */\nclass ResultHandle extends Array\n{\n constructor(job)\n {\n super();\n\n const { Job } = __webpack_require__(/*! . */ \"./src/dcp-client/job/index.js\");\n if (!(job instanceof Job))\n throw new TypeError('ResultHandle must be constructed from a Job object');\n\n /**\n * The length of the available results array.\n * @type {number}\n * @access public\n */\n this.length = 0; // overridden by the proxy, here for JSDoc purposes\n\n this.job = job;\n this.realKeys = job.jobInputData;\n this.realValues = [];\n this.valuesAvailable = [];\n\n return new Proxy(this, {\n get: (target, name) => {\n if ((typeof name === 'string' || typeof name === 'number') && Number.isInteger(parseFloat(name)))\n {\n let i = parseFloat(name);\n if (target.isAvailable(i))\n return target.realValues[i];\n else\n throw new Error(`Result ${i} is not available. It has either not been computed or you need to fetch it.`);\n } \n else if (name === 'length')\n return target.getLength();\n else if (name === 'constructor')\n return Array.constructor;\n else if (RH_ARRAY_METHODS.includes(name))\n {\n let values = target.values();\n return values[name].bind(values);\n }\n else\n {\n // only return methods on this class, don't allow access\n // to array methods other than the allowlisted ones\n let p = target.__proto__[name];\n if (typeof p === 'function') {\n return p.bind(target);\n } else {\n return p;\n }\n }\n }\n });\n }\n\n toJSON ()\n {\n return JSON.stringify(this.values());\n }\n\n isAvailable(index)\n {\n return this.valuesAvailable[index];\n }\n\n reset() {\n // quickly empty the realValues array\n this.realValues.length = this.valuesAvailable.length = 0;\n this.realValues.length = this.valuesAvailable.length =this.realKeys.length;\n }\n\n /**\n * Returns an array of input values. Will only return input values that have a completed result available.\n * @access public\n * @returns {Array<*>}\n */\n keys() {\n // Keys can be a RangeObject, faster to iterate over valuesAvailable\n return this.valuesAvailable.reduce((keysList, valueAvailable, index) => {\n if (valueAvailable) keysList.push(this.realKeys[index]);\n return keysList;\n }, []);\n }\n\n /**\n * Returns an array of results. Will only return results that have been received from the scheduler, if only one result is complete the array will contain one value.\n * @access public\n * @returns {Array<*>}\n */\n values() {\n return this.realValues.filter((v, i) => this.isAvailable(i));\n }\n\n // Aliased as length in proxy, can't use getter because Array.length is not configurable\n getLength() {\n return this.valuesAvailable.reduce((n, v) => (n + (v ? 1 : 0)), 0);\n }\n \n [customInspectSymbol]() /* overrides the output when console.log(result) is called */\n { \n return this.values(); \n }\n\n [Symbol.iterator]() {\n let index = 0;\n let values = this.values(); // use available values\n\n return {\n next: () => ({\n value: values[index++],\n done: index > values.length\n })\n };\n }\n\n /** \n * Returns an array of [input, output] pairs, in sliceNumber order.\n * Return value is undefined if the input is not an ES5 primitive.\n * @access public\n * @returns {Array<*>}\n */\n entries() {\n return this.valuesAvailable.reduce((keyValuePairs, valueAvailable, index) => {\n if (valueAvailable) keyValuePairs.push([\n String(this.realKeys[index]),\n this.realValues[index],\n ]);\n return keyValuePairs;\n }, []);\n }\n\n /** \n * Returns an Object associating input and output values where the inputs are ES5 primitive types.\n * Return value is undefined if the input is not an ES5 primitive.\n * @access public\n * @returns {object}\n */\n fromEntries() {\n return this.entries().reduce((o, [k, v]) => {\n o[k] = v; return o;\n }, {});\n }\n\n /** \n * Return the nth input value/input vector \n * @access public\n * @param {number} n Index in the input set to return the value for.\n * @returns {*} Input set value\n */\n key(n) {\n return this.realKeys[n];\n }\n\n /** \n * Return the value corresponding to the provided key \n * @access public\n * @param {*} key Corresponds to a value in the job's input set.\n * @returns {*} Result corresponding to the input value\n */\n lookupValue(key) {\n // use keys instead of _keys so we only lookup on available results\n let ind = this.keys().indexOf(key);\n if (ind === -1)\n throw new DCPError('Argument passed into the function was not found in input set', 'ERANGE');\n\n return this.values()[ind];\n }\n\n /**\n * Sends request to scheduler to fetch results, the retrieved results will be populated on this object.\n * @param {RangeObject} [rangeObject] - range object to query results\n * @param {string} [emitEvents] - if truthy, emits a `result` event for new results as they are added to the handle, and if set to 'all' emits 'resultUpdated' for duplicate results \n * @access public\n * @emits Job#resultsUpdated\n */\n async fetch(rangeObject, emitEvents) {\n const range = rangeObject && rehydrateRange(rangeObject);\n\n const job = this.job;\n \n // Fetch any existing results\n let ks = await wallet.getId();\n const conn = new protocolV4.Connection(dcpConfig.scheduler.services.resultSubmitter.location, ks);\n\n const { success, payload } = await conn.send('fetchResult', {\n job: job.id,\n owner: job.paymentAccountKeystore.address,\n range,\n }, job.paymentAccountKeystore);\n\n // Unpack results, using fetchURI to decode/fetch result URIs\n await Promise.all(payload.map(async r => {\n if (!r) {\n console.warn(`ResultHandle.fetch: Received result was not defined (${r}), ignoring...`);\n return;\n }\n\n const decodedValue = decodeURIComponent(r.value)\n try\n {\n this.realValues[r.slice - 1] = await fetchURI(decodedValue, dcpConfig.scheduler.location.origin); \n } catch (error)\n {\n // ensure the error is an actual URI parsing error and not due actual problems\n if (error instanceof TypeError)\n this.realValues[r.slice - 1] = decodedValue;\n else\n return console.warn('Error in ResultHandle.fetch():', error.message);\n }\n\n if (emitEvents)\n {\n if (!this.valuesAvailable[r.slice - 1])\n this.job.emit('result', { sliceNumber: r.slice, result: this.realValues[r.slice] });\n else if (emitEvents === 'all')\n {\n this.job.emit('resultsUpdated', { sliceNumber: r.slice }); \n this.job.emit('result', { sliceNumber: r.slice, result: this.realValues[r.slice] });\n }\n }\n this.valuesAvailable[r.slice - 1] = true;\n }));\n await conn.close(null, true);\n }\n \n /**\n * Handle adding a new result\n */\n newResult(result, index)\n {\n if (this.valuesAvailable[index])\n {\n this.realValues[index] = result;\n this.job.emit('resultsUpdated', { sliceNumber: index + 1 }); \n }\n else\n {\n this.valuesAvailable[index] = true;\n this.realValues[index] = result;\n this.job.emit('result', { job: this.job.address, sliceNumber: index + 1, result });\n }\n }\n \n list(rangeObject) {\n let range = rangeObject && rehydrateRange(rangeObject);\n if (!range) range = new RangeObject(1, this.valuesAvailable.length);\n let sparseObjects = [];\n let min = range.start + range.step;\n let goodRange = false;\n let step = range.step;\n for (let i = range.start; i <= range.end; i+= step)\n {\n if (!goodRange && this.isAvailable(i - 1))\n {\n min = i\n goodRange = true;\n }\n if (goodRange && !this.isAvailable(i - 1))\n {\n if (min !== i-step)\n sparseObjects.push(new RangeObject(min, i-step, step))\n else\n sparseObjects.push(new RangeObject(min, i-step))\n goodRange = false;\n }\n }\n if (goodRange)\n {\n if (min !== range.end)\n sparseObjects.push(new RangeObject(min, range.end, step))\n else\n sparseObjects.push(new RangeObject(min, range.end))\n }\n\n try\n {\n return new SparseRangeObject(...sparseObjects);\n }\n catch\n {\n return [];\n }\n\n }\n\n async delete(rangeObject) {\n var range;\n try\n {\n range = rehydrateRange(rangeObject);\n }\n catch\n {\n range = 'all';\n }\n \n const job = this.job;\n let ks = await wallet.getId();\n const conn = new protocolV4.Connection(dcpConfig.scheduler.services.resultSubmitter.location, ks);\n \n const { success, payload } = await conn.send('deleteResult', {\n job: job.id,\n owner: job.paymentAccountKeystore.address,\n range,\n }, job.paymentAccountKeystore);\n \n if (!success)\n throw new Error('ResultHandle.delete: Could not delete slices from scheduler');\n \n await Promise.all(payload.map(r => {\n if (!r) {\n console.warn(`ResultHandle.delete: Received result was not defined (${r}), ignoring...`);\n return;\n }\n this.valuesAvailable[r - 1] = false;\n delete this.realValues[r - 1];\n }));\n \n await conn.close(null, true);\n return payload;\n }\n\n async stat(rangeObject) {\n const range = rehydrateRange(rangeObject);\n throw new Error(\"ResultHandle.stat not implemented\");\n }\n}\n\nObject.assign(exports, {\n ResultHandle,\n});\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/job/result-handle.js?");
4250
4296
 
4251
4297
  /***/ }),
4252
4298
 
@@ -4326,7 +4372,7 @@ eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_mod
4326
4372
  \*************************************************/
4327
4373
  /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
4328
4374
 
4329
- eval("/**\n * @file /src/schedmsg/schedmsg-web.js\n * @author Ryan Rossiter, ryan@kingsds.network\n * @date March 2020\n *\n * This is the SchedMsg implementation for commands that are browser-specific\n * or have browser-specific behaviour.\n */\n\nconst { SchedMsg } = __webpack_require__(/*! ./schedmsg */ \"./src/dcp-client/schedmsg/schedmsg.js\");\n\nclass SchedMsgWeb extends SchedMsg {\n constructor(worker) {\n super(worker);\n this.modal = null;\n\n this.registerHandler('announce', this.onAnnouncement.bind(this));\n this.registerHandler('openPopup', this.onOpenPopup.bind(this));\n this.registerHandler('reload', this.onReload.bind(this));\n }\n\n onAnnouncement({ message }) {\n if (this.modal) {\n this.modal.close();\n }\n\n this.modal = window.userInterface.alert('Announcement', '' /* subtitle */, message,\n /* onClose */ () => this.modal = null);\n }\n\n onOpenPopup({ href }) {\n window.open(href);\n }\n\n onReload() {\n const hash = window.location.hash;\n\n let newUrl = window.location.href.replace(/#.*/, '');\n newUrl += (newUrl.indexOf('?') === -1 ? '?' : '&');\n newUrl += 'dcp=0bf54dbe88ee11e84b28e62b73cbd154d06967ea,' + Date.now() + hash;\n\n window.location.replace(newUrl);\n }\n}\n\nObject.assign(module.exports, {\n SchedMsgWeb\n});\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/schedmsg/schedmsg-web.js?");
4375
+ eval("/**\n * @file /src/schedmsg/schedmsg-web.js\n * @author Ryan Rossiter, ryan@kingsds.network\n * @date March 2020\n *\n * This is the SchedMsg implementation for commands that are browser-specific\n * or have browser-specific behaviour.\n */\n\nconst { SchedMsg } = __webpack_require__(/*! ./schedmsg */ \"./src/dcp-client/schedmsg/schedmsg.js\");\n\nclass SchedMsgWeb extends SchedMsg {\n constructor(worker) {\n super(worker);\n this.modal = null;\n\n this.registerHandler('announce', this.onAnnouncement.bind(this));\n this.registerHandler('openPopup', this.onOpenPopup.bind(this));\n this.registerHandler('reload', this.onReload.bind(this));\n }\n\n onAnnouncement({ message }) {\n if (this.modal) {\n this.modal.close();\n }\n\n this.modal = window.userInterface.alert('Announcement', '' /* subtitle */, message,\n /* onClose */ () => this.modal = null);\n }\n\n onOpenPopup({ href }) {\n window.open(href);\n }\n\n onReload() {\n const hash = window.location.hash;\n\n let newUrl = window.location.href.replace(/#.*/, '');\n newUrl += (newUrl.indexOf('?') === -1 ? '?' : '&');\n newUrl += 'dcp=5895f3465816246196dbdb9c0cd5aa200a943c59,' + Date.now() + hash;\n\n window.location.replace(newUrl);\n }\n}\n\nObject.assign(module.exports, {\n SchedMsgWeb\n});\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/schedmsg/schedmsg-web.js?");
4330
4376
 
4331
4377
  /***/ }),
4332
4378
 
@@ -4399,7 +4445,7 @@ eval("/**\n * @file Wallet API - perform operations related to Addresses,
4399
4445
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4400
4446
 
4401
4447
  "use strict";
4402
- eval("/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js */ \"./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js\")[\"Buffer\"];\n/**\n * @file keystore.js\n * Wallet API Keystore classes\n *\n * @author Wes Garland - wes@kingsds.network\n * @author Duncan Mays - duncan@kingsds.network\n * @author Badrdine Sabhi - badr@kingsds.network\n * @author Ryan Rossiter - ryan@kingsds.network\n * @date April 2020\n */\n\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('wallet/keystore');\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\n\nconst { unlockFailErrorCode } = __webpack_require__(/*! dcp/dcp-client/wallet/error-codes */ \"./src/dcp-client/wallet/error-codes.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\n\nlet useWasm = true;\nlet secp256k1 = null;\n\n/* Place canonical versions of ethereumjs-util and ethereumjs-wallet on\n * exports._internalEth for reference by 'friend' modules. Try to load \n * native (fast) versions on NodeJS, but fallback to internal modules\n * when not present. Modules are argumented with fast keccak\n * hashing code via wasm for internal use.\n */\nexports._internalEth = {};\n\nif ((__webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\").platform) === 'nodejs')\n{\n /* Prefer peer dependencies over webpacked versions whenever possible, \n * because they will have native crypto via scrypt.\n */\n const { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\n try {\n requireNative('bindings')({\n bindings: 'keccak.node',\n try: [\n ['module_root', 'node_modules', 'keccak', 'build', 'Release', 'bindings'], // when running from dcp\n ['module_root', '..', 'keccak', 'build', 'Release', 'bindings'], // when running from dcp-client in dcp\n ],\n });\n exports._internalEth.util = requireNative('ethereumjs-util');\n exports._internalEth.wallet = requireNative('ethereumjs-wallet').default;\n useWasm = false; /* only disable wasm when using node AND have native util, wallet available */\n } catch(e) {\n exports._internalEth.util = __webpack_require__(/*! ethereumjs-util */ \"./node_modules/ethereumjs-util/dist.browser/index.js\");\n exports._internalEth.wallet = __webpack_require__(/*! ethereumjs-wallet */ \"./node_modules/ethereumjs-wallet/dist.browser/index.js\")[\"default\"];\n };\n} else {\n exports._internalEth.util = __webpack_require__(/*! ethereumjs-util */ \"./node_modules/ethereumjs-util/dist.browser/index.js\");\n exports._internalEth.wallet = __webpack_require__(/*! ethereumjs-wallet */ \"./node_modules/ethereumjs-wallet/dist.browser/index.js\")[\"default\"];\n}\n\nconst dcpEth = __webpack_require__(/*! ./eth */ \"./src/dcp-client/wallet/eth.js\");\nconst ethUtil = exports._internalEth.util;\nconst ethWallet = exports._internalEth.wallet;\n\nconst _ConstructOnly = Symbol('Construct Only');\n \n/** Generate a new private key */\nfunction generateNewPrivateKey() {\n return new dcpEth.PrivateKey(ethWallet.generate().getPrivateKey());\n}\n\n/** Accept json from any foreign ethereum wallet [that we understand] and a passphrase,\n * returning the address and private key\n *\n * @param json {string} A JSON keystore, eg. ethereum V3 wallet\n * @param passphrase {string} The passphrase to unlock the keystore\n * @returns {Object|undefined} An object having properties address and privateKey,\n * or undefined if it couldn't be parsed\n */\nasync function parseForeignKeystore(json, passphrase) {\n var fks;\n\n try {\n fks = await ethWallet.fromV3(json, passphrase, { nonStrict: true });\n } catch(e) {\n debugging() && passphrase && console.debug('warning: incorrect passphrase or foreign keystore not in wallet-v3 format', json);\n }\n\n if (!fks) try {\n fks = await ethWallet.fromV1(json, passphrase);\n } catch(e) {}\n\n if (!fks) try {\n fks = await ethWallet.fromEthSale(json, passphrase);\n } catch(e) {}\n\n return fks && {\n address: fks.address,\n privateKey: fks.getPrivateKey(),\n };\n}\n\n/**\n * This method creates functions related to locking and unlocking Keystores which share a common set\n * of private variables and are added to the keystore as own property. The private key must never be\n * stored in a variable in dcp-client which is not part of this private scope.\n *\n * @param ks {Object} instance of keystore\n * @param ew {String} json which represents an Ethereum V3 keystore [ethereum wallet]\n */\nfunction KeystoreLockFunctionsFactory(ks, ew)\n{\n const { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\n \n var pkScope_privateKey; /* The private key when unlocked; falsey when locked */\n var pkScope_autoUnlockDuration; /* lock duration when doing auto-unlock (like sudo), or falsey */\n var pkScope_relockTime; /* when the Keystore will be considered locked again */\n var pkScope_lockTimerHnd; /* Timer handle when we have a pending automatic relock on the event queue */\n var pkScope_unlockBusy = new Synchronizer('idle', ['idle', 'busy']); /* mutex to single-thread unlock */\n var debugLabel = ks.label || ks.address;\n\n function pkScope_resetLockState() {\n debugging('timer') && console.debug(`wallet - reset lock timer state on ${debugLabel}`);\n\n if (pkScope_lockTimerHnd)\n clearTimeout(pkScope_lockTimerHnd)\n \n pkScope_privateKey = undefined;\n pkScope_autoUnlockDuration = 0;\n pkScope_relockTime = 0;\n pkScope_lockTimerHnd = undefined;\n }\n pkScope_resetLockState();\n\n /* Check the timer state, re-locking the Keystore as needed. It is\n * important to call this at the start of timer-sensitive operations, as\n * we can't rely on setTimeout to re-lock Keystores; we must consider the\n * possibility that starving the event loop could cause the private key\n * to be stored for longer than intended\n */\n function pkScope_lockIfTimerExpired() {\n debugging('timer') && console.log(`wallet - lockIfTimerExpired on ${debugLabel}`);\n if (pkScope_lockTimerHnd && pkScope_relockTime <= Date.now()) {\n pkScope_lock()\n }\n }\n \n /**\n * This method unlocks the Keystore object for either a single use or a given amount of time. When the \n * object is unlocked, `getPrivateKey` and related operations do not need to ask for a passphrase, as \n * the private key is \"remembered\" internally. If time is not specified, the object will be immediately \n * locked after the next call to `getPrivateKey`; otherwise, it will remain unlocked until the timer runs out.\n * \n * When the unlock timer is running, calling `unlock` can have multiple effects. In all cases the current\n * running timer will be increased to lockTimerDuration if that will increase the time left.\n * 1. If the autoReset flag is true for this invocation, the autoReset duration is updated only if it\n * will be increased. Thus autoReset is a sticky setting, it can be turned on but not off (except by locking)\n * 2. If the autoReset flag is false for this invocation, the current unlock duration is increased, but \n * the autoReset duration remains the same.\n * @method module:dcp/wallet.Keystore#unlock\n * @param {string} [passphrase] Passphrase to unlock keystore. If null|undefined, `passphrasePrompt` \n * function is invoked to solicit it from the user if the keystore\n * is not already unlocked.\n * @param {number} lockTimerDuration Number of seconds to keep keystore unlocked for. Intended to implement features like `sudo`\n * @param {boolean} [autoReset] Flag to determine if unlock timer should reset on subsequent calls.\n * @access public\n * @async\n * @throws Error with code 'DCP_WA:UNLOCKX' when keystore could not be unlocked.\n */\n async function pkScope_unlock(passphrase, lockTimerDuration, autoReset)\n {\n do /* spin lock to single thread this per keystore */\n {\n await pkScope_unlockBusy.until('idle');\n } while(!pkScope_unlockBusy.testAndSet('idle', 'busy'))\n\n try\n {\n pkScope_lockIfTimerExpired();\n if (!pkScope_privateKey)\n {\n async function pkScope_extractAndSetPrivateKey()\n {\n var fks;\n \n /* used by passphraseTries */\n function unlock_tryFn(passphrase)\n {\n if (pkScope_privateKey) /* unlocked by other \"thread\" - ideally would never happen... */\n return { address: ks.address, privateKey: pkScope_privateKey };\n return parseForeignKeystore(ew, passphrase);\n }\n\n /* used by passphraseTries */\n function unlock_skipCheckFn()\n {\n return !!pkScope_privateKey;\n };\n\n if (passphrase !== undefined && passphrase !== null)\n fks = await parseForeignKeystore(ew, passphrase);\n else\n {\n if (ks.checkEmpty !== false)\n fks = await parseForeignKeystore(ew, '');\n if (!fks)\n fks = await (__webpack_require__(/*! ../wallet */ \"./src/dcp-client/wallet/index.js\").passphraseTries)({ label: ks.label , address: ks.address }, unlock_tryFn, unlock_skipCheckFn);\n }\n \n if (fks)\n pkScope_privateKey = new dcpEth.PrivateKey(fks.privateKey);\n }\n\n await pkScope_extractAndSetPrivateKey();\n if (!pkScope_privateKey)\n throw new DCPError(`Could not unlock keystore '${debugLabel}'`, unlockFailErrorCode);\n }\n assert(pkScope_privateKey);\n\n /* If in autoReset mode, increase the autoUnlockDuration if a longer one was specified */\n if (autoReset && lockTimerDuration > pkScope_autoUnlockDuration)\n pkScope_autoUnlockDuration = lockTimerDuration;\n \n // If lockTimerDuration not defined, set to 0s (immediately lock)\n lockTimerDuration = lockTimerDuration ? lockTimerDuration : 0;\n /* Make this unlock expire in lockTimerDuration seconds [or later if there was a previous longer one] */\n pkScope_scheduleRelock(lockTimerDuration);\n debugging('locks') && console.log(`wallet - ${debugLabel} is unlocked`);\n }\n finally\n {\n pkScope_unlockBusy.set('busy', 'idle');\n }\n }\n\n /** \n * Lock the Keystore; make private key no longer accessible without password entry.\n * @method module:dcp/wallet.Keystore#lock\n * @access public\n */\n function pkScope_lock()\n {\n let debugMsg = `wallet - lock ${debugLabel}`;\n if (pkScope_isUnlocked()) debugMsg += ' (was not locked)';\n debugging('locks') && console.log(debugMsg);\n \n pkScope_resetLockState();\n }\n\n /**\n * Returns private key, may prompt for password. If you need this you're probably doing something wrong.\n * @method module:dcp/wallet.Keystore#getPrivateKey\n * @returns {module:dcp/wallet.PrivateKey}\n * @async\n * @access public\n */\n async function pkScope_getPrivateKey()\n {\n let pk;\n\n pkScope_lockIfTimerExpired();\n if (pkScope_isLocked()) {\n await pkScope_unlock();\n pk = pkScope_privateKey;\n pkScope_lock();\n } else {\n pk = pkScope_privateKey;\n \n if (pkScope_autoUnlockDuration > 0) {\n debugging('timer') && console.log(`wallet - auto reset new lock expire time on ${debugLabel} - ${pkScope_autoUnlockDuration * 1000}ms`);\n pkScope_scheduleRelock(pkScope_autoUnlockDuration);\n } else if (!pkScope_lockTimerHnd) {\n pkScope_lock();\n }\n }\n \n debugging('getPrivateKey') && console.log(`wallet - ${debugLabel} pk=${pk}`);\n return pk;\n }\n\n /* Schedule the next automatic lock of this keystore, overriding any earlier-firing relock\n * @param {number} newDuration seconds until the keystore gets re-locked\n */\n function pkScope_scheduleRelock(newDuration)\n {\n var newDurationMs = newDuration * 1000;\n var next_relockTime = Date.now() + newDurationMs;\n\n if (next_relockTime < pkScope_relockTime)\n return;\n \n debugging('timer') && console.log(`wallet - setting lock expire time on ${debugLabel} - ${next_relockTime}s`);\n pkScope_relockTime = next_relockTime;\n if (pkScope_lockTimerHnd)\n clearTimeout(pkScope_lockTimerHnd);\n pkScope_lockTimerHnd = setTimeout(pkScope_lock, newDurationMs);\n if (DCP_ENV.platform === 'nodejs')\n pkScope_lockTimerHnd.unref();\n }\n\n /**\n * Is the keystore presently unlocked?\n * @method module:dcp/wallet.Keystore#isUnlocked\n * @returns {boolean}\n * @access public\n */\n function pkScope_isUnlocked() {\n return !!pkScope_privateKey;\n }\n\n /**\n * Is the keystore presently locked?\n * @method module:dcp/wallet.Keystore#isLocked\n * @returns {boolean}\n * @access public\n */\n function pkScope_isLocked() {\n return !pkScope_isUnlocked();\n }\n \n ks.isLocked = pkScope_isLocked;\n ks.isUnlocked = pkScope_isUnlocked;\n ks.unlock = pkScope_unlock;\n ks.lock = pkScope_lock;\n ks.getPrivateKey = pkScope_getPrivateKey;\n}\n\n/**\n * Form 1\n * \n * A Keystore object with a randomly generated privateKey and an address\n * that corresponds to it. This form will prompt the user for a password to encrypt itself with.\n * @async\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 2\n * \n * A Keystore object with the provided privateKey and an address that \n * corresponds to it. This form will prompt the user for a password to encrypt itself with.\n * @async\n * @param {string|object} privateKey an Ethereum v3 Keystore object, or privateKey in hex string format\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 3\n * \n * A Keystore object with the provided privateKey and an address that \n * corresponds to it, encrypted with the provided passphrase.\n * @async\n * @param {string|object} privateKey an Ethereum v3 Keystore object, or privateKey in hex string format\n * @param {string} passphrase\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 4\n * \n * A Keystore object parsed from the third party object. If the third party\n * object cannot be parsed the promise will reject.\n * @async\n * @param {object} privateKey an Ethereum v3 Keystore object\n * @param {string} passphrase\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 5\n * \n * A Keystore object parsed from the JSON string. If the JSON or resulting\n * object cannot be parsed the promise will reject.\n * @async\n * @param {string} JsonString - a JSON encoded keystore of some sort\n * @param {string} passphrase\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 6\n * \n * A Keystore object parsed from the third party object. If the third party\n * object cannot be parsed the promise will reject. The user will be prompted for a passphrase.\n * @async\n * @param {object} privateKey Ethereum v3 Keystore object\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 7\n * \n * A Keystore object with a randomly generated privateKey and an address\n * that corresponds to it. This form will used the supplied passphrase to encrypt the private key.\n * @async\n * @param {null} key unused in this form, when null it will be generated.\n * @param {string|null} passphrase the passphrase used to encrypt the new keystore\n * @returns Promise which resolves to {module:dcp/wallet.Keystore}\n * @access public\n * @example\n * let ks = await new Keystore(null, '');\n */\nexports.Keystore = function wallet$$Keystore() {\n if (arguments[0] === _ConstructOnly)\n return;\n\n let ctor = {}\n let ew /* ethereum wallet: (json) backing store for encrypted private keys */\n \n /**\n * Generate JSON object literal representation of this keystore. This can later be passed to\n * the constructor to generate a new Keystore object.\n * \n \n \n * @access public\n * @returns {object}\n * @function module:dcp/wallet.Keystore#toJSON\n */\n this.toJSON = function wallet$$Keystore$toJSON() {\n let base = Object.assign({}, ctor.ks || JSON.parse(ew));\n\n if (this.label)\n base.label = this.label;\n \n return base;\n }\n\n return (async () => {\n const { promptCreatePassphrase } = __webpack_require__(/*! ../wallet */ \"./src/dcp-client/wallet/index.js\");\n \n if (arguments.length >= 2)\n ctor.passphrase = arguments[1] /* all forms which accept passphrases */\n \n if (arguments.length === 3)\n ctor.checkEmpty = arguments[2] /* for get form 4, setting checkempty to false */\n \n if (arguments.length === 0) {\n ctor.privateKey = generateNewPrivateKey(); /* form 1 */\n this.address = ctor.privateKey.toAddress();\n const metaData = {\n label: \"New key\",\n address: this.address,\n };\n ctor.passphrase = await promptCreatePassphrase(metaData);\n }\n else if (arguments[0] === null) /* form 7 */\n ctor.privateKey = generateNewPrivateKey();\n else if (dcpEth.PrivateKey(arguments[0], true)) { /* form 2, 3 */\n ctor.privateKey = new dcpEth.PrivateKey(arguments[0]);\n this.address = ctor.privateKey.toAddress();\n if (ctor.passphrase === false)\n ctor.passphrase = '';\n if (typeof ctor.passphrase === 'undefined') {\n const metaData = {\n label: \"New key\",\n address: this.address,\n };\n ctor.passphrase = await promptCreatePassphrase(metaData); \n }\n }\n else if (arguments[0] instanceof exports.Keystore) { /* form 6 */\n ctor.privateKey = await arguments[0].getPrivateKey();\n ew = JSON.stringify(arguments[0]);\n }\n else if (typeof arguments[0] === 'string' ||\n typeof arguments[0] === 'object') { /* form 4, 5 */\n let jsonO\n\n if (typeof arguments[0] === 'object') { /* form 4 */\n ew = JSON.stringify(arguments[0])\n jsonO = arguments[0]\n } else { /* form 5 */\n ew = arguments[0]\n jsonO = JSON.parse(ew.trim())\n }\n if (jsonO.address)\n this.address = new dcpEth.Address(jsonO.address)\n else {\n let fks\n \n if (ctor.passphrase)\n fks = await parseForeignKeystore(ew, ctor.passphrase)\n else {\n if (ctor.checkEmpty !== false)\n fks = await parseForeignKeystore(ew, '')\n if (!fks)\n {\n /* ks has password */\n let metaData = { label: jsonO.label || jsonO.id };\n fks = await (__webpack_require__(/*! ../wallet */ \"./src/dcp-client/wallet/index.js\").passphraseTries)(metaData,\n async function wallet$$Keystore$Constructor$unlock_tryFn(passphrase) {\n await parseForeignKeystore(ew, passphrase)\n })\n }\n }\n\n if (!fks)\n throw new DCPError(`Could not unlock foreign keystore ${jsonO.label || jsonO.id} to determine public address`, unlockFailErrorCode);\n this.address = fks.address\n }\n if (jsonO.label)\n this.label = jsonO.label\n }\n\n /* By the bottom of this funnel, we have the \n * - defined this.address\n * - dcpEth.PrivateKey or a ethereum wallet (JSON string)\n * - passphrase or '' if making a Keystore from a dcpEth.PrivateKey\n *\n * Now we make sure that we can find the private key by unlocking ew, \n * have an address, and forget the private key\n */\n if (!ew) {\n let buf = ethUtil.toBuffer(ctor.privateKey);\n let i = ethWallet.fromPrivateKey(buf);\n ew = await i.toV3String(ctor.passphrase || '', { n: 1024 });\n }\n if (ctor.privateKey && !this.address) {\n this.address = ctor.privateKey.toAddress()\n }\n this.checkEmpty = ctor.checkEmpty;\n delete ctor.privateKey\n delete ctor.passphrase\n KeystoreLockFunctionsFactory(this, ew)\n\n return this\n })()\n}\n\n/**\n * Creates and returns stringification of SignedMessageObject in the format:\n * JSON.stringify({ owner: this.address, signature: signature, body: messageBody })\n * @async\n * @access public\n * @param {object} messageBody \n * @returns {Promise<string>}\n * @function module:dcp/wallet.Keystore#makeSignedMessage\n */\nexports.Keystore.prototype.makeSignedMessage = async function wallet$$Keystore$makeSignedMessage(messageBody) {\n return JSON.stringify(await this.makeSignedMessageObject(messageBody));\n}\n\n/** @typedef {object} SignedMessageObject\n * @property {object} body\n * @property {Signature} signature\n * @property {module:dcp/wallet.Address} owner\n */\n\n/**\n * Creates and returns stringification of SignedMessageObject in the format:\n * { owner: this.address, signature: signature, body: messageBody }\n * @async\n * @access public\n * @param {object} messageBody \n * @returns {Promise<SignedMessageObject>}\n * @function module:dcp/wallet.Keystore#makeSignedMessageObject\n */\nexports.Keystore.prototype.makeSignedMessageObject = async function wallet$$Keystore$makeSignedMessageObject(messageBody) {\n const signature = await this.makeSignature(messageBody);\n return { owner: this.address, signature: signature, body: messageBody };\n}\n\n/** @typedef {object} SignedLightWeightMessageObject\n * @property {object} body\n * @property {object} auth\n * @property {Signature} signature\n * @property {module:dcp/wallet.Address} owner\n */\n \n/**\n * Creates and returns SignedLightWeightMessageObject in the format:\n * { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n * The signature is computed only on messageLightWeight, in order to improve perf when messageBody is large.\n * @async\n * @access public\n * @param {object} messageLightWeight \n * @param {object} messageBody \n * @returns {Promise<SignedLightWeightMessageObject>}\n * @function module:dcp/wallet.Keystore#makeSignedLightWeightMessageObject\n */\nexports.Keystore.prototype.makeSignedLightWeightMessageObject = async function wallet$$Keystore$makeSignedLightWeightMessageObject(messageLightWeight, messageBody) {\n const signature = await this.makeSignature(messageLightWeight);\n return { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n}\n \n/** @typedef {object} Signature\n * @property {Uint8Array} r\n * @property {Uint8Array} s\n * @property {Uint8Array} v\n */\n\n/**\n * Creates and returns signature for messageBody\n * { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n * The signature is computed only on messageLightWeight, in order to improve perf when messageBody is large.\n * @async\n * @access public\n * @param {object} messageBody \n * @returns {Promise<Signature>}\n * @function module:dcp/wallet.Keystore#makeSignature\n */\nexports.Keystore.prototype.makeSignature = async function wallet$$Keystore$makeSignature(messageBody)\n{\n const { messageToBuffer } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n \n if (typeof messageBody !== 'string') {\n messageBody = JSON.stringify(messageBody);\n }\n const privateKey = await this.getPrivateKey();\n \n if (useWasm && !secp256k1) {\n const { instantiateSecp256k1 } = __webpack_require__(/*! bitcoin-ts */ \"./node_modules/bitcoin-ts/build/module/index.js\"); \n secp256k1 = await instantiateSecp256k1();\n } \n const signFn = useWasm ? exports.ecsign : ethUtil.ecsign;\n const signature = signFn(ethUtil.hashPersonalMessage(messageToBuffer(messageBody)), ethUtil.toBuffer(privateKey.toString()));\n \n return signature;\n}\n\n/**\n * See {@link module:dcp/wallet.Keystore|Keystore constructor} for documentation on all the ways to call this constructor.\n * @classdesc Subclass of {@link module:dcp/wallet.Keystore|Keystore}\n * @class IdKeystore\n * @async\n * @extends module:dcp/wallet.Keystore\n * @access public\n */\nexports.IdKeystore = function walletAPI$$IdKeystore(privateKey, passphrase) { \n return this.constructor.prototype.constructor.apply(this, arguments);\n}\n\n/**\n * See {@link module:dcp/wallet.Keystore|Keystore constructor} for documentation on all the ways to call this constructor.\n * @classdesc Subclass of {@link module:dcp/wallet.Keystore|Keystore}\n * @class AuthKeystore\n * @async\n * @extends module:dcp/wallet.Keystore\n * @access public\n */\nexports.AuthKeystore = function walletAPI$$AuthKeystore(privateKey, passphrase) {\n return this.constructor.prototype.constructor.apply(this, arguments);\n}\n\nexports.IdKeystore.prototype = new exports.Keystore(_ConstructOnly);\nexports.AuthKeystore.prototype = new exports.Keystore(_ConstructOnly);\n\nexports.ecsign = async function (msgHash, privateKey) {\n var sig = secp256k1.signMessageHashRecoverableCompact(privateKey, msgHash);\n sig.signature = Buffer.from(sig.signature, 'Uint8Array');\n\n var ret = {};\n ret.r = sig.signature.slice(0, 32);\n ret.s = sig.signature.slice(32, 64);\n ret.v = sig.recoveryId + 27;\n \n return ret;\n}; \n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/wallet/keystore.js?");
4448
+ eval("/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js */ \"./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js\")[\"Buffer\"];\n/**\n * @file keystore.js\n * Wallet API Keystore classes\n *\n * @author Wes Garland - wes@kingsds.network\n * @author Duncan Mays - duncan@kingsds.network\n * @author Badrdine Sabhi - badr@kingsds.network\n * @author Ryan Rossiter - ryan@kingsds.network\n * @date April 2020\n */\n\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('wallet/keystore');\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\n\nconst { unlockFailErrorCode } = __webpack_require__(/*! dcp/dcp-client/wallet/error-codes */ \"./src/dcp-client/wallet/error-codes.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\n\nlet useWasm = true;\nlet secp256k1 = null;\n\n/* Place canonical versions of ethereumjs-util and ethereumjs-wallet on\n * exports._internalEth for reference by 'friend' modules. Try to load \n * native (fast) versions on NodeJS, but fallback to internal modules\n * when not present. Modules are argumented with fast keccak\n * hashing code via wasm for internal use.\n */\nexports._internalEth = {};\n\nif ((__webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\").platform) === 'nodejs')\n{\n /* Prefer peer dependencies over webpacked versions whenever possible, \n * because they will have native crypto via scrypt.\n */\n const { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\n try {\n requireNative('bindings')({\n bindings: 'keccak.node',\n try: [\n ['module_root', 'node_modules', 'keccak', 'build', 'Release', 'bindings'], // when running from dcp\n ['module_root', '..', 'keccak', 'build', 'Release', 'bindings'], // when running from dcp-client in dcp\n ],\n });\n exports._internalEth.util = requireNative('ethereumjs-util');\n exports._internalEth.wallet = requireNative('ethereumjs-wallet').default;\n useWasm = false; /* only disable wasm when using node AND have native util, wallet available */\n } catch(e) {\n exports._internalEth.util = __webpack_require__(/*! ethereumjs-util */ \"./node_modules/ethereumjs-util/dist.browser/index.js\");\n exports._internalEth.wallet = __webpack_require__(/*! ethereumjs-wallet */ \"./node_modules/ethereumjs-wallet/dist.browser/index.js\")[\"default\"];\n };\n} else {\n exports._internalEth.util = __webpack_require__(/*! ethereumjs-util */ \"./node_modules/ethereumjs-util/dist.browser/index.js\");\n exports._internalEth.wallet = __webpack_require__(/*! ethereumjs-wallet */ \"./node_modules/ethereumjs-wallet/dist.browser/index.js\")[\"default\"];\n}\n\nconst dcpEth = __webpack_require__(/*! ./eth */ \"./src/dcp-client/wallet/eth.js\");\nconst ethUtil = exports._internalEth.util;\nconst ethWallet = exports._internalEth.wallet;\n\nconst _ConstructOnly = Symbol('Construct Only');\n \n/** Generate a new private key */\nfunction generateNewPrivateKey() {\n return new dcpEth.PrivateKey(ethWallet.generate().getPrivateKey());\n}\n\n/** Accept json from any foreign ethereum wallet [that we understand] and a passphrase,\n * returning the address and private key\n *\n * @param json {string} A JSON keystore, eg. ethereum V3 wallet\n * @param passphrase {string} The passphrase to unlock the keystore\n * @returns {Object|undefined} An object having properties address and privateKey,\n * or undefined if it couldn't be parsed\n */\nasync function parseForeignKeystore(json, passphrase) {\n var fks;\n\n try {\n fks = await ethWallet.fromV3(json, passphrase, { nonStrict: true });\n } catch(e) {\n debugging() && passphrase && console.debug('warning: incorrect passphrase or foreign keystore not in wallet-v3 format', json);\n }\n\n if (!fks) try {\n fks = await ethWallet.fromV1(json, passphrase);\n } catch(e) {}\n\n if (!fks) try {\n fks = await ethWallet.fromEthSale(json, passphrase);\n } catch(e) {}\n\n return fks && {\n address: fks.address,\n privateKey: fks.getPrivateKey(),\n };\n}\n\n/**\n * This method creates functions related to locking and unlocking Keystores which share a common set\n * of private variables and are added to the keystore as own property. The private key must never be\n * stored in a variable in dcp-client which is not part of this private scope.\n *\n * @param ks {Object} instance of keystore\n * @param ew {String} json which represents an Ethereum V3 keystore [ethereum wallet]\n */\nfunction KeystoreLockFunctionsFactory(ks, ew)\n{\n const { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\n \n var pkScope_privateKey; /* The private key when unlocked; falsey when locked */\n var pkScope_autoUnlockDuration; /* lock duration when doing auto-unlock (like sudo), or falsey */\n var pkScope_relockTime; /* when the Keystore will be considered locked again */\n var pkScope_lockTimerHnd; /* Timer handle when we have a pending automatic relock on the event queue */\n var pkScope_unlockBusy = new Synchronizer('idle', ['idle', 'busy']); /* mutex to single-thread unlock */\n var debugLabel = ks.label || ks.address;\n\n function pkScope_resetLockState() {\n debugging('timer') && console.debug(`wallet - reset lock timer state on ${debugLabel}`);\n\n if (pkScope_lockTimerHnd)\n clearTimeout(pkScope_lockTimerHnd)\n \n pkScope_privateKey = undefined;\n pkScope_autoUnlockDuration = 0;\n pkScope_relockTime = 0;\n pkScope_lockTimerHnd = undefined;\n }\n pkScope_resetLockState();\n\n /* Check the timer state, re-locking the Keystore as needed. It is\n * important to call this at the start of timer-sensitive operations, as\n * we can't rely on setTimeout to re-lock Keystores; we must consider the\n * possibility that starving the event loop could cause the private key\n * to be stored for longer than intended\n */\n function pkScope_lockIfTimerExpired() {\n debugging('timer') && console.log(`wallet - lockIfTimerExpired on ${debugLabel}`);\n if (pkScope_lockTimerHnd && pkScope_relockTime <= Date.now()) {\n pkScope_lock()\n }\n }\n \n /**\n * This method unlocks the Keystore object for either a single use or a given amount of time. When the \n * object is unlocked, `getPrivateKey` and related operations do not need to ask for a passphrase, as \n * the private key is \"remembered\" internally. If time is not specified, the object will be immediately \n * locked after the next call to `getPrivateKey`; otherwise, it will remain unlocked until the timer runs out.\n * \n * When the unlock timer is running, calling `unlock` can have multiple effects. In all cases the current\n * running timer will be increased to lockTimerDuration if that will increase the time left.\n * 1. If the autoReset flag is true for this invocation, the autoReset duration is updated only if it\n * will be increased. Thus autoReset is a sticky setting, it can be turned on but not off (except by locking)\n * 2. If the autoReset flag is false for this invocation, the current unlock duration is increased, but \n * the autoReset duration remains the same.\n * @method module:dcp/wallet.Keystore#unlock\n * @param {string} [passphrase] Passphrase to unlock keystore. If null|undefined, `passphrasePrompt` \n * function is invoked to solicit it from the user if the keystore\n * is not already unlocked.\n * @param {number} lockTimerDuration Number of seconds to keep keystore unlocked for. Intended to implement features like `sudo`\n * @param {boolean} [autoReset] Flag to determine if unlock timer should reset on subsequent calls.\n * @access public\n * @async\n * @throws Error with code 'DCP_WA:UNLOCKX' when keystore could not be unlocked.\n */\n async function pkScope_unlock(passphrase, lockTimerDuration, autoReset)\n {\n do /* spin lock to single thread this per keystore */\n {\n await pkScope_unlockBusy.until('idle');\n } while(!pkScope_unlockBusy.testAndSet('idle', 'busy'))\n\n try\n {\n pkScope_lockIfTimerExpired();\n if (!pkScope_privateKey)\n {\n async function pkScope_extractAndSetPrivateKey()\n {\n var fks;\n \n /* used by passphraseTries */\n function unlock_tryFn(passphrase)\n {\n if (pkScope_privateKey) /* unlocked by other \"thread\" - ideally would never happen... */\n return { address: ks.address, privateKey: pkScope_privateKey };\n return parseForeignKeystore(ew, passphrase);\n }\n\n /* used by passphraseTries */\n function unlock_skipCheckFn()\n {\n return !!pkScope_privateKey;\n };\n\n if (passphrase !== undefined && passphrase !== null)\n fks = await parseForeignKeystore(ew, passphrase);\n else\n {\n if (ks.checkEmpty !== false)\n fks = await parseForeignKeystore(ew, '');\n if (!fks)\n fks = await (__webpack_require__(/*! ../wallet */ \"./src/dcp-client/wallet/index.js\").passphraseTries)({ label: ks.label , address: ks.address }, unlock_tryFn, unlock_skipCheckFn);\n }\n \n if (fks)\n pkScope_privateKey = new dcpEth.PrivateKey(fks.privateKey);\n }\n\n await pkScope_extractAndSetPrivateKey();\n if (!pkScope_privateKey)\n throw new DCPError(`Could not unlock keystore '${debugLabel}'`, unlockFailErrorCode);\n }\n assert(pkScope_privateKey);\n\n /* If in autoReset mode, increase the autoUnlockDuration if a longer one was specified */\n if (autoReset && lockTimerDuration > pkScope_autoUnlockDuration)\n pkScope_autoUnlockDuration = lockTimerDuration;\n \n /* Make this unlock expire in lockTimerDuration seconds [or later if there was a previous longer one] */\n if (lockTimerDuration >= 0)\n {\n pkScope_scheduleRelock(lockTimerDuration);\n }\n debugging('locks') && console.log(`wallet - ${debugLabel} is unlocked`);\n }\n finally\n {\n pkScope_unlockBusy.set('busy', 'idle');\n }\n }\n\n /** \n * Lock the Keystore; make private key no longer accessible without password entry.\n * @method module:dcp/wallet.Keystore#lock\n * @access public\n */\n function pkScope_lock()\n {\n let debugMsg = `wallet - lock ${debugLabel}`;\n if (pkScope_isUnlocked()) debugMsg += ' (was not locked)';\n debugging('locks') && console.log(debugMsg);\n \n pkScope_resetLockState();\n }\n\n /**\n * Returns private key, may prompt for password. If you need this you're probably doing something wrong.\n * @method module:dcp/wallet.Keystore#getPrivateKey\n * @returns {module:dcp/wallet.PrivateKey}\n * @async\n * @access public\n */\n async function pkScope_getPrivateKey()\n {\n let pk;\n\n pkScope_lockIfTimerExpired();\n if (pkScope_isLocked()) {\n await pkScope_unlock();\n pk = pkScope_privateKey;\n pkScope_lock();\n } else {\n pk = pkScope_privateKey;\n \n if (pkScope_autoUnlockDuration > 0) {\n debugging('timer') && console.log(`wallet - auto reset new lock expire time on ${debugLabel} - ${pkScope_autoUnlockDuration * 1000}ms`);\n pkScope_scheduleRelock(pkScope_autoUnlockDuration);\n } else if (!pkScope_lockTimerHnd) {\n pkScope_lock();\n }\n }\n \n debugging('getPrivateKey') && console.log(`wallet - ${debugLabel} pk=${pk}`);\n return pk;\n }\n\n /* Schedule the next automatic lock of this keystore, overriding any earlier-firing relock\n * @param {number} newDuration seconds until the keystore gets re-locked\n */\n function pkScope_scheduleRelock(newDuration)\n {\n var newDurationMs = newDuration * 1000;\n var next_relockTime = Date.now() + newDurationMs;\n\n if (next_relockTime < pkScope_relockTime)\n return;\n \n debugging('timer') && console.log(`wallet - setting lock expire time on ${debugLabel} - ${next_relockTime}s`);\n pkScope_relockTime = next_relockTime;\n if (pkScope_lockTimerHnd)\n clearTimeout(pkScope_lockTimerHnd);\n pkScope_lockTimerHnd = setTimeout(pkScope_lock, newDurationMs);\n if (DCP_ENV.platform === 'nodejs')\n pkScope_lockTimerHnd.unref();\n }\n\n /**\n * Is the keystore presently unlocked?\n * @method module:dcp/wallet.Keystore#isUnlocked\n * @returns {boolean}\n * @access public\n */\n function pkScope_isUnlocked() {\n return !!pkScope_privateKey;\n }\n\n /**\n * Is the keystore presently locked?\n * @method module:dcp/wallet.Keystore#isLocked\n * @returns {boolean}\n * @access public\n */\n function pkScope_isLocked() {\n return !pkScope_isUnlocked();\n }\n \n ks.isLocked = pkScope_isLocked;\n ks.isUnlocked = pkScope_isUnlocked;\n ks.unlock = pkScope_unlock;\n ks.lock = pkScope_lock;\n ks.getPrivateKey = pkScope_getPrivateKey;\n}\n\n/**\n * Form 1\n * \n * A Keystore object with a randomly generated privateKey and an address\n * that corresponds to it. This form will prompt the user for a password to encrypt itself with.\n * @async\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 2\n * \n * A Keystore object with the provided privateKey and an address that \n * corresponds to it. This form will prompt the user for a password to encrypt itself with.\n * @async\n * @param {string|object} privateKey an Ethereum v3 Keystore object, or privateKey in hex string format\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 3\n * \n * A Keystore object with the provided privateKey and an address that \n * corresponds to it, encrypted with the provided passphrase.\n * @async\n * @param {string|object} privateKey an Ethereum v3 Keystore object, or privateKey in hex string format\n * @param {string} passphrase\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 4\n * \n * A Keystore object parsed from the third party object. If the third party\n * object cannot be parsed the promise will reject.\n * @async\n * @param {object} privateKey an Ethereum v3 Keystore object\n * @param {string} passphrase\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 5\n * \n * A Keystore object parsed from the JSON string. If the JSON or resulting\n * object cannot be parsed the promise will reject.\n * @async\n * @param {string} JsonString - a JSON encoded keystore of some sort\n * @param {string} passphrase\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 6\n * \n * A Keystore object parsed from the third party object. If the third party\n * object cannot be parsed the promise will reject. The user will be prompted for a passphrase.\n * @async\n * @param {object} privateKey Ethereum v3 Keystore object\n * @returns {module:dcp/wallet.Keystore}\n * @access public\n */\n/**\n * Form 7\n * \n * A Keystore object with a randomly generated privateKey and an address\n * that corresponds to it. This form will used the supplied passphrase to encrypt the private key.\n * @async\n * @param {null} key unused in this form, when null it will be generated.\n * @param {string|null} passphrase the passphrase used to encrypt the new keystore\n * @returns Promise which resolves to {module:dcp/wallet.Keystore}\n * @access public\n * @example\n * let ks = await new Keystore(null, '');\n */\nexports.Keystore = function wallet$$Keystore() {\n if (arguments[0] === _ConstructOnly)\n return;\n\n let ctor = {}\n let ew /* ethereum wallet: (json) backing store for encrypted private keys */\n \n /**\n * Generate JSON object literal representation of this keystore. This can later be passed to\n * the constructor to generate a new Keystore object.\n * \n \n \n * @access public\n * @returns {object}\n * @function module:dcp/wallet.Keystore#toJSON\n */\n this.toJSON = function wallet$$Keystore$toJSON() {\n let base = Object.assign({}, ctor.ks || JSON.parse(ew));\n\n if (this.label)\n base.label = this.label;\n \n return base;\n }\n\n return (async () => {\n const { promptCreatePassphrase } = __webpack_require__(/*! ../wallet */ \"./src/dcp-client/wallet/index.js\");\n \n if (arguments.length >= 2)\n ctor.passphrase = arguments[1] /* all forms which accept passphrases */\n \n if (arguments.length === 3)\n ctor.checkEmpty = arguments[2] /* for get form 4, setting checkempty to false */\n \n if (arguments.length === 0) {\n ctor.privateKey = generateNewPrivateKey(); /* form 1 */\n this.address = ctor.privateKey.toAddress();\n const metaData = {\n label: \"New key\",\n address: this.address,\n };\n ctor.passphrase = await promptCreatePassphrase(metaData);\n }\n else if (arguments[0] === null) /* form 7 */\n ctor.privateKey = generateNewPrivateKey();\n else if (dcpEth.PrivateKey(arguments[0], true)) { /* form 2, 3 */\n ctor.privateKey = new dcpEth.PrivateKey(arguments[0]);\n this.address = ctor.privateKey.toAddress();\n if (ctor.passphrase === false)\n ctor.passphrase = '';\n if (typeof ctor.passphrase === 'undefined') {\n const metaData = {\n label: \"New key\",\n address: this.address,\n };\n ctor.passphrase = await promptCreatePassphrase(metaData); \n }\n }\n else if (arguments[0] instanceof exports.Keystore) { /* form 6 */\n ctor.privateKey = await arguments[0].getPrivateKey();\n ew = JSON.stringify(arguments[0]);\n }\n else if (typeof arguments[0] === 'string' ||\n typeof arguments[0] === 'object') { /* form 4, 5 */\n let jsonO\n\n if (typeof arguments[0] === 'object') { /* form 4 */\n ew = JSON.stringify(arguments[0])\n jsonO = arguments[0]\n } else { /* form 5 */\n ew = arguments[0]\n jsonO = JSON.parse(ew.trim())\n }\n if (jsonO.address)\n this.address = new dcpEth.Address(jsonO.address)\n else {\n let fks\n \n if (ctor.passphrase)\n fks = await parseForeignKeystore(ew, ctor.passphrase)\n else {\n if (ctor.checkEmpty !== false)\n fks = await parseForeignKeystore(ew, '')\n if (!fks)\n {\n /* ks has password */\n let metaData = { label: jsonO.label || jsonO.id };\n fks = await (__webpack_require__(/*! ../wallet */ \"./src/dcp-client/wallet/index.js\").passphraseTries)(metaData,\n async function wallet$$Keystore$Constructor$unlock_tryFn(passphrase) {\n await parseForeignKeystore(ew, passphrase)\n })\n }\n }\n\n if (!fks)\n throw new DCPError(`Could not unlock foreign keystore ${jsonO.label || jsonO.id} to determine public address`, unlockFailErrorCode);\n this.address = fks.address\n }\n if (jsonO.label)\n this.label = jsonO.label\n }\n\n /* By the bottom of this funnel, we have the \n * - defined this.address\n * - dcpEth.PrivateKey or a ethereum wallet (JSON string)\n * - passphrase or '' if making a Keystore from a dcpEth.PrivateKey\n *\n * Now we make sure that we can find the private key by unlocking ew, \n * have an address, and forget the private key\n */\n if (!ew) {\n let buf = ethUtil.toBuffer(ctor.privateKey);\n let i = ethWallet.fromPrivateKey(buf);\n ew = await i.toV3String(ctor.passphrase || '', { n: 1024 });\n }\n if (ctor.privateKey && !this.address) {\n this.address = ctor.privateKey.toAddress()\n }\n this.checkEmpty = ctor.checkEmpty;\n delete ctor.privateKey\n delete ctor.passphrase\n KeystoreLockFunctionsFactory(this, ew)\n\n return this\n })()\n}\n\n/**\n * Creates and returns stringification of SignedMessageObject in the format:\n * JSON.stringify({ owner: this.address, signature: signature, body: messageBody })\n * @async\n * @access public\n * @param {object} messageBody \n * @returns {Promise<string>}\n * @function module:dcp/wallet.Keystore#makeSignedMessage\n */\nexports.Keystore.prototype.makeSignedMessage = async function wallet$$Keystore$makeSignedMessage(messageBody) {\n return JSON.stringify(await this.makeSignedMessageObject(messageBody));\n}\n\n/** @typedef {object} SignedMessageObject\n * @property {object} body\n * @property {Signature} signature\n * @property {module:dcp/wallet.Address} owner\n */\n\n/**\n * Creates and returns stringification of SignedMessageObject in the format:\n * { owner: this.address, signature: signature, body: messageBody }\n * @async\n * @access public\n * @param {object} messageBody \n * @returns {Promise<SignedMessageObject>}\n * @function module:dcp/wallet.Keystore#makeSignedMessageObject\n */\nexports.Keystore.prototype.makeSignedMessageObject = async function wallet$$Keystore$makeSignedMessageObject(messageBody) {\n const signature = await this.makeSignature(messageBody);\n return { owner: this.address, signature: signature, body: messageBody };\n}\n\n/** @typedef {object} SignedLightWeightMessageObject\n * @property {object} body\n * @property {object} auth\n * @property {Signature} signature\n * @property {module:dcp/wallet.Address} owner\n */\n \n/**\n * Creates and returns SignedLightWeightMessageObject in the format:\n * { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n * The signature is computed only on messageLightWeight, in order to improve perf when messageBody is large.\n * @async\n * @access public\n * @param {object} messageLightWeight \n * @param {object} messageBody \n * @returns {Promise<SignedLightWeightMessageObject>}\n * @function module:dcp/wallet.Keystore#makeSignedLightWeightMessageObject\n */\nexports.Keystore.prototype.makeSignedLightWeightMessageObject = async function wallet$$Keystore$makeSignedLightWeightMessageObject(messageLightWeight, messageBody) {\n const signature = await this.makeSignature(messageLightWeight);\n return { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n}\n \n/** @typedef {object} Signature\n * @property {Uint8Array} r\n * @property {Uint8Array} s\n * @property {Uint8Array} v\n */\n\n/**\n * Creates and returns signature for messageBody\n * { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n * The signature is computed only on messageLightWeight, in order to improve perf when messageBody is large.\n * @async\n * @access public\n * @param {object} messageBody \n * @returns {Promise<Signature>}\n * @function module:dcp/wallet.Keystore#makeSignature\n */\nexports.Keystore.prototype.makeSignature = async function wallet$$Keystore$makeSignature(messageBody)\n{\n const { messageToBuffer } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n \n if (typeof messageBody !== 'string') {\n messageBody = JSON.stringify(messageBody);\n }\n const privateKey = await this.getPrivateKey();\n \n if (useWasm && !secp256k1) {\n const { instantiateSecp256k1 } = __webpack_require__(/*! bitcoin-ts */ \"./node_modules/bitcoin-ts/build/module/index.js\"); \n secp256k1 = await instantiateSecp256k1();\n } \n const signFn = useWasm ? exports.ecsign : ethUtil.ecsign;\n const signature = signFn(ethUtil.hashPersonalMessage(messageToBuffer(messageBody)), ethUtil.toBuffer(privateKey.toString()));\n \n return signature;\n}\n\n/**\n * See {@link module:dcp/wallet.Keystore|Keystore constructor} for documentation on all the ways to call this constructor.\n * @classdesc Subclass of {@link module:dcp/wallet.Keystore|Keystore}\n * @class IdKeystore\n * @async\n * @extends module:dcp/wallet.Keystore\n * @access public\n */\nexports.IdKeystore = function walletAPI$$IdKeystore(privateKey, passphrase) { \n return this.constructor.prototype.constructor.apply(this, arguments);\n}\n\n/**\n * See {@link module:dcp/wallet.Keystore|Keystore constructor} for documentation on all the ways to call this constructor.\n * @classdesc Subclass of {@link module:dcp/wallet.Keystore|Keystore}\n * @class AuthKeystore\n * @async\n * @extends module:dcp/wallet.Keystore\n * @access public\n */\nexports.AuthKeystore = function walletAPI$$AuthKeystore(privateKey, passphrase) {\n return this.constructor.prototype.constructor.apply(this, arguments);\n}\n\nexports.IdKeystore.prototype = new exports.Keystore(_ConstructOnly);\nexports.AuthKeystore.prototype = new exports.Keystore(_ConstructOnly);\n\nexports.ecsign = async function (msgHash, privateKey) {\n var sig = secp256k1.signMessageHashRecoverableCompact(privateKey, msgHash);\n sig.signature = Buffer.from(sig.signature, 'Uint8Array');\n\n var ret = {};\n ret.r = sig.signature.slice(0, 32);\n ret.s = sig.signature.slice(32, 64);\n ret.v = sig.recoveryId + 27;\n \n return ret;\n}; \n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/wallet/keystore.js?");
4403
4449
 
4404
4450
  /***/ }),
4405
4451
 
@@ -4471,7 +4517,7 @@ eval("/**\n * @file node-localExec.js Node-specific support for cre
4471
4517
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4472
4518
 
4473
4519
  "use strict";
4474
- eval("/**\n * @file This module implements the Worker API, used to create workers for earning DCCs.\n * @author Ryan Rossiter <ryan@kingsds.network>\n * Paul <paul@kingsds.network>\n * @date May 2020\n * June, July 2022\n * \n * @module dcp/worker\n * @access public\n */\n// @ts-check\n\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { SchedMsg } = __webpack_require__(/*! dcp/dcp-client/schedmsg */ \"./src/dcp-client/schedmsg/index.js\");\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { localStorage } = __webpack_require__(/*! dcp/common/dcp-localstorage */ \"./src/common/dcp-localstorage.js\");\nconst { confirmPrompt } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst { Keystore, Address } = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\n\n\n// To use Supervisor2 set the environment variable `USE_SUPERVISOR2`.\nconst USE_SUPERVISOR2 = Boolean((__webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\").getenv)('USE_SUPERVISOR2'));\nconst { Supervisor } = USE_SUPERVISOR2 ? __webpack_require__(/*! ./supervisor2 */ \"./src/dcp-client/worker/supervisor2/index.js\") : __webpack_require__(/*! ./supervisor */ \"./src/dcp-client/worker/supervisor.js\");\n\nconst DISABLE_WORKER_CACHE_KEY = 'disable_worker';\n\n/** @typedef {import('./sandbox').SandboxOptions} SandboxOptions */\n/** @typedef {import('../wallet/keystore').Keystore} Keystore */\n\n/**\n * @access public\n * @typedef {object} SupervisorOptions\n * @property {Address} paymentAddress - Address to deposit earned funds into\n * @property {Keystore} identity - Keystore to use as the supervisor's identity\n * @property {string[]} [jobAddresses=[]] - If set, the supervisor will only fetch work for the provided jobIDs\n * @property {boolean} [localExec=false] - If true, fetched work will not be filtered by compute groups.\n * @property {boolean} [priorityOnly=false] - Whether to only work on priority jobs, i.e. become idle if `jobAddresses` is empty.\n * @property {SandboxOptions} [sandboxOptions] - Options that will be passed to the Sandbox constructor\n * @property {number} [watchdogInterval] - Number of ms between watchdog cycles, defaults to dcpConfig tuning param\n * @property {object[]} [computeGroups] - The compute group descriptors the worker will accept jobs from (+ optionally the default compute group)\n * @property {object} [minimumWage] - The minimum payout per slice the worker will accept from a job.\n * @property {boolean} [leavePublicGroup=false] - Don't fetch slices from public compute group.\n * @property {object} [schedulerConfig] - Overrides for dcpConfig.scheduler.\n * @property {number} [maxWorkingSandboxes] - Max number of concurrently working sandboxes\n * @property {{cpu: number, gpu: number}} [cores] - The number of CPU vCores and GPU devices available for compute.\n * @property {{cpu: number, gpu: number}} [targetLoad] - The proportion of the cores.cpu and cores.gpu to load.\n * @property {{any: string[],\n * fetchData: string[],\n * fetchWorkFunctions: string[],\n * fetchArguments: string[],\n * sendResults: string[]}} [allowedOrigins] - origins for fetching data URIs that are allowed\n */\n\nfunction disableWorker() {\n localStorage.setItem(DISABLE_WORKER_CACHE_KEY, true);\n}\n\n/**\n * Fired when the worker begins fetching slices from the scheduler.\n * @access public\n * @event Worker#fetchStart\n */\nclass Worker extends EventEmitter {\n /**\n * Returns a new Worker instance.\n * @access public\n * @param {module:dcp/worker~SupervisorOptions} supervisorOptions \n */\n constructor(supervisorOptions) {\n super('Worker');\n /**\n * @type {boolean}\n * @access public\n */\n this.working = false;\n /**\n * @type {SchedMsg}\n * @access public\n */\n this.schedMsg = new SchedMsg(this);\n /**\n * @type {Supervisor}\n * @access public\n */\n this.supervisor = new Supervisor(this, supervisorOptions);\n \n debugging() && console.debug('Worker supervisorOptions:', supervisorOptions);\n\n this.supervisor.on('fetchingTask', () => this.emit('fetchStart'));\n //this.supervisor.on('fetchedTask', (fetchedSlicesCount) => this.emit('fetch', fetchedSlicesCount)); // AFAICT UNUSED -- XXXpfr\n this.supervisor.on('fetchedTask', (fetchedSlicesCount) => this.emit('fetchEnd', fetchedSlicesCount));\n this.supervisor.on('fetchTaskFailed', (error) => this.emit('fetchEnd', error));\n this.supervisor.on('fetchTaskFailed', (error) => this.emit('fetchError', error));\n\n this.supervisor.on('submittingResults', () => this.emit('submitStart'));\n this.supervisor.on('submittedResult', () => this.emit('submitEnd'));\n this.supervisor.on('submitResultsFailed', (error) => this.emit('submitEnd', error));\n this.supervisor.on('submitResultsFailed', (error) => this.emit('submitError', error));\n this.supervisor.on('submittedResult', () => this.emit('submit'));\n \n this.supervisor.on('dccCredit', (event) => this.emit('payment', event));\n this.supervisor.on('dccNoCredit', (event) => this.emit('payment', event));\n\n this.supervisor.on('sandboxReady', (sandbox) => this.emit('sandbox', sandbox));\n \n this.supervisor.on('error', (error) => this.emit('error', error));\n this.supervisor.on('warning', (warning) => this.emit('warning', warning));\n }\n\n /**\n * Disables worker instances from being started. The user will need to manually intervene to re-enable workers.\n * \n * @access public\n */\n static disableWorker() {\n disableWorker();\n }\n\n /**\n * Starts the worker.\n * \n * @access public\n */\n async start() {\n if (this.working) throw new Error('Cannot start worker: Already working.');\n\n if (localStorage.getItem(DISABLE_WORKER_CACHE_KEY)) {\n await confirmPrompt(`Worker has been disabled by the DCP Security Team; check the @DC_Protocol Twitter feed for more information before continuing.`)\n if (await confirmPrompt('Are you sure you would like to restart the worker?')) {\n localStorage.removeItem(DISABLE_WORKER_CACHE_KEY);\n console.log(\"Starting worker...\");\n } else {\n return;\n }\n }\n\n this.working = true;\n await this.supervisor.work();\n await this.schedMsg.start();\n this.emit('start');\n }\n\n /**\n * Stops the worker.\n * \n * @access public\n * @param {boolean} [immediate=false] Whether the worker should stop imediately or allow the current slices to finish.\n */\n async stop(immediate=false) {\n if (!this.working) throw new Error('Cannot stop worker: Already stopped.');\n \n this.working = false;\n debugging('shutdown') && console.debug(`151: stopping schedMsg...`);\n await this.schedMsg.stop();\n debugging('shutdown') && console.debug(`153: schedmsg stopped ok; stopping supervisor...`);\n await this.supervisor.stopWork(immediate);\n debugging('shutdown') && console.debug(`155: supervisor stopped ok`);\n this.emit('stop');\n }\n \n /**\n * Set payment address\n * @param {Address} addr - new address to be used\n */\n setPaymentAddress(addr)\n {\n assert(addr instanceof Address);\n this.supervisor.paymentAddress = addr;\n this.emit('paymentAddressChange', addr);\n }\n \n /**\n * Get payment address\n * @returns {Address} - current payment address.\n */\n getPaymentAddress()\n {\n return this.supervisor.paymentAddress;\n }\n \n /**\n * Set identity keystore.\n * Note: connections to the scheduler will only use the new identity if they are closed and recreated.\n * @param {Keystore} ks - new identity to be used\n */\n setIdentity(ks)\n {\n assert(ks instanceof Keystore);\n \n /* compatibility for supervisor 1 */\n if (this.supervisor.setDefaultIdentityKeystore)\n this.supervisor.setDefaultIdentityKeystore(ks);\n else\n this.supervisor.identity = ks;\n this.emit('identityChange', ks);\n }\n \n /**\n * Get identity keystore\n * @returns {Keystore} - the current identity keystore\n */\n getIdentity()\n {\n /* compatibiliy for supervisor 1 */\n if (this.supervisor._identityKeystore)\n return this.supervisor._identityKeystore;\n else\n return this.supervisor.identity;\n }\n \n /**\n * Set max working sandboxes\n * @param {number} max - new max working sandboxes\n */\n setMaxWorkingSandboxes(max)\n {\n this.supervisor.maxWorkingSandboxes = max;\n this.emit('maxSandboxesChange', max);\n }\n \n /**\n * Get max working sandboxes\n * @returns {number} - current max working sandboxes\n */\n getMaxWorkingSandboxes()\n {\n return this.supervisor.maxWorkingSandboxes;\n }\n \n /**\n * Check if there are any working sandboxes within the worker\n * @returns {Boolean} - true if there are working sandboxes.\n */\n hasWorkingSandboxes()\n {\n /* compatibility for supervisor 1 */\n if (this.supervisor.workingSandboxes)\n return this.supervisor.workingSandboxes.length > 0\n else\n return this.supervisor.workingSandboxCount() > 0;\n }\n}\n\nexports.Worker = Worker;\nexports.Supervisor = Supervisor;\nexports.disableWorker = disableWorker;\n\nexports.version = {\n api: '1.0.0',\n provides: '1.0.0' /* dcpConfig.scheduler.compatibility.operations.work */\n};\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/index.js?");
4520
+ eval("/**\n * @file This module implements the Worker API, used to create workers for earning DCCs.\n * @author Ryan Rossiter <ryan@kingsds.network>\n * Paul <paul@kingsds.network>\n * @date May 2020\n * June, July 2022\n * \n * @module dcp/worker\n * @access public\n */\n// @ts-check\n\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { SchedMsg } = __webpack_require__(/*! dcp/dcp-client/schedmsg */ \"./src/dcp-client/schedmsg/index.js\");\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { localStorage } = __webpack_require__(/*! dcp/common/dcp-localstorage */ \"./src/common/dcp-localstorage.js\");\nconst { confirmPrompt } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst { Keystore, Address } = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\n\n\n// To use Supervisor2 set the environment variable `USE_SUPERVISOR2`.\nconst USE_SUPERVISOR2 = Boolean((__webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\").getenv)('USE_SUPERVISOR2'));\nconst { Supervisor } = USE_SUPERVISOR2 ? __webpack_require__(/*! ./supervisor2 */ \"./src/dcp-client/worker/supervisor2/index.js\") : __webpack_require__(/*! ./supervisor */ \"./src/dcp-client/worker/supervisor.js\");\n\nconst DISABLE_WORKER_CACHE_KEY = 'disable_worker';\n\n/** @typedef {import('./sandbox').SandboxOptions} SandboxOptions */\n/** @typedef {import('../wallet/keystore').Keystore} Keystore */\n\n/**\n * @access public\n * @typedef {object} SupervisorOptions\n * @property {Address} paymentAddress - Address to deposit earned funds into\n * @property {Keystore} identity - Keystore to use as the supervisor's identity\n * @property {string[]} [jobAddresses=[]] - If set, the supervisor will only fetch work for the provided jobIDs\n * @property {boolean} [localExec=false] - If true, fetched work will not be filtered by compute groups.\n * @property {boolean} [priorityOnly=false] - Whether to only work on priority jobs, i.e. become idle if `jobAddresses` is empty.\n * @property {SandboxOptions} [sandboxOptions] - Options that will be passed to the Sandbox constructor\n * @property {number} [watchdogInterval] - Number of ms between watchdog cycles, defaults to dcpConfig tuning param\n * @property {object[]} [computeGroups] - The compute group descriptors the worker will accept jobs from (+ optionally the default compute group)\n * @property {object} [minimumWage] - The minimum payout per slice the worker will accept from a job.\n * @property {boolean} [leavePublicGroup=false] - Don't fetch slices from public compute group.\n * @property {object} [schedulerConfig] - Overrides for dcpConfig.scheduler.\n * @property {number} [maxWorkingSandboxes] - Max number of concurrently working sandboxes\n * @property {{cpu: number, gpu: number}} [cores] - The number of CPU vCores and GPU devices available for compute.\n * @property {{cpu: number, gpu: number}} [targetLoad] - The proportion of the cores.cpu and cores.gpu to load.\n * @property {{any: string[],\n * fetchData: string[],\n * fetchWorkFunctions: string[],\n * fetchArguments: string[],\n * sendResults: string[]}} [allowedOrigins] - origins for fetching data URIs that are allowed\n */\n\nfunction disableWorker() {\n localStorage.setItem(DISABLE_WORKER_CACHE_KEY, true);\n}\n\n/**\n * Fired when the worker begins fetching slices from the scheduler.\n * @access public\n * @event Worker#fetchStart\n */\nclass Worker extends EventEmitter {\n /**\n * Returns a new Worker instance.\n * @access public\n * @param {module:dcp/worker~SupervisorOptions} supervisorOptions \n */\n constructor(supervisorOptions) {\n super('Worker');\n /**\n * @type {boolean}\n * @access public\n */\n this.working = false;\n /**\n * @type {SchedMsg}\n * @access public\n */\n this.schedMsg = new SchedMsg(this);\n /**\n * @type {Supervisor}\n * @access public\n */\n this.supervisor = new Supervisor(this, supervisorOptions);\n \n debugging() && console.debug('Worker supervisorOptions:', supervisorOptions);\n\n this.supervisor.on('fetchingTask', () => this.emit('fetchStart'));\n //this.supervisor.on('fetchedTask', (fetchedSlicesCount) => this.emit('fetch', fetchedSlicesCount)); // AFAICT UNUSED -- XXXpfr\n this.supervisor.on('fetchedTask', (fetchedSlicesCount) => this.emit('fetchEnd', fetchedSlicesCount));\n this.supervisor.on('fetchTaskFailed', (error) => this.emit('fetchEnd', error));\n this.supervisor.on('fetchTaskFailed', (error) => this.emit('fetchError', error));\n\n this.supervisor.on('submittingResults', () => this.emit('submitStart'));\n this.supervisor.on('submittedResult', () => this.emit('submitEnd'));\n this.supervisor.on('submitResultsFailed', (error) => this.emit('submitEnd', error));\n this.supervisor.on('submitResultsFailed', (error) => this.emit('submitError', error));\n this.supervisor.on('submittedResult', () => this.emit('submit'));\n \n this.supervisor.on('dccCredit', (event) => this.emit('payment', event));\n this.supervisor.on('dccNoCredit', (event) => this.emit('payment', event));\n\n this.supervisor.on('sandboxReady', (sandbox) => this.emit('sandbox', sandbox));\n \n this.supervisor.on('error', (error) => this.emit('error', error));\n this.supervisor.on('warning', (warning) => this.emit('warning', warning));\n }\n\n /**\n * Disables worker instances from being started. The user will need to manually intervene to re-enable workers.\n * \n * @access public\n */\n static disableWorker() {\n disableWorker();\n }\n\n /**\n * Starts the worker.\n * \n * @access public\n */\n async start() {\n if (this.working) throw new Error('Cannot start worker: Already working.');\n\n if (localStorage.getItem(DISABLE_WORKER_CACHE_KEY)) {\n await confirmPrompt(`Worker has been disabled by the DCP Security Team; check the @DC_Protocol Twitter feed for more information before continuing.`)\n if (await confirmPrompt('Are you sure you would like to restart the worker?')) {\n localStorage.removeItem(DISABLE_WORKER_CACHE_KEY);\n console.log(\"Starting worker...\");\n } else {\n return;\n }\n }\n\n this.working = true;\n await this.supervisor.work();\n await this.schedMsg.start();\n this.emit('start');\n }\n\n /**\n * Stops the worker.\n * \n * @access public\n * @param {boolean} [immediate=false] Whether the worker should stop imediately or allow the current slices to finish.\n */\n async stop(immediate=false) {\n if (!this.working) throw new Error('Cannot stop worker: Already stopped.');\n \n this.working = false;\n debugging('shutdown') && console.debug('151: stopping schedMsg...');\n await this.schedMsg.stop();\n debugging('shutdown') && console.debug('153: schedmsg stopped ok; stopping supervisor...');\n await this.supervisor.stopWork(immediate);\n debugging('shutdown') && console.debug('155: supervisor stopped ok');\n this.emit('stop');\n }\n \n /**\n * Set payment address\n * @param {Address} addr - new address to be used\n */\n setPaymentAddress(addr)\n {\n assert(addr instanceof Address);\n this.supervisor.paymentAddress = addr;\n this.emit('paymentAddressChange', addr);\n }\n \n /**\n * Get payment address\n * @returns {Address} - current payment address.\n */\n getPaymentAddress()\n {\n return this.supervisor.paymentAddress;\n }\n \n /**\n * Set identity keystore.\n * Note: connections to the scheduler will only use the new identity if they are closed and recreated.\n * @param {Keystore} ks - new identity to be used\n */\n setIdentity(ks)\n {\n assert(ks instanceof Keystore);\n \n /* compatibility for supervisor 1 */\n if (this.supervisor.setDefaultIdentityKeystore)\n this.supervisor.setDefaultIdentityKeystore(ks);\n else\n this.supervisor.identity = ks;\n this.emit('identityChange', ks);\n }\n \n /**\n * Get identity keystore\n * @returns {Keystore} - the current identity keystore\n */\n getIdentity()\n {\n /* compatibiliy for supervisor 1 */\n if (this.supervisor._identityKeystore)\n return this.supervisor._identityKeystore;\n else\n return this.supervisor.identity;\n }\n \n /**\n * Set max working sandboxes\n * @param {number} max - new max working sandboxes\n */\n setMaxWorkingSandboxes(max)\n {\n this.supervisor.maxWorkingSandboxes = max;\n this.emit('maxSandboxesChange', max);\n }\n \n /**\n * Get max working sandboxes\n * @returns {number} - current max working sandboxes\n */\n getMaxWorkingSandboxes()\n {\n return this.supervisor.maxWorkingSandboxes;\n }\n \n /**\n * Check if there are any working sandboxes within the worker\n * @returns {Boolean} - true if there are working sandboxes.\n */\n hasWorkingSandboxes()\n {\n /* compatibility for supervisor 1 */\n if (this.supervisor.workingSandboxes)\n return this.supervisor.workingSandboxes.length > 0\n else\n return this.supervisor.workingSandboxCount() > 0;\n }\n}\n\nexports.Worker = Worker;\nexports.Supervisor = Supervisor;\nexports.disableWorker = disableWorker;\n\nexports.version = {\n api: '1.0.0',\n provides: '1.0.0' /* dcpConfig.scheduler.compatibility.operations.work */\n};\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/index.js?");
4475
4521
 
4476
4522
  /***/ }),
4477
4523
 
@@ -4525,7 +4571,7 @@ eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_mod
4525
4571
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4526
4572
 
4527
4573
  "use strict";
4528
- eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_modules/process/browser.js */ \"./node_modules/process/browser.js\");\n/**\n * @file dcp-client/worker/supervisor2/index.js\n * Code managing sandboxes, tasks, jobs, and slices within in a DCP Worker.\n * @author Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * @date Dec 2020\n * June 2022\n * @module supervisor\n */\n\n/* global dcpConfig */ // eslint-disable-line no-redeclare\n// @ts-check\n\n\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\nconst dcp4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst constants = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { setImmediate } = __webpack_require__(/*! dcp/common/dcp-timers */ \"./src/common/dcp-timers.js\");\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst { Keystore, Address } = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst { localStorage } = __webpack_require__(/*! dcp/common/dcp-localstorage */ \"./src/common/dcp-localstorage.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst RingBuffer = __webpack_require__(/*! dcp/utils/ringBuffer */ \"./src/utils/ringBuffer.js\");\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst { JobManager } = __webpack_require__(/*! ./job-manager */ \"./src/dcp-client/worker/supervisor2/job-manager.js\");\nconst { Load } = __webpack_require__(/*! ./load */ \"./src/dcp-client/worker/supervisor2/load.js\");\nconst { Sandbox, SandboxError } = __webpack_require__(/*! ./sandbox2 */ \"./src/dcp-client/worker/supervisor2/sandbox2.js\");\nconst { sliceStatus } = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst hash = __webpack_require__(/*! dcp/common/hash */ \"./src/common/hash.js\");\nconst { calculateJoinHash } = __webpack_require__(/*! dcp/dcp-client/compute-groups */ \"./src/dcp-client/compute-groups/index.js\");\nconst { ModuleCache } = __webpack_require__(/*! ./module-cache */ \"./src/dcp-client/worker/supervisor2/module-cache.js\");\nconst { Inventory, leafMerge, a$sleepMs, ms, pct, generateOpaqueId, booley, compressJobMap, \n toJobMap, truncateAddress, encodeDataURI, makeValueURI, justFetch, stringify } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n//const { CodeFlow } = require('client-oauth2');\n//const { text } = require('./lang').getLocaleStrings('en_CA'); /** XXXpfr @todo Don't know what to do with localization? */\n\n/** @typedef {import('dcp/dcp-client/wallet/eth').Address} Address */\n/** @typedef {import('dcp/protocol-v4/connection/connection').Connection} Connection */\n/** @typedef {string} opaqueId */ // 22 character base64 string\n/** @typedef {import('..').Worker} Worker */\n/** @typedef {import('..').SupervisorOptions} SupervisorOptions */\n/** @typedef {import('./slice2').Slice} Slice */\n/** @typedef {import('dcp/utils').SliceMessage} SliceMessage */\n\nconst addressTruncationLength = 20;\n\n//\n// Configs are moving around in dcpConfig and local worker configs, so set up some defaults.\nlet workerTuning = dcpConfig.worker;\nif (!workerTuning) workerTuning = dcpConfig.Supervisor;\nif (!workerTuning || !workerTuning.dcp || !workerTuning.dcp.tuning || !workerTuning.dcp.tuning.watchdogInterval\n || !workerTuning.sandbox || !workerTuning.allowOrigins || !workerTuning.minimumWage || !workerTuning.computeGroups)\n workerTuning = {\n dcp: {\n tuning: { watchdogInterval: 7, minSandboxStartDelay: 0.1, maxSandboxStartDelay: 0.7 },\n connectionOptions: { default: { identityUnlockTimeout: 15 * 60 /* seconds */ } },\n },\n sandbox: { progressReportInterval: 2 * 60 * 100 },\n allowOrigins: { fetchWorkFunctions: [], fetchArguments: [], fetchData: [], sendResults: [], any: [] },\n minimumWage: { CPU: 0, GPU: 0, 'in': 0, out: 0 },\n leavePublicGroup: false,\n pCores: 0,\n computeGroups: {},\n // The following configs are not in dcpConfig or worker configs (yet), but may be specified in local worker configs to override the defaults.\n pruneFrequency: 15 * 1000, // Maxiumum time interval where we check to prune used sandboxes.\n workerSandboxThreshold: 7, // When maxWorkingSandboxes >= workerSandboxThreshold, we allow an extra 25% of assigned sandboxes that won't be pruned.\n cachedJobsThreshold: 12, // Prune the unused job managers >= cachedJobsThreshold.\n};\n\n//\n// Flags for tracing.\n//\nconst selectiveEnable = false;\nconst displayWarnError = false || selectiveEnable;\nconst selectiveDebugging = selectiveEnable || debugging();\nconst debuggingError = false || selectiveDebugging || displayWarnError;\nconst debuggingWarn = false || selectiveDebugging || displayWarnError;\nconst selectiveDebugging2 = selectiveEnable && false || debugging('supervisor');\nconst displaySliceState = true;\nconst displayCompletedResults = false;\n\n/** \n * Adjust delay times when debugging.\n * The adjustment for niim is automatic, other debuggers must manually change this value.\n */\nlet timeDilation = 1;\nif (DCP_ENV.platform === 'nodejs')\n{\n /** Make timers 10x slower when running in niim */\n timeDilation = (requireNative('module')._cache.niim instanceof requireNative('module').Module) ? 10 : 1;\n}\n\n//\n// Index to functionality -- search for '_Idx' to toggle through the index.\n//\n// 1) Ctor: Supervisor constructor.\n// 2) Important property-like functions.\n// 3) Dtors: screenSaverDestroy, stopWork, purgeJob.\n// 4) Connection code.\n// 5) Work: Distribute slice to sandboxes.\n// 6) Return slices and sent progress reports to result-submitter-results.\n// 7) Task Distributor (TD): requestTask (Rq) support -- communication with TD.\n// 8) Aggregators from the job managers.\n// 9) Sandbox creation and management.\n// 10) Result-submitter-result support functions.\n// 11) Work reject.\n// 12) Unused functions that we need to review.\n//\n\n// _Idx\n//\n// Ctor: Supervisor constructor.\n//\n\n/** \n * Supervisor constructor\n * \n * A supervisor manages the communication with the scheduler, manages sandboxes, and\n * decides which workload should be sent to which sandboxes when.\n *\n * Start state:\n * - initial\n *\n * Intermediate states:\n * - ready\n * - stopping\n *\n * Terminal states:\n * - stopped\n *\n * Valid transitions:\n * - initial -> ready where that happens \n * - ready -> stopping\n * - stopping -> stopped\n *\n * @param {Worker} worker - The worker that created this instance.\n * @param {SupervisorOptions} options - Options for specifying custom behaviour and tuning,\n */\nfunction Supervisor(worker, options)\n{\n assert(options.identity instanceof Keystore);\n assert(options.paymentAddress instanceof Address);\n\n /**\n * Flag to indicate a debug build.\n * Used when we want to display extra information and do extra checks for developers only.\n * @type {boolean}\n */\n this.debugBuild = ((__webpack_require__(/*! dcp/common/dcp-build */ \"./src/common/dcp-build.js\").build) === 'debug');\n /**\n * When Supervisor.sliceTiming is set to be true, it displays the timings of a every slice\n * slice['queueingDelta'] = timespan of when slice is passed to jobManager.runQueuedSlice until sandbox.work\n * slice['executionDelta'] = timespan of execution in sandbox\n * slice['resultDelta'] = timespan of when sandbox finishes executing until recordResult completes.\n * @type {boolean}\n */\n this.sliceTiming = false;\n /** Used for analyzing the completed results in Supervisor.recordResult. */\n this.resultMap = {};\n\n /** @type {ModuleCache} */\n this.moduleCache = new ModuleCache(this);\n\n this.worker = worker;\n this.identity = options.identity;\n this.paymentAddress = options.paymentAddress;\n this.options = options;\n this.maxWorkingSandboxes = options.maxWorkingSandboxes || 1;\n this.maxTotalSandboxes = this.maxWorkingSandboxes;\n\n // We're making the assumption that if a worker has at least 7 sandboxes, \n // then the worker has sufficient resources to handle 25% more sandboxes in memory.\n // This assumption may be overridden by changing workerSandboxThreshold.\n if (this.maxWorkingSandboxes >= this.workerSandboxThreshold)\n this.maxTotalSandboxes = Math.ceil(1.25 * this.maxWorkingSandboxes);\n // When # of sandboxes reaches this level, we more aggressively prune.\n this.mustPruneSandboxLevel = Math.ceil(1.5 * this.maxTotalSandboxes);\n // Last prune time stamp.\n this.lastPrune = 0;\n // General time stamp.\n this.lastTime = 0;\n\n // Supervisor may get created by Worker where options.cores or options.targetLoad is not defined.\n this.numCPU = this.maxWorkingSandboxes;\n this.numGPU = 1;\n this.portionToUseCPU = pct(100);\n this.portionToUseGPU = pct(100);\n\n if (options.cores)\n {\n this.numCPU = options.cores.cpu || this.numCPU;\n this.numGPU = options.cores.gpu || this.numGPU;\n }\n if (options.targetLoad)\n {\n this.portionToUseCPU = options.targetLoad.cpu || pct(100);\n this.portionToUseGPU = options.targetLoad.gpu || pct(100);\n }\n\n this.tuning = {\n maxCPUAlloc: this.portionToUseCPU, /**< Maximum proportion of CPU time to attempt to use. */\n maxGPUAlloc: this.portionToUseGPU, /**< Maximum proportion of GPU time to attempt to use. */\n watchdogInterval: 7, /**< (seconds) How frequently to kick off an unsolicited requestTask. */\n prefetchInterval: 20, /**< (seconds) How many seconds into the future are looking to project capacity during work fetch. */\n minSandboxStartDelay: 0.1, /**< (seconds) Base minimum of this.delayMs, scaled by this.delayScaler. */\n maxSandboxStartDelay: 0.7, /**< (seconds) Base maximum random component of this.delayMs, scaled by this.delayScaler. */\n };\n this.tuning = leafMerge(this.tuning, workerTuning.dcp.tuning);\n if (options.watchdogInterval > 0)\n this.tuning.watchdogInterval = options.watchdogInterval; // Override.\n //this.tuning.watchdogInterval = 0.25;\n\n /**\n * Fine tune this.delayMs.\n * Note: Please discuss any change with Paul, paul@kingsds.network.\n * XXXpfr @todo Finalize the delay tuning.\n */\n this.delayScaler = 0.5;\n\n debugging('supervisor') && console.debug('Supervisor.tuning', this.tuning);\n\n /**\n * Note: targetLoad is not properly implemented yet.\n * XXXpfr @todo Collaborate with Wes to get it right.\n * @type {Load}\n */\n this.targetLoad = new Load({\n cpu: Math.min(this.maxWorkingSandboxes, this.numCPU),\n gpu: Math.min(this.maxWorkingSandboxes, this.numGPU)\n }).scale(this.tuning.maxCPUAlloc, this.tuning.maxGPUAlloc);\n\n /** @type {string[]} */\n this.allowedOrigins = workerTuning.allowOrigins.any;\n /** @type {string[]} */\n this.fetchWorkFunctions = workerTuning.allowOrigins.fetchWorkFunctions;\n /** @type {string[]} */\n this.fetchArguments = workerTuning.allowOrigins.fetchArguments;\n /** @type {string[]} */\n this.fetchData = workerTuning.allowOrigins.fetchData;\n /** @type {string[]} */\n this.sendResults = workerTuning.allowOrigins.sendResults;\n\n // In localExec, do not allow work function or arguments to come from the 'any' origins\n if (this.options.localExec)\n {\n this.allowedOrigins = this.allowedOrigins.concat(options.allowedOrigins.any);\n this.fetchWorkFunctions = this.fetchWorkFunctions.concat(options.allowedOrigins.fetchWorkFunctions);\n this.fetchArguments = this.fetchArguments.concat(options.allowedOrigins.fetchArguments);\n this.fetchData = this.fetchData.concat(options.allowedOrigins.fetchData);\n this.sendResults = this.sendResults.concat(options.allowedOrigins.sendResults)\n }\n\n if (options.allowedOrigins && options.allowedOrigins.length > 0)\n this.allowedOrigins = options.allowedOrigins.concat(this.allowedOrigins);\n \n //\n // The following 3 configs are not in dcpConfig or worker configs (yet), but may be specified in local worker configs to override the defaults.\n //\n /** @type {number} - Maxiumum time interval where we check to prune used sandboxes. */\n this.pruneFrequency = workerTuning.pruneFrequency || 15 * 1000;\n /** @type {number} - When maxWorkingSandboxes >= workerSandboxThreshold, we allow an extra 25% of assigned sandboxes that won't be pruned. */\n this.workerSandboxThreshold = workerTuning.workerSandboxThreshold || 7;\n /** @type {number} - Prune the unused job managers >= cachedJobsThreshold. */\n this.cachedJobsThreshold = workerTuning.cachedJobsThreshold || 12;\n\n /** @type {Object.<Address, JobManager>} */\n this.jobMap = {}; \n /** @type {Sandbox[]} - All sandboxes that are being used by the job managers. Makes sure we don't lose sandboxes. */\n this.sandboxInventory = [];\n /** @type {Sandbox[]} - Started sandboxes that are not in sandboxInventory yet. */\n this.readiedSandboxes = [];\n /** @type {JobManager[]} */\n this.jobManagerInventory = new Inventory('jobManagers');\n /** @type {Synchronizer} */\n this.state = new Synchronizer('initial', [ 'initial', 'ready', 'reconnecting', 'stopping', 'stopped', 'broken']);\n\n /** @type {string} */\n this.lastDcpsid = undefined;\n /** @type {Connection} */\n this.taskDistributor = null;\n /** @type {Connection} */\n this.resultSubmitter = null;\n /** @type {Connection} */\n this.eventRouter = null;\n /** @type {Connection} */\n this.packageManager = null;\n /** @type {Array<object>} */\n this.resultSubmitterMessageQueue = [];\n /** @type {Array<object>} */\n this.eventRouterMessageQueue = [];\n\n /** @type {object} */\n this.schedulerConfig = leafMerge(dcpConfig.scheduler, options.schedulerConfig);\n\n /** @type {opaqueId} */\n this.workerId = localStorage.getItem('workerId');\n if (!this.workerId || this.workerId.length !== constants.workerIdLength)\n {\n this.workerId = generateOpaqueId();\n localStorage.setItem('workerId', this.workerId);\n }\n /** @type {object[]} */\n this.rejectedJobs = [];\n /** \n * An N-slot ring buffer of job addresses. Stores all jobs that have had no more than 1 slice run in the ring buffer.\n * Required for the implementation of discrete jobs \n * @type {RingBuffer} \n */\n this.ringBufferofJobs = new RingBuffer(100); // N = 100 should be more than enough. \n /** @type {boolean} - pseudo-mutex guarding requestTask. */\n this.isFetchingNewWork = false;\n\n // Start up the connections.\n this.instantiateAllConnections();\n\n /**\n * Note: DCP-3241 asks to test Android to see if we need this restriction any longer.\n * XXXpfr @todo Hopefully we can delete this @hack.\n */\n // @hack - dcp-env.isBrowserPlatform is not set unless the platform is _explicitly_ set,\n // using the default detected platform doesn't set it.\n // Fixing that causes an error in the wallet module's startup on web platform, which I\n // probably can't fix in a reasonable time this morning.\n // ~ER2020-02-20\n if (!options.maxWorkingSandboxes\n && DCP_ENV.browserPlatformList.includes(DCP_ENV.platform)\n && navigator.hardwareConcurrency > 1) {\n this.maxWorkingSandboxes = navigator.hardwareConcurrency - 1;\n if (typeof navigator.userAgent === 'string') {\n if (/(Android).*(Chrome|Chromium)/.exec(navigator.userAgent)) {\n this.maxWorkingSandboxes = 1;\n this.emit('warning', 'Doing work with Chromimum browsers on Android is currently limited to one sandbox');\n }\n }\n }\n}\nexports.Supervisor = Supervisor;\nSupervisor.prototype = Object.getPrototypeOf(new EventEmitter('Supervisor')); // Fake out VSCode -- get's rid of a billion red-squigglies.\nSupervisor.prototype = new EventEmitter('Supervisor');\n/**\n * Preserve the constructor property.\n * @constructor\n */\nSupervisor.prototype.constructor = Supervisor;\n\n/**\n * Set up sandboxes and interval timers, then start to search for work.\n **/\nSupervisor.prototype.startWork = function Supervisor$startWork ()\n{\n /* Provide opportunity for calling code to hook ready/error events. */\n setImmediate(async () => {\n try\n {\n if (this.state.isNot('initial'))\n {\n if (this.state.setIf('stopped', 'initial')) {}\n else if (this.state.setIf('reconnecting', 'initial')) {}\n else if (this.state.setIf('broken', 'initial')) {}\n else if (this.state.is('ready')) return\n else throw new Error(`Supervisor startWork is in unexpected state ${this.state}, aborting...`);\n }\n this.instantiateAllConnections();\n\n await this.createSandboxes(this.maxWorkingSandboxes)\n .then(() => this.checkCapabilities());\n\n // Beacon interval timer.\n this.progressReportTimer = setInterval(() => this.emitProgressReport(), (workerTuning.sandbox.progressReportInterval || 2 * 60 * 100));\n // Watchdog: requestTask-driven interval timer.\n this.watchdogTimer = setInterval(() => this.requestTask() , ms(this.tuning.watchdogInterval));\n if (DCP_ENV.platform === 'nodejs' && this.options.localExec)\n {\n /* Interval timer helps keep worker alive forever, which we don't want in localExec. */\n this.progressReportTimer.unref();\n this.watchdogTimer.unref();\n }\n\n this.state.set('initial', 'ready');\n\n setImmediate(() => this.requestTask()); // Don't wait for watchdog.\n }\n catch(error)\n {\n this.state.set('initial', 'broken');\n this.emit('error', error);\n }\n });\n}\n\n/** Construct capabilities when necessary. */\nSupervisor.prototype.checkCapabilities = function Supervisor$checkCapabilities ()\n{\n if (!this.capabilities)\n {\n /**\n * Assign the capabilities of one the sandboxes before fetching slices from the scheduler.\n * @todo Remove this once fetchTask uses the capabilities of every sandbox to fetch slices.\n */\n const sandbox = this.readiedSandboxes.length > 0 ? this.readiedSandboxes[0] : this.sandboxInventory[0];\n if (sandbox)\n {\n this.capabilities = sandbox.capabilities;\n this.emit('capabilitiesCalculated', this.capabilities);\n }\n }\n\n if (DCP_ENV.isBrowserPlatform && this.capabilities.browser)\n this.capabilities.browser.chrome = DCP_ENV.isBrowserChrome;\n}\n\n// _Idx\n//\n// Important property-like functions.\n//\n\n/**\n * Universal delay milliseconds..\n * @returns {number}\n */\nSupervisor.prototype.delayMs = function Supervisor$delayMs (max = this.tuning.maxSandboxStartDelay, min = this.tuning.minSandboxStartDelay)\n{\n // Note: Please discuss any change with Paul, paul@kingsds.network.\n return 1000 * timeDilation * this.delayScaler * (min + Math.random() * (max - min));\n}\n\n/**\n * Indicates whether supervisor is ready for business.\n * @returns {boolean} - When true, the party is on...\n */\nSupervisor.prototype.isReady = function Supervisor$isReady()\n{\n return this.worker.working && this.state.is('ready');\n}\n\n/**\n * Safe access to Connection.close(...).\n * @param {Connection} connection\n * @param {string} [reason='requested']\n * @param {boolean} [immediate=false]\n * @returns {Promise<string>}\n */\nfunction safeClose(connection, reason = 'requested', immediate = false)\n{\n if (connection)\n {\n let msg;\n if (connection.state.is('closed')) msg = 'closed';\n else if (connection.state.is('closing')) msg = 'closing';\n else if (connection.state.is('close-wait')) msg = 'close-wait';\n if (msg)\n {\n debuggingWarn && console.warn(`${msg}, do not try to close again.`);\n return Promise.resolve(msg);\n }\n return connection.close(reason, immediate)\n .then(() => {\n return Promise.resolve(null);\n });\n }\n return Promise.resolve('already closed');\n}\n\n/**\n *\n * @param {string} operation\n * @param {*} data\n * @returns {Promise<string>}\n */\nSupervisor.prototype.saveForResubmitToRS = function Supervisor$saveForResubmitToRS(operation, data)\n{\n this.resultSubmitterMessageQueue.push({ operation, data });\n return safeClose(this.resultSubmitter);\n}\n\n/**\n * Error feedback to user.\n * @param {string} message\n * @param {*} extra\n */\nSupervisor.prototype.error = function Supervisor$error(message, extra)\n{\n const dcpError = new DCPError(message, extra);\n this.emit('error', dcpError);\n}\n\n/**\n * When true, the sandbox complete handler will look for another slice in the same job,\n * and if not found, then proceed to Supervisor.requestTask.\n * @returns {boolean}\n */\nSupervisor.prototype.runSliceFromSameJob = function Supervisor$runSliceFromSameJob()\n{\n //\n // Experimental, should be off by default.\n // Cf. sandbox complete handler in JobManager.hookUpSandboxListeners.\n //\n const disable = true;\n const tooManyJobs = this.activeJobCount() > this.maxWorkingSandboxes;\n return !disable && !tooManyJobs && this.unusedSandboxSlots() < 2;\n}\n\n/**\n * This function is used as the target number of sandboxes to be associated with slices and start working.\n * Warning: Do not rely on this information being 100% accurate -- it may change in the next instant.\n * @returns {number}\n */\nSupervisor.prototype.unusedSandboxSlots = function Supervisor$unusedSandboxSlots ()\n{\n return this.maxWorkingSandboxes - this.workingSliceCount();\n}\n\n/**\n * Create errorObj with error.code if it exists.\n * @param {Error} error\n * @returns {object}\n */\nSupervisor.prototype.checkCode = function Supervisor$checkCode (error)\n{\n if (!error) return '';\n const errorObj = { message: error.message };\n if (error['errorCode']) errorObj['errorCode'] = error['errorCode'];\n if (error['code']) errorObj['code'] = error['code'];\n return errorObj;\n}\n\n/**\n * Remove stack trace from error.\n * May not work when error is a string with no new-lines.\n * @param {string|Error} error\n * @returns {string|Error}\n */\nSupervisor.prototype.removeStackTrace = function Supervisor$removeStackTrace (error)\n{\n if (typeof error === 'string')\n {\n const errorLines = error.split('\\n');\n return (errorLines && errorLines.length > 0) ? errorLines[0] : error;\n }\n if (error instanceof Error)\n return error.message;\n return error;\n}\n\n// _Idx\n//\n// Dtors: screenSaverDestroy, stopWork, purgeJob.\n//\n\n/**\n * If we cannot create a new sandbox, that probably means we're on a screensaver worker\n * and the screensaver is down. So return the slices to the scheduler.\n */\nSupervisor.prototype.screenSaverDestroy = function Supervisor$screenSaverDestroy()\n{\n debugging('supervisor') && console.debug(`Supervisor.screenSaverDestroy: destroying all job managers and terminating all sandboxes.`);\n this.jobManagerInventory.forEach(jm => jm.destroy());\n this.jobManagerInventory = new Inventory('jobManagers');\n\n this.readiedSandboxes.forEach(sandbox => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this.readiedSandboxes = [];\n\n this.sandboxInventory.forEach(sandbox => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this.sandboxInventory = [];\n}\n\n/**\n * Terminates sandboxes and returns slices.\n * Sets the working flag to false, call @this.work to start working again.\n * \n * If forceTerminate is true: Terminates all sandboxes and returns all slices.\n * If forceTerminate is false: Terminates non-working sandboxes and returns initial and ready slices.\n *\n * @param {boolean} [forceTerminate = true] - true if you want to stop the sandboxes from completing their current slice.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.stopWork = async function Supervisor$stopWork (forceTerminate = true)\n{\n selectiveDebugging && console.debug(`Supervisor.stopWork(${forceTerminate},${this.state}): terminating sandboxes and returning slices to scheduler.`);\n\n // Do a hard flush of the microtask queue and finish the current event loop.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)));\n\n if (this.state.setIf('ready', 'stopping')) {}\n else if (this.state.setIf('reconnecting', 'stopping')) {}\n else if (this.state.setIf('broken', 'stopping')) {}\n else if (this.state.is('stopped')) return\n else throw new Error(`Supervisor stopWork is in unexpected state ${this.state}, aborting...`);\n\n this.instantiateAllConnections();\n\n this.readiedSandboxes.forEach((sandbox) => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this.readiedSandboxes = [];\n\n if (forceTerminate)\n {\n for (const jm of this.jobManagerInventory)\n jm.destroy();\n\n this.sandboxInventory.forEach((sandbox) => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n }\n else\n {\n let activeSliceCount = 0;\n const slicesToReturn = [];\n for (const jm of this.jobManagerInventory)\n {\n //jm.dumpSlices(`stopWork1:${jm.address}`);\n const queuedSlices = jm.queuedSlices;\n slicesToReturn.push(...queuedSlices);\n jm.removeSlices(queuedSlices);\n activeSliceCount += jm.activeSlices.length;\n jm.nonWorkingSandboxes.forEach((sandbox) => jm.returnSandbox(sandbox));\n }\n\n const reason = `Supervisor stopWork(${forceTerminate}): returning all non-finished slices that are not working.`;\n this.returnSlices(slicesToReturn, reason, false /*removeSlices*/);\n\n await new Promise((resolve) => {\n if (activeSliceCount === 0)\n resolve();\n // Resolve and finish work once all sandboxes have finished submitting their results.\n this.on('submitFinished', () => {\n if (--activeSliceCount === 0)\n {\n debuggingWarn && console.warn('All sandboxes empty, stopping worker and closing all connections');\n resolve();\n }\n });\n });\n\n for (const jm of this.jobManagerInventory)\n {\n //jm.dumpSlices(`stopWork2:${jm.address}`);\n jm.liveSandboxes.forEach((sandbox) => sandbox.terminate(false));\n jm._sandboxInventory = [];\n }\n }\n this.sandboxInventory = [];\n this.jobManagerInventory = new Inventory('jobManagers');\n\n this.closeConnections();\n\n this.emit('stop');\n this.state.set('stopping', 'stopped');\n}\n\n/**\n* Purge all traces of the job.\n* @param {JobManager} jobManager \n*/\nSupervisor.prototype.purgeJob = function Supervisor$purgeJob (jobManager)\n{\n selectiveDebugging && console.debug(`Supervisor.purgeJob ${jobManager.identifier}.`);\n this.jobManagerInventory.delete(jobManager);\n this.moduleCache.removeJob(jobManager.address);\n jobManager.destroy();\n}\n\n// _Idx\n//\n// Connection code.\n//\n\n/** \n * Connect the supervisor to a given scheduler sub-service. Reconnection \"DDoS\" from workers\n * mitigated via an exponential backoff algorithm.\n *\n * DCPv4 connections are lazily-initiated. Successful connection establishment detected by\n * observing the payload event, which normally triggers during versioning.\n */\nSupervisor.prototype.connectTo = function Supervisor$connectTo(which)\n{\n const config = (which === 'packageManager') ? dcpConfig.packageManager : this.schedulerConfig.services[which];\n const retryMinSleepMs = 100;\n const payloadResetCount = 3; /* How many payloadCount before we reset retryNextSleepMs. */\n \n var retryNextSleepMs = retryMinSleepMs;\n var payloadCount = 0;\n var options = leafMerge(/* ordered from most to least specific */\n workerTuning.dcp.connectionOptions.default,\n workerTuning.dcp.connectionOptions[which],\n workerTuning.dcp.connectionOptions[config.location.href],\n );\n\n /**\n * The payload event handler is an interesting way to handle exponential backoff\n * for the delay in this.reconnect.\n * XXXpfr @todo Revisit exponential backoff for this.reconnect in Sup2 Part II.\n */\n\n const handlePayloadEventFn = (ev) =>\n {\n if (++payloadCount === payloadResetCount)\n {\n this[which].removeEventListener('payload', handlePayloadEventFn);\n retryNextSleepMs = retryMinSleepMs; \n }\n }\n\n this[which] = new dcp4.Connection(config, this.identity, options);\n\n debugging() && console.debug(`Opening new connection ${this[which].id} to ${which}.`)\n\n const messageQueue = this[which + 'MessageQueue'];\n if (messageQueue && messageQueue.length > 0)\n resendRejectedMessages(this[which], messageQueue);\n\n this[which].on('payload', handlePayloadEventFn);\n\n this['reconnect' + which] = this.reconnect.bind(this, which);\n this[which].on('close', () => { this['reconnect' + which](); });\n}\n\n/**\n * Reconnect logic.\n * @param {string} which -- Name of the connection \n */\nSupervisor.prototype.reconnect = async function Supervisor$reconnect(which)\n{\n debugging('supervisor') && console.debug(`Supervisor.reconnect: ${which}:`, this.state.valueOf(), this[which].state.valueOf());\n const stateChange = this.state.testAndSet('ready', 'reconnecting');\n if (stateChange || this.state.is('reconnecting'))\n {\n await a$sleepMs(100); // Sleep for 100ms, maybe exp-backoff later if we need it.\n this.connectTo(which);\n if (stateChange) this.state.set('reconnecting', 'ready');\n debugging() && console.debug(`Supervisor.reconnect: Trying to reconnect ${which}`, this.state.valueOf(), this[which].state.valueOf());\n }\n}\n\n/**\n * Close a connection properly.\n * @param {string} which -- Name of the connection to close.\n */\nSupervisor.prototype.closeConnection = function Supervisor$closeConnection(which)\n{\n if (this[which])\n {\n this[which].off('close', this['reconnect' + which]);\n this[which].close();\n this[which] = null;\n }\n}\n\n/**\n * Close all connections.\n */\nSupervisor.prototype.closeConnections = function Supervisor$closeConnections()\n{\n /** XXXpfr @todo Remove when bug DCP-3094 is fixed. */\n a$sleepMs(500);\n\n this.closeConnection('taskDistributor');\n this.closeConnection('resultSubmitter');\n this.closeConnection('eventRouter');\n this.closeConnection('packageManager');\n}\n\n/**\n * Broadcast keepalive to all connections and optionally start up sandboxes.\n * @param {boolean} [createSandbox=false] - When true, creates sandboxes for unused sandbox slots.\n */\nSupervisor.prototype.keepalive = function Supervisor$keepalive(createSandbox = false)\n{\n return Promise.all([\n this.taskDistributor.keepalive(),\n this.resultSubmitter.keepalive(),\n this.eventRouter .keepalive(),\n this.packageManager .keepalive(),\n (createSandbox ? this.createSandboxes(this.maxWorkingSandboxes) : Promise.resolve()),\n ]);\n}\n\n/**\n * Open all connections. Used when supervisor is instantiated or stopped/started to initially open connections.\n */\nSupervisor.prototype.instantiateAllConnections = function Supervisor$instantiateAllConnections ()\n{\n if (!this.taskDistributor)\n this.connectTo('taskDistributor');\n if (!this.eventRouter)\n this.connectTo('eventRouter');\n if (!this.resultSubmitter)\n this.connectTo('resultSubmitter');\n if (!this.packageManager)\n this.connectTo('packageManager');\n}\n\n/**\n * Try sending messages that were rejected on an old instance of the given connection.\n * @param {Connection} connection\n * @param {Array<object>} messageQueue\n */\nasync function resendRejectedMessages (connection, messageQueue)\n{\n var message = messageQueue.shift();\n do \n {\n try\n {\n await connection.send(message.operation, message.data);\n }\n catch (error)\n {\n debuggingError && console.error(`Failed to resend message ${message.operation} to scheduler: ${error}. Will try again on a new connection.`);\n messageQueue.unshift(message);\n safeClose(connection);\n break;\n }\n message = messageQueue.shift();\n } while (message);\n}\n \n// _Idx\n//\n// Work: Distribute slice to sandboxes.\n//\n\n/** \n * UNUSED\n * @deprecated\n * Round-robin through the job managers, picking 1 slice to run each time.\n * Try to have the same number of working sandboxes for each job.\n * Try to run a slice on every available sandbox.\n */\nSupervisor.prototype.workOnCurrentTask = function Supervisor$workOnCurrentTask ()\n{\n return this.roundRobinSlices();\n}\n\n/**\n * This is for compatibility with Supervisor1 in sa worker.\n * When we get rid of Supervisor1 we can delete the ref in sa worker and then get rid of this function.\n */\nSupervisor.prototype.setDefaultIdentityKeystore = function Supervisor$setDefaultIdentityKeystore ()\n{\n}\n\n/**\n * Called in Worker.start().\n * Initial entry point after Worker constructor.\n * We need to start searching for work here to allow starting and stopping a worker.\n */\nSupervisor.prototype.work = function Supervisor$work ()\n{\n // Set up sandboxes and interval timers, then start to search for work.\n this.startWork();\n}\n\n/** \n * Round-robin through the job managers, picking 1 slice to run each time.\n * Try to have the same number of working sandboxes for each job.\n * Try to run a slice on every available sandbox.\n */\nSupervisor.prototype.roundRobinSlices2 = function Supervisor$roundRobinSlices2 ()\n{\n try\n {\n let first = true;\n const cursor = this.makeJobSelectionCursor(this.jobManagerInventory);\n while (true)\n {\n const slice = cursor.next();\n if (!slice) break; /* No more work can fit. */\n debugging('supervisor') && console.debug('roundRobinSlices: Executing slice', slice.identifier);\n slice.markAsReserved();\n slice.jobManager.runSlice(slice, first ? 0 : this.delayMs());\n first = false;\n }\n }\n finally\n {\n this.isFetchingNewWork = false;\n }\n}\n\n/**\n * We try to balance so that each job has the same number of working slices.\n *\n * NOTES:\n * 1) If count is such that it cannot be distributed evenly, we do not yet randomly assign the extras.\n * E.g. 3 jobs, j1, j2, j3: count = 5 -- so 2 jobs get extras -- the extras need to be assigned randomly.\n * @todo Assign the extras randomly.\n * 2) this.roundRobinSlices is not going to be what we use when sup2 is in final form.\n * We want to this.makeJobSelectionCursor and something like this.roundRobinSlices2\n *\n * In the outer loop,\n * when numworkingSandboxes=1, try to get a slice running for each job with 0 working sandboxes.\n * when numworkingSandboxes=2, try to get a slice running for each job with 1 working sandboxes.\n * when numworkingSandboxes=3, try to get a slice running for each job with 2 working sandboxes. Etc.\n * The idea is to balance the number of slices working on each job.\n * @param {number} [count=0] - The number of new slices to try to get running in sandboxes.\n */\nSupervisor.prototype.roundRobinSlices = function Supervisor$roundRobinSlices (count = 0)\n{\n try\n {\n if (!count) count = this.unusedSandboxSlots();\n if (!count || this.readySliceCount() < 1)\n return Promise.resolve();\n\n const slices = [];\n let numScheduled = 0\n let readyJobManagers = this.jobManagerInventory.filter(jm => jm.ready);\n let first = true;\n selectiveDebugging && console.debug('roundRobinSlices: START count', count, 'numJobMgrs', readyJobManagers.length, 'totalWorking(w/r/wo/wsbx/sbx)', this.workingSliceCount(), this.reservedSliceCount(), this.workingSliceOnlyCount(), this.workingSandboxCount(), this.sandboxCount());\n\n for (let numWorkingSandboxes = 1; numWorkingSandboxes <= this.maxWorkingSandboxes; numWorkingSandboxes++)\n {\n let sliceCount = 0;\n const beginNumScheduled = numScheduled;\n for (const jobMan of readyJobManagers)\n {\n const _readySlices = jobMan.readySlices;\n sliceCount += _readySlices.length\n const skip = numWorkingSandboxes <= jobMan.workingSlices.length;\n\n if (skip || _readySlices.length < 1)\n {\n // Noisy log message turned off by default.\n //debugging('supervisor') && console.debug('RRS0(numS, beginNumS, count, sliceCount, skip, _ready, numWorkingS(loop), workingSlices):', numScheduled, beginNumScheduled, count, sliceCount, skip, _readySlices.length, numWorkingSandboxes, jobMan.workingSlices.length);\n continue;\n }\n\n const slice = _readySlices[0];\n slices.push(slice);\n\n slice.markAsReserved();\n jobMan.runSlice(slice, first ? 0 : this.delayMs());\n\n first = false;\n if (++numScheduled >= count)\n break;\n }\n if (numScheduled >= count)\n {\n debugging('supervisor') && console.debug('RRS1(numS, beginNumS, count, sliceCount):', numScheduled, beginNumScheduled, count, sliceCount);\n break;\n }\n if (beginNumScheduled === numScheduled && sliceCount < 1)\n {\n debugging('supervisor') && console.debug('RRS2(numS, beginNumS, count, sliceCount):', numScheduled, beginNumScheduled, count, sliceCount);\n break;\n }\n }\n if (selectiveDebugging)\n {\n console.debug(`roundRobinSlices(working:(w/r/wo/wsbx/sbx)${this.workingSliceCount()},${this.reservedSliceCount()},${this.workingSliceOnlyCount()},${this.workingSandboxCount()},${this.sandboxCount()}): Started ${slices.length}/${numScheduled}/${count} scheduled slices`, compressSlices(slices));\n const sliceGrouper = {};\n slices.forEach((slice) => {\n const jm = slice.jobManager;\n if (!sliceGrouper[slice.jobAddress]) sliceGrouper[slice.jobAddress] = { cnt: 1, working: jm.workingSlices.length, queued: jm.queuedSlices.length, assigned: jm.assignedSandboxes.length, estimation: jm.isEstimation };\n else sliceGrouper[slice.jobAddress].cnt++;\n });\n console.debug(sliceGrouper);\n }\n if (selectiveDebugging2)\n {\n const jobGrouper = {};\n this.jobManagerInventory.forEach((jm) => {\n jobGrouper[jm.address] = { working: jm.workingSlices.length, queued: jm.queuedSlices.length, assigned: jm.assignedSandboxes.length, estimation: jm.isEstimation };\n });\n console.debug(jobGrouper);\n }\n }\n finally\n {\n this.isFetchingNewWork = false;\n }\n}\n\n/**\n * @callback cbNext\n * @returns {Slice}\n */\n\n/** \n * Factory function which instantiates a JobSelectionCursor. A JobSelectionCursor\n * steps the order that job slices should be selected for execution in the supervisor,\n * given the current state of the supervisor and the availability of jobs when the\n * inventory was snapshot. The entire slice scheduling algorithm is represented by\n * this cursor.\n *\n * The basic idea behind the scheduling of slices in this implementation is to keep as\n * many slices from different jobs running as possible, so as to reduce the likelihood\n * of resource contention between sandboxes.\n *\n * Slices are scheduled in here based on the following ruleset:\n * - pick a slice from the longest job that isn't running\n * - choose a slice from the remaining jobs, in order for shortest to longest slice time\n * - if there are any jobs which are nearly finished, every other slice comes from one\n * of these jobs, selected randomly????? <-- NYI. XXXpfr @todo Think about implementing...\n * - jobs which have slicePriority set by the task-distributor may be chosen in place\n * of slices in the above algorith. Jobs with a slicePriority closer to 1 are more likely\n * to exhibit this behaviour.\n * @param {JobManager[]} jobManagerInventory\n * @returns { { next: cbNext } }\n */\nSupervisor.prototype.makeJobSelectionCursor = function Supervisor$JobSelectionCursor (jobManagerInventory)\n{\n /* Variables in this scope function as state information for next() */\n var jobManIdx = 0;\n /** @type {JobManager[]} - All job managers that are ready that have at least one ready slice. */\n var jobManList;\n /** @type {JobManager[]} - All jobManList elements that correspond to preferred jobs. */\n var prefJobManList;\n /* Number of working sandboxes allowed for a given job. */\n var concurrency = 1;\n const that = this;\n \n function seed (_concurrency)\n {\n function countSandboxes(jobAddress)\n {\n const jobManager = that.jobMap[jobAddress];\n if (!jobManager) throw new Error(`Lost track of job manager for address '${jobAddress}'`);\n return jobManager.workingSlices.length;\n }\n \n // Reset.\n jobManIdx = 0;\n\n /* Populate jobManList with jobs which are ready and have at least one slice which is ready. */\n jobManList = jobManagerInventory.filter((jobMan) => jobMan.ready && jobMan.readySlices.length > 0);\n\n /* Populate jobManList with jobManagers whose # of working slices is less than _concurrency. */\n jobManList = jobManList.filter((jobMan) => countSandboxes(jobMan.address) < _concurrency);\n\n /* Increasing sort. */\n jobManList.sort((a,b) => a.estimateWallMs - b.estimateWallMs);\n\n /* Populate prefJobManList with jobs from jobManList which also have a slicePreference set. */\n prefJobManList = jobManList.filter(jobMan => jobMan.hasOwnProperty('slicePreference') );\n }\n\n /**\n * Takes slices off the ready list, marks it reserved and increments workingSandboxCoun,\n * because the slice will soon be working.\n * @param {JobManager} jobMan \n * @returns {Slice}\n */\n function reserveSlice (jobMan)\n {\n const _readySlices = jobMan.readySlices;\n if (_readySlices.length > 0)\n {\n const slice = _readySlices[0];\n slice.markAsReserved();\n return slice;\n }\n return null;\n }\n\n /**\n * Each invocation of next() identifies one slice to run, or returns false if none can run.\n * @returns {Slice}\n */\n function next ()\n {\n /* Adjust order to schedule the heaviest job's first slice asap. */\n jobManList.unshift(jobManList.pop());\n\n let workingSliceCount;\n do\n {\n seed(concurrency);\n\n /* Maybe schedule a prefered job slice based on random chance .*/\n if (prefJobManList.length > 0)\n {\n let prioRan = Math.random();\n let list = prefJobManList.filter(jm => jm['slicePreference'] >= prioRan);\n\n if (list.length > 0)\n {\n const jobMan = list[list.length * Math.random()];\n const slice = reserveSlice(jobMan);\n if (slice)\n return slice;\n }\n }\n\n /* Schedule a slice from next job; jobs are in increasing order of estimated run time. */\n while (jobManIdx < jobManList.length)\n {\n const jobMan = jobManList[jobManIdx];\n jobManIdx++;\n const slice = reserveSlice(jobMan);\n if (slice)\n return slice;\n }\n\n /* If this is reached, we did not schedule a slice with current seed. We need\n * to re-seed to look for newly-available work and sandboxes, ratcheting up the\n * concurrency (max # of each job running) until we find something we can do.\n */\n workingSliceCount = that.workingSliceCount();\n debugging() && console.debug(`job selection - no slice found from ${jobManList.length} jobs for concurrency=${concurrency} and ${workingSliceCount} working sandboxes`);\n } while (jobManList.length > 0 && workingSliceCount < that.maxWorkingSandboxes && concurrency++ < that.maxWorkingSandboxes);\n\n return false; /* Did not find any more work that fits. */\n }\n \n return { next };\n}\n\n/**\n * Handle sandbox.work(...) errors.\n * @param {Sandbox} sandbox \n * @param {Slice} slice \n * @param {Error} error \n * @return {Promise<string>}\n */\nSupervisor.prototype.handleSandboxWorkError = async function Supervisor$handleSandboxWorkError (sandbox, slice, error)\n{\n if (!slice.isWorking) // Sanity. Exception should never fire.\n throw new Error(`handleSandboxWorkError: slice ${slice.identifier} must be WORKING.`);\n\n let logLevel, reason;\n \n if (error instanceof SandboxError)\n {\n logLevel = 'warn';\n reason = error.errorCode;\n // The message and stack properties of error objects are not enumerable,\n // so they have to be copied into a plain object this way\n // @ts-ignore\n error = Object.getOwnPropertyNames(error).reduce((o, p) => {\n o[p] = error[p]; return o;\n }, { message: 'Unexpected worker error' });\n }\n else\n {\n logLevel = 'error';\n if (error)\n reason = `Slice ${slice.sliceNumber} in state ${slice.state} of job ${slice.jobAddress} failed to complete execution with error ${this.checkCode(error)}.`;\n else\n {\n reason = `Slice ${slice.sliceNumber} in state ${slice.state} of job ${slice.jobAddress} failed to complete execution.`;\n error = new Error(reason);\n }\n // This error was unrelated to the work being done, so just return the slice\n // in the promise.catch in JobManager.runSliceOnSandbox .\n assert(slice.result === null);\n }\n \n this.handleFailedSlice(slice, error);\n\n let errorString;\n switch (reason)\n {\n case 'ENOPROGRESS':\n errorString = 'No progress error in sandbox.\\n';\n break;\n case 'ESLICETOOSLOW':\n errorString = 'Slice too slow error in sandbox.\\n';\n break;\n case 'EUNCAUGHT':\n errorString = `Uncaught error in sandbox ${error.message}.\\n`;\n break;\n case 'EFETCH':\n // The status.js processing does not have a case for 'EFETCH' yet.\n errorString = `Could not fetch data: ${error.message}.\\n`;\n break;\n }\n\n // Always display max info under debug builds, otherwise maximal error.\n // messages are displayed to the worker, only if both worker and client agree.\n const displayMaxInfo = slice.jobManager.displayMaxDiagInfo;\n\n const errorObject = {\n jobAddress: truncateAddress(slice.jobAddress, addressTruncationLength),\n sliceNumber: slice.sliceNumber,\n sandbox: sandbox.id,\n jobName: sandbox.public ? sandbox.public.name : 'unnamed',\n };\n if (displayMaxInfo)\n errorObject.stack += '\\n --------------------\\n' + (error.stack.split('\\n').slice(1).join('\\n'));\n\n if (error.name === 'EWORKREJECT')\n {\n reason = 'EWORKREJECT'; // The status.js processing does not have a case for 'EWORKREJECT' yet.\n error.stack = 'Sandbox was terminated by work.reject()';\n await this.handleWorkReject(sandbox, slice, error.message);\n }\n else\n {\n this.returnSlice(slice, reason);\n slice.jobManager.returnSandbox(sandbox);\n }\n\n if (errorString)\n console[logLevel](errorString, errorObject);\n else if (error.name === 'EWORKREJECT')\n console[logLevel](`Slice rejected work: ${error.message}`)\n else\n console[logLevel](`Slice failed: ${error.message}\\n`, errorObject);\n\n return reason;\n}\n\n/**\n * Slice has thrown error during execution:\n * Mark slice as failed, compensate when job is dicrete, emit events.\n * @param {Slice} slice\n * @param {Error} error\n */\nSupervisor.prototype.handleFailedSlice = function Supervisor$handleFailedSlice (slice, error)\n{\n assert(error, 'error must be valid');\n slice.collectResult(error, false);\n\n // If the slice from a job never completes and the job address exists in the ringBufferofJobs,\n // then we remove it to allow for another slice (from the same job) to be obtained by fetchTask\n this.ringBufferofJobs.buf = this.ringBufferofJobs.filter(element => element !== slice.jobAddress);\n\n this.emit('submitSliceFailed', error);\n this.emit('submitFinished');\n}\n\n// _Idx\n//\n// Return slices and sent progress reports to result-submitter-results.\n// Return slices to result-submitter-status which marks the\n// corresponding row in activeSlices to be rescheduled on usually another worker.\n//\n\n/**\n * Bulk-return multiple slices, possibly for assorted jobs.\n * Returns slices to the scheduler to be redistributed.\n * Called in the sandbox terminate handler and purgeAllWork(jobAddress)\n * and stopWork(forceTerminate).\n *\n * @param {Slice[]} slices - The slices to return to the scheduler.\n * @param {string} [reason='unknown'] - Optional reason for the return: 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @param {boolean} [removeSlices=true] - When true, removes slices from this.sliceInventory .\n * @returns {Promise<*>} - Response from the scheduler.\n */\nSupervisor.prototype.returnSlices = function Supervisor$$returnSlices (slices, reason = 'unknown', removeSlices = true)\n{\n if (!slices || !slices.length) return Promise.resolve();\n debugging('supervisor') && console.debug('Supervisor.returnSlices: Returning slices', slices.map(slice => slice.identifier));\n\n const slicePayload = constructReturnSliceBuckets(slices, reason);\n if (removeSlices) slices.forEach((slice) => slice.jobManager.removeSlice(slice));\n\n try\n {\n return this.resultSubmitter.send('status', {\n worker: this.workerId,\n slices: slicePayload,\n }).catch(error => {\n const errorInfo = slices.map(slice => slice.identifier).sort();\n debuggingError && console.error('Failed to return slice(s)', { errorInfo, error }, 'Will try again on new connection.');\n return this.saveForResubmitToRS('status', { worker: this.workerId, slices: slicePayload });\n });\n }\n catch (error)\n {\n /* resultSubmitter can be null if worker is stopped */\n debuggingError && console.error(`Failed to return slices ${compressSlices(slices)}, no connection to result submitter:`, error);\n }\n}\n\n/** XXXpfr @todo TEMP -- Remove when sup2 replaces sup1 */\nfunction compressSlices(sliceArray)\n{\n const jobSliceMap = toJobMap(sliceArray, slice => slice.sliceNumber);\n return compressJobMap(jobSliceMap, false /* skipFirst*/, addressTruncationLength);\n}\n\n/**\n * Takes a slice and returns it to the scheduler to be redistributed.\n * Usually called when an exception is thrown by sandbox.work(...) .\n * Or when the supervisor tells it to forcibly stop working.\n *\n * @param {Slice} slice - The slice to return to the scheduler.\n * @param {string} [reason] - Optional reason for the return: 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @returns {Promise<*>} - Response from the scheduler.\n */\nSupervisor.prototype.returnSlice = function Supervisor$$returnSlice (slice, reason)\n{\n assert(slice.sliceNumber > 0 && slice.jobManager);\n debugging() && console.debug(`Supervisor.returnSlice: Returning slice ${slice.identifier} with reason ${reason}.`);\n\n if (!this.resultSubmitter)\n this.connectTo('resultSubmitter');\n\n try\n {\n slice.jobManager.removeSlice(slice);\n const payload = slice.getReturnMessagePayload(this.workerId, reason);\n return this.resultSubmitter.send('status', payload)\n .catch(error => {\n debuggingError && console.error('Failed to return slice', {\n sliceNumber: slice.sliceNumber,\n jobAddress: slice.jobAddress,\n status: slice.state.valueOf(),\n error,\n }, 'Will try again on a new connection.');\n return this.saveForResubmitToRS('status', payload);\n });\n }\n catch (error)\n {\n /* resultSubmitter can be null if worker is stopped */\n debuggingError && console.error(`Failed to return slice ${slice.identifier}, no connection to result submitter:`, error);\n }\n}\n\n/**\n * Send beacon to status.js for 'progress' and sliceStatus.scheduled.\n *\n * Run in an interval created in @constructor .\n * @returns {Promise<*>}\n */\nSupervisor.prototype.emitProgressReport = function emitProgressReport () \n{\n const slices = constructSliceBuckets( this.readySlices(), sliceStatus.scheduled );\n constructSliceBuckets( this.workingSlices(), 'progress', slices );\n\n debugging('supervisor') && console.debug('emitProgressReport:', stringify(slices));\n\n if (slices.length)\n {\n const progressReportPayload = {\n worker: this.workerId,\n slices,\n };\n\n try\n {\n return this.resultSubmitter.send('status', progressReportPayload)\n .catch(error => {\n debuggingError && console.error('479: Failed to send status beacon update:', error/*.message*/);\n return this.saveForResubmitToRS('status', progressReportPayload);\n });\n }\n catch (error) \n {\n /* resultSubmitter can be null if worker is stopped */\n debuggingError && console.error(`Failed to emit progress report, no connection to result submitter:`, error);\n }\n }\n}\n\n/**\n * Add a slice to the slice buckets being built. If a sliceBucket already exists for the\n * job-status-authMessage tuple, then the slice will be added to that, otherwise a new\n * sliceBucket will be added to the buckets.\n *\n * @param {Slice[]} slices - The slices.\n * @param {String} status - Status update, eg. progress or scheduled.\n * @param {Object[]} [sliceBuckets] - Slice buckets being built. Will be mutated in place.\n * @returns {Object[]} - mutated sliceBuckets array\n */\nfunction constructSliceBuckets (slices, status, sliceBuckets)\n{\n const jobMap = {};\n for (const slice of slices)\n {\n assert(slice.sliceNumber > 0 );\n if (!jobMap[slice.jobAddress]) jobMap[slice.jobAddress] = [];\n\n // Try to find a sliceBucket in the buckets which matches the job, status, and auth message.\n let sliceBucket = jobMap[slice.jobAddress].find(desc => {\n return desc.status === status\n && desc.authorizationMessage === slice.authorizationMessage;\n });\n\n if (!sliceBucket) jobMap[slice.jobAddress].push(slice.getMessage(status));\n else sliceBucket.sliceNumbers.push(slice.sliceNumber);\n }\n\n if (!sliceBuckets) return Object.values(jobMap);\n sliceBuckets.push(...Object.values(jobMap));\n return sliceBuckets;\n}\n \n/**\n * Add a slice to the returnSlice bucket being built. If a sliceBucket already exists for the\n * job-isEstimation-authMessage-reason tuple, then the slice will be added to that, otherwise a new\n * sliceBucket will be added to the buckets.\n *\n * @param {Slice[]} slices - The slices.\n * @param {String} [reason] - Optional reason to further characterize status; e.g. 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @param {Object[]} [sliceBuckets] - Optional slice buckets being built. Will be mutated in place.\n * @returns {Object[]} - mutated sliceBuckets array\n */\nfunction constructReturnSliceBuckets (slices, reason, sliceBuckets)\n{\n const jobMap = {};\n for (const slice of slices)\n {\n assert(slice.sliceNumber > 0 );\n if (!jobMap[slice.jobAddress]) jobMap[slice.jobAddress] = [];\n \n // Try to find a sliceBucket in the buckets which matches the job, estimation status, reason, and auth message.\n let sliceBucket = jobMap[slice.jobAddress].find(desc => {\n return desc.isEstimationSlice === slice.isEstimation\n && desc.authorizationMessage === slice.authorizationMessage\n && desc.reason === reason;\n });\n\n if (!sliceBucket) \n jobMap[slice.jobAddress].push(slice.getMessage('return', { isEstimationSlice: slice.isEstimation, reason }));\n else sliceBucket.sliceNumbers.push(slice.sliceNumber);\n }\n\n if (!sliceBuckets) return Object.values(jobMap);\n sliceBuckets.push(...Object.values(jobMap));\n return sliceBuckets;\n}\n \n// _Idx\n//\n// Task Distributor (TD): requestTask (Rq) support -- communication with TD.\n//\n\n/**\n * XXXpfr @todo Needs Work\n * For a given job, the scheduler stores an EMA approximation of average slice completion time in\n * jobPerfData.sliceCPUTime (and jobPerfData.sliceGPUTime, but we don't do the GPU analysis yet.)\n * However, each worker also tracks the same information and the ratio of local-info to scheduler-info\n * is returned by this.conversionQuantum so we can tell the task distributor how much work to return\n * from requestTask so that the work actually takes 5 minutes to complete when using all the worker sandboxes.\n * Note: \n * We average the completion times over the current jobs.\n * Define completion time in terms of sliceC(G)PUTime and sliceC(G)PUDensity\n * completion-time = (sliceCGPUTime + sliceCGPUTime) / ( sliceCPUDensity + sliceGPUDensity);\n * The local completion time is an EMA approximation of local completion-time as computed by Supervisor.recordResult.\n * The scheduler completion-time is computed directly from the corresponding row in jobPerfData.\n */\nSupervisor.prototype.conversionQuantum = function Supervisor$conversionQuantum()\n{\n let globalSpeed = 0, localSpeed = 0;\n for (const jobMan of this.jobManagerInventory)\n {\n const _globalTime = jobMan.globalTime;\n const _localTime = jobMan.statistics.ema;\n if (_globalTime > 0 && _localTime > 0)\n {\n //console.debug('conversionQuantum: local', _localTime, 'global', _globalTime);\n globalSpeed += _globalTime;\n localSpeed += _localTime;\n }\n }\n const conversion = globalSpeed > 0 ? localSpeed / globalSpeed : 1;\n return Math.min(Math.max(conversion, 0.2), 5.0); // Truncate if conversion is too bizarre.\n}\n\n/**\n * Remove all unreferenced jobs in this.jobManagerInventory and this.moduleCache.\n * Since job-managers are inserted into this.jobManagerInventory with a push, the job managers at the beginning are oldest.\n * Only delete #deleteCount of the oldest job-managers:\n * let deleteCount = this.jobManagerInventory.length - cachedJobsThreshold;\n * Edit cachedJobsThreshold to adjust the cache cleanup threshold.\n * @param {object[]} [newJobKeys=[]] - Jobs that should not be removed from this.jobManagerInventory and this.moduleCache.\n */\nSupervisor.prototype.clearUnusedJobManagersAndModuleCache = function Supervisor$clearUnusedJobManagersAndModuleCache(newJobKeys=[])\n{\n let deleteCount = this.jobManagerInventory.length - this.cachedJobsThreshold;\n if (deleteCount > 0)\n {\n selectiveDebugging && console.debug(`Supervisor.clearUnusedJobManagersAndModuleCache START: deleteCount ${deleteCount}/${this.jobManagerInventory.length}/${this.cachedJobsThreshold}.`);\n const jobMap = {};\n newJobKeys.forEach(jobAddress => { jobMap[jobAddress] = 1; });\n for (const jobManager of this.jobManagerInventory)\n {\n if (!jobMap[jobManager.address])\n {\n const sliceInventory = jobManager.sliceInventory.filter(slice => slice.isActive || slice.isQueued);\n if (sliceInventory.length < 1)\n {\n this.purgeJob(jobManager);\n if (--deleteCount < 1)\n break;\n }\n }\n }\n selectiveDebugging && console.debug(`Supervisor.clearUnusedJobManagersAndModuleCache FINISH: deleteCount ${deleteCount}/${this.jobManagerInventory.length}/${this.cachedJobsThreshold}.`);\n }\n}\n\n/**\n * Ask the scheduler (task distributor) for work.\n * @param {number} [unusedSandboxSlots]\n * @param {object[]} [jobs=[]] \n */\nSupervisor.prototype.requestTask = function Supervisor$requestTask (unusedSandboxSlots, jobs = [])\n{\n if (!this.isReady() || this.isFetchingNewWork)\n return Promise.resolve();\n\n if(!unusedSandboxSlots) unusedSandboxSlots = this.unusedSandboxSlots();\n if (unusedSandboxSlots < 1)\n {\n debugging('supervisor') && console.debug('requestTask: There are no unused sandbox slots.');\n return Promise.resolve();\n }\n\n // Refresh connections.\n this.instantiateAllConnections();\n\n // We prune for over this.maxTotalSandboxes about every 15 seconds, or when must prune level is reached.\n if (this.sandboxCount() > this.mustPruneSandboxLevel || Date.now() > this.lastPrune + this.pruneFrequency)\n {\n this.lastPrune = Date.now();\n this.pruneSandboxes();\n }\n\n try\n {\n this.isFetchingNewWork = true;\n const numCPUSlotToFill = this.numberOfAvailableSandboxSlots(unusedSandboxSlots);\n if (numCPUSlotToFill < 1)\n {\n //debugging() && console.debug('Predicted workload too high; not fetching additional work yet'); <-- Save Wes' msg...\n debugging() && console.debug('Supervisor.requestTask: We have enough, so start executing some slices.');\n return this.roundRobinSlices(); // roundRobinSlices guarantees this.isFetchingNewWork === false\n }\n\n /** XXXpfr @todo Get together with Wes to figure this out. */\n //let predictedLoad = this.predictLoad(Date.now() + ms(this.tuning.prefetchInterval)).load;\n\n const request = {\n numCores: numCPUSlotToFill,\n coreStats: this.getStatisticsCPU(),\n numGPUs: this.numGPU,\n //targetLoad: this.targetLoad.subtract(predictedLoad), /** XXXpfr @todo Get together with Wes to figure this out. */\n conversionQuantum: this.conversionQuantum(),\n capabilities: this.capabilities,\n paymentAddress: this.paymentAddress,\n jobAddresses: jobs.concat(this.options.jobAddresses || []), // When set, only fetches slices for these jobs.\n localExec: this.options.localExec,\n workerComputeGroups: this.generateWorkerComputeGroups(),\n minimumWage: workerTuning.minimumWage || this.options.minimumWage,\n loadedJobs: this.jobManagerInventory.map(jobMan => jobMan.address),\n readyJobs: this.jobManagerInventory.filter(jobMan => jobMan.ready).map(jobMan => jobMan.address),\n previouslyWorkedJobs: this.ringBufferofJobs.buf, // Only discrete jobs.\n rejectedJobs: this.rejectedJobs,\n };\n // Workers should be part of the public compute group by default.\n if (!booley(workerTuning.leavePublicGroup) && !booley(this.options.leavePublicGroup))\n request.workerComputeGroups.push(constants.computeGroups.public);\n\n // Call Task Distributor and handle response with this.addTaskToWorkload.\n this.fetchTask(request, (response) => this.addTaskToWorkload(request, response));\n }\n catch (error)\n {\n // Paranoid double-checking we don't accidently leave a live this.isFetchingNewWork.\n this.isFetchingNewWork = false;\n throw error;\n }\n}\n\n/** Gets the logical and physical number of cores and also the total number of sandboxes the worker is allowed to run. */\nSupervisor.prototype.getStatisticsCPU = function Supervisor$getStatisticsCPU ()\n{\n if (DCP_ENV.isBrowserPlatform)\n {\n return {\n worker: this.workerId,\n lCores: window.navigator.hardwareConcurrency,\n pCores: workerTuning.pCores || window.navigator.hardwareConcurrency,\n sandbox: this.maxWorkingSandboxes,\n }\n }\n\n return {\n worker: this.workerId,\n lCores: requireNative('os').cpus().length,\n pCores: requireNative('physical-cpu-count'),\n sandbox: this.maxWorkingSandboxes,\n }\n}\n\n/**\n * Callback for fetchTask.\n * @param {object} request \n * @param {object} response\n */\nSupervisor.prototype.addTaskToWorkload = function Supervisor$addTaskToWorkload (request, response)\n{\n try\n {\n const payload = response.payload;\n if (!response.success)\n {\n debugging() && console.debug('Task fetch failure; request=', request);\n debugging() && console.debug('Task fetch failure; response=', payload);\n // Only report errors when in 'ready' state.\n if (this.isReady()) throw new DCPError('Unable to fetch task for worker', payload);\n else return;\n }\n\n const sliceCount = payload.body.task.length || 0;\n if (sliceCount < 1)\n {\n if (selectiveDebugging2 && (this.lastTime + 7000 < Date.now()))\n {\n this.lastTime = Date.now();\n // Display the state of every slice.\n if (displaySliceState)\n {\n /** @type {JobManager} */\n const jm = this.jobManagerInventory.top();\n jm.dumpSlices(false /*details*/);\n }\n // Display completed results so far.\n if (displayCompletedResults && this.queuedSliceCount() < 1)\n {\n const values = Object.values(this.resultMap);\n if (values.length > 0)\n {\n values.forEach((descriptor) => descriptor.slices.sort((x,y) => x-y))\n console.debug(`Recorded results: job managers ${values.length}:`, this.resultMap);\n }\n }\n }\n this.emit('fetchedTask', { jobs: 0, slices: sliceCount });\n // There may be an extra slice to process.\n // roundRobinSlices guarantees this.isFetchingNewWork === false;\n return this.roundRobinSlices();\n }\n\n /**\n * payload structure: { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n * messageLightWeight: { workerId: worker, jobSlices, schedulerId, jobCommissions }\n * messageBody: { newJobs: await-getNewJobsForTask(dbScheduler, task, request), task }\n */\n const { body, ...authorizationMessage } = payload;\n /** @type {{ newJobs: object, task: SliceMessage[] }} */\n const { newJobs, task } = body;\n assert(newJobs); // It should not be possible to have !newJobs -- we throw on !success.\n const newJobKeys = Object.keys(newJobs);\n const jobCount = newJobKeys.length;\n\n /*\n * Ensure all jobs received from the scheduler (task distributor) are:\n * 1. If we have specified specific jobs the worker may work on, the received jobs are in the specified job list\n * 2. If we are in localExec, at most 1 unique job type was received (since localExec workers are designated for only one job)\n * If the received jobs are not within these parameters, stop the worker since the scheduler cannot be trusted at that point.\n */\n if (request.jobAddresses.length > 0 && !newJobKeys.every((ele) => request.jobAddresses.includes(ele))\n || request.localExec && jobCount > 1)\n {\n this.error(\"Worker received slices it shouldn't have. Rejecting the work and stopping.\");\n process.exit(1);\n }\n\n selectiveDebugging && console.debug(`Supervisor.addTaskToWorkload: task: ${task.length}/${request.numCores}/${this.maxWorkingSandboxes}, conversion: ${request.conversionQuantum}, jobs: ${jobCount}, authSlices: ${compressJobMap(authorizationMessage.auth.authSlices, true /* skipFirst*/, addressTruncationLength /* digits*/)}`);\n\n // Clear out job managers w/o any queued slices,\n // and remove corresponding job references from module cache.\n // When a cached module no longer has any job references it is removed from the cache.\n this.clearUnusedJobManagersAndModuleCache(newJobKeys);\n\n // this.jobMap: job.address --> jobManager\n /** @type {Object.<Address, JobManager>} */\n this.jobMap = {};\n this.jobManagerInventory.forEach(jobManager => {\n this.jobMap[jobManager.address] = jobManager;\n });\n\n /** @type {Object.<Address, SliceMessage[]>} */\n const jobSliceMap = {};\n task.forEach((element) => {\n const address = String(element.jobAddress);\n if (!jobSliceMap[address]) jobSliceMap[address] = [element];\n else jobSliceMap[address].push(element);\n });\n\n debugging('supervisor') && console.debug('requestTask: slices, newJobs and jobMap', task.length, Object.keys(newJobs), Object.keys(this.jobMap));\n\n // Populate the job managers with slices, creating new job managers when necessary.\n // Set up discrete job ring buffer.\n for (const [jobAddress, jobEl] of Object.entries(newJobs))\n {\n if (this.jobMap.hasOwnProperty(jobAddress))\n {\n /** @type {JobManager} */\n const jm = this.jobMap[jobAddress];\n jm.update(jobEl, jobSliceMap[jobAddress], authorizationMessage);\n }\n else\n {\n // Add the slice messages to the job manager ctor, so that slice construction is after job manager is ready.\n const jobManager = new JobManager(this, jobEl, jobSliceMap[jobAddress], authorizationMessage);\n this.jobMap[jobAddress] = jobManager;\n this.jobManagerInventory.push(jobManager);\n\n // Populate the ring buffer based on job's discrete property.\n assert(jobEl.requirements);\n if (jobEl.requirements.discrete && this.ringBufferofJobs.find(address => address === jobEl.address) === undefined)\n this.ringBufferofJobs.push(jobEl.address);\n }\n }\n\n /**\n * The requestTask event fires when the supervisor has finished trying to\n * fetch work from the scheduler (task distributor). The data emitted is the\n * number of jobs and new slices to work on in the fetched task.\n *\n * @event Supervisor#requestTask\n * @type {object}\n */\n this.emit('fetchedTask', { jobs: jobCount, slices: sliceCount });\n\n // Start working on the slices.\n setImmediate(() => this.roundRobinSlices());\n }\n catch (error)\n {\n this.isFetchingNewWork = false; // Paranoid double checking that we don't leave this.isFetchingNewWork live.\n this.emit('fetchTaskFailed', error);\n debuggingError && console.error('Supervisor.requestTask failed!', error);\n }\n}\n\n/**\n * Returns the number of unused sandbox slots to fill -- sent to requestTask.\n * @returns {number}\n */\nSupervisor.prototype.numberOfAvailableSandboxSlots = function Supervisor$numberOfAvailableSandboxSlots(unusedSandboxSlots)\n{\n const _readySlices = this.readySlices();\n let numCores;\n if (this.options.priorityOnly && this.options.jobAddresses.length === 0)\n numCores = 0;\n else if (_readySlices.length > 1) // We have slices ready, no need to fetch.\n numCores = 0;\n else\n {\n // There are almost no ready slices (there may be 0 or 1), fetch a full task.\n // The task is full, in the sense that it will contain slices whose\n // aggregate execution time is this.maxWorkingSandboxes * 5-minutes.\n // However, there can only be unusedSandboxSlots # of long slices.\n // Thus we need to know whether the last slice in this.readySlices() is long or not.\n // (A long slice has estimated execution time >= 5-minutes on an average worker.)\n const longSliceCount = (_readySlices.length > 0 && _readySlices[0].isLong) ? 1 : 0;\n numCores = unusedSandboxSlots - longSliceCount;\n debugging('supervisor') && console.debug('numberOfAvailableSandboxSlots', numCores, unusedSandboxSlots, longSliceCount);\n }\n return numCores;\n}\n\n/**\n * @callback cbAddTaskToWorkload\n * @param {Response} response\n * @returns {Promise<void>}\n */\n\n/**\n * Call to fetch new slices from task distributor.\n * @param {*} request\n * @param {cbAddTaskToWorkload} addTaskToWorkload\n * @returns {Promise<void>}\n */\nSupervisor.prototype.fetchTask = async function Supervisor$fetchTask (request, addTaskToWorkload)\n{\n // Fetch a new task if we have insufficient slices queued, then start workers\n assert(this.isFetchingNewWork);\n\n this.instantiateAllConnections();\n\n // Top up sandboxes when necessary.\n const moreSandboxes = this.maxWorkingSandboxes - this.sandboxCount();\n if (moreSandboxes > 0)\n {\n await this.carefullyCreateSandboxes(moreSandboxes)\n .then(() => this.checkCapabilities()); /** XXXpfr @todo Do we need to check capabilities again? */\n }\n\n const fetchTimeout = setTimeout(() => {\n this.isFetchingNewWork = false;\n this.emit('warning', 'Fetch exceeded timeout, will reconnect at next watchdog interval');\n safeClose(this.taskDistributor, 'Fetch timed out', Math.random() > 0.5).catch(error => {\n this.error('Failed to close task-distributor connection', error);\n });\n safeClose(this.resultSubmitter, 'Fetch timed out', Math.random() > 0.5).catch(error => {\n this.error('Failed to close result-submitter connection', error);\n });\n this.instantiateAllConnections();\n }, 3 * 60 * 1000); // Max out at 3 minutes to fetch.\n\n // Ensure result submitter and task distributor connections before fetching tasks.\n try\n {\n await this.taskDistributor.keepalive();\n await this.resultSubmitter.keepalive();\n await this.taskDistributor.keepalive();\n }\n catch (e)\n {\n this.isFetchingNewWork = false;\n this.error('Failed to connect to result submitter, refusing to fetch slices. Will try again at next fetch cycle.', e);\n clearTimeout(fetchTimeout);\n safeClose(this.taskDistributor, 'Failed to connect to task-distributor', true).catch(error => {\n this.error('Failed to close task-distributor connection', error);\n });\n safeClose(this.resultSubmitter, 'Failed to connect to result-submitter', true).catch(error => {\n this.error('Failed to close result-submitter connection', error);\n });\n return Promise.resolve();\n }\n\n this.emit('fetchingTask');\n\n if (!this.taskDistributor) return\n return this.taskDistributor.send('requestTask', request)\n .then((response) => {\n addTaskToWorkload(response);\n })\n .catch((error) => {\n this.isFetchingNewWork = false; // Redundant.\n this.emit('fetchTaskFailed', error);\n this.error('Unable to request task from scheduler. Will try again on a new connection.', error);\n safeClose(this.taskDistributor, 'Failed to request task.', true);\n })\n .finally(() => {\n this.isFetchingNewWork = false;\n clearTimeout(fetchTimeout);\n });\n}\n\n/**\n * Generate the workerComputeGroups property of the requestTask message. \n * \n * Concatenate the compute groups object from dcpConfig with the list of compute groups\n * from the supervisor, and remove the public group if accidentally present. Finally,\n * we transform joinSecrets/joinHash into joinHashHash for secure transmission.\n *\n * @note computeGroup objects with joinSecrets are mutated to record their hashes. This\n * affects the supervisor options and dcpConfig. Re-adding a joinSecret property\n * to one of these will cause the hash to be recomputed.\n */\nSupervisor.prototype.generateWorkerComputeGroups = function Supervisor$generateWorkerComputeGroups ()\n{\n let computeGroups = Object.values(workerTuning.computeGroups || {});\n if (this.options.computeGroups)\n computeGroups = computeGroups.concat(this.options.computeGroups);\n computeGroups = computeGroups.filter(group => group.id !== constants.computeGroups.public.id);\n const hashedComputeGroups = [];\n for (const group of computeGroups)\n {\n const groupCopy = Object.assign({}, group);\n if ((group.joinSecret || group.joinHash) && (!group.joinHashHash || this.lastDcpsid !== this.taskDistributor.dcpsid))\n {\n let joinHash;\n if (group.joinHash)\n joinHash = group.joinHash.replace(/\\s+/g, ''); // strip whitespace\n else\n joinHash = calculateJoinHash(groupCopy);\n\n groupCopy.joinHashHash = hash.calculate(hash.eh1, joinHash, this.taskDistributor.dcpsid);\n delete groupCopy.joinSecret;\n delete groupCopy.joinHash;\n debugging('computeGroups') && console.debug(`Calculated joinHash=${joinHash} for`, groupCopy);\n }\n hashedComputeGroups.push(groupCopy);\n }\n this.lastDcpsid = this.taskDistributor.dcpsid;\n debugging('computeGroups') && console.debug('Requesting ', computeGroups.length, 'non-public groups for session', this.lastDcpsid);\n return hashedComputeGroups;\n}\n\n// _Idx\n//\n// Aggregators from the job managers.\n// Note: Not all functions are used yet.\n//\n/** XXXpfr @todo Figure out which aggregators to keep. */\n\n/**\n * Gather the count of job managers with queuedSlices.\n * @returns {number}\n */\nSupervisor.prototype.activeJobCount = function Supervisor$activeJobCount ()\n{\n let count = 0;\n this.jobManagerInventory.forEach((jobManager) => {\n if (jobManager.queuedSlices.length > 0) count++;\n });\n return count;\n}\n\n/**\n * Gather the ready slices from the job managers.\n * @returns {Slice[]}\n */\nSupervisor.prototype.readySlices = function Supervisor$readySlices ()\n{\n const readySlices = [];\n this.jobManagerInventory.forEach((jobManager) => {\n readySlices.push(...jobManager.readySlices);\n });\n return readySlices;\n}\n\n/**\n * Gather the working slices in the job managers.\n * @returns {Slice[]}\n */\nSupervisor.prototype.workingSlices = function Supervisor$workingSlices ()\n{\n const workingSlices = [];\n this.jobManagerInventory.forEach((jobManager) => {\n workingSlices.push(...jobManager.workingSlices);\n });\n return workingSlices;\n}\n\n/**\n * Gather the count of various kinds of slices over all the job managers.\n * @param {string} predicate - 'all;, 'ready', 'queued', 'reserved', 'working', 'workingOnly'.\n * @returns {number}\n */\nSupervisor.prototype.predicateSliceCount = function Supervisor$predicateSliceCount (predicate)\n{\n let count = 0;\n switch (predicate)\n {\n case 'all':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.sliceInventory.length;\n });\n break\n case 'ready':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.readySlices.length;\n });\n break;\n case 'queued':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.queuedSlices.length;\n });\n break;\n case 'reserved':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.reservedSlices.length;\n });\n break;\n case 'working': // both working and reserved (viz., soon-to-be-working)\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.workingSlices.length;\n });\n break;\n case 'workingOnly':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.workingSlicesOnly.length;\n });\n break;\n }\n return count;\n}\n/** @returns {number} */\nSupervisor.prototype.sliceCount = function Supervisor$sliceCount () { return this.predicateSliceCount('all'); }\n/** @returns {number} */\nSupervisor.prototype.readySliceCount = function Supervisor$readySliceCount () { return this.predicateSliceCount('ready'); }\n/** @returns {number} */\nSupervisor.prototype.queuedSliceCount = function Supervisor$queuedSliceCount () { return this.predicateSliceCount('queued'); }\n/** @returns {number} */\nSupervisor.prototype.reservedSliceCount = function Supervisor$reservedSliceCount () { return this.predicateSliceCount('reserved'); }\n/** @returns {number} */\nSupervisor.prototype.workingSliceCount = function Supervisor$workingSliceCount () { return this.predicateSliceCount('working'); }\n/** @returns {number} */\nSupervisor.prototype.workingSliceOnlyCount = function Supervisor$workingSliceOnlyCount () { return this.predicateSliceCount('workingOnly'); }\n\n/**\n * Gather the count of working sandboxes over all the job managers.\n * @returns {number}\n */\nSupervisor.prototype.sandboxCount = function Supervisor$sandboxCount ()\n{\n return this.readiedSandboxes.length + this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated).length;\n}\n\n/**\n * Gather the count of working sandboxes over all the job managers.\n * @returns {number}\n */\nSupervisor.prototype.workingSandboxCount = function Supervisor$workingSandboxCount ()\n{\n return this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated && sandbox.isWorking).length;\n}\n\n// _Idx\n//\n// Sandbox creation and management.\n// \n\n/**\n * Create and start a Sandbox.\n * When this.readiedSandboxes.length > 0, use one of those sandboxes, instead of creating a new one.\n * @param {number} [delayMs=0] - The delay ms when calling sandbox.start(delayMs) .\n * @returns {Promise<Sandbox>}\n */\nSupervisor.prototype.createSandbox = function Supervisor$createSandbox (delayMs = 0)\n{\n const that = this;\n function getReadiedSandbox()\n {\n const sandbox = that.readiedSandboxes.pop();\n that.sandboxInventory.push(sandbox);\n return Promise.resolve(sandbox);\n }\n\n if (this.readiedSandboxes.length > 0)\n return getReadiedSandbox();\n\n // Do not place in this.readiedSandboxes, we'll directly use the return value of createSandbox.\n return this.createNewSandbox(delayMs, true/*putInInventory*/)\n .catch(() => {\n return this.carefullyCreateSandboxes(1)\n .then(() => {\n return getReadiedSandbox();\n });\n });\n}\n \n/**\n * Create and start a Sandbox.\n * Store it in this.readiedSandboxes or this.sandboxInventory according to putInInventory.\n * @param {number} [delayMs=0] - Millisecond delay when calling sandbox.start(delayMs), otherwise return it and use it.\n * @param {boolean} [putInInventory=false]\n * @returns {Promise<Sandbox>}\n */\nSupervisor.prototype.createNewSandbox = function Supervisor$createNewSandbox (delayMs = 0, putInInventory = false)\n{\n const rawSandbox = new Sandbox(this, { ...this.options.sandboxOptions });\n this.hookUpSandboxListeners(rawSandbox);\n return rawSandbox.start(delayMs)\n .then((sandbox) => {\n if (putInInventory) this.sandboxInventory.push(sandbox);\n else this.readiedSandboxes.push(sandbox);\n return sandbox;\n })\n .catch((error) => {\n if (!error) error = new Error('Unknown error creating sandbox.');\n debuggingWarn && console.warn(`Supervisor.createNewSandbox: Failed to start sandbox ${rawSandbox.identifier}.`, error);\n rawSandbox.terminate(false);\n if (error.code === 'ENOWORKER')\n throw new DCPError(\"Cannot use localExec without dcp-worker installed. Use the command 'npm install dcp-worker' to install the neccessary modules.\", 'ENOWORKER');\n throw error;\n });\n}\n\n/**\n * Bulk: create and start sandboxes and save in this.readiedSandboxes.\n * Call this function when there's a chance the evaluator is down.\n * @param {number} count - The number of sandboxes to create.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.carefullyCreateSandboxes = async function Supervisor$carefullyCreateSandboxes (count)\n{\n if (count < 1) return;\n // If the evaluator cannot start (e.g. if the evalServer is not running),\n // then the while loop will keep retrying until the evalServer comes online.\n let retry = 0;\n while (true)\n {\n try\n {\n await this.createNewSandbox();\n if (count > 1)\n this.createSandboxes(count - 1);\n }\n catch (error)\n {\n if (error.code === 'ENOWORKER') throw error;\n // Now assume the evaluator is down and keep retrying.\n /** XXXpfr @todo Need better indicator that evaluator is down. */\n if ((retry++ % 6) === 0)\n this.error(`Failed to ready sandboxes; will keep retrying: ${this.checkCode(error)}`);\n await a$sleepMs(1000 * Math.max(5, retry));\n }\n }\n}\n\n/**\n * Bulk: create and start sandboxes and save in this.readiedSandboxes.\n * @param {number} count - The number of sandboxes to create.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.createSandboxes = async function Supervisor$createSandboxes (count)\n{\n assert(count > 0);\n const promises = [], errors = [];\n for (let k = 0; k < count; k++)\n {\n promises.push(\n this.createNewSandbox(k === 0 ? 0: this.delayMs())\n .catch((error) => errors.push(this.checkCode(error))));\n }\n\n await Promise.all(promises);\n\n if (errors.length)\n this.emit('warning', `Failed to ready ${errors.length} of ${count} sandboxes: ${errors.map(err => err.message)}`);\n\n // Sort so that pop() will return sandboxes in increasing order.\n this.readiedSandboxes.sort((x,y) => y.id - x.id);\n\n debugging('supervisor') && console.debug(`createSandboxes: Created ${count-errors.length} sandboxes.`, this.readiedSandboxes.map(s => s.id));\n}\n\n/**\n * For a given sandbox, hook up all the Sandbox listeners.\n * @param {Sandbox} sandbox \n */\nSupervisor.prototype.hookUpSandboxListeners = function hookUpSandboxListeners (sandbox) \n{\n sandbox.addListener('ready', () => this.emit('sandboxReady', sandbox));\n\n sandbox.addListener('start', () => {\n this.emit('sandboxStart', sandbox);\n\n if (sandbox.slice)\n {\n try\n {\n const statusPayload = sandbox.slice.getMessagePayload(this.workerId, 'begin');\n return this.resultSubmitter.send('status', statusPayload).catch((error) => {\n debuggingError && console.error(`Error sending 'status' for slice ${sandbox.slice.identifier}:\\n\\t${error}\\n\\tWill try again on a new connection`);\n return this.saveForResubmitToRS('status', statusPayload);\n });\n } \n catch (error)\n {\n /* resultSubmitterConnection can be null if worker is stopped */\n debuggingError && console.error(`Failed to send 'begin' status for slice ${sandbox.slice.identifier}, no connection to result submitter`, error);\n }\n }\n });\n\n sandbox.addListener('workEmit', ({ eventName, payload }) => {\n // Need to check if the sandbox hasn't been assigned a slice yet.\n if (!sandbox.slice)\n this.error(`Sandbox not assigned a slice before sending workEmit message to scheduler.\\n\\t'workEmit' event originates from '${eventName}' event`);\n else\n {\n const slice = sandbox.slice;\n // Sometimes a sliceNumber===0 workEmit comes in before the client bundle is properly loaded.\n // Also happens with minor dcp-client version mismatches.\n // sliceNumber===0 <==> authorizationMessage undefined...\n if (!slice.authorizationMessage)\n this.emit('warning', `workEmit: missing authorization message for slice ${slice.identifier}`);\n else if (this.eventRouter) // No reason to emit if event router is closed.\n {\n const workEmitPayload = {\n eventName,\n payload,\n job: slice.jobAddress,\n slice: slice.sliceNumber,\n worker: this.workerId,\n authorizationMessage : slice.authorizationMessage,\n };\n\n const workEmitPromise = this.eventRouter.send('workEmit', workEmitPayload).catch(error => {\n debuggingWarn && console.warn(`workEmit: Unable to send ${eventName} for slice ${slice.identifier}: ${error.message}.\\n\\tTrying again on a new connection.`);\n this.eventRouterMessageQueue.push({ operation: 'workEmit', data: workEmitPayload })\n safeClose(this.eventRouter); // stopWork could slip-in during eventRouter.send\n if (this.debugBuild) this.error('workEmit error:', error);\n });\n\n if (this.debugBuild)\n {\n workEmitPromise.then(result => {\n if (!result) this.emit('warning', `workEmit: Event router did not accept event ${eventName}`);\n });\n }\n }\n }\n });\n\n sandbox.on('rejectedWorkMetrics', (data) => {\n // If the slice already has rejectedTimeReport, add this data to it. If not, assign this data to slices rejectedTimeReport property\n if (sandbox.slice) \n {\n if (!sandbox.slice.rejectedTimeReport) sandbox.slice.rejectedTimeReport = data.timeReport;\n else \n {\n ['total', 'CPU', 'webGL'].forEach((key) => {\n if (data.timeReport[key]) sandbox.slice.rejectedTimeReport[key] += data.timeReport[key];\n })\n }\n }\n });\n\n // If the sandbox terminated and we are not shutting down, then we should return all work which is\n // currently not being computed if all sandboxes are dead and the attempt to create a new one fails.\n sandbox.on('terminated', async () => {\n let nonTerminatedSandboxes = this.sandboxInventory.filter(sbx => !sbx.isTerminated);\n if (nonTerminatedSandboxes.length === 0 && this.worker.working)\n {\n debugging('supervisor') && console.debug(`hookUpSandboxListeners: Try to create 1 sandbox in the sandbox-terminated-handler.`);\n const _sandbox = await this.createNewSandbox()\n .catch((error) => {\n debugging('supervisor') && console.warn('Failed to replace terminated sandbox; evalserver may be gone.', error.message);\n error.message = 'Failed to replace terminated sandbox: ' + error.message;\n this.emit('warning', error);\n });\n\n // If we cannot create a new sandbox, that probably means we're on a screensaver worker\n // and the screensaver is down. So return the slices to the scheduler.\n if (!_sandbox) this.screenSaverDestroy();\n }\n });\n\n sandbox.on('error', (error) => this.emit('error', error));\n sandbox.on('warning', (warning) => this.emit('warning', warning));\n}\n\n/**\n * Terminate extra sandboxes over the limit: this.maxTotalSandboxes.\n * First terminate assigned sandboxes which are unlikely to be used with the current ready slices.\n * Then terminate the unassigned sandboxes: this.readiedSandboxes.\n * (There should be no readied sandboxes at this point.)\n * Then round-robin prune 1 assigned sandbox from each jobmanager.\n * XXXpfr @todo Prioritize sandboxes that we wish to keep.\n * E.g. When a sandbox is especially expensive to assign.\n */\nSupervisor.prototype.pruneSandboxes = function Supervisor$pruneSandboxes () \n{\n let pruneCount = this.sandboxCount() - this.maxTotalSandboxes;\n if (pruneCount <= 0) return;\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes START: pruneCount ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n // Only prune the extras: jm.assignedSandboxes.length > jm.queuedSlices.length .\n // Round-robin prune 1 extra assigned sandbox from each jobmanager.\n const readyJobManagers = this.jobManagerInventory.filter(jm => jm.ready);\n while (true)\n {\n const _pruneCount = pruneCount;\n for (const jm of readyJobManagers)\n {\n if (jm.pruneExtraAssignedSandbox())\n {\n if (--pruneCount < 1)\n {\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n return;\n }\n }\n }\n if (pruneCount === _pruneCount)\n break;\n }\n assert(pruneCount > 0);\n // Prune the excess non-assigned sandboxes -- we should never hit this.\n if (this.readiedSandboxes.length > 0)\n {\n const toPrune = this.readiedSandboxes.slice(0, pruneCount);\n this.readiedSandboxes = this.readiedSandboxes.slice(pruneCount);\n toPrune.forEach(sandbox => sandbox.terminate(false));\n pruneCount -= toPrune.length;\n if (pruneCount < 1)\n {\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n return;\n }\n }\n // Round-robin prune 1 assigned sandbox from each jobmanager.\n while (true)\n {\n const _pruneCount = pruneCount;\n for (const jm of readyJobManagers)\n {\n if (jm.pruneAssignedSandbox())\n {\n if (--pruneCount < 1)\n {\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n return;\n }\n }\n }\n if (pruneCount === _pruneCount)\n break;\n }\n this.sandboxInventory = this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated);\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n}\n\n// _Idx\n//\n// Result-submitter-result support functions.\n// Send in the results!!!\n//\n\n/**\n * Submits the slice results to the result-submitter service.\n * Then remove the slice from the its job manager.\n *\n * @param {Slice} slice - The slice to submit.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.recordResult = function Supervisor$recordResult (slice)\n{\n // It is possible for slice.result to be undefined when there are upstream errors.\n if ( !(slice && slice.result))\n throw new Error(`recordResult: slice.result is undefined for slice ${slice.identifier}. This is ok when there are upstream errors.`); \n if (!slice.isComplete)\n throw new Error('Cannot record result for slice that has not completed execution successfully.');\n\n debugging('supervisor') && console.debug(`supervisor: recording result for slice ${slice.identifier}.`);\n\n /* @see result-submitter::result for full message details */\n const metrics = { GPUTime: 0, CPUTime: 0, CPUDensity: 0, GPUDensity: 0, total: 0 };\n const payloadData = {\n slice: slice.sliceNumber,\n job: slice.jobAddress,\n worker: this.workerId,\n paymentAddress: this.paymentAddress,\n metrics,\n authorizationMessage: slice.authorizationMessage,\n }\n\n const timeReport = slice.timeReport;\n if (timeReport)\n {\n debugging('supervisor') && console.debug('recordResult timeReport', timeReport);\n // If slice takes less than 1ms to execute, CPUTime will be 0, so compensate.\n if (timeReport.CPU < 1)\n {\n timeReport.CPU++;\n timeReport.total++;\n }\n if (timeReport.total < timeReport.CPU + timeReport.webGL)\n {\n // Compensate or throw? For now we compensate.\n debuggingWarn && console.warn(`Supervisor.recordResult:: Inconsistent time report -- total < CPU + webGL -- ${stringify(timeReport)}`)\n //throw new Error(`recordResult: Inconsistent time report -- total < CPU + webGL -- ${stringify(timeReport)}`)\n timeReport.total = timeReport.CPU + timeReport.webGL;\n }\n if (timeReport.total > 0)\n {\n slice.jobManager.updateStatistics(timeReport);\n metrics.total = timeReport.total;\n metrics.CPUTime = timeReport.CPU;\n metrics.GPUTime = timeReport.webGL;\n metrics.CPUDensity = metrics.CPUTime / timeReport.total;\n metrics.GPUDensity = metrics.GPUTime / timeReport.total;\n }\n }\n\n this.emit('submittingResult');\n\n if (!this.resultSubmitter)\n this.connectTo('resultSubmitter');\n\n if (slice.resultStorageType === 'pattern')\n return this.sendResultToRemote(slice)\n .then((response) => {\n payloadData.result = response;\n this.sendToResultSubmitter(slice, payloadData);\n });\n\n payloadData.result = encodeDataURI(slice.result.result);\n return this.sendToResultSubmitter(slice, payloadData);\n}\n\n/**\n * @param {Slice} slice\n * @param {*} payloadData\n * @returns {Promise<void>}\n */\nSupervisor.prototype.sendToResultSubmitter = function Supervisor$sendToResultSubmitter (slice, payloadData)\n{\n const that = this;\n function handleRSError (error, payloadData)\n {\n that.error(`Failed to submit results to scheduler for slice ${payloadData.slice} of job ${payloadData.job}`, error);\n //slice.jobManager.dumpSlices('recordResult');\n that.saveForResubmitToRS('result', payloadData)\n .then((msg) => {\n if (!error && msg) error = new Error(`resultSubmitter is ${msg}`);\n that.emit('submitSliceFailed', error);\n throw error;\n });\n }\n\n try\n {\n debugging('supervisor') && console.debug('Supervisor.recordResult: payloadData', payloadData.result.slice(0, 256), slice.identifier);\n\n return this.resultSubmitter.send('result', payloadData)\n .then((resp) => {\n if (!resp.success)\n throw resp.payload;\n\n debugging('supervisor') && console.debug('recordResult: SUCCESS', slice.identifier);\n\n const receipt = {\n accepted: true,\n payment: resp.payload.slicePaymentAmount,\n };\n this.emit('submittedResult', resp.payload);\n this.emit('dccCredit', receipt);\n })\n .catch ((error) => {\n handleRSError (error, payloadData);\n });\n }\n catch (error)\n {\n handleRSError (error, payloadData);\n }\n finally\n {\n slice.markAsFinished();\n this.emit('submitFinished');\n // Remove the slice from the job manager.\n slice.jobManager.removeSlice(slice);\n if (this.sliceTiming)\n {\n slice['resultDelta'] = Date.now() - slice['resultDelta'];\n console.debug(`recordResult(${slice['queueingDelta']}, ${slice['executionDelta']}, ${slice['resultDelta']}): Completed slice ${slice.identifier}.`);\n }\n if (selectiveDebugging)\n {\n if (!this.resultMap[slice.jobAddress]) this.resultMap[slice.jobAddress] = { slices: [], totalTimes: [] };\n this.resultMap[slice.jobAddress].slices.push(slice.sliceNumber);\n this.resultMap[slice.jobAddress].totalTimes.push(payloadData.metrics.total);\n }\n }\n}\n\n/**\n * Send a work function's result to a server that speaks our DCP Remote Data Server protocol.\n * E.g. https://gitlab.com/Distributed-Compute-Protocol/dcp-rds\n *\n * @param {Slice} slice - Slice object whose result we are sending.\n * @returns {Promise<string>}\n * @throws When HTTP status not in the 2xx range.\n */\nSupervisor.prototype.sendResultToRemote = function Supervisor$sendResultToRemote (slice)\n{ \n /** XXXpfr @todo: Support file upload and other contentTypes. */\n function serializeContent(result, postParams) {\n const ctArray = postParams.contentType.split(';');\n switch (ctArray[0]) {\n case 'application/json':\n return JSON.stringify(result);\n case 'application/kvin':\n return (__webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\").serialize)(result);\n case 'application/octet-stream':\n case 'application/gzip':\n if (ctArray.length === 2 && ctArray[1] === 'base64')\n return btoa(result);\n // fall-through\n case 'text/plain':\n case 'text/html':\n case 'text/css':\n case 'text/csv':\n case 'text/javascript':\n return result;\n\n default:\n {\n const mtArray = ctArray[0].split('/');\n if (mtArray[0] === 'image' || mtArray[0] === 'video' || mtArray[0] === 'audio')\n return (ctArray.length === 2 && ctArray[1] === 'base64') ? btoa(result) : result;\n throw new Error(`sendResultToRemote: Unsupported contentType ${postParams.contentType}`);\n }\n }\n }\n\n // Construct postParams.\n const postParams = { ...slice.resultStorageParams };\n if (!postParams.contentType)\n postParams.contentType = 'application/json';\n if (!postParams.element)\n postParams.element = slice.sliceNumber;\n debugging('supervisor') && console.debug('sendResultToRemote postParams: ', postParams);\n\n // Construct result.\n const result = slice.result.result;\n if (result)\n postParams.content = serializeContent(result, postParams);\n else\n postParams.error = serializeContent(slice.error, postParams);\n debugging('supervisor') && console.debug('sendResultToRemote content: ', (result ? postParams.content : postParams.error).slice(0, 512));\n\n // Construct url.\n const sliceResultUri = makeValueURI('pattern', slice.resultStorageDetails, {\n slice: slice.sliceNumber,\n job: slice.jobAddress,\n });\n debugging() && console.debug('sendResultToRemote sliceResultUri: ', sliceResultUri);\n const url = new DcpURL(sliceResultUri);\n\n // Check allowed origins.\n if (this.makeSafeOriginList('sendResults').indexOf(url.origin) === -1)\n throw new Error(`Invalid origin for remote result storage: '${url.origin}'`);\n\n // Fetch.\n return justFetch(url, 'JSON', 'POST', false, postParams)\n .then((response) => encodeDataURI(JSON.stringify(response)));\n}\n\n// _Idx\n//\n// Reject.\n//\n\n/**\n * Handles reassigning or returning a slice that was rejected by a sandbox.\n *\n * If the slice does not have a rejected property already, reassign the\n * slice to a new sandbox and add a rejected property to the slice to\n * indicate it has already rejected once.\n *\n * If the slice rejects with a reason, or has a rejected time stamp\n * (ie. has been rejected once already) then return all slices from the\n * job to the scheduler and terminate all sandboxes with that jobAddress.\n *\n * The sandbox will be terminated.\n *\n * @param {Sandbox} sandbox\n * @param {Slice} slice\n */\nSupervisor.prototype.handleWorkReject = async function Supervisor$handleWorkReject (sandbox, slice, rejectReason)\n{\n debugging() && console.debug('handleWorkReject', rejectReason, slice.rejectedTimeStamp, slice.identifier);\n\n // Do a hard flush of the microtask queue and finish the current event loop.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)));\n\n const jobManager = slice.jobManager;\n jobManager.rejectedJobReasons.push(rejectReason); // memoize reasons\n\n // First time rejecting without a reason. Try assigning slice to a new sandbox.\n if (rejectReason === 'false' && !slice.rejectedTimeStamp)\n {\n // Set rejected time stamp.\n slice.rejectedTimeStamp = Date.now();\n // Schedule the slice for execution.\n jobManager.scheduleSlice(slice, true /* placeInTheFrontOfTheQueue*/);\n // Slice has been rescheduled, but we still need to terminate the sandbox.\n jobManager.returnSandbox(sandbox);\n }\n else\n { \n // Slice has a reason OR rejected without a reason already and got stamped.\n // Add to array of rejected jobs.\n let rejectedJob = {\n address: slice.jobAddress,\n reasons: jobManager.rejectedJobReasons,\n }\n this.rejectedJobs.push(rejectedJob);\n\n // Purge the job.\n this.purgeJob(jobManager);\n\n // Tell everyone all about it, when allowed.\n if (jobManager.displayMaxDiagInfo)\n {\n const suffixMsg = '\\n\\tAll slices with the same jobAddress returned to the scheduler.\\n\\tAll sandboxes with the same jobAddress are terminated.';\n if (slice.rejectedTimeStamp)\n this.emit('warning', `work.reject: The slice ${slice.identifier} was rejected twice.${suffixMsg}`);\n else\n this.emit('warning', `work.reject: The slice ${slice.identifier} was rejected with reason ${rejectReason}.${suffixMsg}`);\n }\n }\n}\n\n// _Idx\n//\n// Unused functions that we need to review.\n// 1) destroy, shutdown, halt -- possibly need to incorporate these ideas in stopWork\n// 2) predictLoad -- XXXpfr: I really feel bad about not being able to figure out how to incorporate\n// this into the design of sup2. This was a central part of Wes' design of sup2.\n// I need to collaborate with Wes to resolve my ignorance.\n//\n\n/**\n * UNUSED\n * @deprecated -- may use later\n **/\nSupervisor.prototype.destroy = function Supervisor$destory()\n{\n selectiveDebugging && console.debug(`Supervisor.screenSaverDestroy: destroying Supervisor and everything else.`);\n this.stopWork(true /*forceTerminate*/);\n if (this.state) this.state.destroy();\n if (this.progressReportTimer) clearInterval(this.progressReportTimer);\n if (this.watchdogTimer) clearInterval(this.watchdogTimer);\n this.state = null;\n this.progressReportTimer = null;\n this.watchdogTimer = null;\n this.jobManagerInventory = null;\n this.sandboxInventory = [];\n this.readiedSandboxes = [];\n this.closeConnections();\n}\n\n/**\n * UNUSED\n * @deprecated -- may use later \n * Halt the Supervisor as quickly as possible.\n **/\nSupervisor.prototype.halt = function Supervisor$halt()\n{\n this.state.setIf('ready', 'stopping');\n if (!this.state.is('stopping'))\n throw new Error(`Supervisor has an invalid state ${this.state} for halt`);\n clearInterval(this.watchdogTimer);\n\n for (let jobMan of this.jobManagerInventory)\n {\n jobMan.state.setIf('ready', 'stop');\n for (const sandbox of jobMan.workingSandboxes)\n {\n sandbox.stop(); // NYI -- will terminate.\n }\n }\n}\n \n/**\n * UNUSED\n * @deprecated -- may use later \n * Shutdown the supervisor; attempts to return work which will not be finished before timeout expires.\n * The shutdown is complete once this supervisor emits the stopped state change.\n */\nSupervisor.prototype.shutdown = function Supervisor$shutdown(timeoutMs)\n{\n var ps = [], returnSliceInventory = [];\n var timer;\n\n this.state.setIf('ready', 'stopping');\n if (!this.state.is('stopping'))\n throw new Error(`Supervisor has an invalid state ${this.state} for shutdown`);\n clearInterval(this.watchdogTimer);\n\n for (let jobMan of this.jobManagerInventory)\n {\n jobMan.state.setIf('ready', 'stop');\n\n for (let slice of jobMan.sliceInventory)\n {\n if (slice.state.is('initial') || slice.state.is('ready'))\n {\n returnSliceInventory.push(slice);\n }\n else if (slice.state.is(sliceStatus.working))\n {\n ps.push(new Promise((resolve, reject) => {\n slice.state.on('change', (status) => {\n if (status === 'done')\n resolve();\n });\n }));\n }\n }\n }\n\n const reason = 'Supervisor.shutdown';\n this.returnSlices(returnSliceInventory, reason);\n timer = setTimeout(this.halt.bind(this), timeoutMs);\n Promise.all(ps)\n .then(() => {\n clearTimeout(timer);\n this.state.set('stopping', 'stopped');\n })\n .catch((e) => {\n if (e.code !== 'DCP_SUPERVISOR_ESYNC')\n throw e; /* becomes unhandled rejection */\n });\n}\n\n/** \n * Factory function which generates a list of origins which are safe to communicate \n * with for this purpose. Currently-valid purposes (more will be added):\n * - any\n * - fetchData\n * - fetchWorkFunctions\n * - fetchArguments\n * - sendResults\n */\nSupervisor.prototype.makeSafeOriginList = function Supervisor$$makeSafeOriginList(purpose)\n{\n var list = [];\n \n if (this[purpose])\n list = list.concat(this[purpose]);\n \n /* Add 'any' origin(s) to list iff not in localExec, or in localExec and purpose is sendResults */\n if (!this.options.localExec || (this.options.localExec && purpose === 'sendResults'))\n list = list.concat(this.allowedOrigins)\n \n return list;\n}\n \n/**\n * UNUSED -- DOES NOT WORK YET.\n * NEED TO WORK WITH WES TO FIGURE OUT BEST WAY TO GET PREDICTLOAD TO WORK.\n * Predict the load on this supervisor based on the local job measurement data.\n * Works by looking at current conditions and available slices, and tries to guess\n * in what order they will be finished, working, etc. \n *\n * The simulation is very naive, but is expected to be accurate several seconds\n * into the future, particularly as we approach the end of a task.\n *\n * @param {number} whenMs \n * \n * @returns {Object<load, jobManagerInventory>} where load is and instance of Load and the predicted \n * load at the prediction time, and jobManagerInventory \n * is a counterfeit which holds the predicted state of \n * the jobManagerInventory at that time.\n */\nSupervisor.prototype.predictLoad = function Supervisor$predictLoad (whenMs)\n{\n /** @type {JobManager[]} */\n var jmi = new Inventory(); /* Inventory of counterfeit JobManagers. */\n var load = new Load(0,0); /* This \"current\" load throughout the prediction. */\n /** @type {Slice} */\n var next; /* The next slice to \"finish\". */\n\n /* Initialize data structures for prediction from current activity. */\n for (let jobMan of this.jobManagerInventory.filter(jm => jm.state.is('ready') && jm.sliceInventory.length))\n {\n jobMan = jobMan.counterfeit();\n jmi.push(jobMan);\n jobMan.sliceInventory.forEach((s) => s.state.setIf('initial', 'ready'));\n }\n next = findNextSlice();\n \n /**\n * Routine that finds the slice that will end next (soonest.)\n * @returns {Slice}\n */\n function findNextSlice()\n {\n /** @type {Slice} */\n var _next;\n for (let jobMan of jmi)\n {\n const _workingSlices = jobMan.workingSlices;\n for (let slice of _workingSlices)\n {\n //\n // slice.etaMs is the estimated time interval until slice execution completes.\n //\n // If the slice hasn't started,\n // slice.etaMs = slice.jobManager.estimateWallMs,\n // else if the slice has completed execution:\n // slice.etaMs = 0.\n // else if the slice has started:\n // slice.jobManager.estimateWallMs - (Date.now() - slice.startTime).\n //\n if (_next && (_next.etaMs <= slice.etaMs))\n continue;\n\n _next = slice;\n }\n }\n load.add(_next.jobManager.metrics);\n \n return _next;\n }\n\n /* At this point, jmi is an Inventory of counterfeit job managers that are \"ready\" for\n * work, next.etaMs is the time interval until the next slice will finish, and we have\n * a reasonably accurate picture of our current load.\n *\n * Next, we \"end\" this slice, try to fill all cores, and push the timeline forward to\n * the next predicted end of slice.\n */\n for (next = findNextSlice();\n next && (next.etaMs < whenMs);\n next = findNextSlice())\n {\n let ended = next;\n let cursor = this.makeJobSelectionCursor(jmi);\n\n /* \"end\" this slice */\n load.subtract(ended.jobManager.metrics);\n /* Fake out collecting result to transition state to FINISHED. */\n ended.collectResult(null);\n\n /* \"start\" as many slices as we can - given our CPU/GPU constraints, slice data in memory, etc */\n while (this.targetLoad.fits(load))\n {\n let slice = cursor.next();\n if (!slice)\n break; /* Running out of work that fits. */\n\n if (!load.fits(this.targetLoad, slice.jobManager.metrics))\n continue;\n\n /* Pick a ready slice from this job and add its anticipated load to our current load if it will fit */\n slice = slice.jobManager.readySlices.shift();\n slice.markAsWorking(); // ?? Not sure this is correct.\n //slice.etaMs = ended.etaMs + slice.jobManager.estimateWallMs; wtf?!?! <--- LOOK HERE\n\n load.add(slice.jobManager.metrics);\n }\n }\n\n return { load, jobManagerInventory: jmi };\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/index.js?");
4574
+ eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_modules/process/browser.js */ \"./node_modules/process/browser.js\");\n/**\n * @file dcp-client/worker/supervisor2/index.js\n * Code managing sandboxes, tasks, jobs, and slices within in a DCP Worker.\n * @author Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * @date Dec 2020\n * June 2022\n * @module supervisor\n */\n\n/* global dcpConfig */ // eslint-disable-line no-redeclare\n// @ts-check\n\n\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\nconst dcp4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst constants = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst { setImmediate } = __webpack_require__(/*! dcp/common/dcp-timers */ \"./src/common/dcp-timers.js\");\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst { Keystore, Address } = __webpack_require__(/*! dcp/dcp-client/wallet */ \"./src/dcp-client/wallet/index.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst { localStorage } = __webpack_require__(/*! dcp/common/dcp-localstorage */ \"./src/common/dcp-localstorage.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst RingBuffer = __webpack_require__(/*! dcp/utils/ringBuffer */ \"./src/utils/ringBuffer.js\");\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst { JobManager } = __webpack_require__(/*! ./job-manager */ \"./src/dcp-client/worker/supervisor2/job-manager.js\");\nconst { Load } = __webpack_require__(/*! ./load */ \"./src/dcp-client/worker/supervisor2/load.js\");\nconst { Sandbox, SandboxError } = __webpack_require__(/*! ./sandbox2 */ \"./src/dcp-client/worker/supervisor2/sandbox2.js\");\nconst { sliceStatus } = __webpack_require__(/*! dcp/common/scheduler-constants */ \"./src/common/scheduler-constants.js\");\nconst hash = __webpack_require__(/*! dcp/common/hash */ \"./src/common/hash.js\");\nconst { calculateJoinHash } = __webpack_require__(/*! dcp/dcp-client/compute-groups */ \"./src/dcp-client/compute-groups/index.js\");\nconst { ModuleCache } = __webpack_require__(/*! ./module-cache */ \"./src/dcp-client/worker/supervisor2/module-cache.js\");\nconst { Inventory, leafMerge, a$sleepMs, ms, pct, generateOpaqueId, booley, compressJobMap, \n toJobMap, truncateAddress, encodeDataURI, makeValueURI, justFetch, stringify } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n//const { CodeFlow } = require('client-oauth2');\n//const { text } = require('./lang').getLocaleStrings('en_CA'); /** XXXpfr @todo Don't know what to do with localization? */\n\n/** @typedef {import('dcp/dcp-client/wallet/eth').Address} Address */\n/** @typedef {import('dcp/protocol-v4/connection/connection').Connection} Connection */\n/** @typedef {string} opaqueId */ // 22 character base64 string\n/** @typedef {import('..').Worker} Worker */\n/** @typedef {import('..').SupervisorOptions} SupervisorOptions */\n/** @typedef {import('./slice2').Slice} Slice */\n/** @typedef {import('dcp/utils').SliceMessage} SliceMessage */\n\nconst addressTruncationLength = 20;\n\n//\n// Configs are moving around in dcpConfig and local worker configs, so set up some defaults.\nlet workerTuning = dcpConfig.worker;\nif (!workerTuning) workerTuning = dcpConfig.Supervisor;\nif (!workerTuning || !workerTuning.dcp || !workerTuning.dcp.tuning || !workerTuning.dcp.tuning.watchdogInterval\n || !workerTuning.sandbox || !workerTuning.allowOrigins || !workerTuning.minimumWage || !workerTuning.computeGroups)\n workerTuning = {\n dcp: {\n tuning: { watchdogInterval: 7, minSandboxStartDelay: 0.1, maxSandboxStartDelay: 0.7 },\n connectionOptions: { default: { identityUnlockTimeout: 15 * 60 /* seconds */ } },\n },\n sandbox: { progressReportInterval: 2 * 60 * 100 },\n allowOrigins: { fetchWorkFunctions: [], fetchArguments: [], fetchData: [], sendResults: [], any: [] },\n minimumWage: { CPU: 0, GPU: 0, 'in': 0, out: 0 },\n leavePublicGroup: false,\n pCores: 0,\n computeGroups: {},\n // The following configs are not in dcpConfig or worker configs (yet), but may be specified in local worker configs to override the defaults.\n pruneFrequency: 15 * 1000, // Maxiumum time interval where we check to prune used sandboxes.\n workerSandboxThreshold: 7, // When maxWorkingSandboxes >= workerSandboxThreshold, we allow an extra 25% of assigned sandboxes that won't be pruned.\n cachedJobsThreshold: 12, // Prune the unused job managers >= cachedJobsThreshold.\n};\n\n//\n// Flags for tracing.\n//\nconst selectiveEnable = false;\nconst displayWarnError = false || selectiveEnable;\nconst selectiveDebugging = selectiveEnable || debugging();\nconst debuggingError = false || selectiveDebugging || displayWarnError;\nconst debuggingWarn = false || selectiveDebugging || displayWarnError;\nconst selectiveDebugging2 = selectiveEnable && false || debugging('supervisor');\nconst displaySliceState = true;\nconst displayCompletedResults = false;\n\n/** \n * Adjust delay times when debugging.\n * The adjustment for niim is automatic, other debuggers must manually change this value.\n */\nlet timeDilation = 1;\nif (DCP_ENV.platform === 'nodejs')\n{\n /** Make timers 10x slower when running in niim */\n timeDilation = (requireNative('module')._cache.niim instanceof requireNative('module').Module) ? 10 : 1;\n}\n\n//\n// Index to functionality -- search for '_Idx' to toggle through the index.\n//\n// 1) Ctor: Supervisor constructor.\n// 2) Important property-like functions.\n// 3) Dtors: screenSaverDestroy, stopWork, purgeJob.\n// 4) Connection code.\n// 5) Work: Distribute slice to sandboxes.\n// 6) Return slices and sent progress reports to result-submitter-results.\n// 7) Task Distributor (TD): requestTask (Rq) support -- communication with TD.\n// 8) Aggregators from the job managers.\n// 9) Sandbox creation and management.\n// 10) Result-submitter-result support functions.\n// 11) Work reject.\n// 12) Unused functions that we need to review.\n//\n\n// _Idx\n//\n// Ctor: Supervisor constructor.\n//\n\n/** \n * Supervisor constructor\n * \n * A supervisor manages the communication with the scheduler, manages sandboxes, and\n * decides which workload should be sent to which sandboxes when.\n *\n * Start state:\n * - initial\n *\n * Intermediate states:\n * - ready\n * - stopping\n *\n * Terminal states:\n * - stopped\n *\n * Valid transitions:\n * - initial -> ready where that happens \n * - ready -> stopping\n * - stopping -> stopped\n *\n * @param {Worker} worker - The worker that created this instance.\n * @param {SupervisorOptions} options - Options for specifying custom behaviour and tuning,\n */\nfunction Supervisor(worker, options)\n{\n assert(options.identity instanceof Keystore);\n assert(options.paymentAddress instanceof Address);\n\n /**\n * Flag to indicate a debug build.\n * Used when we want to display extra information and do extra checks for developers only.\n * @type {boolean}\n */\n this.debugBuild = ((__webpack_require__(/*! dcp/common/dcp-build */ \"./src/common/dcp-build.js\").build) === 'debug');\n /**\n * When Supervisor.sliceTiming is set to be true, it displays the timings of a every slice\n * slice['queueingDelta'] = timespan of when slice is passed to jobManager.runQueuedSlice until sandbox.work\n * slice['executionDelta'] = timespan of execution in sandbox\n * slice['resultDelta'] = timespan of when sandbox finishes executing until recordResult completes.\n * @type {boolean}\n */\n this.sliceTiming = false;\n /** Used for analyzing the completed results in Supervisor.recordResult. */\n this.resultMap = {};\n\n /** @type {ModuleCache} */\n this.moduleCache = new ModuleCache(this);\n\n this.worker = worker;\n this.identity = options.identity;\n this.paymentAddress = options.paymentAddress;\n this.options = options;\n this.maxWorkingSandboxes = options.maxWorkingSandboxes || 1;\n this.maxTotalSandboxes = this.maxWorkingSandboxes;\n\n // We're making the assumption that if a worker has at least 7 sandboxes, \n // then the worker has sufficient resources to handle 25% more sandboxes in memory.\n // This assumption may be overridden by changing workerSandboxThreshold.\n if (this.maxWorkingSandboxes >= this.workerSandboxThreshold)\n this.maxTotalSandboxes = Math.ceil(1.25 * this.maxWorkingSandboxes);\n // When # of sandboxes reaches this level, we more aggressively prune.\n this.mustPruneSandboxLevel = Math.ceil(1.5 * this.maxTotalSandboxes);\n // Last prune time stamp.\n this.lastPrune = 0;\n // General time stamp.\n this.lastTime = 0;\n\n // Supervisor may get created by Worker where options.cores or options.targetLoad is not defined.\n this.numCPU = this.maxWorkingSandboxes;\n this.numGPU = 1;\n this.portionToUseCPU = pct(100);\n this.portionToUseGPU = pct(100);\n\n if (options.cores)\n {\n this.numCPU = options.cores.cpu || this.numCPU;\n this.numGPU = options.cores.gpu || this.numGPU;\n }\n if (options.targetLoad)\n {\n this.portionToUseCPU = options.targetLoad.cpu || pct(100);\n this.portionToUseGPU = options.targetLoad.gpu || pct(100);\n }\n\n this.tuning = {\n maxCPUAlloc: this.portionToUseCPU, /**< Maximum proportion of CPU time to attempt to use. */\n maxGPUAlloc: this.portionToUseGPU, /**< Maximum proportion of GPU time to attempt to use. */\n watchdogInterval: 7, /**< (seconds) How frequently to kick off an unsolicited requestTask. */\n prefetchInterval: 20, /**< (seconds) How many seconds into the future are looking to project capacity during work fetch. */\n minSandboxStartDelay: 0.1, /**< (seconds) Base minimum of this.delayMs, scaled by this.delayScaler. */\n maxSandboxStartDelay: 0.7, /**< (seconds) Base maximum random component of this.delayMs, scaled by this.delayScaler. */\n };\n this.tuning = leafMerge(this.tuning, workerTuning.dcp.tuning);\n if (options.watchdogInterval > 0)\n this.tuning.watchdogInterval = options.watchdogInterval; // Override.\n //this.tuning.watchdogInterval = 0.25;\n\n /**\n * Fine tune this.delayMs.\n * Note: Please discuss any change with Paul, paul@kingsds.network.\n * XXXpfr @todo Finalize the delay tuning.\n */\n this.delayScaler = 0.5;\n\n debugging('supervisor') && console.debug('Supervisor.tuning', this.tuning);\n\n /**\n * Note: targetLoad is not properly implemented yet.\n * XXXpfr @todo Collaborate with Wes to get it right.\n * @type {Load}\n */\n this.targetLoad = new Load({\n cpu: Math.min(this.maxWorkingSandboxes, this.numCPU),\n gpu: Math.min(this.maxWorkingSandboxes, this.numGPU)\n }).scale(this.tuning.maxCPUAlloc, this.tuning.maxGPUAlloc);\n\n /** @type {string[]} */\n this.allowedOrigins = workerTuning.allowOrigins.any;\n /** @type {string[]} */\n this.fetchWorkFunctions = workerTuning.allowOrigins.fetchWorkFunctions;\n /** @type {string[]} */\n this.fetchArguments = workerTuning.allowOrigins.fetchArguments;\n /** @type {string[]} */\n this.fetchData = workerTuning.allowOrigins.fetchData;\n /** @type {string[]} */\n this.sendResults = workerTuning.allowOrigins.sendResults;\n\n // In localExec, do not allow work function or arguments to come from the 'any' origins\n if (this.options.localExec)\n {\n this.allowedOrigins = this.allowedOrigins.concat(options.allowedOrigins.any);\n this.fetchWorkFunctions = this.fetchWorkFunctions.concat(options.allowedOrigins.fetchWorkFunctions);\n this.fetchArguments = this.fetchArguments.concat(options.allowedOrigins.fetchArguments);\n this.fetchData = this.fetchData.concat(options.allowedOrigins.fetchData);\n this.sendResults = this.sendResults.concat(options.allowedOrigins.sendResults)\n }\n\n if (options.allowedOrigins && options.allowedOrigins.length > 0)\n this.allowedOrigins = options.allowedOrigins.concat(this.allowedOrigins);\n \n //\n // The following 3 configs are not in dcpConfig or worker configs (yet), but may be specified in local worker configs to override the defaults.\n //\n /** @type {number} - Maxiumum time interval where we check to prune used sandboxes. */\n this.pruneFrequency = workerTuning.pruneFrequency || 15 * 1000;\n /** @type {number} - When maxWorkingSandboxes >= workerSandboxThreshold, we allow an extra 25% of assigned sandboxes that won't be pruned. */\n this.workerSandboxThreshold = workerTuning.workerSandboxThreshold || 7;\n /** @type {number} - Prune the unused job managers >= cachedJobsThreshold. */\n this.cachedJobsThreshold = workerTuning.cachedJobsThreshold || 12;\n\n /** @type {Object.<Address, JobManager>} */\n this.jobMap = {}; \n /** @type {Sandbox[]} - All sandboxes that are being used by the job managers. Makes sure we don't lose sandboxes. */\n this.sandboxInventory = [];\n /** @type {Sandbox[]} - Started sandboxes that are not in sandboxInventory yet. */\n this.readiedSandboxes = [];\n /** @type {JobManager[]} */\n this.jobManagerInventory = new Inventory('jobManagers');\n /** @type {Synchronizer} */\n this.state = new Synchronizer('initial', [ 'initial', 'ready', 'reconnecting', 'stopping', 'stopped', 'broken']);\n\n /** @type {string} */\n this.lastDcpsid = undefined;\n /** @type {Connection} */\n this.taskDistributor = null;\n /** @type {Connection} */\n this.resultSubmitter = null;\n /** @type {Connection} */\n this.eventRouter = null;\n /** @type {Connection} */\n this.packageManager = null;\n /** @type {Array<object>} */\n this.resultSubmitterMessageQueue = [];\n /** @type {Array<object>} */\n this.eventRouterMessageQueue = [];\n\n /** @type {object} */\n this.schedulerConfig = leafMerge(dcpConfig.scheduler, options.schedulerConfig);\n\n /** @type {opaqueId} */\n this.workerId = localStorage.getItem('workerId');\n if (!this.workerId || this.workerId.length !== constants.workerIdLength)\n {\n this.workerId = generateOpaqueId();\n localStorage.setItem('workerId', this.workerId);\n }\n /** @type {object[]} */\n this.rejectedJobs = [];\n /** \n * An N-slot ring buffer of job addresses. Stores all jobs that have had no more than 1 slice run in the ring buffer.\n * Required for the implementation of discrete jobs \n * @type {RingBuffer} \n */\n this.ringBufferofJobs = new RingBuffer(100); // N = 100 should be more than enough. \n /** @type {boolean} - pseudo-mutex guarding requestTask. */\n this.isFetchingNewWork = false;\n\n // Start up the connections.\n this.instantiateAllConnections();\n\n /**\n * Note: DCP-3241 asks to test Android to see if we need this restriction any longer.\n * XXXpfr @todo Hopefully we can delete this @hack.\n */\n // @hack - dcp-env.isBrowserPlatform is not set unless the platform is _explicitly_ set,\n // using the default detected platform doesn't set it.\n // Fixing that causes an error in the wallet module's startup on web platform, which I\n // probably can't fix in a reasonable time this morning.\n // ~ER2020-02-20\n if (!options.maxWorkingSandboxes\n && DCP_ENV.browserPlatformList.includes(DCP_ENV.platform)\n && navigator.hardwareConcurrency > 1) {\n this.maxWorkingSandboxes = navigator.hardwareConcurrency - 1;\n if (typeof navigator.userAgent === 'string') {\n if (/(Android).*(Chrome|Chromium)/.exec(navigator.userAgent)) {\n this.maxWorkingSandboxes = 1;\n this.emit('warning', 'Doing work with Chromimum browsers on Android is currently limited to one sandbox');\n }\n }\n }\n}\nexports.Supervisor = Supervisor;\nSupervisor.prototype = Object.getPrototypeOf(new EventEmitter('Supervisor')); // Fake out VSCode -- get's rid of a billion red-squigglies.\nSupervisor.prototype = new EventEmitter('Supervisor');\n/**\n * Preserve the constructor property.\n * @constructor\n */\nSupervisor.prototype.constructor = Supervisor;\n\n/**\n * Set up sandboxes and interval timers, then start to search for work.\n **/\nSupervisor.prototype.startWork = function Supervisor$startWork ()\n{\n /* Provide opportunity for calling code to hook ready/error events. */\n setImmediate(async () => {\n try\n {\n if (this.state.isNot('initial'))\n {\n if (this.state.setIf('stopped', 'initial')) {}\n else if (this.state.setIf('reconnecting', 'initial')) {}\n else if (this.state.setIf('broken', 'initial')) {}\n else if (this.state.is('ready')) return\n else throw new Error(`Supervisor startWork is in unexpected state ${this.state}, aborting...`);\n }\n this.instantiateAllConnections();\n\n await this.createSandboxes(this.maxWorkingSandboxes)\n .then(() => this.checkCapabilities());\n\n // Beacon interval timer.\n this.progressReportTimer = setInterval(() => this.emitProgressReport(), (workerTuning.sandbox.progressReportInterval || 2 * 60 * 100));\n // Watchdog: requestTask-driven interval timer.\n this.watchdogTimer = setInterval(() => this.requestTask() , ms(this.tuning.watchdogInterval));\n if (DCP_ENV.platform === 'nodejs' && this.options.localExec)\n {\n /* Interval timer helps keep worker alive forever, which we don't want in localExec. */\n this.progressReportTimer.unref();\n this.watchdogTimer.unref();\n }\n\n this.state.set('initial', 'ready');\n\n setImmediate(() => this.requestTask()); // Don't wait for watchdog.\n }\n catch(error)\n {\n this.state.set('initial', 'broken');\n this.emit('error', error);\n }\n });\n}\n\n/** Construct capabilities when necessary. */\nSupervisor.prototype.checkCapabilities = function Supervisor$checkCapabilities ()\n{\n if (!this.capabilities)\n {\n /**\n * Assign the capabilities of one the sandboxes before fetching slices from the scheduler.\n * @todo Remove this once fetchTask uses the capabilities of every sandbox to fetch slices.\n */\n const sandbox = this.readiedSandboxes.length > 0 ? this.readiedSandboxes[0] : this.sandboxInventory[0];\n if (sandbox)\n {\n this.capabilities = sandbox.capabilities;\n this.emit('capabilitiesCalculated', this.capabilities);\n }\n }\n\n if (DCP_ENV.isBrowserPlatform && this.capabilities.browser)\n this.capabilities.browser.chrome = DCP_ENV.isBrowserChrome;\n}\n\n// _Idx\n//\n// Important property-like functions.\n//\n\n/**\n * Universal delay milliseconds..\n * @returns {number}\n */\nSupervisor.prototype.delayMs = function Supervisor$delayMs (max = this.tuning.maxSandboxStartDelay, min = this.tuning.minSandboxStartDelay)\n{\n // Note: Please discuss any change with Paul, paul@kingsds.network.\n return 1000 * timeDilation * this.delayScaler * (min + Math.random() * (max - min));\n}\n\n/**\n * Indicates whether supervisor is ready for business.\n * @returns {boolean} - When true, the party is on...\n */\nSupervisor.prototype.isReady = function Supervisor$isReady()\n{\n return this.worker.working && this.state.is('ready');\n}\n\n/**\n * Safe access to Connection.close(...).\n * @param {Connection} connection\n * @param {string} [reason='requested']\n * @param {boolean} [immediate=false]\n * @returns {Promise<string>}\n */\nfunction safeClose(connection, reason = 'requested', immediate = false)\n{\n if (connection)\n {\n let msg;\n if (connection.state.is('closed')) msg = 'closed';\n else if (connection.state.is('closing')) msg = 'closing';\n else if (connection.state.is('close-wait')) msg = 'close-wait';\n if (msg)\n {\n debuggingWarn && console.warn(`${msg}, do not try to close again.`);\n return Promise.resolve(msg);\n }\n return connection.close(reason, immediate)\n .then(() => {\n return Promise.resolve(null);\n });\n }\n return Promise.resolve('already closed');\n}\n\n/**\n *\n * @param {string} operation\n * @param {*} data\n * @returns {Promise<string>}\n */\nSupervisor.prototype.saveForResubmitToRS = function Supervisor$saveForResubmitToRS(operation, data)\n{\n this.resultSubmitterMessageQueue.push({ operation, data });\n return safeClose(this.resultSubmitter);\n}\n\n/**\n * Error feedback to user.\n * @param {string} message\n * @param {{message}} [coreMessage]\n */\nSupervisor.prototype.error = function Supervisor$error(message, coreMessage)\n{\n if (coreMessage)\n {\n const errorObject = Object.assign({ Error: message }, this.checkCode(coreMessage, true /*isInner*/));\n this.emit('error', errorObject);\n }\n else\n this.emit('error', message);\n}\n\n/**\n * When true, the sandbox complete handler will look for another slice in the same job,\n * and if not found, then proceed to Supervisor.requestTask.\n * @returns {boolean}\n */\nSupervisor.prototype.runSliceFromSameJob = function Supervisor$runSliceFromSameJob()\n{\n //\n // Experimental, should be off by default.\n // Cf. sandbox complete handler in JobManager.hookUpSandboxListeners.\n //\n const disable = true;\n const tooManyJobs = this.activeJobCount() > this.maxWorkingSandboxes;\n return !disable && !tooManyJobs && this.unusedSandboxSlots() < 2;\n}\n\n/**\n * This function is used as the target number of sandboxes to be associated with slices and start working.\n * Warning: Do not rely on this information being 100% accurate -- it may change in the next instant.\n * @returns {number}\n */\nSupervisor.prototype.unusedSandboxSlots = function Supervisor$unusedSandboxSlots ()\n{\n return this.maxWorkingSandboxes - this.workingSliceCount();\n}\n\n/**\n * Create errorObj with error.code if it exists.\n * Return everything when this.debugBuild.\n * @todo Need better mechanism for displaying everything.\n * @param {{message}} error\n * @param {boolean} [isInner=false]\n * @returns {object}\n */\nSupervisor.prototype.checkCode = function Supervisor$checkCode (error, isInner = false)\n{\n if (this.debugBuild || !(error && error.message)) return error;\n const errorObj = isInner ? { InnerError: error.message } : { message: error.message };\n if (error['errorCode']) errorObj['errorCode'] = error['errorCode'];\n if (error['code']) errorObj['code'] = error['code'];\n return errorObj;\n}\n\n/**\n * Remove stack trace from error.\n * May not work when error is a string with no new-lines.\n * @param {string|{message}} error\n * @returns {string|{message}}\n */\nSupervisor.prototype.removeStackTrace = function Supervisor$removeStackTrace (error)\n{\n if (typeof error === 'string')\n {\n const errorLines = error.split('\\n');\n return (errorLines && errorLines.length > 0) ? errorLines[0] : error;\n }\n if (error['message'])\n return this.checkCode(error)\n return error;\n}\n\n// _Idx\n//\n// Dtors: screenSaverDestroy, stopWork, purgeJob.\n//\n\n/**\n * If we cannot create a new sandbox, that probably means we're on a screensaver worker\n * and the screensaver is down. So return the slices to the scheduler.\n */\nSupervisor.prototype.screenSaverDestroy = function Supervisor$screenSaverDestroy()\n{\n debugging('supervisor') && console.debug(`Supervisor.screenSaverDestroy: destroying all job managers and terminating all sandboxes.`);\n this.jobManagerInventory.forEach(jm => jm.destroy());\n this.jobManagerInventory = new Inventory('jobManagers');\n\n this.readiedSandboxes.forEach(sandbox => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this.readiedSandboxes = [];\n\n this.sandboxInventory.forEach(sandbox => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this.sandboxInventory = [];\n}\n\n/**\n * Terminates sandboxes and returns slices.\n * Sets the working flag to false, call @this.work to start working again.\n * \n * If forceTerminate is true: Terminates all sandboxes and returns all slices.\n * If forceTerminate is false: Terminates non-working sandboxes and returns initial and ready slices.\n *\n * @param {boolean} [forceTerminate = true] - true if you want to stop the sandboxes from completing their current slice.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.stopWork = async function Supervisor$stopWork (forceTerminate = true)\n{\n selectiveDebugging && console.debug(`Supervisor.stopWork(${forceTerminate},${this.state}): terminating sandboxes and returning slices to scheduler.`);\n\n // Do a hard flush of the microtask queue and finish the current event loop.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)));\n\n if (this.state.setIf('ready', 'stopping')) {}\n else if (this.state.setIf('reconnecting', 'stopping')) {}\n else if (this.state.setIf('broken', 'stopping')) {}\n else if (this.state.is('stopped')) return\n else throw new Error(`Supervisor stopWork is in unexpected state ${this.state}, aborting...`);\n\n this.instantiateAllConnections();\n\n this.readiedSandboxes.forEach((sandbox) => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this.readiedSandboxes = [];\n\n if (forceTerminate)\n {\n for (const jm of this.jobManagerInventory)\n jm.destroy();\n\n this.sandboxInventory.forEach((sandbox) => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n }\n else\n {\n let activeSliceCount = 0;\n const slicesToReturn = [];\n for (const jm of this.jobManagerInventory)\n {\n //jm.dumpSlices(`stopWork1:${jm.address}`);\n const queuedSlices = jm.queuedSlices;\n slicesToReturn.push(...queuedSlices);\n jm.removeSlices(queuedSlices);\n activeSliceCount += jm.activeSlices.length;\n jm.nonWorkingSandboxes.forEach((sandbox) => jm.returnSandbox(sandbox));\n }\n\n const reason = `Supervisor stopWork(${forceTerminate}): returning all non-finished slices that are not working.`;\n this.returnSlices(slicesToReturn, reason, false /*removeSlices*/);\n\n await new Promise((resolve) => {\n if (activeSliceCount === 0)\n resolve();\n // Resolve and finish work once all sandboxes have finished submitting their results.\n this.on('submitFinished', () => {\n if (--activeSliceCount === 0)\n {\n debuggingWarn && console.warn('All sandboxes empty, stopping worker and closing all connections');\n resolve();\n }\n });\n });\n\n for (const jm of this.jobManagerInventory)\n {\n //jm.dumpSlices(`stopWork2:${jm.address}`);\n jm.liveSandboxes.forEach((sandbox) => sandbox.terminate(false));\n jm._sandboxInventory = [];\n }\n }\n this.sandboxInventory = [];\n this.jobManagerInventory = new Inventory('jobManagers');\n\n this.closeConnections();\n\n this.emit('stop');\n this.state.set('stopping', 'stopped');\n}\n\n/**\n* Purge all traces of the job.\n* @param {JobManager} jobManager \n*/\nSupervisor.prototype.purgeJob = function Supervisor$purgeJob (jobManager)\n{\n selectiveDebugging && console.debug(`Supervisor.purgeJob ${jobManager.identifier}.`);\n this.jobManagerInventory.delete(jobManager);\n this.moduleCache.removeJob(jobManager.address);\n jobManager.destroy();\n}\n\n// _Idx\n//\n// Connection code.\n//\n\n/** \n * Connect the supervisor to a given scheduler sub-service. Reconnection \"DDoS\" from workers\n * mitigated via an exponential backoff algorithm.\n *\n * DCPv4 connections are lazily-initiated. Successful connection establishment detected by\n * observing the payload event, which normally triggers during versioning.\n */\nSupervisor.prototype.connectTo = function Supervisor$connectTo(which)\n{\n const config = (which === 'packageManager') ? dcpConfig.packageManager : this.schedulerConfig.services[which];\n const retryMinSleepMs = 100;\n const payloadResetCount = 3; /* How many payloadCount before we reset retryNextSleepMs. */\n \n var retryNextSleepMs = retryMinSleepMs;\n var payloadCount = 0;\n var options = leafMerge(/* ordered from most to least specific */\n workerTuning.dcp.connectionOptions.default,\n workerTuning.dcp.connectionOptions[which],\n workerTuning.dcp.connectionOptions[config.location.href],\n );\n\n /**\n * The payload event handler is an interesting way to handle exponential backoff\n * for the delay in this.reconnect.\n * XXXpfr @todo Revisit exponential backoff for this.reconnect in Sup2 Part II.\n */\n\n const handlePayloadEventFn = (ev) =>\n {\n if (++payloadCount === payloadResetCount)\n {\n this[which].removeEventListener('payload', handlePayloadEventFn);\n retryNextSleepMs = retryMinSleepMs; \n }\n }\n\n this[which] = new dcp4.Connection(config, this.identity, options);\n\n debugging() && console.debug(`Opening new connection ${this[which].id} to ${which}.`)\n\n const messageQueue = this[which + 'MessageQueue'];\n if (messageQueue && messageQueue.length > 0)\n resendRejectedMessages(this[which], messageQueue);\n\n this[which].on('payload', handlePayloadEventFn);\n\n this['reconnect' + which] = this.reconnect.bind(this, which);\n this[which].on('close', () => { this['reconnect' + which](); });\n}\n\n/**\n * Reconnect logic.\n * @param {string} which -- Name of the connection \n */\nSupervisor.prototype.reconnect = async function Supervisor$reconnect(which)\n{\n debugging('supervisor') && console.debug(`Supervisor.reconnect: ${which}:`, this.state.valueOf(), this[which].state.valueOf());\n const stateChange = this.state.testAndSet('ready', 'reconnecting');\n if (stateChange || this.state.is('reconnecting'))\n {\n await a$sleepMs(100); // Sleep for 100ms, maybe exp-backoff later if we need it.\n this.connectTo(which);\n if (stateChange) this.state.set('reconnecting', 'ready');\n debugging() && console.debug(`Supervisor.reconnect: Trying to reconnect ${which}`, this.state.valueOf(), this[which].state.valueOf());\n }\n}\n\n/**\n * Close a connection properly.\n * @param {string} which -- Name of the connection to close.\n */\nSupervisor.prototype.closeConnection = function Supervisor$closeConnection(which)\n{\n if (this[which])\n {\n this[which].off('close', this['reconnect' + which]);\n this[which].close();\n this[which] = null;\n }\n}\n\n/**\n * Close all connections.\n */\nSupervisor.prototype.closeConnections = function Supervisor$closeConnections()\n{\n /** XXXpfr @todo Remove when bug DCP-3094 is fixed. */\n a$sleepMs(500);\n\n this.closeConnection('taskDistributor');\n this.closeConnection('resultSubmitter');\n this.closeConnection('eventRouter');\n this.closeConnection('packageManager');\n}\n\n/**\n * Broadcast keepalive to all connections and optionally start up sandboxes.\n * @param {boolean} [createSandbox=false] - When true, creates sandboxes for unused sandbox slots.\n */\nSupervisor.prototype.keepalive = function Supervisor$keepalive(createSandbox = false)\n{\n return Promise.all([\n this.taskDistributor.keepalive(),\n this.resultSubmitter.keepalive(),\n this.eventRouter .keepalive(),\n this.packageManager .keepalive(),\n (createSandbox ? this.createSandboxes(this.maxWorkingSandboxes) : Promise.resolve()),\n ]);\n}\n\n/**\n * Open all connections. Used when supervisor is instantiated or stopped/started to initially open connections.\n */\nSupervisor.prototype.instantiateAllConnections = function Supervisor$instantiateAllConnections ()\n{\n if (!this.taskDistributor)\n this.connectTo('taskDistributor');\n if (!this.eventRouter)\n this.connectTo('eventRouter');\n if (!this.resultSubmitter)\n this.connectTo('resultSubmitter');\n if (!this.packageManager)\n this.connectTo('packageManager');\n}\n\n/**\n * Try sending messages that were rejected on an old instance of the given connection.\n * @param {Connection} connection\n * @param {Array<object>} messageQueue\n */\nasync function resendRejectedMessages (connection, messageQueue)\n{\n var message = messageQueue.shift();\n do \n {\n try\n {\n await connection.send(message.operation, message.data);\n }\n catch (error)\n {\n debuggingError && console.error(`Failed to resend message ${message.operation} to scheduler: ${error}. Will try again on a new connection.`);\n messageQueue.unshift(message);\n safeClose(connection);\n break;\n }\n message = messageQueue.shift();\n } while (message);\n}\n \n// _Idx\n//\n// Work: Distribute slice to sandboxes.\n//\n\n/** \n * UNUSED\n * @deprecated\n * Round-robin through the job managers, picking 1 slice to run each time.\n * Try to have the same number of working sandboxes for each job.\n * Try to run a slice on every available sandbox.\n */\nSupervisor.prototype.workOnCurrentTask = function Supervisor$workOnCurrentTask ()\n{\n return this.roundRobinSlices();\n}\n\n/**\n * This is for compatibility with Supervisor1 in sa worker.\n * When we get rid of Supervisor1 we can delete the ref in sa worker and then get rid of this function.\n */\nSupervisor.prototype.setDefaultIdentityKeystore = function Supervisor$setDefaultIdentityKeystore ()\n{\n}\n\n/**\n * Called in Worker.start().\n * Initial entry point after Worker constructor.\n * We need to start searching for work here to allow starting and stopping a worker.\n */\nSupervisor.prototype.work = function Supervisor$work ()\n{\n // Set up sandboxes and interval timers, then start to search for work.\n this.startWork();\n}\n\n/** \n * Round-robin through the job managers, picking 1 slice to run each time.\n * Try to have the same number of working sandboxes for each job.\n * Try to run a slice on every available sandbox.\n */\nSupervisor.prototype.roundRobinSlices2 = function Supervisor$roundRobinSlices2 ()\n{\n try\n {\n let first = true;\n const cursor = this.makeJobSelectionCursor(this.jobManagerInventory);\n while (true)\n {\n const slice = cursor.next();\n if (!slice) break; /* No more work can fit. */\n debugging('supervisor') && console.debug('roundRobinSlices: Executing slice', slice.identifier);\n slice.markAsReserved();\n slice.jobManager.runSlice(slice, first ? 0 : this.delayMs());\n first = false;\n }\n }\n finally\n {\n this.isFetchingNewWork = false;\n }\n}\n\n/**\n * We try to balance so that each job has the same number of working slices.\n *\n * NOTES:\n * 1) If count is such that it cannot be distributed evenly, we do not yet randomly assign the extras.\n * E.g. 3 jobs, j1, j2, j3: count = 5 -- so 2 jobs get extras -- the extras need to be assigned randomly.\n * @todo Assign the extras randomly.\n * 2) this.roundRobinSlices is not going to be what we use when sup2 is in final form.\n * We want to this.makeJobSelectionCursor and something like this.roundRobinSlices2\n *\n * In the outer loop,\n * when numworkingSandboxes=1, try to get a slice running for each job with 0 working sandboxes.\n * when numworkingSandboxes=2, try to get a slice running for each job with 1 working sandboxes.\n * when numworkingSandboxes=3, try to get a slice running for each job with 2 working sandboxes. Etc.\n * The idea is to balance the number of slices working on each job.\n * @param {number} [count=0] - The number of new slices to try to get running in sandboxes.\n */\nSupervisor.prototype.roundRobinSlices = function Supervisor$roundRobinSlices (count = 0)\n{\n try\n {\n if (!count) count = this.unusedSandboxSlots();\n if (!count || this.readySliceCount() < 1)\n return Promise.resolve();\n\n const slices = [];\n let numScheduled = 0\n let readyJobManagers = this.jobManagerInventory.filter(jm => jm.ready);\n let first = true;\n selectiveDebugging && console.debug('roundRobinSlices: START count', count, 'numJobMgrs', readyJobManagers.length, 'totalWorking(w/r/wo/wsbx/sbx)', this.workingSliceCount(), this.reservedSliceCount(), this.workingSliceOnlyCount(), this.workingSandboxCount(), this.sandboxCount());\n\n for (let numWorkingSandboxes = 1; numWorkingSandboxes <= this.maxWorkingSandboxes; numWorkingSandboxes++)\n {\n let sliceCount = 0;\n const beginNumScheduled = numScheduled;\n for (const jobMan of readyJobManagers)\n {\n const _readySlices = jobMan.readySlices;\n sliceCount += _readySlices.length\n const skip = numWorkingSandboxes <= jobMan.workingSlices.length;\n\n if (skip || _readySlices.length < 1)\n {\n // Noisy log message turned off by default.\n //debugging('supervisor') && console.debug('RRS0(numS, beginNumS, count, sliceCount, skip, _ready, numWorkingS(loop), workingSlices):', numScheduled, beginNumScheduled, count, sliceCount, skip, _readySlices.length, numWorkingSandboxes, jobMan.workingSlices.length);\n continue;\n }\n\n const slice = _readySlices[0];\n slices.push(slice);\n\n slice.markAsReserved();\n jobMan.runSlice(slice, first ? 0 : this.delayMs());\n\n first = false;\n if (++numScheduled >= count)\n break;\n }\n if (numScheduled >= count)\n {\n debugging('supervisor') && console.debug('RRS1(numS, beginNumS, count, sliceCount):', numScheduled, beginNumScheduled, count, sliceCount);\n break;\n }\n if (beginNumScheduled === numScheduled && sliceCount < 1)\n {\n debugging('supervisor') && console.debug('RRS2(numS, beginNumS, count, sliceCount):', numScheduled, beginNumScheduled, count, sliceCount);\n break;\n }\n }\n if (selectiveDebugging)\n {\n console.debug(`roundRobinSlices(working:(w/r/wo/wsbx/sbx)${this.workingSliceCount()},${this.reservedSliceCount()},${this.workingSliceOnlyCount()},${this.workingSandboxCount()},${this.sandboxCount()}): Started ${slices.length}/${numScheduled}/${count} scheduled slices`, compressSlices(slices));\n const sliceGrouper = {};\n slices.forEach((slice) => {\n const jm = slice.jobManager;\n if (!sliceGrouper[slice.jobAddress]) sliceGrouper[slice.jobAddress] = { cnt: 1, working: jm.workingSlices.length, queued: jm.queuedSlices.length, assigned: jm.assignedSandboxes.length, estimation: jm.isEstimation };\n else sliceGrouper[slice.jobAddress].cnt++;\n });\n console.debug(sliceGrouper);\n }\n if (selectiveDebugging2)\n {\n const jobGrouper = {};\n this.jobManagerInventory.forEach((jm) => {\n jobGrouper[jm.address] = { working: jm.workingSlices.length, queued: jm.queuedSlices.length, assigned: jm.assignedSandboxes.length, estimation: jm.isEstimation };\n });\n console.debug(jobGrouper);\n }\n }\n finally\n {\n this.isFetchingNewWork = false;\n }\n}\n\n/**\n * @callback cbNext\n * @returns {Slice}\n */\n\n/** \n * Factory function which instantiates a JobSelectionCursor. A JobSelectionCursor\n * steps the order that job slices should be selected for execution in the supervisor,\n * given the current state of the supervisor and the availability of jobs when the\n * inventory was snapshot. The entire slice scheduling algorithm is represented by\n * this cursor.\n *\n * The basic idea behind the scheduling of slices in this implementation is to keep as\n * many slices from different jobs running as possible, so as to reduce the likelihood\n * of resource contention between sandboxes.\n *\n * Slices are scheduled in here based on the following ruleset:\n * - pick a slice from the longest job that isn't running\n * - choose a slice from the remaining jobs, in order for shortest to longest slice time\n * - if there are any jobs which are nearly finished, every other slice comes from one\n * of these jobs, selected randomly????? <-- NYI. XXXpfr @todo Think about implementing...\n * - jobs which have slicePriority set by the task-distributor may be chosen in place\n * of slices in the above algorith. Jobs with a slicePriority closer to 1 are more likely\n * to exhibit this behaviour.\n * @param {JobManager[]} jobManagerInventory\n * @returns { { next: cbNext } }\n */\nSupervisor.prototype.makeJobSelectionCursor = function Supervisor$JobSelectionCursor (jobManagerInventory)\n{\n /* Variables in this scope function as state information for next() */\n var jobManIdx = 0;\n /** @type {JobManager[]} - All job managers that are ready that have at least one ready slice. */\n var jobManList;\n /** @type {JobManager[]} - All jobManList elements that correspond to preferred jobs. */\n var prefJobManList;\n /* Number of working sandboxes allowed for a given job. */\n var concurrency = 1;\n const that = this;\n \n function seed (_concurrency)\n {\n function countSandboxes(jobAddress)\n {\n const jobManager = that.jobMap[jobAddress];\n if (!jobManager) throw new Error(`Lost track of job manager for address '${jobAddress}'`);\n return jobManager.workingSlices.length;\n }\n \n // Reset.\n jobManIdx = 0;\n\n /* Populate jobManList with jobs which are ready and have at least one slice which is ready. */\n jobManList = jobManagerInventory.filter((jobMan) => jobMan.ready && jobMan.readySlices.length > 0);\n\n /* Populate jobManList with jobManagers whose # of working slices is less than _concurrency. */\n jobManList = jobManList.filter((jobMan) => countSandboxes(jobMan.address) < _concurrency);\n\n /* Increasing sort. */\n jobManList.sort((a,b) => a.estimateWallMs - b.estimateWallMs);\n\n /* Populate prefJobManList with jobs from jobManList which also have a slicePreference set. */\n prefJobManList = jobManList.filter(jobMan => jobMan.hasOwnProperty('slicePreference') );\n }\n\n /**\n * Takes slices off the ready list, marks it reserved and increments workingSandboxCoun,\n * because the slice will soon be working.\n * @param {JobManager} jobMan \n * @returns {Slice}\n */\n function reserveSlice (jobMan)\n {\n const _readySlices = jobMan.readySlices;\n if (_readySlices.length > 0)\n {\n const slice = _readySlices[0];\n slice.markAsReserved();\n return slice;\n }\n return null;\n }\n\n /**\n * Each invocation of next() identifies one slice to run, or returns false if none can run.\n * @returns {Slice}\n */\n function next ()\n {\n /* Adjust order to schedule the heaviest job's first slice asap. */\n jobManList.unshift(jobManList.pop());\n\n let workingSliceCount;\n do\n {\n seed(concurrency);\n\n /* Maybe schedule a prefered job slice based on random chance .*/\n if (prefJobManList.length > 0)\n {\n let prioRan = Math.random();\n let list = prefJobManList.filter(jm => jm['slicePreference'] >= prioRan);\n\n if (list.length > 0)\n {\n const jobMan = list[list.length * Math.random()];\n const slice = reserveSlice(jobMan);\n if (slice)\n return slice;\n }\n }\n\n /* Schedule a slice from next job; jobs are in increasing order of estimated run time. */\n while (jobManIdx < jobManList.length)\n {\n const jobMan = jobManList[jobManIdx];\n jobManIdx++;\n const slice = reserveSlice(jobMan);\n if (slice)\n return slice;\n }\n\n /* If this is reached, we did not schedule a slice with current seed. We need\n * to re-seed to look for newly-available work and sandboxes, ratcheting up the\n * concurrency (max # of each job running) until we find something we can do.\n */\n workingSliceCount = that.workingSliceCount();\n debugging() && console.debug(`job selection - no slice found from ${jobManList.length} jobs for concurrency=${concurrency} and ${workingSliceCount} working sandboxes`);\n } while (jobManList.length > 0 && workingSliceCount < that.maxWorkingSandboxes && concurrency++ < that.maxWorkingSandboxes);\n\n return false; /* Did not find any more work that fits. */\n }\n \n return { next };\n}\n\n/**\n * Handle sandbox.work(...) errors.\n * @param {Sandbox} sandbox \n * @param {Slice} slice \n * @param {Error} error \n * @return {Promise<string>}\n */\nSupervisor.prototype.handleSandboxWorkError = async function Supervisor$handleSandboxWorkError (sandbox, slice, error)\n{\n if (!slice.isWorking) // Sanity. Exception should never fire.\n throw new Error(`handleSandboxWorkError: slice ${slice.identifier} must be WORKING.`);\n\n let logLevel, reason;\n \n if (error instanceof SandboxError)\n {\n logLevel = 'warn';\n reason = error.errorCode;\n // The message and stack properties of error objects are not enumerable,\n // so they have to be copied into a plain object this way\n // @ts-ignore\n error = Object.getOwnPropertyNames(error).reduce((o, p) => {\n o[p] = error[p]; return o;\n }, { message: 'Unexpected worker error' });\n }\n else\n {\n logLevel = 'error';\n if (error)\n reason = `Slice ${slice.sliceNumber} in state ${slice.state} of job ${slice.jobAddress} failed to complete execution with error ${this.checkCode(error)}.`;\n else\n {\n reason = `Slice ${slice.sliceNumber} in state ${slice.state} of job ${slice.jobAddress} failed to complete execution.`;\n error = new Error(reason);\n }\n // This error was unrelated to the work being done, so just return the slice\n // in the promise.catch in JobManager.runSliceOnSandbox .\n assert(slice.result === null);\n }\n \n this.handleFailedSlice(slice, error);\n\n let errorString;\n switch (reason)\n {\n case 'ENOPROGRESS':\n errorString = 'No progress error in sandbox.\\n';\n break;\n case 'ESLICETOOSLOW':\n errorString = 'Slice too slow error in sandbox.\\n';\n break;\n case 'EUNCAUGHT':\n errorString = `Uncaught error in sandbox ${error.message}.\\n`;\n break;\n case 'EFETCH':\n // The status.js processing does not have a case for 'EFETCH' yet.\n errorString = `Could not fetch data: ${error.message}.\\n`;\n break;\n }\n\n // Always display max info under debug builds, otherwise maximal error.\n // messages are displayed to the worker, only if both worker and client agree.\n const displayMaxInfo = slice.jobManager.displayMaxDiagInfo;\n\n const errorObject = {\n jobAddress: truncateAddress(slice.jobAddress, addressTruncationLength),\n sliceNumber: slice.sliceNumber,\n sandbox: sandbox.id,\n jobName: sandbox.public ? sandbox.public.name : 'unnamed',\n };\n if (displayMaxInfo)\n errorObject.stack += '\\n --------------------\\n' + (error.stack.split('\\n').slice(1).join('\\n'));\n\n if (error.name === 'EWORKREJECT')\n {\n reason = 'EWORKREJECT'; // The status.js processing does not have a case for 'EWORKREJECT' yet.\n error.stack = 'Sandbox was terminated by work.reject()';\n await this.handleWorkReject(sandbox, slice, error.message);\n }\n else\n {\n this.returnSlice(slice, reason);\n slice.jobManager.returnSandbox(sandbox);\n }\n\n if (errorString)\n console[logLevel](errorString, errorObject);\n else if (error.name === 'EWORKREJECT')\n console[logLevel](`Slice rejected work: ${error.message}`)\n else\n console[logLevel](`Slice failed: ${error.message}\\n`, errorObject);\n\n return reason;\n}\n\n/**\n * Slice has thrown error during execution:\n * Mark slice as failed, compensate when job is dicrete, emit events.\n * @param {Slice} slice\n * @param {Error} error\n */\nSupervisor.prototype.handleFailedSlice = function Supervisor$handleFailedSlice (slice, error)\n{\n assert(error, 'error must be valid');\n slice.collectResult(error, false);\n\n // If the slice from a job never completes and the job address exists in the ringBufferofJobs,\n // then we remove it to allow for another slice (from the same job) to be obtained by fetchTask\n this.ringBufferofJobs.buf = this.ringBufferofJobs.filter(element => element !== slice.jobAddress);\n\n this.emit('submitSliceFailed', error);\n this.emit('submitFinished');\n}\n\n// _Idx\n//\n// Return slices and sent progress reports to result-submitter-results.\n// Return slices to result-submitter-status which marks the\n// corresponding row in activeSlices to be rescheduled on usually another worker.\n//\n\n/**\n * Bulk-return multiple slices, possibly for assorted jobs.\n * Returns slices to the scheduler to be redistributed.\n * Called in the sandbox terminate handler and purgeAllWork(jobAddress)\n * and stopWork(forceTerminate).\n *\n * @param {Slice[]} slices - The slices to return to the scheduler.\n * @param {string} [reason='unknown'] - Optional reason for the return: 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @param {boolean} [removeSlices=true] - When true, removes slices from this.sliceInventory .\n * @returns {Promise<*>} - Response from the scheduler.\n */\nSupervisor.prototype.returnSlices = function Supervisor$$returnSlices (slices, reason = 'unknown', removeSlices = true)\n{\n if (!slices || !slices.length) return Promise.resolve();\n debugging('supervisor') && console.debug('Supervisor.returnSlices: Returning slices', slices.map(slice => slice.identifier));\n\n const slicePayload = constructReturnSliceBuckets(slices, reason);\n if (removeSlices) slices.forEach((slice) => slice.jobManager.removeSlice(slice));\n\n try\n {\n return this.resultSubmitter.send('status', {\n worker: this.workerId,\n slices: slicePayload,\n }).catch(error => {\n const errorInfo = slices.map(slice => slice.identifier).sort();\n debuggingError && console.error('Failed to return slice(s)', { errorInfo, error }, 'Will try again on new connection.');\n return this.saveForResubmitToRS('status', { worker: this.workerId, slices: slicePayload });\n });\n }\n catch (error)\n {\n /* resultSubmitter can be null if worker is stopped */\n debuggingError && console.error(`Failed to return slices ${compressSlices(slices)}, no connection to result submitter:`, error);\n }\n}\n\n/** XXXpfr @todo TEMP -- Remove when sup2 replaces sup1 */\nfunction compressSlices(sliceArray)\n{\n const jobSliceMap = toJobMap(sliceArray, slice => slice.sliceNumber);\n return compressJobMap(jobSliceMap, false /* skipFirst*/, addressTruncationLength);\n}\n\n/**\n * Takes a slice and returns it to the scheduler to be redistributed.\n * Usually called when an exception is thrown by sandbox.work(...) .\n * Or when the supervisor tells it to forcibly stop working.\n *\n * @param {Slice} slice - The slice to return to the scheduler.\n * @param {string} [reason] - Optional reason for the return: 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @returns {Promise<*>} - Response from the scheduler.\n */\nSupervisor.prototype.returnSlice = function Supervisor$$returnSlice (slice, reason)\n{\n assert(slice.sliceNumber > 0 && slice.jobManager);\n debugging() && console.debug(`Supervisor.returnSlice: Returning slice ${slice.identifier} with reason ${reason}.`);\n\n if (!this.resultSubmitter)\n this.connectTo('resultSubmitter');\n\n try\n {\n slice.jobManager.removeSlice(slice);\n const payload = slice.getReturnMessagePayload(this.workerId, reason);\n return this.resultSubmitter.send('status', payload)\n .catch(error => {\n debuggingError && console.error('Failed to return slice', {\n sliceNumber: slice.sliceNumber,\n jobAddress: slice.jobAddress,\n status: slice.state.valueOf(),\n error,\n }, 'Will try again on a new connection.');\n return this.saveForResubmitToRS('status', payload);\n });\n }\n catch (error)\n {\n /* resultSubmitter can be null if worker is stopped */\n debuggingError && console.error(`Failed to return slice ${slice.identifier}, no connection to result submitter:`, error);\n }\n}\n\n/**\n * Send beacon to status.js for 'progress' and sliceStatus.scheduled.\n *\n * Run in an interval created in @constructor .\n * @returns {Promise<*>}\n */\nSupervisor.prototype.emitProgressReport = function emitProgressReport () \n{\n const slices = constructSliceBuckets( this.readySlices(), sliceStatus.scheduled );\n constructSliceBuckets( this.workingSlices(), 'progress', slices );\n\n debugging('supervisor') && console.debug('emitProgressReport:', stringify(slices));\n\n if (slices.length)\n {\n const progressReportPayload = {\n worker: this.workerId,\n slices,\n };\n\n try\n {\n return this.resultSubmitter.send('status', progressReportPayload)\n .catch(error => {\n debuggingError && console.error('479: Failed to send status beacon update:', error/*.message*/);\n return this.saveForResubmitToRS('status', progressReportPayload);\n });\n }\n catch (error) \n {\n /* resultSubmitter can be null if worker is stopped */\n debuggingError && console.error(`Failed to emit progress report, no connection to result submitter:`, error);\n }\n }\n}\n\n/**\n * Add a slice to the slice buckets being built. If a sliceBucket already exists for the\n * job-status-authMessage tuple, then the slice will be added to that, otherwise a new\n * sliceBucket will be added to the buckets.\n *\n * @param {Slice[]} slices - The slices.\n * @param {String} status - Status update, eg. progress or scheduled.\n * @param {Object[]} [sliceBuckets] - Slice buckets being built. Will be mutated in place.\n * @returns {Object[]} - mutated sliceBuckets array\n */\nfunction constructSliceBuckets (slices, status, sliceBuckets)\n{\n const jobMap = {};\n for (const slice of slices)\n {\n assert(slice.sliceNumber > 0 );\n if (!jobMap[slice.jobAddress]) jobMap[slice.jobAddress] = [];\n\n // Try to find a sliceBucket in the buckets which matches the job, status, and auth message.\n let sliceBucket = jobMap[slice.jobAddress].find(desc => {\n return desc.status === status\n && desc.authorizationMessage === slice.authorizationMessage;\n });\n\n if (!sliceBucket) jobMap[slice.jobAddress].push(slice.getMessage(status));\n else sliceBucket.sliceNumbers.push(slice.sliceNumber);\n }\n\n if (!sliceBuckets) return Object.values(jobMap);\n sliceBuckets.push(...Object.values(jobMap));\n return sliceBuckets;\n}\n \n/**\n * Add a slice to the returnSlice bucket being built. If a sliceBucket already exists for the\n * job-isEstimation-authMessage-reason tuple, then the slice will be added to that, otherwise a new\n * sliceBucket will be added to the buckets.\n *\n * @param {Slice[]} slices - The slices.\n * @param {String} [reason] - Optional reason to further characterize status; e.g. 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @param {Object[]} [sliceBuckets] - Optional slice buckets being built. Will be mutated in place.\n * @returns {Object[]} - mutated sliceBuckets array\n */\nfunction constructReturnSliceBuckets (slices, reason, sliceBuckets)\n{\n const jobMap = {};\n for (const slice of slices)\n {\n assert(slice.sliceNumber > 0 );\n if (!jobMap[slice.jobAddress]) jobMap[slice.jobAddress] = [];\n \n // Try to find a sliceBucket in the buckets which matches the job, estimation status, reason, and auth message.\n let sliceBucket = jobMap[slice.jobAddress].find(desc => {\n return desc.isEstimationSlice === slice.isEstimation\n && desc.authorizationMessage === slice.authorizationMessage\n && desc.reason === reason;\n });\n\n if (!sliceBucket) \n jobMap[slice.jobAddress].push(slice.getMessage('return', { isEstimationSlice: slice.isEstimation, reason }));\n else sliceBucket.sliceNumbers.push(slice.sliceNumber);\n }\n\n if (!sliceBuckets) return Object.values(jobMap);\n sliceBuckets.push(...Object.values(jobMap));\n return sliceBuckets;\n}\n \n// _Idx\n//\n// Task Distributor (TD): requestTask (Rq) support -- communication with TD.\n//\n\n/**\n * XXXpfr @todo Needs Work\n * For a given job, the scheduler stores an EMA approximation of average slice completion time in\n * jobPerfData.sliceCPUTime (and jobPerfData.sliceGPUTime, but we don't do the GPU analysis yet.)\n * However, each worker also tracks the same information and the ratio of local-info to scheduler-info\n * is returned by this.conversionQuantum so we can tell the task distributor how much work to return\n * from requestTask so that the work actually takes 5 minutes to complete when using all the worker sandboxes.\n * Note: \n * We average the completion times over the current jobs.\n * Define completion time in terms of sliceC(G)PUTime and sliceC(G)PUDensity\n * completion-time = (sliceCGPUTime + sliceCGPUTime) / ( sliceCPUDensity + sliceGPUDensity);\n * The local completion time is an EMA approximation of local completion-time as computed by Supervisor.recordResult.\n * The scheduler completion-time is computed directly from the corresponding row in jobPerfData.\n */\nSupervisor.prototype.conversionQuantum = function Supervisor$conversionQuantum()\n{\n let globalSpeed = 0, localSpeed = 0;\n for (const jobMan of this.jobManagerInventory)\n {\n const _globalTime = jobMan.globalTime;\n const _localTime = jobMan.statistics.ema;\n if (_globalTime > 0 && _localTime > 0)\n {\n //console.debug('conversionQuantum: local', _localTime, 'global', _globalTime);\n globalSpeed += _globalTime;\n localSpeed += _localTime;\n }\n }\n const conversion = globalSpeed > 0 ? localSpeed / globalSpeed : 1;\n return Math.min(Math.max(conversion, 0.2), 5.0); // Truncate if conversion is too bizarre.\n}\n\n/**\n * Remove all unreferenced jobs in this.jobManagerInventory and this.moduleCache.\n * Since job-managers are inserted into this.jobManagerInventory with a push, the job managers at the beginning are oldest.\n * Only delete #deleteCount of the oldest job-managers:\n * let deleteCount = this.jobManagerInventory.length - cachedJobsThreshold;\n * Edit cachedJobsThreshold to adjust the cache cleanup threshold.\n * @param {object[]} [newJobKeys=[]] - Jobs that should not be removed from this.jobManagerInventory and this.moduleCache.\n */\nSupervisor.prototype.clearUnusedJobManagersAndModuleCache = function Supervisor$clearUnusedJobManagersAndModuleCache(newJobKeys=[])\n{\n let deleteCount = this.jobManagerInventory.length - this.cachedJobsThreshold;\n if (deleteCount > 0)\n {\n selectiveDebugging && console.debug(`Supervisor.clearUnusedJobManagersAndModuleCache START: deleteCount ${deleteCount}/${this.jobManagerInventory.length}/${this.cachedJobsThreshold}.`);\n const jobMap = {};\n newJobKeys.forEach(jobAddress => { jobMap[jobAddress] = 1; });\n for (const jobManager of this.jobManagerInventory)\n {\n if (!jobMap[jobManager.address])\n {\n const sliceInventory = jobManager.sliceInventory.filter(slice => slice.isActive || slice.isQueued);\n if (sliceInventory.length < 1)\n {\n this.purgeJob(jobManager);\n if (--deleteCount < 1)\n break;\n }\n }\n }\n selectiveDebugging && console.debug(`Supervisor.clearUnusedJobManagersAndModuleCache FINISH: deleteCount ${deleteCount}/${this.jobManagerInventory.length}/${this.cachedJobsThreshold}.`);\n }\n}\n\n/**\n * Ask the scheduler (task distributor) for work.\n * @param {number} [unusedSandboxSlots]\n * @param {object[]} [jobs=[]] \n */\nSupervisor.prototype.requestTask = function Supervisor$requestTask (unusedSandboxSlots, jobs = [])\n{\n if (!this.isReady() || this.isFetchingNewWork)\n return Promise.resolve();\n\n if(!unusedSandboxSlots) unusedSandboxSlots = this.unusedSandboxSlots();\n if (unusedSandboxSlots < 1)\n {\n debugging('supervisor') && console.debug('requestTask: There are no unused sandbox slots.');\n return Promise.resolve();\n }\n\n // Refresh connections.\n this.instantiateAllConnections();\n\n // We prune for over this.maxTotalSandboxes about every 15 seconds, or when must prune level is reached.\n if (this.sandboxCount() > this.mustPruneSandboxLevel || Date.now() > this.lastPrune + this.pruneFrequency)\n {\n this.lastPrune = Date.now();\n this.pruneSandboxes();\n }\n\n try\n {\n this.isFetchingNewWork = true;\n const numCPUSlotToFill = this.numberOfAvailableSandboxSlots(unusedSandboxSlots);\n if (numCPUSlotToFill < 1)\n {\n //debugging() && console.debug('Predicted workload too high; not fetching additional work yet'); <-- Save Wes' msg...\n debugging() && console.debug('Supervisor.requestTask: We have enough, so start executing some slices.');\n return this.roundRobinSlices(); // roundRobinSlices guarantees this.isFetchingNewWork === false\n }\n\n /** XXXpfr @todo Get together with Wes to figure this out. */\n //let predictedLoad = this.predictLoad(Date.now() + ms(this.tuning.prefetchInterval)).load;\n\n const request = {\n numCores: numCPUSlotToFill,\n coreStats: this.getStatisticsCPU(),\n numGPUs: this.numGPU,\n //targetLoad: this.targetLoad.subtract(predictedLoad), /** XXXpfr @todo Get together with Wes to figure this out. */\n conversionQuantum: this.conversionQuantum(),\n capabilities: this.capabilities,\n paymentAddress: this.paymentAddress,\n jobAddresses: jobs.concat(this.options.jobAddresses || []), // When set, only fetches slices for these jobs.\n localExec: this.options.localExec,\n workerComputeGroups: this.generateWorkerComputeGroups(),\n minimumWage: workerTuning.minimumWage || this.options.minimumWage,\n loadedJobs: this.jobManagerInventory.map(jobMan => jobMan.address),\n readyJobs: this.jobManagerInventory.filter(jobMan => jobMan.ready).map(jobMan => jobMan.address),\n previouslyWorkedJobs: this.ringBufferofJobs.buf, // Only discrete jobs.\n rejectedJobs: this.rejectedJobs,\n };\n // Workers should be part of the public compute group by default.\n if (!booley(workerTuning.leavePublicGroup) && !booley(this.options.leavePublicGroup))\n request.workerComputeGroups.push(constants.computeGroups.public);\n\n // Call Task Distributor and handle response with this.addTaskToWorkload.\n this.fetchTask(request, (response) => this.addTaskToWorkload(request, response));\n }\n catch (error)\n {\n // Paranoid double-checking we don't accidently leave a live this.isFetchingNewWork.\n this.isFetchingNewWork = false;\n throw error;\n }\n}\n\n/** Gets the logical and physical number of cores and also the total number of sandboxes the worker is allowed to run. */\nSupervisor.prototype.getStatisticsCPU = function Supervisor$getStatisticsCPU ()\n{\n if (DCP_ENV.isBrowserPlatform)\n {\n return {\n worker: this.workerId,\n lCores: window.navigator.hardwareConcurrency,\n pCores: workerTuning.pCores || window.navigator.hardwareConcurrency,\n sandbox: this.maxWorkingSandboxes,\n }\n }\n\n return {\n worker: this.workerId,\n lCores: requireNative('os').cpus().length,\n pCores: requireNative('physical-cpu-count'),\n sandbox: this.maxWorkingSandboxes,\n }\n}\n\n/**\n * Callback for fetchTask.\n * @param {object} request \n * @param {object} response\n */\nSupervisor.prototype.addTaskToWorkload = function Supervisor$addTaskToWorkload (request, response)\n{\n try\n {\n const payload = response.payload;\n if (!response.success)\n {\n debugging() && console.debug('Task fetch failure; request=', request);\n debugging() && console.debug('Task fetch failure; response=', payload);\n // Only report errors when in 'ready' state.\n if (this.isReady()) throw new DCPError('Unable to fetch task for worker', payload);\n else return;\n }\n\n const sliceCount = payload.body.task.length || 0;\n if (sliceCount < 1)\n {\n if (selectiveDebugging2 && (this.lastTime + 7000 < Date.now()))\n {\n this.lastTime = Date.now();\n // Display the state of every slice.\n if (displaySliceState)\n {\n /** @type {JobManager} */\n const jm = this.jobManagerInventory.top();\n jm.dumpSlices(false /*details*/);\n }\n // Display completed results so far.\n if (displayCompletedResults && this.queuedSliceCount() < 1)\n {\n const values = Object.values(this.resultMap);\n if (values.length > 0)\n {\n values.forEach((descriptor) => descriptor.slices.sort((x,y) => x-y))\n console.debug(`Recorded results: job managers ${values.length}:`, this.resultMap);\n }\n }\n }\n this.emit('fetchedTask', { jobs: 0, slices: sliceCount });\n // There may be an extra slice to process.\n // roundRobinSlices guarantees this.isFetchingNewWork === false;\n return this.roundRobinSlices();\n }\n\n /**\n * payload structure: { owner: this.address, signature: signature, auth: messageLightWeight, body: messageBody };\n * messageLightWeight: { workerId: worker, jobSlices, schedulerId, jobCommissions }\n * messageBody: { newJobs: await-getNewJobsForTask(dbScheduler, task, request), task }\n */\n const { body, ...authorizationMessage } = payload;\n /** @type {{ newJobs: object, task: SliceMessage[] }} */\n const { newJobs, task } = body;\n assert(newJobs); // It should not be possible to have !newJobs -- we throw on !success.\n const newJobKeys = Object.keys(newJobs);\n const jobCount = newJobKeys.length;\n\n /*\n * Ensure all jobs received from the scheduler (task distributor) are:\n * 1. If we have specified specific jobs the worker may work on, the received jobs are in the specified job list\n * 2. If we are in localExec, at most 1 unique job type was received (since localExec workers are designated for only one job)\n * If the received jobs are not within these parameters, stop the worker since the scheduler cannot be trusted at that point.\n */\n if (request.jobAddresses.length > 0 && !newJobKeys.every((ele) => request.jobAddresses.includes(ele))\n || request.localExec && jobCount > 1)\n {\n this.error(\"Worker received slices it shouldn't have. Rejecting the work and stopping.\");\n process.exit(1);\n }\n\n selectiveDebugging && console.debug(`Supervisor.addTaskToWorkload: task: ${task.length}/${request.numCores}/${this.maxWorkingSandboxes}, conversion: ${request.conversionQuantum}, jobs: ${jobCount}, authSlices: ${compressJobMap(authorizationMessage.auth.authSlices, true /* skipFirst*/, addressTruncationLength /* digits*/)}`);\n\n // Clear out job managers w/o any queued slices,\n // and remove corresponding job references from module cache.\n // When a cached module no longer has any job references it is removed from the cache.\n this.clearUnusedJobManagersAndModuleCache(newJobKeys);\n\n // this.jobMap: job.address --> jobManager\n /** @type {Object.<Address, JobManager>} */\n this.jobMap = {};\n this.jobManagerInventory.forEach(jobManager => {\n this.jobMap[jobManager.address] = jobManager;\n });\n\n /** @type {Object.<Address, SliceMessage[]>} */\n const jobSliceMap = {};\n task.forEach((element) => {\n const address = String(element.jobAddress);\n if (!jobSliceMap[address]) jobSliceMap[address] = [element];\n else jobSliceMap[address].push(element);\n });\n\n debugging('supervisor') && console.debug('requestTask: slices, newJobs and jobMap', task.length, Object.keys(newJobs), Object.keys(this.jobMap));\n\n // Populate the job managers with slices, creating new job managers when necessary.\n // Set up discrete job ring buffer.\n for (const [jobAddress, jobEl] of Object.entries(newJobs))\n {\n if (this.jobMap.hasOwnProperty(jobAddress))\n {\n /** @type {JobManager} */\n const jm = this.jobMap[jobAddress];\n jm.update(jobEl, jobSliceMap[jobAddress], authorizationMessage);\n }\n else\n {\n // Add the slice messages to the job manager ctor, so that slice construction is after job manager is ready.\n const jobManager = new JobManager(this, jobEl, jobSliceMap[jobAddress], authorizationMessage);\n this.jobMap[jobAddress] = jobManager;\n this.jobManagerInventory.push(jobManager);\n\n // Populate the ring buffer based on job's discrete property.\n assert(jobEl.requirements);\n if (jobEl.requirements.discrete && this.ringBufferofJobs.find(address => address === jobEl.address) === undefined)\n this.ringBufferofJobs.push(jobEl.address);\n }\n }\n\n /**\n * The requestTask event fires when the supervisor has finished trying to\n * fetch work from the scheduler (task distributor). The data emitted is the\n * number of jobs and new slices to work on in the fetched task.\n *\n * @event Supervisor#requestTask\n * @type {object}\n */\n this.emit('fetchedTask', { jobs: jobCount, slices: sliceCount });\n\n // Start working on the slices.\n setImmediate(() => this.roundRobinSlices());\n }\n catch (error)\n {\n this.isFetchingNewWork = false; // Paranoid double checking that we don't leave this.isFetchingNewWork live.\n this.emit('fetchTaskFailed', error);\n debuggingError && console.error('Supervisor.requestTask failed!', error);\n }\n}\n\n/**\n * Returns the number of unused sandbox slots to fill -- sent to requestTask.\n * @returns {number}\n */\nSupervisor.prototype.numberOfAvailableSandboxSlots = function Supervisor$numberOfAvailableSandboxSlots(unusedSandboxSlots)\n{\n const _readySlices = this.readySlices();\n let numCores;\n if (this.options.priorityOnly && this.options.jobAddresses.length === 0)\n numCores = 0;\n else if (_readySlices.length > 1) // We have slices ready, no need to fetch.\n numCores = 0;\n else\n {\n // There are almost no ready slices (there may be 0 or 1), fetch a full task.\n // The task is full, in the sense that it will contain slices whose\n // aggregate execution time is this.maxWorkingSandboxes * 5-minutes.\n // However, there can only be unusedSandboxSlots # of long slices.\n // Thus we need to know whether the last slice in this.readySlices() is long or not.\n // (A long slice has estimated execution time >= 5-minutes on an average worker.)\n const longSliceCount = (_readySlices.length > 0 && _readySlices[0].isLong) ? 1 : 0;\n numCores = unusedSandboxSlots - longSliceCount;\n debugging('supervisor') && console.debug('numberOfAvailableSandboxSlots', numCores, unusedSandboxSlots, longSliceCount);\n }\n return numCores;\n}\n\n/**\n * @callback cbAddTaskToWorkload\n * @param {Response} response\n * @returns {Promise<void>}\n */\n\n/**\n * Call to fetch new slices from task distributor.\n * @param {*} request\n * @param {cbAddTaskToWorkload} addTaskToWorkload\n * @returns {Promise<void>}\n */\nSupervisor.prototype.fetchTask = async function Supervisor$fetchTask (request, addTaskToWorkload)\n{\n // Fetch a new task if we have insufficient slices queued, then start workers\n assert(this.isFetchingNewWork);\n\n this.instantiateAllConnections();\n\n // Top up sandboxes when necessary.\n const moreSandboxes = this.maxWorkingSandboxes - this.sandboxCount();\n if (moreSandboxes > 0)\n {\n await this.carefullyCreateSandboxes(moreSandboxes)\n .then(() => this.checkCapabilities()); /** XXXpfr @todo Do we need to check capabilities again? */\n }\n\n const fetchTimeout = setTimeout(() => {\n this.isFetchingNewWork = false;\n this.emit('warning', 'Fetch exceeded timeout, will reconnect at next watchdog interval');\n safeClose(this.taskDistributor, 'Fetch timed out', Math.random() > 0.5).catch(error => {\n this.error('Failed to close task-distributor connection', error);\n });\n safeClose(this.resultSubmitter, 'Fetch timed out', Math.random() > 0.5).catch(error => {\n this.error('Failed to close result-submitter connection', error);\n });\n this.instantiateAllConnections();\n }, 3 * 60 * 1000); // Max out at 3 minutes to fetch.\n\n // Ensure result submitter and task distributor connections before fetching tasks.\n try\n {\n await this.taskDistributor.keepalive();\n await this.resultSubmitter.keepalive();\n await this.taskDistributor.keepalive();\n }\n catch (e)\n {\n this.isFetchingNewWork = false;\n this.error('Failed to connect to result submitter, refusing to fetch slices. Will try again at next fetch cycle.', e);\n clearTimeout(fetchTimeout);\n safeClose(this.taskDistributor, 'Failed to connect to task-distributor', true).catch(error => {\n this.error('Failed to close task-distributor connection', error);\n });\n safeClose(this.resultSubmitter, 'Failed to connect to result-submitter', true).catch(error => {\n this.error('Failed to close result-submitter connection', error);\n });\n return Promise.resolve();\n }\n\n this.emit('fetchingTask');\n\n if (!this.taskDistributor) return\n return this.taskDistributor.send('requestTask', request)\n .then((response) => {\n addTaskToWorkload(response);\n })\n .catch((error) => {\n this.isFetchingNewWork = false; // Redundant.\n this.emit('fetchTaskFailed', error);\n this.error('Unable to request task from scheduler. Will try again on a new connection.', error);\n safeClose(this.taskDistributor, 'Failed to request task.', true);\n })\n .finally(() => {\n this.isFetchingNewWork = false;\n clearTimeout(fetchTimeout);\n });\n}\n\n/**\n * Generate the workerComputeGroups property of the requestTask message. \n * \n * Concatenate the compute groups object from dcpConfig with the list of compute groups\n * from the supervisor, and remove the public group if accidentally present. Finally,\n * we transform joinSecrets/joinHash into joinHashHash for secure transmission.\n *\n * @note computeGroup objects with joinSecrets are mutated to record their hashes. This\n * affects the supervisor options and dcpConfig. Re-adding a joinSecret property\n * to one of these will cause the hash to be recomputed.\n */\nSupervisor.prototype.generateWorkerComputeGroups = function Supervisor$generateWorkerComputeGroups ()\n{\n let computeGroups = Object.values(workerTuning.computeGroups || {});\n if (this.options.computeGroups)\n computeGroups = computeGroups.concat(this.options.computeGroups);\n computeGroups = computeGroups.filter(group => group.id !== constants.computeGroups.public.id);\n const hashedComputeGroups = [];\n for (const group of computeGroups)\n {\n const groupCopy = Object.assign({}, group);\n if ((group.joinSecret || group.joinHash) && (!group.joinHashHash || this.lastDcpsid !== this.taskDistributor.dcpsid))\n {\n let joinHash;\n if (group.joinHash)\n joinHash = group.joinHash.replace(/\\s+/g, ''); // strip whitespace\n else\n joinHash = calculateJoinHash(groupCopy);\n\n groupCopy.joinHashHash = hash.calculate(hash.eh1, joinHash, this.taskDistributor.dcpsid);\n delete groupCopy.joinSecret;\n delete groupCopy.joinHash;\n debugging('computeGroups') && console.debug(`Calculated joinHash=${joinHash} for`, groupCopy);\n }\n hashedComputeGroups.push(groupCopy);\n }\n this.lastDcpsid = this.taskDistributor.dcpsid;\n debugging('computeGroups') && console.debug('Requesting ', computeGroups.length, 'non-public groups for session', this.lastDcpsid);\n return hashedComputeGroups;\n}\n\n// _Idx\n//\n// Aggregators from the job managers.\n// Note: Not all functions are used yet.\n//\n/** XXXpfr @todo Figure out which aggregators to keep. */\n\n/**\n * Gather the count of job managers with queuedSlices.\n * @returns {number}\n */\nSupervisor.prototype.activeJobCount = function Supervisor$activeJobCount ()\n{\n let count = 0;\n this.jobManagerInventory.forEach((jobManager) => {\n if (jobManager.queuedSlices.length > 0) count++;\n });\n return count;\n}\n\n/**\n * Gather the ready slices from the job managers.\n * @returns {Slice[]}\n */\nSupervisor.prototype.readySlices = function Supervisor$readySlices ()\n{\n const readySlices = [];\n this.jobManagerInventory.forEach((jobManager) => {\n readySlices.push(...jobManager.readySlices);\n });\n return readySlices;\n}\n\n/**\n * Gather the working slices in the job managers.\n * @returns {Slice[]}\n */\nSupervisor.prototype.workingSlices = function Supervisor$workingSlices ()\n{\n const workingSlices = [];\n this.jobManagerInventory.forEach((jobManager) => {\n workingSlices.push(...jobManager.workingSlices);\n });\n return workingSlices;\n}\n\n/**\n * Gather the count of various kinds of slices over all the job managers.\n * @param {string} predicate - 'all;, 'ready', 'queued', 'reserved', 'working', 'workingOnly'.\n * @returns {number}\n */\nSupervisor.prototype.predicateSliceCount = function Supervisor$predicateSliceCount (predicate)\n{\n let count = 0;\n switch (predicate)\n {\n case 'all':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.sliceInventory.length;\n });\n break\n case 'ready':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.readySlices.length;\n });\n break;\n case 'queued':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.queuedSlices.length;\n });\n break;\n case 'reserved':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.reservedSlices.length;\n });\n break;\n case 'working': // both working and reserved (viz., soon-to-be-working)\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.workingSlices.length;\n });\n break;\n case 'workingOnly':\n this.jobManagerInventory.forEach((jobManager) => {\n count += jobManager.workingSlicesOnly.length;\n });\n break;\n }\n return count;\n}\n/** @returns {number} */\nSupervisor.prototype.sliceCount = function Supervisor$sliceCount () { return this.predicateSliceCount('all'); }\n/** @returns {number} */\nSupervisor.prototype.readySliceCount = function Supervisor$readySliceCount () { return this.predicateSliceCount('ready'); }\n/** @returns {number} */\nSupervisor.prototype.queuedSliceCount = function Supervisor$queuedSliceCount () { return this.predicateSliceCount('queued'); }\n/** @returns {number} */\nSupervisor.prototype.reservedSliceCount = function Supervisor$reservedSliceCount () { return this.predicateSliceCount('reserved'); }\n/** @returns {number} */\nSupervisor.prototype.workingSliceCount = function Supervisor$workingSliceCount () { return this.predicateSliceCount('working'); }\n/** @returns {number} */\nSupervisor.prototype.workingSliceOnlyCount = function Supervisor$workingSliceOnlyCount () { return this.predicateSliceCount('workingOnly'); }\n\n/**\n * Gather the count of working sandboxes over all the job managers.\n * @returns {number}\n */\nSupervisor.prototype.sandboxCount = function Supervisor$sandboxCount ()\n{\n return this.readiedSandboxes.length + this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated).length;\n}\n\n/**\n * Gather the count of working sandboxes over all the job managers.\n * @returns {number}\n */\nSupervisor.prototype.workingSandboxCount = function Supervisor$workingSandboxCount ()\n{\n return this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated && sandbox.isWorking).length;\n}\n\n// _Idx\n//\n// Sandbox creation and management.\n// \n\n/**\n * Create and start a Sandbox.\n * When this.readiedSandboxes.length > 0, use one of those sandboxes, instead of creating a new one.\n * @param {number} [delayMs=0] - The delay ms when calling sandbox.start(delayMs) .\n * @returns {Promise<Sandbox>}\n */\nSupervisor.prototype.createSandbox = function Supervisor$createSandbox (delayMs = 0)\n{\n const that = this;\n function getReadiedSandbox()\n {\n const sandbox = that.readiedSandboxes.pop();\n that.sandboxInventory.push(sandbox);\n return Promise.resolve(sandbox);\n }\n\n if (this.readiedSandboxes.length > 0)\n return getReadiedSandbox();\n\n // Do not place in this.readiedSandboxes, we'll directly use the return value of createSandbox.\n return this.createNewSandbox(delayMs, true/*putInInventory*/)\n .catch(() => {\n return this.carefullyCreateSandboxes(1)\n .then(() => {\n return getReadiedSandbox();\n });\n });\n}\n \n/**\n * Create and start a Sandbox.\n * Store it in this.readiedSandboxes or this.sandboxInventory according to putInInventory.\n * @param {number} [delayMs=0] - Millisecond delay when calling sandbox.start(delayMs), otherwise return it and use it.\n * @param {boolean} [putInInventory=false]\n * @returns {Promise<Sandbox>}\n */\nSupervisor.prototype.createNewSandbox = function Supervisor$createNewSandbox (delayMs = 0, putInInventory = false)\n{\n const rawSandbox = new Sandbox(this, { ...this.options.sandboxOptions });\n this.hookUpSandboxListeners(rawSandbox);\n return rawSandbox.start(delayMs)\n .then((sandbox) => {\n if (putInInventory) this.sandboxInventory.push(sandbox);\n else this.readiedSandboxes.push(sandbox);\n return sandbox;\n })\n .catch((error) => {\n if (!error) error = new Error('Unknown error creating sandbox.');\n debuggingWarn && console.warn(`Supervisor.createNewSandbox: Failed to start sandbox ${rawSandbox.identifier}.`, error);\n rawSandbox.terminate(false);\n if (error.code === 'ENOWORKER')\n throw new DCPError(\"Cannot use localExec without dcp-worker installed. Use the command 'npm install dcp-worker' to install the neccessary modules.\", 'ENOWORKER');\n throw error;\n });\n}\n\n/**\n * Bulk: create and start sandboxes and save in this.readiedSandboxes.\n * Call this function when there's a chance the evaluator is down.\n * @param {number} count - The number of sandboxes to create.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.carefullyCreateSandboxes = async function Supervisor$carefullyCreateSandboxes (count)\n{\n if (count < 1) return;\n // If the evaluator cannot start (e.g. if the evalServer is not running),\n // then the while loop will keep retrying until the evalServer comes online.\n let retry = 0;\n while (true)\n {\n try\n {\n await this.createNewSandbox();\n if (count > 1)\n this.createSandboxes(count - 1);\n }\n catch (error)\n {\n if (error.code === 'ENOWORKER') throw error;\n // Now assume the evaluator is down and keep retrying.\n /** XXXpfr @todo Need better indicator that evaluator is down. */\n if ((retry++ % 6) === 0)\n this.error('Failed to ready sandboxes; will keep retrying.', error);\n await a$sleepMs(1000 * Math.max(5, retry));\n }\n }\n}\n\n/**\n * Bulk: create and start sandboxes and save in this.readiedSandboxes.\n * @param {number} count - The number of sandboxes to create.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.createSandboxes = async function Supervisor$createSandboxes (count)\n{\n assert(count > 0);\n const promises = [], errors = [];\n for (let k = 0; k < count; k++)\n {\n promises.push(\n this.createNewSandbox(k === 0 ? 0: this.delayMs())\n .catch((error) => errors.push(this.checkCode(error))));\n }\n\n await Promise.all(promises);\n\n if (errors.length)\n this.emit('warning', `Failed to ready ${errors.length} of ${count} sandboxes: ${errors.map(err => \"\\n\\t\" + err.message)}`);\n\n // Sort so that pop() will return sandboxes in increasing order.\n this.readiedSandboxes.sort((x,y) => y.id - x.id);\n\n debugging('supervisor') && console.debug(`createSandboxes: Created ${count-errors.length} sandboxes.`, this.readiedSandboxes.map(s => s.id));\n}\n\n/**\n * For a given sandbox, hook up all the Sandbox listeners.\n * @param {Sandbox} sandbox \n */\nSupervisor.prototype.hookUpSandboxListeners = function hookUpSandboxListeners (sandbox) \n{\n sandbox.addListener('ready', () => this.emit('sandboxReady', sandbox));\n\n sandbox.addListener('start', () => {\n this.emit('sandboxStart', sandbox);\n\n if (sandbox.slice)\n {\n try\n {\n const statusPayload = sandbox.slice.getMessagePayload(this.workerId, 'begin');\n return this.resultSubmitter.send('status', statusPayload).catch((error) => {\n debuggingError && console.error(`Error sending 'status' for slice ${sandbox.slice.identifier}:\\n\\t${error}\\n\\tWill try again on a new connection`);\n return this.saveForResubmitToRS('status', statusPayload);\n });\n } \n catch (error)\n {\n /* resultSubmitterConnection can be null if worker is stopped */\n debuggingError && console.error(`Failed to send 'begin' status for slice ${sandbox.slice.identifier}, no connection to result submitter`, error);\n }\n }\n });\n\n sandbox.addListener('workEmit', ({ eventName, payload }) => {\n // Need to check if the sandbox hasn't been assigned a slice yet.\n if (!sandbox.slice)\n this.error(`Sandbox not assigned a slice before sending workEmit message to scheduler.\\n\\t'workEmit' event originates from '${eventName}' event`);\n else\n {\n const slice = sandbox.slice;\n // Sometimes a sliceNumber===0 workEmit comes in before the client bundle is properly loaded.\n // Also happens with minor dcp-client version mismatches.\n // sliceNumber===0 <==> authorizationMessage undefined...\n if (!slice.authorizationMessage)\n {\n if (slice.sliceNumber > 0)\n this.emit('warning', `workEmit: missing authorization message for slice ${slice.identifier}`);\n }\n else if (this.eventRouter) // No reason to emit if event router is closed.\n {\n const workEmitPayload = {\n eventName,\n payload,\n job: slice.jobAddress,\n slice: slice.sliceNumber,\n worker: this.workerId,\n authorizationMessage : slice.authorizationMessage,\n };\n\n const workEmitPromise = this.eventRouter.send('workEmit', workEmitPayload).catch(error => {\n debuggingWarn && console.warn(`workEmit: Unable to send ${eventName} for slice ${slice.identifier}: ${error.message}.\\n\\tTrying again on a new connection.`);\n this.eventRouterMessageQueue.push({ operation: 'workEmit', data: workEmitPayload })\n safeClose(this.eventRouter); // stopWork could slip-in during eventRouter.send\n if (this.debugBuild) this.error('workEmit error:', error);\n });\n\n if (this.debugBuild)\n {\n workEmitPromise.then(result => {\n if (!result) this.emit('warning', `workEmit: Event router did not accept event ${eventName}`);\n });\n }\n }\n }\n });\n\n sandbox.on('rejectedWorkMetrics', (data) => {\n // If the slice already has rejectedTimeReport, add this data to it. If not, assign this data to slices rejectedTimeReport property\n if (sandbox.slice) \n {\n if (!sandbox.slice.rejectedTimeReport) sandbox.slice.rejectedTimeReport = data.timeReport;\n else \n {\n ['total', 'CPU', 'webGL'].forEach((key) => {\n if (data.timeReport[key]) sandbox.slice.rejectedTimeReport[key] += data.timeReport[key];\n })\n }\n }\n });\n\n // If the sandbox terminated and we are not shutting down, then we should return all work which is\n // currently not being computed if all sandboxes are dead and the attempt to create a new one fails.\n sandbox.on('terminated', async () => {\n let nonTerminatedSandboxes = this.sandboxInventory.filter(sbx => !sbx.isTerminated);\n if (nonTerminatedSandboxes.length === 0 && this.worker.working)\n {\n debugging('supervisor') && console.debug(`hookUpSandboxListeners: Try to create 1 sandbox in the sandbox-terminated-handler.`);\n const _sandbox = await this.createNewSandbox()\n .catch((error) => {\n debugging('supervisor') && console.warn('Failed to replace terminated sandbox; evalserver may be gone.', error.message);\n error.message = 'Failed to replace terminated sandbox: ' + error.message;\n this.emit('warning', error);\n });\n\n // If we cannot create a new sandbox, that probably means we're on a screensaver worker\n // and the screensaver is down. So return the slices to the scheduler.\n if (!_sandbox) this.screenSaverDestroy();\n }\n });\n\n sandbox.on('error', (error) => this.emit('error', error));\n sandbox.on('warning', (warning) => this.emit('warning', warning));\n}\n\n/**\n * Terminate extra sandboxes over the limit: this.maxTotalSandboxes.\n * First terminate assigned sandboxes which are unlikely to be used with the current ready slices.\n * Then terminate the unassigned sandboxes: this.readiedSandboxes.\n * (There should be no readied sandboxes at this point.)\n * Then round-robin prune 1 assigned sandbox from each jobmanager.\n * XXXpfr @todo Prioritize sandboxes that we wish to keep.\n * E.g. When a sandbox is especially expensive to assign.\n */\nSupervisor.prototype.pruneSandboxes = function Supervisor$pruneSandboxes () \n{\n let pruneCount = this.sandboxCount() - this.maxTotalSandboxes;\n if (pruneCount <= 0) return;\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes START: pruneCount ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n // Only prune the extras: jm.assignedSandboxes.length > jm.queuedSlices.length .\n // Round-robin prune 1 extra assigned sandbox from each jobmanager.\n const readyJobManagers = this.jobManagerInventory.filter(jm => jm.ready);\n while (true)\n {\n const _pruneCount = pruneCount;\n for (const jm of readyJobManagers)\n {\n if (jm.pruneExtraAssignedSandbox())\n {\n if (--pruneCount < 1)\n {\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n return;\n }\n }\n }\n if (pruneCount === _pruneCount)\n break;\n }\n assert(pruneCount > 0);\n // Prune the excess non-assigned sandboxes -- we should never hit this.\n if (this.readiedSandboxes.length > 0)\n {\n const toPrune = this.readiedSandboxes.slice(0, pruneCount);\n this.readiedSandboxes = this.readiedSandboxes.slice(pruneCount);\n toPrune.forEach(sandbox => sandbox.terminate(false));\n pruneCount -= toPrune.length;\n if (pruneCount < 1)\n {\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n return;\n }\n }\n // Round-robin prune 1 assigned sandbox from each jobmanager.\n while (true)\n {\n const _pruneCount = pruneCount;\n for (const jm of readyJobManagers)\n {\n if (jm.pruneAssignedSandbox())\n {\n if (--pruneCount < 1)\n {\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n return;\n }\n }\n }\n if (pruneCount === _pruneCount)\n break;\n }\n this.sandboxInventory = this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated);\n selectiveDebugging && console.debug(`Supervisor.pruneSandboxes FINISH: unpruned ${pruneCount}/${this.sandboxCount()}/${this.maxTotalSandboxes}.`);\n}\n\n// _Idx\n//\n// Result-submitter-result support functions.\n// Send in the results!!!\n//\n\n/**\n * Submits the slice results to the result-submitter service.\n * Then remove the slice from the its job manager.\n *\n * @param {Slice} slice - The slice to submit.\n * @returns {Promise<void>}\n */\nSupervisor.prototype.recordResult = function Supervisor$recordResult (slice)\n{\n // It is possible for slice.result to be undefined when there are upstream errors.\n if ( !(slice && slice.result))\n throw new Error(`recordResult: slice.result is undefined for slice ${slice.identifier}. This is ok when there are upstream errors.`); \n if (!slice.isComplete)\n throw new Error('Cannot record result for slice that has not completed execution successfully.');\n\n debugging('supervisor') && console.debug(`supervisor: recording result for slice ${slice.identifier}.`);\n\n /* @see result-submitter::result for full message details */\n const metrics = { GPUTime: 0, CPUTime: 0, CPUDensity: 0, GPUDensity: 0, total: 0 };\n const payloadData = {\n slice: slice.sliceNumber,\n job: slice.jobAddress,\n worker: this.workerId,\n paymentAddress: this.paymentAddress,\n metrics,\n authorizationMessage: slice.authorizationMessage,\n }\n\n const timeReport = slice.timeReport;\n if (timeReport)\n {\n debugging('supervisor') && console.debug('recordResult timeReport', timeReport);\n // If slice takes less than 1ms to execute, CPUTime will be 0, so compensate.\n if (timeReport.CPU < 1)\n {\n timeReport.CPU++;\n timeReport.total++;\n }\n if (timeReport.total < timeReport.CPU + timeReport.webGL)\n {\n // Compensate or throw? For now we compensate.\n debuggingWarn && console.warn(`Supervisor.recordResult:: Inconsistent time report -- total < CPU + webGL -- ${stringify(timeReport)}`)\n //throw new Error(`recordResult: Inconsistent time report -- total < CPU + webGL -- ${stringify(timeReport)}`)\n timeReport.total = timeReport.CPU + timeReport.webGL;\n }\n if (timeReport.total > 0)\n {\n slice.jobManager.updateStatistics(timeReport);\n metrics.total = timeReport.total;\n metrics.CPUTime = timeReport.CPU;\n metrics.GPUTime = timeReport.webGL;\n metrics.CPUDensity = metrics.CPUTime / timeReport.total;\n metrics.GPUDensity = metrics.GPUTime / timeReport.total;\n }\n }\n\n this.emit('submittingResult');\n\n if (!this.resultSubmitter)\n this.connectTo('resultSubmitter');\n\n if (slice.resultStorageType === 'pattern')\n return this.sendResultToRemote(slice)\n .then((response) => {\n payloadData.result = response;\n this.sendToResultSubmitter(slice, payloadData);\n });\n\n payloadData.result = encodeDataURI(slice.result.result);\n return this.sendToResultSubmitter(slice, payloadData);\n}\n\n/**\n * @param {Slice} slice\n * @param {*} payloadData\n * @returns {Promise<void>}\n */\nSupervisor.prototype.sendToResultSubmitter = function Supervisor$sendToResultSubmitter (slice, payloadData)\n{\n const that = this;\n function handleRSError (error, payloadData)\n {\n that.error(`Failed to submit results to scheduler for slice ${payloadData.slice} of job ${payloadData.job}`, error);\n //slice.jobManager.dumpSlices('recordResult');\n that.saveForResubmitToRS('result', payloadData)\n .then((msg) => {\n if (!error && msg) error = new Error(`resultSubmitter is ${msg}`);\n that.emit('submitSliceFailed', error);\n throw error;\n });\n }\n\n try\n {\n debugging('supervisor') && console.debug('Supervisor.recordResult: payloadData', payloadData.result.slice(0, 256), slice.identifier);\n\n return this.resultSubmitter.send('result', payloadData)\n .then((resp) => {\n if (!resp.success)\n throw resp.payload;\n\n debugging('supervisor') && console.debug('recordResult: SUCCESS', slice.identifier);\n\n const receipt = {\n accepted: true,\n payment: resp.payload.slicePaymentAmount,\n };\n this.emit('submittedResult', resp.payload);\n this.emit('dccCredit', receipt);\n })\n .catch ((error) => {\n handleRSError (error, payloadData);\n });\n }\n catch (error)\n {\n handleRSError (error, payloadData);\n }\n finally\n {\n slice.markAsFinished();\n this.emit('submitFinished');\n // Remove the slice from the job manager.\n slice.jobManager.removeSlice(slice);\n if (this.sliceTiming)\n {\n slice['resultDelta'] = Date.now() - slice['resultDelta'];\n console.debug(`recordResult(${slice['queueingDelta']}, ${slice['executionDelta']}, ${slice['resultDelta']}): Completed slice ${slice.identifier}.`);\n }\n if (selectiveDebugging)\n {\n if (!this.resultMap[slice.jobAddress]) this.resultMap[slice.jobAddress] = { slices: [], totalTimes: [] };\n this.resultMap[slice.jobAddress].slices.push(slice.sliceNumber);\n this.resultMap[slice.jobAddress].totalTimes.push(payloadData.metrics.total);\n }\n }\n}\n\n/**\n * Send a work function's result to a server that speaks our DCP Remote Data Server protocol.\n * E.g. https://gitlab.com/Distributed-Compute-Protocol/dcp-rds\n *\n * @param {Slice} slice - Slice object whose result we are sending.\n * @returns {Promise<string>}\n * @throws When HTTP status not in the 2xx range.\n */\nSupervisor.prototype.sendResultToRemote = function Supervisor$sendResultToRemote (slice)\n{ \n /** XXXpfr @todo: Support file upload and other contentTypes. */\n function serializeContent(result, postParams) {\n const ctArray = postParams.contentType.split(';');\n switch (ctArray[0]) {\n case 'application/json':\n return JSON.stringify(result);\n case 'application/kvin':\n return (__webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\").serialize)(result);\n case 'application/octet-stream':\n case 'application/gzip':\n if (ctArray.length === 2 && ctArray[1] === 'base64')\n return btoa(result);\n // fall-through\n case 'text/plain':\n case 'text/html':\n case 'text/css':\n case 'text/csv':\n case 'text/javascript':\n return result;\n\n default:\n {\n const mtArray = ctArray[0].split('/');\n if (mtArray[0] === 'image' || mtArray[0] === 'video' || mtArray[0] === 'audio')\n return (ctArray.length === 2 && ctArray[1] === 'base64') ? btoa(result) : result;\n throw new Error(`sendResultToRemote: Unsupported contentType ${postParams.contentType}`);\n }\n }\n }\n\n // Construct postParams.\n const postParams = { ...slice.resultStorageParams };\n if (!postParams.contentType)\n postParams.contentType = 'application/json';\n if (!postParams.element)\n postParams.element = slice.sliceNumber;\n debugging('supervisor') && console.debug('sendResultToRemote postParams: ', postParams);\n\n // Construct result.\n const result = slice.result.result;\n if (result)\n postParams.content = serializeContent(result, postParams);\n else\n postParams.error = serializeContent(slice.error, postParams);\n debugging('supervisor') && console.debug('sendResultToRemote content: ', (result ? postParams.content : postParams.error).slice(0, 512));\n\n // Construct url.\n const sliceResultUri = makeValueURI('pattern', slice.resultStorageDetails, {\n slice: slice.sliceNumber,\n job: slice.jobAddress,\n });\n debugging() && console.debug('sendResultToRemote sliceResultUri: ', sliceResultUri);\n const url = new DcpURL(sliceResultUri);\n\n // Check allowed origins.\n if (this.makeSafeOriginList('sendResults').indexOf(url.origin) === -1)\n throw new Error(`Invalid origin for remote result storage: '${url.origin}'`);\n\n // Fetch.\n return justFetch(url, 'JSON', 'POST', false, postParams)\n .then((response) => encodeDataURI(JSON.stringify(response)));\n}\n\n// _Idx\n//\n// Reject.\n//\n\n/**\n * Handles reassigning or returning a slice that was rejected by a sandbox.\n *\n * If the slice does not have a rejected property already, reassign the\n * slice to a new sandbox and add a rejected property to the slice to\n * indicate it has already rejected once.\n *\n * If the slice rejects with a reason, or has a rejected time stamp\n * (ie. has been rejected once already) then return all slices from the\n * job to the scheduler and terminate all sandboxes with that jobAddress.\n *\n * The sandbox will be terminated.\n *\n * @param {Sandbox} sandbox\n * @param {Slice} slice\n */\nSupervisor.prototype.handleWorkReject = async function Supervisor$handleWorkReject (sandbox, slice, rejectReason)\n{\n debugging() && console.debug('handleWorkReject', rejectReason, slice.rejectedTimeStamp, slice.identifier);\n\n // Do a hard flush of the microtask queue and finish the current event loop.\n await new Promise((resolve) => setImmediate(() => setTimeout(resolve, 0)));\n\n const jobManager = slice.jobManager;\n jobManager.rejectedJobReasons.push(rejectReason); // memoize reasons\n\n // First time rejecting without a reason. Try assigning slice to a new sandbox.\n if (rejectReason === 'false' && !slice.rejectedTimeStamp)\n {\n // Set rejected time stamp.\n slice.rejectedTimeStamp = Date.now();\n // Schedule the slice for execution.\n jobManager.scheduleSlice(slice, true /* placeInTheFrontOfTheQueue*/);\n // Slice has been rescheduled, but we still need to terminate the sandbox.\n jobManager.returnSandbox(sandbox);\n }\n else\n { \n // Slice has a reason OR rejected without a reason already and got stamped.\n // Add to array of rejected jobs.\n let rejectedJob = {\n address: slice.jobAddress,\n reasons: jobManager.rejectedJobReasons,\n }\n this.rejectedJobs.push(rejectedJob);\n\n // Purge the job.\n this.purgeJob(jobManager);\n\n // Tell everyone all about it, when allowed.\n if (jobManager.displayMaxDiagInfo)\n {\n const suffixMsg = '\\n\\tAll slices with the same jobAddress returned to the scheduler.\\n\\tAll sandboxes with the same jobAddress are terminated.';\n if (slice.rejectedTimeStamp)\n this.emit('warning', `work.reject: The slice ${slice.identifier} was rejected twice.${suffixMsg}`);\n else\n this.emit('warning', `work.reject: The slice ${slice.identifier} was rejected with reason ${rejectReason}.${suffixMsg}`);\n }\n }\n}\n\n// _Idx\n//\n// Unused functions that we need to review.\n// 1) destroy, shutdown, halt -- possibly need to incorporate these ideas in stopWork\n// 2) predictLoad -- XXXpfr: I really feel bad about not being able to figure out how to incorporate\n// this into the design of sup2. This was a central part of Wes' design of sup2.\n// I need to collaborate with Wes to resolve my ignorance.\n//\n\n/**\n * UNUSED\n * @deprecated -- may use later\n **/\nSupervisor.prototype.destroy = function Supervisor$destory()\n{\n selectiveDebugging && console.debug(`Supervisor.screenSaverDestroy: destroying Supervisor and everything else.`);\n this.stopWork(true /*forceTerminate*/);\n if (this.state) this.state.destroy();\n if (this.progressReportTimer) clearInterval(this.progressReportTimer);\n if (this.watchdogTimer) clearInterval(this.watchdogTimer);\n this.state = null;\n this.progressReportTimer = null;\n this.watchdogTimer = null;\n this.jobManagerInventory = null;\n this.sandboxInventory = [];\n this.readiedSandboxes = [];\n this.closeConnections();\n}\n\n/**\n * UNUSED\n * @deprecated -- may use later \n * Halt the Supervisor as quickly as possible.\n **/\nSupervisor.prototype.halt = function Supervisor$halt()\n{\n this.state.setIf('ready', 'stopping');\n if (!this.state.is('stopping'))\n throw new Error(`Supervisor has an invalid state ${this.state} for halt`);\n clearInterval(this.watchdogTimer);\n\n for (let jobMan of this.jobManagerInventory)\n {\n jobMan.state.setIf('ready', 'stop');\n for (const sandbox of jobMan.workingSandboxes)\n {\n sandbox.stop(); // NYI -- will terminate.\n }\n }\n}\n \n/**\n * UNUSED\n * @deprecated -- may use later \n * Shutdown the supervisor; attempts to return work which will not be finished before timeout expires.\n * The shutdown is complete once this supervisor emits the stopped state change.\n */\nSupervisor.prototype.shutdown = function Supervisor$shutdown(timeoutMs)\n{\n var ps = [], returnSliceInventory = [];\n var timer;\n\n this.state.setIf('ready', 'stopping');\n if (!this.state.is('stopping'))\n throw new Error(`Supervisor has an invalid state ${this.state} for shutdown`);\n clearInterval(this.watchdogTimer);\n\n for (let jobMan of this.jobManagerInventory)\n {\n jobMan.state.setIf('ready', 'stop');\n\n for (let slice of jobMan.sliceInventory)\n {\n if (slice.state.is('initial') || slice.state.is('ready'))\n {\n returnSliceInventory.push(slice);\n }\n else if (slice.state.is(sliceStatus.working))\n {\n ps.push(new Promise((resolve, reject) => {\n slice.state.on('change', (status) => {\n if (status === 'done')\n resolve();\n });\n }));\n }\n }\n }\n\n const reason = 'Supervisor.shutdown';\n this.returnSlices(returnSliceInventory, reason);\n timer = setTimeout(this.halt.bind(this), timeoutMs);\n Promise.all(ps)\n .then(() => {\n clearTimeout(timer);\n this.state.set('stopping', 'stopped');\n })\n .catch((e) => {\n if (e.code !== 'DCP_SUPERVISOR_ESYNC')\n throw e; /* becomes unhandled rejection */\n });\n}\n\n/** \n * Factory function which generates a list of origins which are safe to communicate \n * with for this purpose. Currently-valid purposes (more will be added):\n * - any\n * - fetchData\n * - fetchWorkFunctions\n * - fetchArguments\n * - sendResults\n */\nSupervisor.prototype.makeSafeOriginList = function Supervisor$$makeSafeOriginList(purpose)\n{\n var list = [];\n \n if (this[purpose])\n list = list.concat(this[purpose]);\n \n /* Add 'any' origin(s) to list iff not in localExec, or in localExec and purpose is sendResults */\n if (!this.options.localExec || (this.options.localExec && purpose === 'sendResults'))\n list = list.concat(this.allowedOrigins)\n \n return list;\n}\n \n/**\n * UNUSED -- DOES NOT WORK YET.\n * NEED TO WORK WITH WES TO FIGURE OUT BEST WAY TO GET PREDICTLOAD TO WORK.\n * Predict the load on this supervisor based on the local job measurement data.\n * Works by looking at current conditions and available slices, and tries to guess\n * in what order they will be finished, working, etc. \n *\n * The simulation is very naive, but is expected to be accurate several seconds\n * into the future, particularly as we approach the end of a task.\n *\n * @param {number} whenMs \n * \n * @returns {Object<load, jobManagerInventory>} where load is and instance of Load and the predicted \n * load at the prediction time, and jobManagerInventory \n * is a counterfeit which holds the predicted state of \n * the jobManagerInventory at that time.\n */\nSupervisor.prototype.predictLoad = function Supervisor$predictLoad (whenMs)\n{\n /** @type {JobManager[]} */\n var jmi = new Inventory(); /* Inventory of counterfeit JobManagers. */\n var load = new Load(0,0); /* This \"current\" load throughout the prediction. */\n /** @type {Slice} */\n var next; /* The next slice to \"finish\". */\n\n /* Initialize data structures for prediction from current activity. */\n for (let jobMan of this.jobManagerInventory.filter(jm => jm.state.is('ready') && jm.sliceInventory.length))\n {\n jobMan = jobMan.counterfeit();\n jmi.push(jobMan);\n jobMan.sliceInventory.forEach((s) => s.state.setIf('initial', 'ready'));\n }\n next = findNextSlice();\n \n /**\n * Routine that finds the slice that will end next (soonest.)\n * @returns {Slice}\n */\n function findNextSlice()\n {\n /** @type {Slice} */\n var _next;\n for (let jobMan of jmi)\n {\n const _workingSlices = jobMan.workingSlices;\n for (let slice of _workingSlices)\n {\n //\n // slice.etaMs is the estimated time interval until slice execution completes.\n //\n // If the slice hasn't started,\n // slice.etaMs = slice.jobManager.estimateWallMs,\n // else if the slice has completed execution:\n // slice.etaMs = 0.\n // else if the slice has started:\n // slice.jobManager.estimateWallMs - (Date.now() - slice.startTime).\n //\n if (_next && (_next.etaMs <= slice.etaMs))\n continue;\n\n _next = slice;\n }\n }\n load.add(_next.jobManager.metrics);\n \n return _next;\n }\n\n /* At this point, jmi is an Inventory of counterfeit job managers that are \"ready\" for\n * work, next.etaMs is the time interval until the next slice will finish, and we have\n * a reasonably accurate picture of our current load.\n *\n * Next, we \"end\" this slice, try to fill all cores, and push the timeline forward to\n * the next predicted end of slice.\n */\n for (next = findNextSlice();\n next && (next.etaMs < whenMs);\n next = findNextSlice())\n {\n let ended = next;\n let cursor = this.makeJobSelectionCursor(jmi);\n\n /* \"end\" this slice */\n load.subtract(ended.jobManager.metrics);\n /* Fake out collecting result to transition state to FINISHED. */\n ended.collectResult(null);\n\n /* \"start\" as many slices as we can - given our CPU/GPU constraints, slice data in memory, etc */\n while (this.targetLoad.fits(load))\n {\n let slice = cursor.next();\n if (!slice)\n break; /* Running out of work that fits. */\n\n if (!load.fits(this.targetLoad, slice.jobManager.metrics))\n continue;\n\n /* Pick a ready slice from this job and add its anticipated load to our current load if it will fit */\n slice = slice.jobManager.readySlices.shift();\n slice.markAsWorking(); // ?? Not sure this is correct.\n //slice.etaMs = ended.etaMs + slice.jobManager.estimateWallMs; wtf?!?! <--- LOOK HERE\n\n load.add(slice.jobManager.metrics);\n }\n }\n\n return { load, jobManagerInventory: jmi };\n}\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/index.js?");
4529
4575
 
4530
4576
  /***/ }),
4531
4577
 
@@ -4536,7 +4582,7 @@ eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_mod
4536
4582
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4537
4583
 
4538
4584
  "use strict";
4539
- eval("/**\n * @file dcp-client/worker/supervisor2/job-manager.js\n *\n * A support class for Supervisor2.\n * It is a wrapper for the job object returned from the requestTask,\n * along with tracking slices and sandboxes associated with the job. \n *\n * @author Wes Garland, wes@kingsds.network,\n * Paul, paul@kingsds.network,\n * @date Dec 2020,\n * June 2022,\n * @module JobManager\n */\n\n/* global dcpConfig */ // eslint-disable-line no-redeclare\n// @ts-check\n\n\n/** @typedef {import('./').Supervisor} Supervisor */\n/** @typedef {import('./sandbox2').Sandbox} Sandbox */\n/** @typedef {import('dcp/common/dcp-url').DcpURL} DcpURL */\n/** @typedef {import('dcp/utils').SliceMessage} SliceMessage */\n/** @typedef {string} opaqueId */ // 22 character base64 string \n/** @typedef {string} address */ // String(Address)\n\nconst inspect = Symbol.for('nodejs.util.inspect.custom');\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst { Inventory } = __webpack_require__(/*! dcp/utils/inventory */ \"./src/utils/inventory.js\");\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst { Slice } = __webpack_require__(/*! ./slice2 */ \"./src/dcp-client/worker/supervisor2/slice2.js\");\nconst { Load } = __webpack_require__(/*! ./load */ \"./src/dcp-client/worker/supervisor2/load.js\");\nconst { Statistics } = __webpack_require__(/*! ./rolling-statistics */ \"./src/dcp-client/worker/supervisor2/rolling-statistics.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst { rehydrateRange } = __webpack_require__(/*! dcp/dcp-client/range-object */ \"./src/dcp-client/range-object.js\");\nconst kvin = __webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\");\nconst scopedKvin = new kvin.KVIN({Object: ({}).constructor,\n Array: ([]).constructor, \n Function: (()=>{}).constructor});\nconst { fetchURI, dumpObject, truncateAddress, stringify } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n\nconst addressTruncationLength = 20;\nlet workerTuning = dcpConfig.worker;\nif (!workerTuning) workerTuning = dcpConfig.Supervisor;\nif (!workerTuning || !workerTuning.maxSandboxErrorsPerSlice || !workerTuning.allowConsoleAccess)\n workerTuning = { maxSandboxErrorsPerSlice: 2, allowConsoleAccess: false };\n\n// This is for debugging and performance testing.\n// Several functions chain and return non-awaited promises.\n// When AWAIT_ALL is true, we await the promises, which makes easier debugging.\nconst AWAIT_ALL = false;\n\n// Debug tracing helpers.\nconst selectiveEnable = false;\nconst displayWarnError = false || selectiveEnable;\nconst selectiveDebugging = selectiveEnable || debugging();\nconst debuggingError = false || selectiveDebugging || displayWarnError;\nconst debuggingWarn = false || selectiveDebugging || displayWarnError;\nconst selectiveDebugging2 = selectiveEnable && false || debugging('jobmanager');\n\nconst INITIAL = 'initial';\nconst READY = 'ready';\nconst STOP = 'stop';\nconst REFUSE = 'refuse';\nconst BROKEN = 'broken';\n\n//\n// Index to functionality -- search for '_Idx' to toggle through the index.\n//\n// 1) Ctor: JobManager class definition and various properties.\n// 2) Statistics: updateStatistics, update.\n// 3) Dtors: destroy, stopWork.\n// 4) Assign sandboxes and execute slices.\n// 5) Remove from array and return sandbox.\n// 6) Fetch: WorkFn, slices, arguments.\n// 7) Miscellaneous.\n// 8) Unused functions that we need to review.\n//\n\n// _Idx\n//\n// Ctor: JobManager class definition and various properties.\n// A JobManager handles all scheduling of slices for a given job.\n// It's also responsible for fetching slice data, work functions and arguments.\n// And it collects statistics about slice completion times and resource usage.\n// All functionality across jobs is handled by Supervisor.\n//\n\n/**\n * JobManager Constructor. An instance of JobManager knows everything about a given\n * job within the supervisor, including:\n * - work function code\n * - work function arguments\n * - all the SliceManager instances that go with this job\n * - how long a slice of this job is expected to run\n *\n * Instances of JobManager emit the following events:\n * - addSlice(sliceHandle)\n * - deleteSlice(sliceHandle)\n * - statusChange(new state, old state);\n *\n * JobManager States\n *\n * Start state:\n * initial\n *\n * Intermediate states:\n * ready\n *\n * Terminal states:\n * broken - job could not be initialized\n * refuse - for some reason, we have decided that we don't want work for this job\n * stop - job manager has been stopped\n *\n * Valid transitions:\n * initial -> broken\n * initial -> ready\n * ready -> stop\n * \n * NOTE: If you ever use a property with a leading underscore you are probably making a mistake.\n * But if you must, please ask paul, yarn, bryan or eddie for a CR.\n */\nclass JobManager extends EventEmitter\n{\n /**\n * @constructor\n * @param {Supervisor} parent - Owning Supervisor.\n * @param {object} jobMessage - Job Descriptor from getNewJobsForTask.\n * @param {SliceMessage[]} sliceMessages - Messages from task distributor describing slices.\n * @param {object} authorizationMessage - The signature that shipped with the task authorizing this worker.\n */\n constructor(parent, jobMessage, sliceMessages, authorizationMessage)\n {\n super('JobManager');\n /** @type {Supervisor} */\n this._supervisor = parent;\n /** @type {Slice[]} */\n this._sliceInventory = []; // All slices for this.address.\n /** @type {Sandbox[]} */\n this._sandboxInventory = []; // All sandboxes for this.address.\n /** @type {Synchronizer} */\n this._state = new Synchronizer(INITIAL, [ INITIAL, READY, STOP, REFUSE, BROKEN ]);\n this.state.on('change', (neu, old) => this.emit('statusChange', neu, old));\n\n /** XXXpfr @todo Is there any reason to do a deeper clone here. */\n this.jobMessage = { ...jobMessage };\n\n /** @type {string} */\n this._address = String(this.jobMessage.address);\n /** @type {Load} */\n this._load = null;\n /** @type {Statistics} */\n this._statistics = new Statistics(0.25);\n /** @type {number} */\n this._emaSliceTime = 0;\n /** @type {string[]} */\n this.rejectedJobReasons = [];\n /** @type {boolean} */\n this.isEstimation = true;\n\n const that = this;\n /** \n * Start loading dependencies in the background. Once these are loaded, this.state will \n * transition to 'ready' and the job will be ready for work.\n */\n (async function supervisor$$JobManager$$loadDependencies() {\n await that.fetchJob(that.jobMessage);\n })(/* iife */)\n .then (() => {\n debugging('jobmanager') && console.debug('JobManager is transitioning to READY', this.identifier);\n this.state.set(INITIAL, READY);\n this.addSlices(sliceMessages, authorizationMessage);\n })\n .catch((error) => {\n debuggingError && console.error('fetchJob has failed', error);\n this.state.set(INITIAL, BROKEN);\n })\n .finally(() => {\n selectiveDebugging && console.debug('JobManager.loadDependencies completed.', this.identifier);\n });\n }\n\n /** @type {string} */\n get address () { return this._address; }\n /** @type {string} */\n get uuid () { return this.jobMessage.uuid; }\n /** @type {boolean} */\n get workerConsole () { return this.jobMessage.workerConsole; }\n /** @type {object} */\n get requirements () { return this.jobMessage.requirements; }\n\n // These 3 properties have type object.\n get mro () { return this.jobMessage.mro; }\n get arguments () { return this.jobMessage.arguments; }\n get workFunction () { return this.jobMessage.workFunction; }\n\n /** @type {{computeGroups: Array<{opaqueId: string, name: string, description: string}>, name: string, description: string, link: string}} */\n get public () { return this.jobMessage.public; }\n /** @type {{sliceCPUTime: number, sliceGPUTime: number, sliceCPUDensity: number, sliceGPUDensity: number}} */\n get metrics () { return this.jobMessage.metrics; }\n /** @type {Supervisor} */\n get supervisor () { return this._supervisor; }\n /** @type {Synchronizer} */\n get state () { return this._state; }\n\n /** @type {boolean} */\n get initial () { return this.state.is(INITIAL); }\n /** @type {boolean} */\n get ready () { return this.state.is(READY); }\n /** @type {boolean} */\n get stopped () { return this.state.is(STOP); }\n /** @type {boolean} */\n get refuse () { return this.state.is(REFUSE); }\n /** @type {boolean} */\n get broken () { return this.state.is(BROKEN); }\n\n /** @type {Sandbox[]} */\n get sandboxInventory () { return this._sandboxInventory; }\n /** @type {Sandbox[]} */\n get liveSandboxes () { return this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated); }\n /** @type {Sandbox[]} */\n get assignedSandboxes () { return this.sandboxInventory.filter((sandbox) => sandbox.isAssigned); }\n /** @type {Sandbox[]} */\n get workingSandboxes () { return this.sandboxInventory.filter((sandbox) => sandbox.isWorking); }\n /** @type {Sandbox[]} */\n get nonWorkingSandboxes () { return this.sandboxInventory.filter((sandbox) => !sandbox.isWorking && !sandbox.isTerminated); }\n /** @type {Slice[]} */\n get sliceInventory () { return this._sliceInventory; }\n /** @type {Slice[]} */\n get readySlices () { return this.sliceInventory.filter((slice) => slice.isReady); }\n /** @type {Slice[]} - ready and soon-to-be-ready */\n get queuedSlices () { return this.sliceInventory.filter((slice) => slice.isQueued); }\n /** @type {Slice[]} */\n get reservedSlices () { return this.sliceInventory.filter((slice) => slice.isReserved); }\n /** @type {Slice[]} - unassigned, ready and reserved */\n get activeSlices () { return this.sliceInventory.filter((slice) => slice.isActive); }\n /** @type {Slice[]} - working and soon-to-be-working */\n get workingSlices () { return this.sliceInventory.filter((slice) => slice.isWorking || slice.isReserved); }\n /** @type {Slice[]} - working only */\n get workingSlicesOnly () { return this.sliceInventory.filter((slice) => slice.isWorking); }\n\n /** @type {Load} */\n get load () { return this._load; }\n /** @type {number} */\n get delayMs () { return this.supervisor.delayMs(); }\n /** @type {Statistics} */\n get statistics () { return this._statistics; }\n /** @type {number} */\n get emaSliceTime () { return this._emaSliceTime; }\n /** @type {number} */\n get globalTime () { \n const denominator = this.metrics.sliceCPUDensity + this.metrics.sliceGPUDensity;\n return denominator > 0 ? (this.metrics.sliceCPUTime + this.metrics.sliceGPUTime) / denominator : 0;\n }\n /** @type {string} */\n get identifier () { return `${truncateAddress(this.address, addressTruncationLength)}.${this.state}`; }\n /** @type {string} */\n get [inspect] () { return `[Object JobManager <${this.public.name}::${this.address}::${this.state}>]`; }\n /** \n * Estimate the wall time (i.e. actual elapsed time on a wall clock) for a slice of this job.\n * We pick the ema time (alpha=0.25) + 0.7 * stddev to incorporate uncertainty.\n * About 76% of slice completion times are <= ema + 0.7 * stddev.\n * This estimate is very primitive and assumes that CPU and GPU code do not run at the same\n * time.\n *\n * @type {number} - estimated time period in milliseconds\n */ \n get estimateWallMs () { return this.emaSliceTime + 0.7 * this.statistics.stddev; } /** XXXpfr @todo make this.statistics.stddev reflect this.globalTime too. */\n /** @type {boolean} */\n get debugBuild () { return this.supervisor.debugBuild; }\n /**\n * Always display max info under debug builds, otherwise maximal error\n * messages are displayed to the worker, only if both worker and client agree.\n * @type {boolean} - When true, display stack trace and other enhanced diag info.\n **/\n get displayMaxDiagInfo () { return this.workerConsole && workerTuning.allowConsoleAccess || this.debugBuild; }\n\n // _Idx\n //\n // Statistics: updateStatistics, update.\n //\n\n /** @param {{ total, CPU, webGL }} timeReport */\n updateStatistics (timeReport)\n {\n this.statistics.add(timeReport.total);\n this._emaSliceTime = 0.5 * (this._emaSliceTime + this.statistics.ema); /** XXXpfr @todo double smoothing, need to test and compare against this.statistics.ema . */\n debugging('jobmanager') && console.debug('JobManager.updateStatistics: mean', this.statistics.mean, 'stddev', this.statistics.stddev, 'ema', this.statistics.ema, 'ma', this.statistics.ma, 'x', this.statistics.x);\n }\n\n /**\n * Update jobMessage, add some slices to inventory and possibly update the initial seed of the statistics.\n * @param {object} jobMessage - Job Descriptor from getNewJobsForTask.\n * @param {SliceMessage[]} sliceMessages - Messages from task distributor describing slices.\n * @param {object} authorizationMessage - The signature that shipped with the task authorizing this worker.\n */\n update (jobMessage, sliceMessages, authorizationMessage)\n {\n this.jobMessage = Object.assign(this.jobMessage, { ...jobMessage });\n this.addSlices(sliceMessages, authorizationMessage);\n if (!this._emaSliceTime || this.statistics.count < 1)\n this._emaSliceTime = this.globalTime;\n // this._load = new Load(this.metrics.sliceCPUTime, this.metrics.sliceGPUTime); // SAVE for Sup2 Part II\n }\n\n // _Idx\n //\n // Dtors: destroy, stopWork.\n //\n\n /**\n * Destructor.\n */\n destroy()\n {\n selectiveDebugging && console.debug(`JobManager.destroy: terminating sandboxes and returning slices to scheduler for job manager ${this.identifier}.`);\n \n this.sandboxInventory.forEach((sandbox) => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this._sandboxInventory = [];\n\n const slicesToReturn = this.sliceInventory.filter((slice) => !slice.isFinished);\n const reason = `JobManager destroy: ${this.identifier}`;\n this.supervisor.returnSlices(slicesToReturn, reason, false /*removeSlices*/);\n this._sliceInventory = [];\n }\n\n /**\n * Terminates sandboxes and returns slices.\n * Sets the working flag to false, call @this.work to start working again.\n * \n * If forceTerminate is true: Terminates all sandboxes and returns all slices.\n * If forceTerminate is false: Terminates non-working sandboxes and returns queued slices.\n *\n * @param {boolean} [forceTerminate = true] - true if you want to stop the sandboxes from completing their current slice.\n */\n stopWork (forceTerminate = true)\n {\n selectiveDebugging && console.debug(`JobManager.stopWork(${forceTerminate}): terminating sandboxes and returning slices to scheduler.`);\n if (forceTerminate)\n {\n this.destroy();\n }\n else\n {\n // Return all non-finished slices which are not working.\n const reason = `JobManager stopWork(false): ${this.identifier}`;\n this.supervisor.returnSlices(this.queuedSlices, reason);\n this._sliceInventory = [];\n\n // Terminate all sandboxes that are not working.\n let idleSandboxes = this.sandboxInventory.filter(w => !w.isWorking);\n for (const sandbox of idleSandboxes)\n this.returnSandbox(sandbox);\n this._sandboxInventory = [];\n }\n }\n \n /**\n * Terminate 1 assigned sandbox when there are more than threshold assigned sandboxes.\n * @param {number} [threshold=0] - integer >= 0\n * @returns {boolean}\n */\n pruneAssignedSandbox(threshold = 0)\n {\n const _assignedSandboxes = this.assignedSandboxes;\n if (_assignedSandboxes.length > threshold)\n {\n debugging('supervisor') && console.debug(`JobManager.pruneAssignedSandbox terminate ${_assignedSandboxes[0].identifier}, threshold ${threshold}`);\n _assignedSandboxes[0].markAsUnready(); // Remove from this.assignedSandboxes.\n this.returnSandbox(_assignedSandboxes[0]);\n return true;\n }\n return false;\n }\n\n /**\n * Terminate 1 assigned sandbox when there are more than this.queuedSlices.length assigned sandboxes.\n * @returns {boolean}\n */\n pruneExtraAssignedSandbox()\n {\n return this.pruneAssignedSandbox(this.queuedSlices.length);\n }\n\n // _Idx\n //\n // Work: Assign sandboxes and execute slices.\n //\n // hookUpSandboxListeners handles the sandbox complete event and tries to schedule another slice\n // from the same job on the sandbox. If there aren't any slices from the same job, it tries slices\n // from other job managers, and if there are no other ready slices, it calls Supervisor.requestTask\n // to get more work from TD .\n //\n // Several functions chain and return non-awaited promises.\n // When AWAIT_ALL is true, we await the promises, which makes easier debugging.\n //\n\n /**\n * Hook up JobManager specific Sandbox event listenters.\n * @param {Sandbox} sandbox\n */\n hookUpSandboxListeners(sandbox)\n {\n // Sandbox error handler.\n sandbox.on('sandboxError', (error) => JobManager.handleSandboxError(this, sandbox, error));\n // Sandbox complete handler.\n // When any sandbox completes, try to run another slice in the same jobManager.\n // Otherwise, go through the Supervisor.requestTask protocol.\n sandbox.addListener('complete', () => {\n if (this.supervisor.isReady())\n {\n const _readySlices = this.readySlices;\n if (this.supervisor.runSliceFromSameJob() && _readySlices.length > 0)\n {\n const slice = _readySlices[0];\n slice.markAsReserved();\n this.runSlice(slice, this.delayMs);\n }\n else if (!this.supervisor.isFetchingNewWork)\n {\n const unusedSandboxSlots = this.supervisor.unusedSandboxSlots();\n if (unusedSandboxSlots > 0)\n this.supervisor.requestTask(unusedSandboxSlots);\n }\n }\n });\n }\n\n /**\n * Create a Sandbox.\n * Start it. Assign it. Add it to jobManager inventory.\n * @param {number} delayStartMs - The delay ms to pass to sandbox.start .\n * @param {number} delayAssignMs - The delay ms to pass to sandbox.assign .\n * @returns {Promise<Sandbox>}\n */\n async readySandbox (delayStartMs, delayAssignMs)\n {\n const sandbox = await this.supervisor.createSandbox(delayStartMs);\n this.hookUpSandboxListeners(sandbox);\n return sandbox.assign(this, delayAssignMs)\n .catch((error) => { // error could be a string coming from evaluator\n const errorObj = { jobAddress: truncateAddress(this.address, addressTruncationLength), error: this.checkStackTrace(error) };\n this.supervisor.error(`Failed to assign job to sandbox: ${errorObj}`);\n this.returnSandbox(sandbox);\n throw error;\n });\n }\n\n /**\n * Mark both sandbox and slice as working.\n * @param {Sandbox} sandbox\n * @param {Slice} slice\n */\n markAsWorking(sandbox, slice)\n {\n assert(sandbox);\n assert(slice);\n slice.markAsWorking();\n sandbox.markAsWorking(slice);\n }\n\n /**\n * Create or reuse a sandbox and run a slice on it.\n * @param {Slice} slice - The slice to execute on a sandbox.\n * @param {number} [delayMs=0] - The delay ms to pass to sandbox.start .\n * @returns {Promise<void>}\n */\n async runSlice(slice, delayMs)\n {\n assert(slice);\n if (this.supervisor.sliceTiming) slice['queueingDelta'] = Date.now();\n\n const _assignedSandboxes = this.assignedSandboxes;\n if (AWAIT_ALL)\n {\n if (_assignedSandboxes.length > 0 && this.supervisor.readiedSandboxes.length < 1)\n {\n const sandbox = _assignedSandboxes[0];\n this.markAsWorking(sandbox, slice);\n return await this.runSliceOnSandbox(slice, sandbox, this.delayMs); \n }\n try\n {\n const sandbox = await this.readySandbox(delayMs/*delayStartMs*/, 0.5 * this.delayMs/*delayAssignMs*/);\n this.sandboxInventory.push(sandbox);\n this.markAsWorking(sandbox, slice);\n return await this.runSliceOnSandbox(slice, sandbox, this.delayMs);\n }\n catch (error)\n {\n // Any exception from readySandbox will have already been emitted.\n debugging('jobmanager') && console.error(`JobManager.runSlice: Failure trying to run slice ${slice.identifier} on a sandbox.`, error);\n this.supervisor.returnSlice(slice, 'EUNCAUGHT');\n }\n }\n else\n {\n if (_assignedSandboxes.length > 0 && this.supervisor.readiedSandboxes.length < 1)\n {\n const sandbox = _assignedSandboxes[0];\n this.markAsWorking(sandbox, slice);\n return this.runSliceOnSandbox(slice, sandbox, this.delayMs); \n }\n return this.readySandbox(delayMs/*delayStartMs*/, this.delayMs/*delayAssignMs*/)\n .then((sandbox) => {\n this.sandboxInventory.push(sandbox);\n this.markAsWorking(sandbox, slice);\n return this.runSliceOnSandbox(slice, sandbox, this.delayMs);\n })\n .catch((error) => {\n // Any exception from readySandbox will have already been emitted.\n debugging('jobmanager') && console.error(`JobManager.runSlice: Failure trying to run slice ${slice.identifier} on a sandbox.`, error);\n this.supervisor.returnSlice(slice, 'EUNCAUGHT');\n })\n }\n }\n\n /**\n * Execute slice on sandbox, collect results or handle errors, and clean up.\n * @param {Slice} slice - The slice to execute on the sandbox.\n * @param {Sandbox} sandbox - The sandbox on which to execute the slice.\n * @param {number} [delayMs=0] - The delay ms to pass to sandbox.work .\n * @returns {Promise<void>}\n */\n async runSliceOnSandbox(slice, sandbox, delayMs = 0)\n {\n selectiveDebugging2 && console.debug(`runSliceOnSandbox ${sandbox.identifier}~${slice.identifier} #readySlices ${this.readySlices.length}/${this.supervisor.readySliceCount()}, #SBs, ${this.sandboxInventory.length}/${this.supervisor.sandboxCount()}, #working(w/r/wo/wsbx), ${this.workingSlices.length}/${this.reservedSlices.length}/${this.workingSlicesOnly.length}/${this.workingSandboxes.length}, #sup-working(w/r/wo/wsbx), ${this.supervisor.workingSliceCount()}/${this.supervisor.reservedSliceCount()}/${this.supervisor.workingSliceOnlyCount()}/${this.supervisor.workingSandboxCount()}, #assigned, ${this.assignedSandboxes.length}, #readiedSBs, ${this.supervisor.readiedSandboxes.length}, #localSBs, ${this.sandboxInventory.map(s => Number(s.id)).sort((x,y)=>x-y)}`);\n //console.log('runSliceOnSandbox', Date.now() - this.supervisor.lastTime, slice.identifier); // SAVE temporarily\n assert(slice && sandbox);\n slice.verifyWorking();\n sandbox.verifyWorking();\n\n if (this.supervisor.sliceTiming)\n {\n slice['queueingDelta'] = Date.now() - slice['queueingDelta'];\n slice['executionDelta'] = Date.now();\n }\n slice.startTime = Date.now();\n\n if (AWAIT_ALL)\n {\n try\n {\n const result = await sandbox.work(delayMs);\n debugging('jobmanager') && console.debug(`runSliceOnSandbox - success: ${sandbox.id}~${slice.identifier}`, result);\n slice.collectResult(result, true);\n sandbox.changeWorkingToAssigned();\n this.supervisor.recordResult(slice);\n }\n catch (error)\n {\n const reason = await this.supervisor.handleSandboxWorkError(sandbox, slice, error);\n debuggingError && console.error(`runSliceOnSandbox - failure: ${sandbox.id}~${slice.identifier}`, reason, error);\n }\n finally\n {\n slice.startTime = null;\n if (this.supervisor.sliceTiming)\n {\n slice['executionDelta'] = Date.now() - slice['executionDelta'];\n slice['resultDelta'] = Date.now();\n }\n }\n }\n else\n {\n return sandbox.work(delayMs)\n .then((result) => {\n debugging('jobmanager') && console.debug(`runSliceOnSandbox - success: ${sandbox.id}~${slice.identifier}`, result);\n slice.collectResult(result, true);\n sandbox.changeWorkingToAssigned();\n this.supervisor.recordResult(slice);\n })\n .catch(async (error) => {\n const reason = await this.supervisor.handleSandboxWorkError(sandbox, slice, error);\n debuggingError && console.error(`runSliceOnSandbox - failure: ${sandbox.id}~${slice.identifier}`, reason, error);\n })\n .finally(() => {\n slice.startTime = null;\n if (this.supervisor.sliceTiming)\n {\n slice['executionDelta'] = Date.now() - slice['executionDelta'];\n slice['resultDelta'] = Date.now();\n }\n });\n }\n }\n\n /**\n * Sandbox has had an error which is not from the work function: terminate it\n * and try to redo the slice.\n * @param {JobManager} jobManager \n * @param {Sandbox} sandbox \n * @param {Error} error \n */\n static handleSandboxError (jobManager, sandbox, error)\n {\n const slice = sandbox.slice;\n if (slice)\n {\n if (!slice.isWorking) // Sanity. Exception should never fire.\n throw new Error(`handleSandboxError: slice ${slice.identifier} must be WORKING.`);\n\n slice['sandboxErrorCount'] = (slice['sandboxErrorCount'] || 0) + 1;\n sandbox.slice = null;\n if (slice['sandboxErrorCount'] < (workerTuning.maxSandboxErrorsPerSlice || 2))\n slice.resetState();\n else\n {\n slice.collectResult(error, false /*success*/);\n const reason = `sandboxError event handler, too many errors ${slice['sandboxErrorCount']}: ${sandbox.id}~${jobManager.identifier}`;\n jobManager.supervisor.returnSlice(slice, reason);\n }\n jobManager.supervisor.error(`JobManager.handleSandboxError: Sandbox ${sandbox.identifier}...(${sandbox.public.name}/${slice['sandboxErrorCount']}) with slice ${slice.identifier} had error.`, error);\n }\n else\n jobManager.supervisor.error(`JobManager.handleSandboxError: Sandbox ${sandbox.identifier} has no slice...(${sandbox.public.name} had error.`, error);\n\n jobManager.returnSandbox(sandbox); /* terminate the sandbox */\n }\n\n // _Idx\n //\n // Remove from array and return sandbox.\n //\n\n /**\n * Remove sandbox from this.sandboxInventory and terminate is not already terminated.\n * @param {Sandbox} sandbox - The sandbox to remove from this.sandboxInventory.\n * @param {boolean} [assertExists=true] - When true (and type=array) then assert that sandbox exists in this.sandboxInventory.\n */\n returnSandbox(sandbox, assertExists = true)\n {\n debugging('supervisor') && console.debug(`JobManager.returnSandbox: ${sandbox.identifier}, assertExists ${assertExists}`);\n if (this.sandboxInventory && this.sandboxInventory.length > 0)\n JobManager.removeElement(this.sandboxInventory, sandbox, assertExists);\n // The cleaning up of terminated sandboxes in this.supervisor.sandboxInventory happens in this.supervisor.purgeSandboxes.\n if (!sandbox.isTerminated)\n sandbox.terminate(false);\n }\n /**\n * Remove slice from this.sliceInventory.\n * @param {Slice} slice - The slice to remove from this.sliceInventory.\n * @param {boolean} [assertExists=true] - When true (and type=array) then assert that slice exists in this.sliceInventory.\n */\n removeSlice(slice, assertExists = true)\n {\n assert(this.sliceInventory instanceof Array);\n if (this.sliceInventory && this.sliceInventory.length > 0)\n JobManager.removeElement(this.sliceInventory, slice, assertExists);\n }\n\n /**\n * Remove slices from this.sliceInventory.\n * @param {Slice[]} slices - The slices to remove from this.sliceInventory.\n */\n removeSlices(slices)\n {\n assert(this.sliceInventory instanceof Array);\n if (this.sliceInventory && this.sliceInventory.length > 0)\n this._sliceInventory = this.sliceInventory.filter((slice) => slices.indexOf(slice) === -1);\n }\n\n /**\n * Remove element from arrayLike.\n * @param {Array<*>} array\n * @param {object|number} element\n * @param {boolean} [assertExists=true]\n */\n static removeElement(array, element, assertExists = true)\n {\n let index = array.indexOf(element);\n if (assertExists)\n {\n assert(index !== -1);\n array.splice(index, 1);\n } else if (index !== -1) array.splice(index, 1);\n }\n\n // _Idx\n //\n // Fetch: WorkFn, slices, arguments.\n //\n\n /**\n * Fetch work function, work function arguments and possibly the range object describing the jobs slices..\n * @param {object} mpe - messagePayloadElement: job object returned by task-jobs.js . \n */\n async fetchJob(mpe)\n {\n if (debugging('worker'))\n dumpObject(mpe, 'JobManager.fetchJob: mpe', 512);\n\n if (AWAIT_ALL)\n {\n if (!mpe.workFunction)\n {\n mpe.workFunction = await fetchURI(mpe.codeLocation, this.supervisor.makeSafeOriginList('fetchWorkFunctions'));\n if(mpe.requirements.useStrict)\n mpe.useStrict = true;\n delete mpe.codeLocation;\n }\n if (!mpe.arguments)\n {\n let promises = [];\n let uris = mpe.argumentsLocation;\n if (uris)\n for (let i = 0; i < uris.length; i++)\n promises.push(fetchURI(uris[i].value, this.supervisor.makeSafeOriginList('fetchArguments')));\n\n mpe.arguments = await Promise.all(promises);\n\n // node localExec jobs read arguments from a file, so need to ensure they are properly parsed after being read.\n if (this.supervisor.options.localExec && !DCP_ENV.isBrowserPlatform)\n mpe.arguments = scopedKvin.parse(mpe.arguments[0]);\n\n delete mpe.argumentsLocation;\n }\n // if job input data is range object, we send the range object URI to the worker\n if (!mpe.mro && mpe.MROLocation)\n {\n mpe.mro = await fetchURI(mpe.MROLocation, this.supervisor.makeSafeOriginList('fetchData'));\n delete mpe.MROLocation;\n }\n }\n else\n {\n let promises = [];\n\n // Get workFn.\n if (!mpe.workFunction)\n {\n const workFunctionPromise = fetchURI(mpe.codeLocation, this.supervisor.makeSafeOriginList('fetchWorkFunctions'))\n .then((workFunction) => {\n mpe.workFunction = workFunction;\n if (mpe.requirements.useStrict)\n mpe.useStrict = true;\n delete mpe.codeLocation;\n });\n promises.push(workFunctionPromise);\n }\n\n // if job input data is range object, we send the range object URI to the worker\n if (!mpe.mro && mpe.MROLocation)\n {\n // Do we need this? SAVE.\n // if (this.supervisor.options.localExec && !DCP_ENV.isBrowserPlatform)\n // mpe.MROLocation = await fetchURI(mpe.MROLocation, this.supervisor.allowedOrigins, this.supervisor.fetchData);\n\n const mroPromise = fetchURI(mpe.MROLocation, this.supervisor.makeSafeOriginList('fetchData'))\n .then((mro) => {\n mpe.mro = mro;\n delete mpe.MROLocation;\n });\n promises.push(mroPromise)\n }\n\n // Get workFn args.\n if (!mpe.arguments && mpe.argumentsLocation)\n {\n mpe.arguments = new Array(mpe.argumentsLocation.length);\n for (let k = 0; k < mpe.argumentsLocation.length; k++)\n promises.push(fetchURI(mpe.argumentsLocation[k].value, this.supervisor.makeSafeOriginList('fetchArguments'))\n .then((arg) => (mpe.arguments[k] = arg) ));\n }\n\n await Promise.all(promises);\n\n if (mpe.argumentsLocation) delete mpe.argumentsLocation;\n }\n\n return mpe;\n }\n\n /**\n * Look up slice.datumUri or use the range object this.mro, constructed by fetchJob.\n * @param {Slice} slice \n * @returns {Promise<{ inputDatum, dataError }>}\n */\n async fetchSliceData (datumUri, slice) // eslint-disable-line require-await\n {\n function catchHandler(error)\n {\n const dataError = error;\n dataError['errorCode'] = (error.code === 'EFETCH') ? 'EFETCH' : 'EUNCAUGHTERROR';\n return dataError;\n }\n\n if (!datumUri && !this.state.is(READY))\n throw new Error(`When !datumUri, JobManager '${this.identifier}' must be in READY state.`);\n\n try\n {\n if (!datumUri)\n {\n if (!this.mro) throw new Error('Must complete call to JobManager.fetchJob before calling JobManager.fetchSliceData.');\n const ro = rehydrateRange(this.mro);\n // Slice numbers start at 1.\n const inputDatum = ro[slice.sliceNumber - 1];\n debugging('jobmanager') && console.debug(`Fetched mro datum: ${stringify(inputDatum, 512)}`);\n return { inputDatum, dataError: null };\n }\n\n return fetchURI(datumUri, this.supervisor.makeSafeOriginList('fetchData'))\n .then((inputDatum) => {\n debugging('jobmanager') && console.debug(`Fetched datum: ${stringify(inputDatum, 512)}`);\n return { inputDatum, dataError: null };\n })\n .catch((error) => {\n const dataError = catchHandler(error);\n return { inputDatum: null, dataError };\n });\n }\n catch (error)\n {\n const dataError = catchHandler(error);\n return { inputDatum: null, dataError };\n }\n }\n\n // _Idx\n //\n // Miscellaneous.\n //\n\n /**\n * Debugging helper.\n * @param {boolean} [details=true]\n */\n dumpSlices(details = true)\n {\n if (this.sliceInventory.length < 1) return;\n const queued = [], working = [], complete = [], failed = [], finished = [], extras = [];\n for (const slice of this.sliceInventory)\n {\n if (slice.isUnassigned || slice.isReady)\n queued.push(slice);\n else if (slice.isReserved || slice.isWorking)\n working.push(slice);\n else if (slice.isComplete)\n complete.push(slice);\n else if (slice.hasFailed)\n failed.push(slice);\n else if (slice.isFinished)\n finished.push(slice);\n else\n extras.push(slice);\n }\n console.log(`:--JobManager.dumpSlices--${this.identifier} slices ${this.sliceInventory.length}-------`);\n console.log(`-----queued(${queued.length})-----------------------------------------------------------------`);\n if (details)\n {\n for (const slice of queued)\n console.log(slice.identifier);\n console.log(`-----working(${working.length})----------------------------------------------------------------`);\n for (const slice of working)\n console.log(slice.identifier);\n console.log(`-----complete(${complete.length})----------------------------------------------------------------`);\n for (const slice of complete)\n console.log(slice.identifier);\n console.log(`-----failed(${failed.length})----------------------------------------------------------------`);\n for (const slice of failed)\n console.log(slice.identifier);\n console.log(`-----finished(${finished.length})---------------------------------------------------------------`);\n for (const slice of finished)\n console.log(slice.identifier);\n console.log(`-----extras(${extras.length})---------------------------------------------------------------`);\n for (const slice of extras)\n console.log(slice.identifier);\n }\n else\n {\n console.log(`-----working(${working.length})----------------------------------------------------------------`);\n console.log(`-----complete(${complete.length})----------------------------------------------------------------`);\n console.log(`-----failed(${failed.length})----------------------------------------------------------------`);\n console.log(`-----finished(${finished.length})---------------------------------------------------------------`);\n console.log(`-----extras(${extras.length})---------------------------------------------------------------`);\n }\n console.log('-----------------------------------------------------------------------------------');\n }\n\n /**\n * Calls removeStackTrace when !this.displayMaxDiagInfo .\n * @param {string|Error} error\n * @returns {string|Error}\n */\n checkStackTrace(error)\n {\n return this.displayMaxDiagInfo ? error : this.supervisor.removeStackTrace(error);\n }\n\n /**\n * Add slices to the job manager's inventory.\n *\n * @param {SliceMessage[]} sliceMessages - Messages from task distributor describing slices.\n * @param {object} authorizationMessage - The signature that shipped with the task authorizing this worker.\n */\n addSlices (sliceMessages, authorizationMessage)\n {\n sliceMessages.forEach((sliceMessage) => {\n const slice = new Slice(this, sliceMessage, authorizationMessage);\n if (!slice.isEstimation) this.isEstimation = false;\n this.sliceInventory.push(slice);\n });\n }\n\n /**\n * Schedule the slice to be executed.\n * @param {Slice} slice\n * @param {boolean} [placeInTheFrontOfTheQueue=false]\n */\n scheduleSlice(slice, placeInTheFrontOfTheQueue = false)\n {\n // Reset slice state to allow execution.\n slice.resetState();\n // Enqueue in the to-be-executed queue.\n if (placeInTheFrontOfTheQueue) this.sliceInventory.unshift(slice);\n else this.sliceInventory.push(slice);\n }\n\n /**\n * XXXpfr @todo UNUSED but may be used soon\n * Returns a counterfeit JobManager - a generic object with most of the same\n * ownProperties as a real exports.JobManager. Any inventories and synchronizer are\n * duplicated (equivalent state, no events copied, ~ shallow clone)\n * @returns {JobManager}\n */\n counterfeit()\n {\n /** @type {JobManager} */\n const fake = {};\n \n for (let prop in Object.keys(this))\n {\n if (this[prop] instanceof Inventory)\n fake[prop] = this[prop].duplicate();\n else if (this[prop] instanceof Synchronizer)\n fake[prop] = this[prop].valueOf();\n else\n fake[prop] = this[prop];\n }\n\n return fake;\n }\n\n}\nexports.JobManager = JobManager;\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/job-manager.js?");
4585
+ eval("/**\n * @file dcp-client/worker/supervisor2/job-manager.js\n *\n * A support class for Supervisor2.\n * It is a wrapper for the job object returned from the requestTask,\n * along with tracking slices and sandboxes associated with the job. \n *\n * @author Wes Garland, wes@kingsds.network,\n * Paul, paul@kingsds.network,\n * @date Dec 2020,\n * June 2022,\n * @module JobManager\n */\n\n/* global dcpConfig */ // eslint-disable-line no-redeclare\n// @ts-check\n\n\n/** @typedef {import('./').Supervisor} Supervisor */\n/** @typedef {import('./sandbox2').Sandbox} Sandbox */\n/** @typedef {import('dcp/common/dcp-url').DcpURL} DcpURL */\n/** @typedef {import('dcp/utils').SliceMessage} SliceMessage */\n/** @typedef {string} opaqueId */ // 22 character base64 string \n/** @typedef {string} address */ // String(Address)\n\nconst inspect = Symbol.for('nodejs.util.inspect.custom');\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst { Inventory } = __webpack_require__(/*! dcp/utils/inventory */ \"./src/utils/inventory.js\");\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst { Slice } = __webpack_require__(/*! ./slice2 */ \"./src/dcp-client/worker/supervisor2/slice2.js\");\nconst { Load } = __webpack_require__(/*! ./load */ \"./src/dcp-client/worker/supervisor2/load.js\");\nconst { Statistics } = __webpack_require__(/*! ./rolling-statistics */ \"./src/dcp-client/worker/supervisor2/rolling-statistics.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst { rehydrateRange } = __webpack_require__(/*! dcp/dcp-client/range-object */ \"./src/dcp-client/range-object.js\");\nconst kvin = __webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\");\nconst scopedKvin = new kvin.KVIN({Object: ({}).constructor,\n Array: ([]).constructor, \n Function: (()=>{}).constructor});\nconst { fetchURI, dumpObject, truncateAddress, stringify } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n\nconst addressTruncationLength = 20;\nlet workerTuning = dcpConfig.worker;\nif (!workerTuning) workerTuning = dcpConfig.Supervisor;\nif (!workerTuning || !workerTuning.maxSandboxErrorsPerSlice || !workerTuning.allowConsoleAccess)\n workerTuning = { maxSandboxErrorsPerSlice: 2, allowConsoleAccess: false };\n\n// This is for debugging and performance testing.\n// Several functions chain and return non-awaited promises.\n// When AWAIT_ALL is true, we await the promises, which makes easier debugging.\nconst AWAIT_ALL = false;\n\n// Debug tracing helpers.\nconst selectiveEnable = false;\nconst displayWarnError = false || selectiveEnable;\nconst selectiveDebugging = selectiveEnable || debugging();\nconst debuggingError = false || selectiveDebugging || displayWarnError;\nconst debuggingWarn = false || selectiveDebugging || displayWarnError;\nconst selectiveDebugging2 = selectiveEnable && false || debugging('jobmanager');\n\nconst INITIAL = 'initial';\nconst READY = 'ready';\nconst STOP = 'stop';\nconst REFUSE = 'refuse';\nconst BROKEN = 'broken';\n\n//\n// Index to functionality -- search for '_Idx' to toggle through the index.\n//\n// 1) Ctor: JobManager class definition and various properties.\n// 2) Statistics: updateStatistics, update.\n// 3) Dtors: destroy, stopWork.\n// 4) Assign sandboxes and execute slices.\n// 5) Remove from array and return sandbox.\n// 6) Fetch: WorkFn, slices, arguments.\n// 7) Miscellaneous.\n// 8) Unused functions that we need to review.\n//\n\n// _Idx\n//\n// Ctor: JobManager class definition and various properties.\n// A JobManager handles all scheduling of slices for a given job.\n// It's also responsible for fetching slice data, work functions and arguments.\n// And it collects statistics about slice completion times and resource usage.\n// All functionality across jobs is handled by Supervisor.\n//\n\n/**\n * JobManager Constructor. An instance of JobManager knows everything about a given\n * job within the supervisor, including:\n * - work function code\n * - work function arguments\n * - all the SliceManager instances that go with this job\n * - how long a slice of this job is expected to run\n *\n * Instances of JobManager emit the following events:\n * - addSlice(sliceHandle)\n * - deleteSlice(sliceHandle)\n * - statusChange(new state, old state);\n *\n * JobManager States\n *\n * Start state:\n * initial\n *\n * Intermediate states:\n * ready\n *\n * Terminal states:\n * broken - job could not be initialized\n * refuse - for some reason, we have decided that we don't want work for this job\n * stop - job manager has been stopped\n *\n * Valid transitions:\n * initial -> broken\n * initial -> ready\n * ready -> stop\n * \n * NOTE: If you ever use a property with a leading underscore you are probably making a mistake.\n * But if you must, please ask paul, yarn, bryan or eddie for a CR.\n */\nclass JobManager extends EventEmitter\n{\n /**\n * @constructor\n * @param {Supervisor} parent - Owning Supervisor.\n * @param {object} jobMessage - Job Descriptor from getNewJobsForTask.\n * @param {SliceMessage[]} sliceMessages - Messages from task distributor describing slices.\n * @param {object} authorizationMessage - The signature that shipped with the task authorizing this worker.\n */\n constructor(parent, jobMessage, sliceMessages, authorizationMessage)\n {\n super('JobManager');\n /** @type {Supervisor} */\n this._supervisor = parent;\n /** @type {Slice[]} */\n this._sliceInventory = []; // All slices for this.address.\n /** @type {Sandbox[]} */\n this._sandboxInventory = []; // All sandboxes for this.address.\n /** @type {Synchronizer} */\n this._state = new Synchronizer(INITIAL, [ INITIAL, READY, STOP, REFUSE, BROKEN ]);\n this.state.on('change', (neu, old) => this.emit('statusChange', neu, old));\n\n /** XXXpfr @todo Is there any reason to do a deeper clone here. */\n this.jobMessage = { ...jobMessage };\n\n /** @type {string} */\n this._address = String(this.jobMessage.address);\n /** @type {Load} */\n this._load = null;\n /** @type {Statistics} */\n this._statistics = new Statistics(0.25);\n /** @type {number} */\n this._emaSliceTime = 0;\n /** @type {string[]} */\n this.rejectedJobReasons = [];\n /** @type {boolean} */\n this.isEstimation = true;\n\n const that = this;\n /** \n * Start loading dependencies in the background. Once these are loaded, this.state will \n * transition to 'ready' and the job will be ready for work.\n */\n (async function supervisor$$JobManager$$loadDependencies() {\n await that.fetchJob(that.jobMessage);\n })(/* iife */)\n .then (() => {\n debugging('jobmanager') && console.debug('JobManager is transitioning to READY', this.identifier);\n this.state.set(INITIAL, READY);\n this.addSlices(sliceMessages, authorizationMessage);\n })\n .catch((error) => {\n debuggingError && console.error('fetchJob has failed', error);\n this.state.set(INITIAL, BROKEN);\n })\n .finally(() => {\n selectiveDebugging && console.debug('JobManager.loadDependencies completed.', this.identifier);\n });\n }\n\n /** @type {string} */\n get address () { return this._address; }\n /** @type {string} */\n get uuid () { return this.jobMessage.uuid; }\n /** @type {boolean} */\n get workerConsole () { return this.jobMessage.workerConsole; }\n /** @type {object} */\n get requirements () { return this.jobMessage.requirements; }\n\n // These 3 properties have type object.\n get mro () { return this.jobMessage.mro; }\n get arguments () { return this.jobMessage.arguments; }\n get workFunction () { return this.jobMessage.workFunction; }\n\n /** @type {{computeGroups: Array<{opaqueId: string, name: string, description: string}>, name: string, description: string, link: string}} */\n get public () { return this.jobMessage.public; }\n /** @type {{sliceCPUTime: number, sliceGPUTime: number, sliceCPUDensity: number, sliceGPUDensity: number}} */\n get metrics () { return this.jobMessage.metrics; }\n /** @type {Supervisor} */\n get supervisor () { return this._supervisor; }\n /** @type {Synchronizer} */\n get state () { return this._state; }\n\n /** @type {boolean} */\n get initial () { return this.state.is(INITIAL); }\n /** @type {boolean} */\n get ready () { return this.state.is(READY); }\n /** @type {boolean} */\n get stopped () { return this.state.is(STOP); }\n /** @type {boolean} */\n get refuse () { return this.state.is(REFUSE); }\n /** @type {boolean} */\n get broken () { return this.state.is(BROKEN); }\n\n /** @type {Sandbox[]} */\n get sandboxInventory () { return this._sandboxInventory; }\n /** @type {Sandbox[]} */\n get liveSandboxes () { return this.sandboxInventory.filter((sandbox) => !sandbox.isTerminated); }\n /** @type {Sandbox[]} */\n get assignedSandboxes () { return this.sandboxInventory.filter((sandbox) => sandbox.isAssigned); }\n /** @type {Sandbox[]} */\n get workingSandboxes () { return this.sandboxInventory.filter((sandbox) => sandbox.isWorking); }\n /** @type {Sandbox[]} */\n get nonWorkingSandboxes () { return this.sandboxInventory.filter((sandbox) => !sandbox.isWorking && !sandbox.isTerminated); }\n /** @type {Slice[]} */\n get sliceInventory () { return this._sliceInventory; }\n /** @type {Slice[]} */\n get readySlices () { return this.sliceInventory.filter((slice) => slice.isReady); }\n /** @type {Slice[]} - ready and soon-to-be-ready */\n get queuedSlices () { return this.sliceInventory.filter((slice) => slice.isQueued); }\n /** @type {Slice[]} */\n get reservedSlices () { return this.sliceInventory.filter((slice) => slice.isReserved); }\n /** @type {Slice[]} - unassigned, ready and reserved */\n get activeSlices () { return this.sliceInventory.filter((slice) => slice.isActive); }\n /** @type {Slice[]} - working and soon-to-be-working */\n get workingSlices () { return this.sliceInventory.filter((slice) => slice.isWorking || slice.isReserved); }\n /** @type {Slice[]} - working only */\n get workingSlicesOnly () { return this.sliceInventory.filter((slice) => slice.isWorking); }\n\n /** @type {Load} */\n get load () { return this._load; }\n /** @type {number} */\n get delayMs () { return this.supervisor.delayMs(); }\n /** @type {Statistics} */\n get statistics () { return this._statistics; }\n /** @type {number} */\n get emaSliceTime () { return this._emaSliceTime; }\n /** @type {number} */\n get globalTime () { \n const denominator = this.metrics.sliceCPUDensity + this.metrics.sliceGPUDensity;\n return denominator > 0 ? (this.metrics.sliceCPUTime + this.metrics.sliceGPUTime) / denominator : 0;\n }\n /** @type {string} */\n get identifier () { return `${truncateAddress(this.address, addressTruncationLength)}.${this.state}`; }\n /** @type {string} */\n get [inspect] () { return `[Object JobManager <${this.public.name}::${this.address}::${this.state}>]`; }\n /** \n * Estimate the wall time (i.e. actual elapsed time on a wall clock) for a slice of this job.\n * We pick the ema time (alpha=0.25) + 0.7 * stddev to incorporate uncertainty.\n * About 76% of slice completion times are <= ema + 0.7 * stddev.\n * This estimate is very primitive and assumes that CPU and GPU code do not run at the same\n * time.\n *\n * @type {number} - estimated time period in milliseconds\n */ \n get estimateWallMs () { return this.emaSliceTime + 0.7 * this.statistics.stddev; } /** XXXpfr @todo make this.statistics.stddev reflect this.globalTime too. */\n /** @type {boolean} */\n get debugBuild () { return this.supervisor.debugBuild; }\n /**\n * Always display max info under debug builds, otherwise maximal error\n * messages are displayed to the worker, only if both worker and client agree.\n * @type {boolean} - When true, display stack trace and other enhanced diag info.\n **/\n get displayMaxDiagInfo () { return this.workerConsole && workerTuning.allowConsoleAccess || this.debugBuild; }\n\n // _Idx\n //\n // Statistics: updateStatistics, update.\n //\n\n /** @param {{ total, CPU, webGL }} timeReport */\n updateStatistics (timeReport)\n {\n this.statistics.add(timeReport.total);\n this._emaSliceTime = 0.5 * (this._emaSliceTime + this.statistics.ema); /** XXXpfr @todo double smoothing, need to test and compare against this.statistics.ema . */\n debugging('jobmanager') && console.debug('JobManager.updateStatistics: mean', this.statistics.mean, 'stddev', this.statistics.stddev, 'ema', this.statistics.ema, 'ma', this.statistics.ma, 'x', this.statistics.x);\n }\n\n /**\n * Update jobMessage, add some slices to inventory and possibly update the initial seed of the statistics.\n * @param {object} jobMessage - Job Descriptor from getNewJobsForTask.\n * @param {SliceMessage[]} sliceMessages - Messages from task distributor describing slices.\n * @param {object} authorizationMessage - The signature that shipped with the task authorizing this worker.\n */\n update (jobMessage, sliceMessages, authorizationMessage)\n {\n this.jobMessage = Object.assign(this.jobMessage, { ...jobMessage });\n this.addSlices(sliceMessages, authorizationMessage);\n if (!this._emaSliceTime || this.statistics.count < 1)\n this._emaSliceTime = this.globalTime;\n // this._load = new Load(this.metrics.sliceCPUTime, this.metrics.sliceGPUTime); // SAVE for Sup2 Part II\n }\n\n // _Idx\n //\n // Dtors: destroy, stopWork.\n //\n\n /**\n * Destructor.\n */\n destroy()\n {\n selectiveDebugging && console.debug(`JobManager.destroy: terminating sandboxes and returning slices to scheduler for job manager ${this.identifier}.`);\n \n this.sandboxInventory.forEach((sandbox) => {\n if (!sandbox.isTerminated) sandbox.terminate(false);\n });\n this._sandboxInventory = [];\n\n const slicesToReturn = this.sliceInventory.filter((slice) => !slice.isFinished);\n const reason = `JobManager destroy: ${this.identifier}`;\n this.supervisor.returnSlices(slicesToReturn, reason, false /*removeSlices*/);\n this._sliceInventory = [];\n }\n\n /**\n * Terminates sandboxes and returns slices.\n * Sets the working flag to false, call @this.work to start working again.\n * \n * If forceTerminate is true: Terminates all sandboxes and returns all slices.\n * If forceTerminate is false: Terminates non-working sandboxes and returns queued slices.\n *\n * @param {boolean} [forceTerminate = true] - true if you want to stop the sandboxes from completing their current slice.\n */\n stopWork (forceTerminate = true)\n {\n selectiveDebugging && console.debug(`JobManager.stopWork(${forceTerminate}): terminating sandboxes and returning slices to scheduler.`);\n if (forceTerminate)\n {\n this.destroy();\n }\n else\n {\n // Return all non-finished slices which are not working.\n const reason = `JobManager stopWork(false): ${this.identifier}`;\n this.supervisor.returnSlices(this.queuedSlices, reason);\n this._sliceInventory = [];\n\n // Terminate all sandboxes that are not working.\n let idleSandboxes = this.sandboxInventory.filter(w => !w.isWorking);\n for (const sandbox of idleSandboxes)\n this.returnSandbox(sandbox);\n this._sandboxInventory = [];\n }\n }\n \n /**\n * Terminate 1 assigned sandbox when there are more than threshold assigned sandboxes.\n * @param {number} [threshold=0] - integer >= 0\n * @returns {boolean}\n */\n pruneAssignedSandbox(threshold = 0)\n {\n const _assignedSandboxes = this.assignedSandboxes;\n if (_assignedSandboxes.length > threshold)\n {\n debugging('supervisor') && console.debug(`JobManager.pruneAssignedSandbox terminate ${_assignedSandboxes[0].identifier}, threshold ${threshold}`);\n _assignedSandboxes[0].markAsUnready(); // Remove from this.assignedSandboxes.\n this.returnSandbox(_assignedSandboxes[0]);\n return true;\n }\n return false;\n }\n\n /**\n * Terminate 1 assigned sandbox when there are more than this.queuedSlices.length assigned sandboxes.\n * @returns {boolean}\n */\n pruneExtraAssignedSandbox()\n {\n return this.pruneAssignedSandbox(this.queuedSlices.length);\n }\n\n // _Idx\n //\n // Work: Assign sandboxes and execute slices.\n //\n // hookUpSandboxListeners handles the sandbox complete event and tries to schedule another slice\n // from the same job on the sandbox. If there aren't any slices from the same job, it tries slices\n // from other job managers, and if there are no other ready slices, it calls Supervisor.requestTask\n // to get more work from TD .\n //\n // Several functions chain and return non-awaited promises.\n // When AWAIT_ALL is true, we await the promises, which makes easier debugging.\n //\n\n /**\n * Hook up JobManager specific Sandbox event listenters.\n * @param {Sandbox} sandbox\n */\n hookUpSandboxListeners(sandbox)\n {\n // Sandbox error handler.\n sandbox.on('sandboxError', (error) => JobManager.handleSandboxError(this, sandbox, error));\n // Sandbox complete handler.\n // When any sandbox completes, try to run another slice in the same jobManager.\n // Otherwise, go through the Supervisor.requestTask protocol.\n sandbox.addListener('complete', () => {\n if (this.supervisor.isReady())\n {\n const _readySlices = this.readySlices;\n if (this.supervisor.runSliceFromSameJob() && _readySlices.length > 0)\n {\n const slice = _readySlices[0];\n slice.markAsReserved();\n this.runSlice(slice, this.delayMs);\n }\n else if (!this.supervisor.isFetchingNewWork)\n {\n const unusedSandboxSlots = this.supervisor.unusedSandboxSlots();\n if (unusedSandboxSlots > 0)\n this.supervisor.requestTask(unusedSandboxSlots);\n }\n }\n });\n }\n\n /**\n * Create a Sandbox.\n * Start it. Assign it. Add it to jobManager inventory.\n * @param {number} delayStartMs - The delay ms to pass to sandbox.start .\n * @param {number} delayAssignMs - The delay ms to pass to sandbox.assign .\n * @returns {Promise<Sandbox>}\n */\n async readySandbox (delayStartMs, delayAssignMs)\n {\n const sandbox = await this.supervisor.createSandbox(delayStartMs);\n this.hookUpSandboxListeners(sandbox);\n return sandbox.assign(this, delayAssignMs)\n .catch((error) => { // error could be a string coming from evaluator\n this.supervisor.error(`Failed to assign job ${truncateAddress(this.address, addressTruncationLength)} to sandbox ${sandbox.id}`, error);\n this.returnSandbox(sandbox);\n throw error;\n });\n }\n\n /**\n * Mark both sandbox and slice as working.\n * @param {Sandbox} sandbox\n * @param {Slice} slice\n */\n markAsWorking(sandbox, slice)\n {\n assert(sandbox);\n assert(slice);\n slice.markAsWorking();\n sandbox.markAsWorking(slice);\n }\n\n /**\n * Create or reuse a sandbox and run a slice on it.\n * @param {Slice} slice - The slice to execute on a sandbox.\n * @param {number} [delayMs=0] - The delay ms to pass to sandbox.start .\n * @returns {Promise<void>}\n */\n async runSlice(slice, delayMs)\n {\n assert(slice);\n if (this.supervisor.sliceTiming) slice['queueingDelta'] = Date.now();\n\n const _assignedSandboxes = this.assignedSandboxes;\n if (AWAIT_ALL)\n {\n if (_assignedSandboxes.length > 0 && this.supervisor.readiedSandboxes.length < 1)\n {\n const sandbox = _assignedSandboxes[0];\n this.markAsWorking(sandbox, slice);\n return await this.runSliceOnSandbox(slice, sandbox, this.delayMs); \n }\n try\n {\n const sandbox = await this.readySandbox(delayMs/*delayStartMs*/, 0.5 * this.delayMs/*delayAssignMs*/);\n this.sandboxInventory.push(sandbox);\n this.markAsWorking(sandbox, slice);\n return await this.runSliceOnSandbox(slice, sandbox, this.delayMs);\n }\n catch (error)\n {\n // Any exception from readySandbox will have already been emitted.\n debugging('jobmanager') && console.error(`JobManager.runSlice: Failure trying to run slice ${slice.identifier} on a sandbox.`, error);\n this.supervisor.returnSlice(slice, 'EUNCAUGHT');\n }\n }\n else\n {\n if (_assignedSandboxes.length > 0 && this.supervisor.readiedSandboxes.length < 1)\n {\n const sandbox = _assignedSandboxes[0];\n this.markAsWorking(sandbox, slice);\n return this.runSliceOnSandbox(slice, sandbox, this.delayMs); \n }\n return this.readySandbox(delayMs/*delayStartMs*/, this.delayMs/*delayAssignMs*/)\n .then((sandbox) => {\n this.sandboxInventory.push(sandbox);\n this.markAsWorking(sandbox, slice);\n return this.runSliceOnSandbox(slice, sandbox, this.delayMs);\n })\n .catch((error) => {\n // Any exception from readySandbox will have already been emitted.\n debugging('jobmanager') && console.error(`JobManager.runSlice: Failure trying to run slice ${slice.identifier} on a sandbox.`, error);\n this.supervisor.returnSlice(slice, 'EUNCAUGHT');\n })\n }\n }\n\n /**\n * Execute slice on sandbox, collect results or handle errors, and clean up.\n * @param {Slice} slice - The slice to execute on the sandbox.\n * @param {Sandbox} sandbox - The sandbox on which to execute the slice.\n * @param {number} [delayMs=0] - The delay ms to pass to sandbox.work .\n * @returns {Promise<void>}\n */\n async runSliceOnSandbox(slice, sandbox, delayMs = 0)\n {\n selectiveDebugging2 && console.debug(`runSliceOnSandbox ${sandbox.identifier}~${slice.identifier} #readySlices ${this.readySlices.length}/${this.supervisor.readySliceCount()}, #SBs, ${this.sandboxInventory.length}/${this.supervisor.sandboxCount()}, #working(w/r/wo/wsbx), ${this.workingSlices.length}/${this.reservedSlices.length}/${this.workingSlicesOnly.length}/${this.workingSandboxes.length}, #sup-working(w/r/wo/wsbx), ${this.supervisor.workingSliceCount()}/${this.supervisor.reservedSliceCount()}/${this.supervisor.workingSliceOnlyCount()}/${this.supervisor.workingSandboxCount()}, #assigned, ${this.assignedSandboxes.length}, #readiedSBs, ${this.supervisor.readiedSandboxes.length}, #localSBs, ${this.sandboxInventory.map(s => Number(s.id)).sort((x,y)=>x-y)}`);\n //console.log('runSliceOnSandbox', Date.now() - this.supervisor.lastTime, slice.identifier); // SAVE temporarily\n assert(slice && sandbox);\n slice.verifyWorking();\n sandbox.verifyWorking();\n\n if (this.supervisor.sliceTiming)\n {\n slice['queueingDelta'] = Date.now() - slice['queueingDelta'];\n slice['executionDelta'] = Date.now();\n }\n slice.startTime = Date.now();\n\n if (AWAIT_ALL)\n {\n try\n {\n const result = await sandbox.work(delayMs);\n debugging('jobmanager') && console.debug(`runSliceOnSandbox - success: ${sandbox.id}~${slice.identifier}`, result);\n slice.collectResult(result, true);\n sandbox.changeWorkingToAssigned();\n this.supervisor.recordResult(slice);\n }\n catch (error)\n {\n const reason = await this.supervisor.handleSandboxWorkError(sandbox, slice, error);\n debuggingError && console.error(`runSliceOnSandbox - failure: ${sandbox.id}~${slice.identifier}`, reason, error);\n }\n finally\n {\n slice.startTime = null;\n if (this.supervisor.sliceTiming)\n {\n slice['executionDelta'] = Date.now() - slice['executionDelta'];\n slice['resultDelta'] = Date.now();\n }\n }\n }\n else\n {\n return sandbox.work(delayMs)\n .then((result) => {\n debugging('jobmanager') && console.debug(`runSliceOnSandbox - success: ${sandbox.id}~${slice.identifier}`, result);\n slice.collectResult(result, true);\n sandbox.changeWorkingToAssigned();\n this.supervisor.recordResult(slice);\n })\n .catch(async (error) => {\n const reason = await this.supervisor.handleSandboxWorkError(sandbox, slice, error);\n debuggingError && console.error(`runSliceOnSandbox - failure: ${sandbox.id}~${slice.identifier}`, reason, error);\n })\n .finally(() => {\n slice.startTime = null;\n if (this.supervisor.sliceTiming)\n {\n slice['executionDelta'] = Date.now() - slice['executionDelta'];\n slice['resultDelta'] = Date.now();\n }\n });\n }\n }\n\n /**\n * Sandbox has had an error which is not from the work function: terminate it\n * and try to redo the slice.\n * @param {JobManager} jobManager \n * @param {Sandbox} sandbox \n * @param {Error} error \n */\n static handleSandboxError (jobManager, sandbox, error)\n {\n const slice = sandbox.slice;\n if (slice)\n {\n if (!slice.isWorking) // Sanity. Exception should never fire.\n throw new Error(`handleSandboxError: slice ${slice.identifier} must be WORKING.`);\n\n slice['sandboxErrorCount'] = (slice['sandboxErrorCount'] || 0) + 1;\n sandbox.slice = null;\n if (slice['sandboxErrorCount'] < (workerTuning.maxSandboxErrorsPerSlice || 2))\n slice.resetState();\n else\n {\n slice.collectResult(error, false /*success*/);\n const reason = `sandboxError event handler, too many errors ${slice['sandboxErrorCount']}: ${sandbox.id}~${jobManager.identifier}`;\n jobManager.supervisor.returnSlice(slice, reason);\n }\n jobManager.supervisor.error(`JobManager.handleSandboxError: Sandbox ${sandbox.identifier}...(${sandbox.public.name}/${slice['sandboxErrorCount']}) with slice ${slice.identifier} had error.`, error);\n }\n else\n jobManager.supervisor.error(`JobManager.handleSandboxError: Sandbox ${sandbox.identifier} has no slice...(${sandbox.public.name} had error.`, error);\n\n jobManager.returnSandbox(sandbox); /* terminate the sandbox */\n }\n\n // _Idx\n //\n // Remove from array and return sandbox.\n //\n\n /**\n * Remove sandbox from this.sandboxInventory and terminate is not already terminated.\n * @param {Sandbox} sandbox - The sandbox to remove from this.sandboxInventory.\n * @param {boolean} [assertExists=true] - When true (and type=array) then assert that sandbox exists in this.sandboxInventory.\n */\n returnSandbox(sandbox, assertExists = true)\n {\n debugging('supervisor') && console.debug(`JobManager.returnSandbox: ${sandbox.identifier}, assertExists ${assertExists}`);\n if (this.sandboxInventory && this.sandboxInventory.length > 0)\n JobManager.removeElement(this.sandboxInventory, sandbox, assertExists);\n // The cleaning up of terminated sandboxes in this.supervisor.sandboxInventory happens in this.supervisor.purgeSandboxes.\n if (!sandbox.isTerminated)\n sandbox.terminate(false);\n }\n /**\n * Remove slice from this.sliceInventory.\n * @param {Slice} slice - The slice to remove from this.sliceInventory.\n * @param {boolean} [assertExists=true] - When true (and type=array) then assert that slice exists in this.sliceInventory.\n */\n removeSlice(slice, assertExists = true)\n {\n assert(this.sliceInventory instanceof Array);\n if (this.sliceInventory && this.sliceInventory.length > 0)\n JobManager.removeElement(this.sliceInventory, slice, assertExists);\n }\n\n /**\n * Remove slices from this.sliceInventory.\n * @param {Slice[]} slices - The slices to remove from this.sliceInventory.\n */\n removeSlices(slices)\n {\n assert(this.sliceInventory instanceof Array);\n if (this.sliceInventory && this.sliceInventory.length > 0)\n this._sliceInventory = this.sliceInventory.filter((slice) => slices.indexOf(slice) === -1);\n }\n\n /**\n * Remove element from arrayLike.\n * @param {Array<*>} array\n * @param {object|number} element\n * @param {boolean} [assertExists=true]\n */\n static removeElement(array, element, assertExists = true)\n {\n let index = array.indexOf(element);\n if (assertExists)\n {\n assert(index !== -1);\n array.splice(index, 1);\n } else if (index !== -1) array.splice(index, 1);\n }\n\n // _Idx\n //\n // Fetch: WorkFn, slices, arguments.\n //\n\n /**\n * Fetch work function, work function arguments and possibly the range object describing the jobs slices..\n * @param {object} mpe - messagePayloadElement: job object returned by task-jobs.js . \n */\n async fetchJob(mpe)\n {\n if (debugging('worker'))\n dumpObject(mpe, 'JobManager.fetchJob: mpe', 512);\n\n if (AWAIT_ALL)\n {\n if (!mpe.workFunction)\n {\n mpe.workFunction = await fetchURI(mpe.codeLocation, this.supervisor.makeSafeOriginList('fetchWorkFunctions'));\n if(mpe.requirements.useStrict)\n mpe.useStrict = true;\n delete mpe.codeLocation;\n }\n if (!mpe.arguments)\n {\n let promises = [];\n let uris = mpe.argumentsLocation;\n if (uris)\n for (let i = 0; i < uris.length; i++)\n promises.push(fetchURI(uris[i].value, this.supervisor.makeSafeOriginList('fetchArguments')));\n\n mpe.arguments = await Promise.all(promises);\n\n // node localExec jobs read arguments from a file, so need to ensure they are properly parsed after being read.\n if (this.supervisor.options.localExec && !DCP_ENV.isBrowserPlatform)\n mpe.arguments = scopedKvin.parse(mpe.arguments[0]);\n\n delete mpe.argumentsLocation;\n }\n // if job input data is range object, we send the range object URI to the worker\n if (!mpe.mro && mpe.MROLocation)\n {\n mpe.mro = await fetchURI(mpe.MROLocation, this.supervisor.makeSafeOriginList('fetchData'));\n delete mpe.MROLocation;\n }\n }\n else\n {\n let promises = [];\n\n // Get workFn.\n if (!mpe.workFunction)\n {\n const workFunctionPromise = fetchURI(mpe.codeLocation, this.supervisor.makeSafeOriginList('fetchWorkFunctions'))\n .then((workFunction) => {\n mpe.workFunction = workFunction;\n if (mpe.requirements.useStrict)\n mpe.useStrict = true;\n delete mpe.codeLocation;\n });\n promises.push(workFunctionPromise);\n }\n\n // if job input data is range object, we send the range object URI to the worker\n if (!mpe.mro && mpe.MROLocation)\n {\n // Do we need this? SAVE.\n // if (this.supervisor.options.localExec && !DCP_ENV.isBrowserPlatform)\n // mpe.MROLocation = await fetchURI(mpe.MROLocation, this.supervisor.allowedOrigins, this.supervisor.fetchData);\n\n const mroPromise = fetchURI(mpe.MROLocation, this.supervisor.makeSafeOriginList('fetchData'))\n .then((mro) => {\n mpe.mro = mro;\n delete mpe.MROLocation;\n });\n promises.push(mroPromise)\n }\n\n // Get workFn args.\n if (!mpe.arguments && mpe.argumentsLocation)\n {\n mpe.arguments = new Array(mpe.argumentsLocation.length);\n for (let k = 0; k < mpe.argumentsLocation.length; k++)\n promises.push(fetchURI(mpe.argumentsLocation[k].value, this.supervisor.makeSafeOriginList('fetchArguments'))\n .then((arg) => (mpe.arguments[k] = arg) ));\n }\n\n await Promise.all(promises);\n\n if (mpe.argumentsLocation) delete mpe.argumentsLocation;\n }\n\n return mpe;\n }\n\n /**\n * Look up slice.datumUri or use the range object this.mro, constructed by fetchJob.\n * @param {Slice} slice \n * @returns {Promise<{ inputDatum, dataError }>}\n */\n async fetchSliceData (datumUri, slice) // eslint-disable-line require-await\n {\n function catchHandler(error)\n {\n const dataError = error;\n dataError['errorCode'] = (error.code === 'EFETCH') ? 'EFETCH' : 'EUNCAUGHTERROR';\n return dataError;\n }\n\n if (!datumUri && !this.state.is(READY))\n throw new Error(`When !datumUri, JobManager '${this.identifier}' must be in READY state.`);\n\n try\n {\n if (!datumUri)\n {\n if (!this.mro) throw new Error('Must complete call to JobManager.fetchJob before calling JobManager.fetchSliceData.');\n const ro = rehydrateRange(this.mro);\n // Slice numbers start at 1.\n const inputDatum = ro[slice.sliceNumber - 1];\n debugging('jobmanager') && console.debug(`Fetched mro datum: ${stringify(inputDatum, 512)}`);\n return { inputDatum, dataError: null };\n }\n\n return fetchURI(datumUri, this.supervisor.makeSafeOriginList('fetchData'))\n .then((inputDatum) => {\n debugging('jobmanager') && console.debug(`Fetched datum: ${stringify(inputDatum, 512)}`);\n return { inputDatum, dataError: null };\n })\n .catch((error) => {\n const dataError = catchHandler(error);\n return { inputDatum: null, dataError };\n });\n }\n catch (error)\n {\n const dataError = catchHandler(error);\n return { inputDatum: null, dataError };\n }\n }\n\n // _Idx\n //\n // Miscellaneous.\n //\n\n /**\n * Debugging helper.\n * @param {boolean} [details=true]\n */\n dumpSlices(details = true)\n {\n if (this.sliceInventory.length < 1) return;\n const queued = [], working = [], complete = [], failed = [], finished = [], extras = [];\n for (const slice of this.sliceInventory)\n {\n if (slice.isUnassigned || slice.isReady)\n queued.push(slice);\n else if (slice.isReserved || slice.isWorking)\n working.push(slice);\n else if (slice.isComplete)\n complete.push(slice);\n else if (slice.hasFailed)\n failed.push(slice);\n else if (slice.isFinished)\n finished.push(slice);\n else\n extras.push(slice);\n }\n console.log(`:--JobManager.dumpSlices--${this.identifier} slices ${this.sliceInventory.length}-------`);\n console.log(`-----queued(${queued.length})-----------------------------------------------------------------`);\n if (details)\n {\n for (const slice of queued)\n console.log(slice.identifier);\n console.log(`-----working(${working.length})----------------------------------------------------------------`);\n for (const slice of working)\n console.log(slice.identifier);\n console.log(`-----complete(${complete.length})----------------------------------------------------------------`);\n for (const slice of complete)\n console.log(slice.identifier);\n console.log(`-----failed(${failed.length})----------------------------------------------------------------`);\n for (const slice of failed)\n console.log(slice.identifier);\n console.log(`-----finished(${finished.length})---------------------------------------------------------------`);\n for (const slice of finished)\n console.log(slice.identifier);\n console.log(`-----extras(${extras.length})---------------------------------------------------------------`);\n for (const slice of extras)\n console.log(slice.identifier);\n }\n else\n {\n console.log(`-----working(${working.length})----------------------------------------------------------------`);\n console.log(`-----complete(${complete.length})----------------------------------------------------------------`);\n console.log(`-----failed(${failed.length})----------------------------------------------------------------`);\n console.log(`-----finished(${finished.length})---------------------------------------------------------------`);\n console.log(`-----extras(${extras.length})---------------------------------------------------------------`);\n }\n console.log('-----------------------------------------------------------------------------------');\n }\n\n /**\n * Calls removeStackTrace when !this.displayMaxDiagInfo .\n * @param {string|Error} error\n * @returns {string|Error}\n */\n checkStackTrace(error)\n {\n return this.displayMaxDiagInfo ? error : this.supervisor.removeStackTrace(error);\n }\n\n /**\n * Add slices to the job manager's inventory.\n *\n * @param {SliceMessage[]} sliceMessages - Messages from task distributor describing slices.\n * @param {object} authorizationMessage - The signature that shipped with the task authorizing this worker.\n */\n addSlices (sliceMessages, authorizationMessage)\n {\n sliceMessages.forEach((sliceMessage) => {\n const slice = new Slice(this, sliceMessage, authorizationMessage);\n if (!slice.isEstimation) this.isEstimation = false;\n this.sliceInventory.push(slice);\n });\n }\n\n /**\n * Schedule the slice to be executed.\n * @param {Slice} slice\n * @param {boolean} [placeInTheFrontOfTheQueue=false]\n */\n scheduleSlice(slice, placeInTheFrontOfTheQueue = false)\n {\n // Reset slice state to allow execution.\n slice.resetState();\n // Enqueue in the to-be-executed queue.\n if (placeInTheFrontOfTheQueue) this.sliceInventory.unshift(slice);\n else this.sliceInventory.push(slice);\n }\n\n /**\n * XXXpfr @todo UNUSED but may be used soon\n * Returns a counterfeit JobManager - a generic object with most of the same\n * ownProperties as a real exports.JobManager. Any inventories and synchronizer are\n * duplicated (equivalent state, no events copied, ~ shallow clone)\n * @returns {JobManager}\n */\n counterfeit()\n {\n /** @type {JobManager} */\n const fake = {};\n \n for (let prop in Object.keys(this))\n {\n if (this[prop] instanceof Inventory)\n fake[prop] = this[prop].duplicate();\n else if (this[prop] instanceof Synchronizer)\n fake[prop] = this[prop].valueOf();\n else\n fake[prop] = this[prop];\n }\n\n return fake;\n }\n\n}\nexports.JobManager = JobManager;\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/job-manager.js?");
4540
4586
 
4541
4587
  /***/ }),
4542
4588
 
@@ -4556,7 +4602,7 @@ eval("/**\n * @file worker/supervisor2/load.js\n *\n * A support class for Super
4556
4602
  \***********************************************************/
4557
4603
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4558
4604
 
4559
- eval("/**\n * @file worker/module-cache.js\n *\n * A cache for modules.\n *\n * @author Matthew Palma, mpalma@kingsds.network\n * Paul, paul@kingsds.network\n * @date May 2019\n * June 2022\n */\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\n \nclass ModuleCache extends EventEmitter\n{\n constructor (supervisor)\n {\n super('ModuleCache')\n this.supervisor = supervisor;\n this.cache = {}; // Modules keyed by module group.\n }\n\n /**\n * Returns an object listing what jobs and modules are currently cached.\n * @returns {string} - in the form: moduleGroup1: [ job1 ], moduleGroup2: [ job1, job2 ]...\n */\n get cacheDescription ()\n {\n const desc = {};\n for (const [ id, moduleJobs ] of Object.entries(this.cache))\n desc[id] = moduleJobs.jobs;\n return JSON.stringify(desc);\n }\n\n /**\n * Attempts to look up an item from the cache.\n * @param {string} id - the items module group name\n * @param {string} job - the job calling fetch\n * @returns {any} The value of the stored item or undefined if nothing is found\n */\n fetch(id, job)\n {\n const moduleJobs = this.cache[id];\n if (!moduleJobs) return moduleJobs;\n if (moduleJobs.jobs.indexOf(job) === -1 )\n moduleJobs.jobs.push(job);\n return moduleJobs.module;\n }\n\n /**\n * Stores a fetched value for one of the caches.\n * @param {string} id - the items identifier module group name\n * @param {string} job - the job calling fetch\n * @param {any} value - the item to store\n * @returns {any} - @value passed in\n */\n store (id, job, value)\n {\n if (!this.cache[id]) this.cache[id] = { module: value, jobs: [ job ] };\n else if (this.cache[id].jobs.indexOf(job) === -1) this.cache[id].jobs.push(job);\n return value\n }\n \n /**\n * Removes a job reference from a module cache.\n * When there are no jobs left, the module cache entry is destroyed.\n * @param {string} job - the job to be removed.\n */\n removeJob (job)\n {\n for (const [ id, moduleJobs ] of Object.entries(this.cache))\n {\n moduleJobs.jobs = moduleJobs.jobs.filter(_job => _job !== job);\n if (moduleJobs.jobs.length < 1) delete this.cache[id];\n }\n }\n\n /**\n * Attempts to fetch a module group from the cache and\n * if it's not found it attempts to fetch then store\n * the module group from the package manager.\n *\n * @param {array} modulesArray - the array of modules requested \n * - (when stringified it's the identifier of the module group)\n *\n * @returns {Promise<object>} - the module group\n * @throws when the module group can not be fetched\n */\n async fetchModule(modulesArray, job)\n {\n const cacheKey = JSON.stringify(modulesArray);\n let modules = this.fetch(cacheKey, job);\n if (modules !== null)\n {\n debugging() && console.debug('FetchModule: Cache hit', { cacheKey, job });\n return modules;\n }\n\n const {\n success,\n payload: responsePayload,\n } = await this.supervisor.packageManagerConnection.send('fetchModule', {\n modules: modulesArray,\n });\n\n if (!success)\n {\n /**\n * Preserving the error message by not rewrapping it with DCPError in case\n * we want to let clients know which module couldn't be fetched.\n */\n throw responsePayload;\n }\n\n modules = await responsePayload;\n return this.store(cacheKey, modules, job);\n }\n}\nexports.ModuleCache = ModuleCache;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/module-cache.js?");
4605
+ eval("/**\n * @file worker/module-cache.js\n *\n * A cache for modules.\n *\n * @author Matthew Palma, mpalma@kingsds.network\n * Paul, paul@kingsds.network\n * @date May 2019\n * June 2022\n */\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\n \nclass ModuleCache extends EventEmitter\n{\n constructor (supervisor)\n {\n super('ModuleCache')\n this.supervisor = supervisor;\n this.cache = {}; // Modules keyed by module group.\n }\n\n /**\n * Returns an object listing what jobs and modules are currently cached.\n * @returns {string} - in the form: moduleGroup1: [ job1 ], moduleGroup2: [ job1, job2 ]...\n */\n get cacheDescription ()\n {\n const desc = {};\n for (const [ id, moduleJobs ] of Object.entries(this.cache))\n desc[id] = moduleJobs.jobs;\n return JSON.stringify(desc);\n }\n\n /**\n * Attempts to look up an item from the cache.\n * @param {string} id - the items module group name\n * @param {string} job - the job calling fetch\n * @returns {any} The value of the stored item or undefined if nothing is found\n */\n fetch(id, job)\n {\n const moduleJobs = this.cache[id];\n if (!moduleJobs) return moduleJobs;\n if (moduleJobs.jobs.indexOf(job) === -1 )\n moduleJobs.jobs.push(job);\n return moduleJobs.module;\n }\n\n /**\n * Stores a fetched value for one of the caches.\n * @param {string} id - the items identifier module group name\n * @param {string} job - the job calling fetch\n * @param {any} value - the item to store\n * @returns {any} - @value passed in\n */\n store (id, job, value)\n {\n if (!this.cache[id]) this.cache[id] = { module: value, jobs: [ job ] };\n else if (this.cache[id].jobs.indexOf(job) === -1) this.cache[id].jobs.push(job);\n return value\n }\n \n /**\n * Removes a job reference from a module cache.\n * When there are no jobs left, the module cache entry is destroyed.\n * @param {string} job - the job to be removed.\n */\n removeJob (job)\n {\n for (const [ id, moduleJobs ] of Object.entries(this.cache))\n {\n moduleJobs.jobs = moduleJobs.jobs.filter(_job => _job !== job);\n if (moduleJobs.jobs.length < 1) delete this.cache[id];\n }\n }\n\n /**\n * Attempts to fetch a module group from the cache and\n * if it's not found it attempts to fetch then store\n * the module group from the package manager.\n *\n * @param {array} modulesArray - the array of modules requested \n * - (when stringified it's the identifier of the module group)\n *\n * @returns {Promise<object>} - the module group\n * @throws when the module group can not be fetched\n */\n async fetchModule(modulesArray, job)\n {\n const cacheKey = JSON.stringify(modulesArray);\n let modules = this.fetch(cacheKey, job);\n if (modules)\n {\n debugging() && console.debug('FetchModule: Cache hit', { cacheKey, job });\n return modules;\n }\n\n const {\n success,\n payload: responsePayload,\n } = await this.supervisor.packageManager.send('fetchModule', {\n modules: modulesArray,\n });\n\n if (!success)\n {\n /**\n * Preserving the error message by not rewrapping it with DCPError in case\n * we want to let clients know which module couldn't be fetched.\n */\n debugging() && console.debug('FetchModule failure:', responsePayload);\n throw responsePayload;\n }\n\n modules = await responsePayload;\n return this.store(cacheKey, job, modules);\n }\n}\nexports.ModuleCache = ModuleCache;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/module-cache.js?");
4560
4606
 
4561
4607
  /***/ }),
4562
4608
 
@@ -4577,7 +4623,7 @@ eval("/**\n * @file worker/supervisor2/rolling-statistics.js\n *\n * A support c
4577
4623
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4578
4624
 
4579
4625
  "use strict";
4580
- eval("/* eslint-disable require-await */\n// NOTE - need timeout/postmessage function\n/**\n * @file dcp-client/worker/supervisor2/sandbox.js\n *\n * A sandbox that when constructed and assigned can do work for\n * a distributed slice. A sandbox runs for a single slice at a time.\n *\n * Usage (simplified...):\n * const sandbox = new Sandbox(this, { ...this.options.sandboxOptions });\n * await sandbox.start(delayStartMs);\n * await sandbox.assign(this, delayAssignMs);\n * return sandbox.work(delayWorkMs)\n * .then((result) => { \n * slice.collectResult(result, true);\n * sandbox.changeWorkingToAssigned();\n * this.supervisor.recordResult(slice)\n * })\n * .catch((error) => {\n * slice.collectResult(error, false);\n * const reason = this.supervisor.handleSandboxWorkError(sandbox, slice, error);\n * this.supervisor.returnSlice(slice, reason);\n * this.returnSandbox(sandbox);\n * });\n *\n * Debug flags:\n * Sandbox.debugWork = true // - turns off 30 second timeout to let user debug sandbox innards more easily\n * Sandbox.debugState = true // - logs all state transitions for this sandbox\n * Sandbox.debugEvents = true // - logs all events received from the sandbox\n *\n * @author Matthew Palma, mpalma@kingsds.network\n * Ryan Rossiter, ryan@kingsds.network\n * Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * @date May 2019\n * May 2019\n * Decemeber 2020\n * June 2022\n * @module sandbox\n */\n\n/* global dcpConfig */ // eslint-disable-line no-redeclare\n// @ts-check\n\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { assert, assertEq3 } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst nanoid = (__webpack_require__(/*! nanoid */ \"./node_modules/nanoid/index.browser.js\").nanoid);\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { truncateAddress } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst kvin = __webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\");\nconst scopedKvin = new kvin.KVIN({Object: ({}).constructor,\n Array: ([]).constructor, \n Function: (()=>{}).constructor});\n\nlet timeDilation = 1;\nif (DCP_ENV.platform === 'nodejs') {\n const { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\n /** Make timers 10x slower when running in niim */\n timeDilation = (requireNative('module')._cache.niim instanceof requireNative('module').Module) ? 10 : 1;\n}\nconst addressTruncationLength = 20;\nconst workerTuning = dcpConfig.worker;\ntimeDilation = 1;\nlet sbCnter = 0; // Global counter of terminated sandboxes guarded by debugging().\n\n/**\n * Wraps console.debug to emulate debug module prefixing messages on npm.\n * @param {...any} args\n */\nconst debug = (...args) => {\n if (debugging()) {\n console.debug('Sandbox:', ...args);\n }\n};\n\n// Sandbox states\nconst UNREADY = 'UNREADY' // No Sandbox (web worker, saworker, etc) has been constructed yet\nconst READYING = 'READYING' // Sandbox is being constructed and environment (bravojs, env) is being set up\nconst READY_FOR_ASSIGN = 'READY_FOR_ASSIGN' // Sandbox is ready to be assigned\nconst ASSIGNED = 'ASSIGNED' // Sandbox is assigned but not working\nconst ASSIGNING = 'ASSIGNING' // Sandbox is in the process of being ASSIGNED\nconst WORKING = 'WORKING' // Sandbox is working\nconst BROKEN = 'BROKEN' // Sandbox is broken and should be terminated.\nconst EVAL_RESULT_PREFIX = 'evalResult::';\n\nclass SandboxError extends Error { constructor(errorCode, ...args) { super(...args); this.errorCode = errorCode; }}\nclass NoProgressError extends SandboxError { constructor(...args) { super('ENOPROGRESS', ...args); } }\nclass SliceTooSlowError extends SandboxError { constructor(...args) { super('ESLICETOOSLOW', ...args); } }\nclass UncaughtExceptionError extends SandboxError { constructor(...args) { super('EUNCAUGHT', ...args); } }\nclass RemoteFetchError extends SandboxError { constructor(...args) { super('EFETCH', ...args); } }\n\nfunction fillInError(errorCtor, errorIn) {\n const errorOut = new errorCtor(errorIn.message);\n errorOut.name = errorIn.name;\n errorOut.fileName = errorIn.fileName;\n errorOut.lineNumber = errorIn.lineNumber;\n errorOut.stack = errorIn.stack;\n return errorOut;\n}\n\n/** @typedef {import('dcp/common/dcp-events').EventEmitter} EventEmitter */\n/** @typedef {import('./index').Supervisor} Supervisor */\n/** @typedef {import('./slice2').Slice} Slice */\n/** @typedef {import('./job-manager').JobManager} JobManager */\n/** @typedef {import('./module-cache').ModuleCache} ModuleCache */\n\n/**\n * @access public\n * @typedef {object} SandboxOptions\n * @constructor {function} [SandboxConstructor]\n * @property {boolean} [ignoreNoProgress] - When true, the sandbox will not be stopped for not calling progress\n */\n\nclass Sandbox extends EventEmitter {\n /**\n * A Sandbox (i.e. a worker sandbox) which executes distributed slices.\n *\n * @constructor\n * @param {Supervisor} supervisor\n * @param {SandboxOptions} options\n */\n constructor (supervisor, options) {\n super('Sandbox');\n /** @type {Supervisor} */\n this.supervisor = supervisor; /** XXXpfr @todo Temporary for sandbox1 compat. Remove later. Directly pass in moduleCache. */\n /** @type {ModuleCache} */\n this.moduleCache = supervisor.moduleCache;\n /** @type {SandboxOptions} */\n this.options = {\n ignoreNoProgress: false,\n ...options,\n SandboxConstructor: options.SandboxConstructor || (__webpack_require__(/*! ../evaluators */ \"./src/dcp-client/worker/evaluators/index.js\").BrowserEvaluator),\n }\n /** @type {Synchronizer} */\n this.state = new Synchronizer(UNREADY, [ UNREADY, READYING, READY_FOR_ASSIGN, ASSIGNING, ASSIGNED, WORKING, BROKEN ]);\n\n /** @type {string} */\n this.jobAddress = null;\n /** @type {object} */\n this.evaluatorHandle = null;\n /** @type {object} */\n this.capabilities = null;\n /** @type {EventEmitter} */\n this.ee = new EventEmitter('SandboxInternal');\n\n /** @type {boolean} */\n this.terminated = false;\n /** @type {number?} */\n this.progress = 100;\n /** @type {object} */\n this.progressReports = null;\n /** @type {object} */\n this.progressTimeout = null;\n /** @type {object} */\n this.sliceTimeout = null;\n /** @type {object} */\n this.rejectionData = null;\n /** @type {Slice} */\n this.slice = null;\n /** @type {number?} */\n this.sliceStartTime = null;\n /** @type {number} */\n this.id = Sandbox.getNewId();\n\n this.ringMessageHandlers = [\n this.handleRing0Message,\n this.handleRing1Message,\n this.handleRing2Message,\n this.handleRing3Message,\n ];\n\n this.resetSliceTimeReport();\n }\n\n /** @type {string} - debug string that characterizes sandbox. */\n get identifier() {\n if (!this.jobAddress) return `${this.id}.${this.state}`;\n return `${this.id}.${truncateAddress(this.jobAddress, addressTruncationLength)}.${this.state}`;\n }\n\n static getNewId() {\n return Sandbox.idCounter++;\n }\n\n /** @type {boolean} */\n get isReadyForAssign () {\n return this.state.is(READY_FOR_ASSIGN);\n }\n /** @type {boolean} */\n get isAssigned () {\n return this.state.is(ASSIGNED);\n }\n /** @type {boolean} */\n get isWorking () {\n return this.state.is(WORKING);\n }\n /** @type {boolean} */\n get isBroken () {\n return this.state.is(BROKEN);\n }\n /** @type {boolean} */\n get isTerminated () {\n return this.terminated;\n }\n\n /**\n * Mark WORKING sandbox as ASSIGNED in preparation for possible reuse,\n */\n changeWorkingToAssigned () {\n this.state.testAndSet(WORKING, ASSIGNED);\n }\n \n /**\n * Remove from collection of ASSIGNED sandboxes in preparation for termination,\n */\n markAsUnready() {\n this.state.testAndSet(ASSIGNED, UNREADY);\n }\n \n /**\n * Transitions: ASSIGNED --> WORKING and assigns the slice.\n * @param {Slice} slice \n */\n markAsWorking (slice) {\n if (!this.isAssigned)\n throw new Error(`Sandbox ${this.identifier} is not ready to work`);\n this.state.set(ASSIGNED, WORKING);\n this.slice = slice;\n }\n \n /**\n * Fancy assert.\n */\n verifyWorking () {\n if (!this.isWorking) {\n throw new Error(`Sandbox ${this.identifier} is not working`);\n }\n }\n\n /**\n * Error feedback to user.\n * @param {string} message\n * @param {*} extra\n */\n error(message, extra)\n {\n const dcpError = new DCPError(message, extra);\n this.emit('error', dcpError);\n }\n\n /**\n * Readies the sandbox. This will result in the sandbox being ready and not assigned,\n * it will need to be assigned with a job before it is able to do work.\n *\n * @todo maybe preload specific modules or let the cache pass in what modules to load?\n * @param {number} [delay=0]\n * @returns {Promise<Sandbox>}\n * @throws on failure to ready\n */\n async start(delay = 0) {\n this.state.set(UNREADY, READYING);\n\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay * timeDilation));\n\n try {\n // RING 0\n this.evaluatorHandle = new this.options.SandboxConstructor({\n name: `DCP Sandbox #${this.id}`,\n });\n this.evaluatorHandle.onerror = this.onerror.bind(this);\n\n const messageHandler = this.onmessage.bind(this);\n this.evaluatorHandle.onmessage = function onmessage(event)\n {\n let data;\n if (event.data.serialized)\n data = kvin.parse(event.data.message);\n else\n data = kvin.unmarshal(event.data);\n messageHandler({ data });\n }\n\n const evaluatorPostMessage = this.evaluatorHandle.postMessage.bind(this.evaluatorHandle);\n this.evaluatorHandle.postMessage = function postMessage(message)\n {\n evaluatorPostMessage(scopedKvin.marshal(message));\n }\n\n const that = this;\n this.evaluatorHandle.addEventListener('end', () => that.terminate(true));\n\n // Now in RING 1\n\n // Now in RING 2\n await this.describe();\n this.state.set(READYING, READY_FOR_ASSIGN);\n this.emit('ready', this);\n } catch (error) {\n this.error('Failed to start the sandbox.', error);\n this.state.set(READYING, BROKEN);\n this.terminate(false);\n throw error;\n }\n \n return this;\n }\n\n /**\n * This will assign the sandbox with a job, loading its sandbox code\n * into the sandbox.\n *\n * @param {JobManager} jobManager - The job manager that will be the owner of this sandbox.\n * @param {number} [delay=0]\n * @returns {Promise<Sandbox>}\n * @throws on initialization failure\n */\n async assign (jobManager, delay = 0) {\n \n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay * timeDilation));\n \n this.state.set(READY_FOR_ASSIGN, ASSIGNING);\n this.jobAddress = jobManager.address;\n this.job = jobManager.jobMessage;\n \n assertEq3(this.job.address, this.jobAddress);\n assert(typeof this.job === 'object');\n assert(typeof this.job.requirements === 'object');\n assert(Array.isArray(this.job.dependencies));\n assert(Array.isArray(this.job.requirePath));\n\n // Extract public data from job, with defaults\n this.public = Object.assign({\n name: `Anonymous Job ${truncateAddress(this.jobAddress, addressTruncationLength)}`,\n description: 'Discreetly helping make the world smarter.',\n link: 'https://distributed.computer/about',\n }, this.job.public);\n\n // Future: We may want other filename tags for appliances // RR Nov 2019\n\n // Important: The order of applying requirements before loading the sandbox code\n // is important for modules and sandbox code to set globals over the whitelist.\n await this.applySandboxRequirements(this.job.requirements);\n await this.assignEvaluator();\n \n return this;\n }\n \n async assignEvaluator() {\n debug('Begin assigning job to evaluator');\n const that = this;\n\n return new Promise(function sandbox$$assignEvaluatorPromise(resolve, reject) {\n const message = {\n request: 'assign',\n job: that.job,\n sandboxConfig: workerTuning.sandbox,\n };\n\n /* note - onFailListener used for removal. This is necessary due to a bug in ee.once. /wg Feb 2022 */\n \n const onSuccess = (event) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener('reject', onFailListener);\n that.emit('assigned', event.jobAddress); /** XXXpfr @todo Who is listening to event 'assigned' ? */\n debug('Job assigned to evaluator');\n resolve();\n };\n\n const onFail = (error) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener('assigned', onSuccessListener);\n reject(error);\n };\n\n const onSuccessListener = that.ee.once('assigned', onSuccess); // Emitted by handleRing2Message.\n const onFailListener = that.ee.once('reject', onFail);\n that.evaluatorHandle.postMessage(message);\n }).then((event) => {\n that.state.set(ASSIGNING, ASSIGNED);\n return event;\n }).catch(error => {\n that.state.set(ASSIGNING, BROKEN);\n debug('Failure in assigning job to evaluator', error);\n throw error;\n });\n }\n\n /**\n * Evaluates a string inside the sandbox.\n *\n * @param {string} code - the code to evaluate in the sandbox\n * @param {string} filename - the name of the 'file' to help with debugging,\n * no longer working though?\n * @returns {Promise} - resolves with eval result on success, rejects\n * otherwise\n */\n eval(code, filename) {\n const that = this;\n \n return new Promise(function sandbox$$eval$Promise(resolve, reject) {\n let msgId = nanoid();\n let msg = {\n request: 'eval',\n data: code,\n filename,\n msgId, \n };\n\n const eventId = EVAL_RESULT_PREFIX + msgId;\n\n const onSuccess = (event) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener('reject', onFailListener);\n resolve(event);\n }\n\n const onFail = (error) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener(eventId, onSuccessListener);\n reject(error);\n }\n\n const onSuccessListener = that.ee.once(eventId, onSuccess);\n const onFailListener = that.ee.once('reject', onFail);\n\n that.evaluatorHandle.postMessage(msg);\n })\n }\n\n /**\n * Resets the state of the bootstrap, without resetting the sandbox function if assigned.\n * Mostly used to reset the progress status before reusing a sandbox on another slice.\n * Must be called after @start.\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n resetSandboxState () {\n const that = this;\n\n return new Promise(function sandbox$resetSandboxStatePromise(resolve, reject) {\n let successCb, failTimeout;\n let msg = {\n request: 'resetState',\n };\n\n successCb = that.ee.once('resetStateDone', function sandbox$resetSandboxState$success () {\n if (failTimeout === false)\n return; /* already rejected */\n clearTimeout(failTimeout);\n failTimeout = false;\n resolve();\n });\n\n failTimeout = setTimeout(function sandbox$resetSandboxState$fail() {\n if (failTimeout === false)\n return; /* already resolved */\n \n that.ee.removeListener('resetStateDone', successCb);\n that.terminate(false);\n failTimeout = false;\n\n reject(new Error('resetState never received resetStateDone event from sandbox'));\n }, 3000 * timeDilation); /* XXXwg need tuneable */\n\n assert(that.evaluatorHandle); // It is possible that that.terminate nulls out evaluatorHandle before getting here.\n that.evaluatorHandle.postMessage(msg);\n });\n }\n\n /**\n * Clear all timers that are set inside the sandbox (evaluator) environment.\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n clearSandboxTimers() {\n const that = this;\n \n return new Promise(function sandbox$clearSandboxTimersPromise(resolve, reject) {\n let successCb, failTimeout;\n let msg = {\n request: 'clearTimers',\n };\n\n successCb = that.ee.once('clearTimersDone', function sandbox$clearSandboxTimers$success() {\n if (failTimeout === false)\n return; /* already rejected */\n clearTimeout(failTimeout);\n failTimeout = false;\n resolve();\n });\n\n failTimeout = setTimeout(function sanbox$clearSandboxTimers$fail() {\n if (failTimeout === false)\n return; /* already resolved */\n \n that.ee.removeListener('clearTimersDone', successCb);\n that.terminate(false);\n failTimeout = false;\n \n reject(new Error('clearTimers never received clearTimersDone event from sandbox'));\n }, 3000 * timeDilation); /* XXXwg need tuneable */\n\n if (that.evaluatorHandle) // Sometimes that.terminate nulls out evaluatorHandle before getting here.\n that.evaluatorHandle.postMessage(msg);\n });\n }\n\n /**\n * Sends a post message to describe its capabilities.\n *\n * Side effect: Sets the capabilities property of the current sandbox.\n *\n * @returns {Promise} Resolves with the sandbox's capabilities. Rejects with\n * an error saying a response was not received.\n * @memberof Sandbox\n */\n describe() {\n debug('Beginning to describe evaluator');\n const that = this;\n \n return new Promise(function sandbox$describePromise(resolve, reject) {\n if (that.evaluatorHandle === null) {\n return reject(new Error('Evaluator has not been initialized.'));\n }\n\n /**\n * Opted to create a flag for the describe response being received so that\n * we don't have to *hoist* the timeout's id to clear it in the response\n * handler.\n */\n let didReceiveDescribeResponse = false;\n const describeResponseHandler = that.ee.once('describe', (data) => { // Emitted by handleRing2Message.\n didReceiveDescribeResponse = true;\n const { capabilities } = data;\n if (typeof capabilities === 'undefined') {\n reject(new Error('Did not receive capabilities from describe response.'));\n }\n that.capabilities = capabilities;\n\n // Currently only used in tests. May use the event in the future.\n that.emit('described', capabilities); /** XXXpfr @todo Who is listening to event 'described' ? */\n debug('Evaluator has been described');\n resolve(capabilities);\n });\n const describeResponseFailedHandler = () => {\n if (!didReceiveDescribeResponse) {\n that.ee.removeListener('describe', describeResponseHandler);\n that.terminate(false);\n reject(new Error( 'Describe message timed-out. No describe response was received from the describe command.'));\n }\n };\n\n const message = {\n request: 'describe',\n };\n\n // Arbitrarily set the waiting time.\n setTimeout(describeResponseFailedHandler, 6000 * timeDilation); /* XXXwg need tuneable */\n assert(that.evaluatorHandle); // It is possible that that.terminate nulls out evaluatorHandle before getting here.\n that.evaluatorHandle.postMessage(message);\n });\n }\n\n /**\n * Passes the job's requirements object into the sandbox so that the global\n * access lists can be updated accordingly.\n *\n * e.g. disallow access to OffscreenCanvas without\n * environment.offscreenCanvas=true present.\n *\n * Must be called after @start.\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n applySandboxRequirements(requirements) {\n const that = this;\n \n return new Promise(function sandbox$applySandboxRequirementsPromise(resolve, reject) {\n const message = {\n requirements,\n request: 'applyRequirements',\n };\n let wereRequirementsApplied = false;\n\n const successCb = that.ee.once(\n 'applyRequirementsDone',\n function sandbox$applyRequirements$success() {\n wereRequirementsApplied = true;\n resolve();\n },\n );\n\n assert(typeof message.requirements === 'object');\n that.evaluatorHandle.postMessage(message);\n\n setTimeout(function sandbox$finishApplySandboxRequirements() {\n if (!wereRequirementsApplied) {\n that.ee.removeListener('applyRequirementsDone', successCb);\n that.terminate(false);\n reject(new Error('applyRequirements never received applyRequirementsDone response from sandbox'));\n }\n }, 3000 * timeDilation); /* XXXwg needs tunable */\n });\n }\n\n /**\n * Executes a slice received from the supervisor.\n * Must be called after this.start, this.assign and this.markAsWorking .\n *\n * @param {number} [delay = 0] the delay that this method should wait before beginning work, used to avoid starting all sandboxes at once\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n\n async work (delay = 0) {\n const that = this;\n\n if (!this.slice)\n throw new Error('The slice in Sandbox.work has not been set; be sure to call markAsWorking before.');\n\n // cf. DCP-1720\n this.resetSliceTimeReport();\n \n // Now wait for the delay if provided, prevents many sandboxes starting at once from crashing the supervisor\n if (delay > 0) await new Promise(resolve => setTimeout(resolve, (delay + 1) * timeDilation));\n if (!this.isWorking) return; // sandbox.terminate could have been called during the delay timeout\n\n // Prepare the sandbox to begin work\n // will be replaced by `assign` message that should be called before emitting a `work` message\n if (this.jobAddress !== this.slice.jobAddress) {\n throw new Error(`Sandbox.run: Sandbox is already assigned and jobAddress doesn't match previous (${this.jobAddress} !== ${this.slice.jobAddress})`);\n }\n\n let sliceHnd = { job: this.public, sandbox: this };\n await this.resetSandboxState();\n if (!this.slice) {\n this.error(`Slice for job ${this.jobAddress} vanished during work initialization - aborting`);\n return;\n }\n\n const { datum: inputDatum, error: dataError } = this.slice;\n\n this.resetProgressTimeout();\n this.resetSliceTimeout();\n\n return new Promise(function sandbox$$workPromise(resolve, reject) {\n let onSuccess, onFail\n\n onSuccess = that.ee.once('resolve', function sandbox$$work$success (event) {\n that.ee.removeListener('reject', onFail)\n resolve(event)\n }.bind(that));\n\n onFail = that.ee.once('reject', function sandbox$$work$fail (err) {\n that.ee.removeListener('resolve', onSuccess)\n reject(err)\n }.bind(that))\n\n that.sliceStartTime = Date.now();\n that.progress = null;\n that.progressReports = {\n last: undefined,\n lastDeterministic: undefined,\n };\n\n that.resetProgressTimeout();\n that.resetSliceTimeout();\n that.emit('start', sliceHnd);\n \n if (dataError) {\n that.ee.removeListener('resolve', onSuccess);\n that.ee.removeListener('reject', onFail);\n that.emit('workEmit', {\n eventName: 'error',\n payload: {\n message: dataError.message,\n stack: dataError.stack,\n name: that.public.name\n }\n });\n setTimeout(() => reject(dataError), 0)\n\n } else {\n that.evaluatorHandle.postMessage({\n request: 'main',\n data: inputDatum,\n })\n }\n })\n .then(async function sandbox$$work$then(event) {\n // prevent any hanging timers from being fired\n await that.clearSandboxTimers();\n\n /** @todo Should sliceHnd just be replaced with that.public? */\n /** @todo Decide which event is right. */\n //that.emit('slice', sliceHnd); // Unused -- XXXpfr\n that.emit('sliceFinish', event);\n that.emit('complete', that.jobAddress);\n\n that.state.set(WORKING, ASSIGNED);\n that.slice = false;\n\n return event;\n })\n .catch((err) => { \n if (err.name === 'EWORKREJECT') {\n that.rejectionData = err;\n that.evaluatorHandle.postMessage({ request: 'resetAndGetCPUTime' })\n } else { // sandbox termination for rejected work happens in Supervisor.handleRejectedWork\n // Ceci is the reject callback for when the slice throws an error\n that.terminate(false);\n }\n\n that.emit('error', err, 'slice');\n\n if (err instanceof NoProgressError) {\n that.emit('workEmit', {\n eventName: 'noProgress',\n payload: {\n timestamp: Date.now() - that.sliceStartTime,\n data: that.slice.datumUri,\n progressReports: that.progressReports,\n }\n });\n }\n throw err;\n })\n .finally(function sandbox$$work$finally() {\n that.emit('end', sliceHnd); /** XXXpfr @todo Who is listening to event 'end' ? */\n });\n }\n\n resetProgressTimeout() {\n if (this.progressTimeout) {\n clearTimeout(this.progressTimeout);\n this.progressTimeout = null;\n }\n\n this.progressTimeout = setTimeout(() => {\n if (this.options.ignoreNoProgress)\n return this.emit('warning', \"ENOPROGRESS silenced by localExec: In a remote worker, this slice would be stopped for not calling progress frequently enough.\");\n\n this.ee.emit('reject', new NoProgressError(`No progress event was received in the last ${workerTuning.sandbox.progressTimeout / 1000} seconds.`));\n }, +workerTuning.sandbox.progressTimeout * timeDilation);\n }\n\n resetSliceTimeout() {\n if (this.sliceTimeout) clearTimeout(this.sliceTimeout);\n\n this.sliceTimeout = setTimeout(() => {\n if (Sandbox.debugWork) return this.emit('warning', 'Sandbox.debugWork: Ignoring slice timeout');\n\n this.ee.emit('reject', new SliceTooSlowError(`Slice took longer than ${workerTuning.sandbox.sliceTimeout / 1000} seconds.`));\n }, +workerTuning.sandbox.sliceTimeout * timeDilation);\n }\n \n async handleRing0Message(data) {\n debugging('event:ring-0') && debug('event:ring-0', data);\n //handling a true ring 0 message\n switch (data.request) {\n case 'sandboxLoaded':\n // emit externally\n this.emit('sandboxLoaded', this)\n break;\n\n case 'scriptLoaded':\n // emit externally\n this.emit('scriptLoaded', data);\n if(data.result !== \"success\") {\n this.onerror(data);\n }\n break;\n \n case 'clearTimersDone':\n this.ee.emit(data.request, data);\n break;\n case 'totalCPUTime':\n this.updateTime(data);\n if (this.ee.listenerCount('resolve') > 0) {\n this.completeData.timeReport = this.sliceTimeReport;\n this.ee.emit('resolve', this.completeData);\n delete this.completeData;\n } else {\n this.rejectionData.timeReport = this.sliceTimeReport\n this.emit('rejectedWorkMetrics', this.rejectionData) // If there is no internal listener for 'resolve', the slice was rejected and\n delete this.rejectionData; // we need to send the rejected metrics to the supervisor\n } \n break;\n case 'error': {\n // Warning: rejecting here with just event.data.error causes issues\n // where the reject handlers modify the object so it interferes with the\n // workEmit event payload, wrapping in an Error instance copies the values\n const wrappedError = fillInError(UncaughtExceptionError, data.error);\n\n if (this.ee.listenerCount('reject') > 0) {\n this.ee.emit('reject', wrappedError);\n } else {\n // This will happen if the error is thrown during initialization\n throw wrappedError;\n }\n break;\n }\n default: {\n this.error('Received unhandled request from sandbox: ' + data.request + '\\n\\t' + JSON.stringify(data));\n break;\n } \n }\n }\n\n async handleRing1Message(data) {\n switch (data.request) {\n case 'applyRequirementsDone':\n // emit internally\n this.ee.emit(data.request, data)\n break;\n default: {\n this.error('Received unhandled request from sandbox ring 1: ' + data.request + '\\n\\t' + JSON.stringify(data));\n break; \n }\n }\n }\n\n async handleRing2Message(data) {\n debugging('event:ring-2') && debug('event:ring-2', data);\n switch (data.request) {\n case 'dependency': {\n let moduleData;\n try {\n moduleData = await this.moduleCache.fetchModule(data.data, this.jobAddress);\n } catch (error) {\n /*\n * In the event of an error here, we want to let the client know there was a problem in\n * loading their module. However, there hasn't yet been an actual slice assigned to the sandbox.\n * Therefore, we assign 'slice 0' to the sandbox, a slice that will never exist, and is used\n * purely for this purpose. \n */\n this.slice = {\n jobAddress: this.jobAddress,\n sliceNumber: 0,\n };\n\n const payload = {\n name: error.name,\n timestamp: error.timestamp,\n message: error.message,\n };\n\n const wrappedError = fillInError(RemoteFetchError, error);\n\n this.emit('workEmit', {\n eventName: 'error',\n payload,\n });\n this.ee.emit('reject', wrappedError);\n break;\n }\n this.evaluatorHandle.postMessage({\n request: 'moduleGroup',\n data: moduleData,\n id: data.id,\n });\n break;\n }\n case 'error':\n /*\n * Ring 2 error messages will only fire for problems inside of the worker that are separate from\n * the work function. In most cases there are other handlers for situations where 'error' may be emitted\n * such as timeouts if the expected message isn't recieved. Thus, we will output the error, but nothing else.\n */\n this.error('event:ring-2: some error happened.', data.error);\n break;\n case 'describe':\n case 'evalResult':\n case 'resetStateDone':\n case 'assigned':\n // emit internally\n this.ee.emit(data.request, data);\n break;\n case 'reject':\n // emit internally\n this.ee.emit(data.request, data.error);\n break;\n default: {\n this.error(`Received unhandled request from sandbox ring 2. Data: ${JSON.stringify(data, null, 2)}`);\n break;\n }\n }\n }\n\n async handleRing3Message(data) {\n switch (data.request) {\n case 'complete':\n clearTimeout(this.progressTimeout);\n clearTimeout(this.sliceTimeout);\n this.progressTimeout = this.sliceTimeout = null;\n\n if (this.progress === null) {\n if (this.options.ignoreNoProgress) {\n this.emit('warning', \"ENOPROGRESS silenced by localExec: Progress was not called during this slice's execution, in a remote sandbox this would cause the slice to be failed\");\n } else {\n // If a progress update was never received (progress === null) then reject\n this.ee.emit('reject', new NoProgressError('Sandbox never emitted a progress event.'));\n break;\n }\n }\n this.evaluatorHandle.postMessage({ request: 'resetAndGetCPUTime' })\n this.progress = 100;\n this.completeData = data;\n // The timing report and resolve will be emitted when the CPU time is received. \n break;\n case 'progress': {\n let { progress, indeterminate, throttledReports, value } = data;\n this.progress = progress;\n const progressReport = {\n timestamp: Date.now() - this.sliceStartTime,\n progress,\n value,\n throttledReports,\n }\n this.progressReports.last = progressReport;\n if (!indeterminate) {\n this.progressReports.lastDeterministic = progressReport;\n }\n\n this.resetProgressTimeout();\n this.emit('sliceProgress', data);\n break;\n }\n case 'noProgress':\n this.ee.emit('reject', new NoProgressError(data.message));\n break;\n case 'console':\n data.payload.message = scopedKvin.marshal(data.payload.message);\n this.emit('workEmit', {\n eventName: 'console',\n payload: data.payload,\n });\n break;\n case 'emitEvent':/* ad-hoc event from the sandbox (work.emit) */\n this.emit('workEmit', {\n eventName: 'custom',\n payload: data.payload\n });\n break;\n case 'measurement':\n this.updateTime(data);\n break;\n case 'sandboxError': /* the sandbox itself has an error condition */\n this.emit('sandboxError', data.error);\n break;\n case 'workError': { /* the work function threw/rejected */\n this.emit('workEmit', {\n eventName: 'error',\n payload: data.error,\n });\n\n // Warning: rejecting here with just .data.error causes issues\n // where the reject handlers modify the object so it interferes with the\n // workEmit payload, wrapping in an Error instance copies the values\n const wrappedError = fillInError(UncaughtExceptionError, data.error);\n\n if (this.ee.listenerCount('reject') > 0) {\n this.ee.emit('reject', wrappedError);\n } else {\n // This will happen if the error is thrown during initialization\n throw wrappedError;\n }\n break;\n }\n default: {\n this.error('Received unhandled request from sandbox ring 3: ' + data.request + '\\n\\t' + JSON.stringify(data));\n break; \n }\n }\n }\n\n /**\n * Handles progress and completion events from sandbox.\n * Unless explicitly returned out of this function will re-emit the event\n * on @this.ee where the name of the event is event.data.request.\n *\n * @param {object} event - event received from the sandbox\n */\n async onmessage(event) {\n debugging('event') && debug('event', event);\n if (Sandbox.debugEvents) {\n console.debug('sandbox - eventDebug:', {\n id: this.id,\n state: this.state.valueOf(),\n event: JSON.stringify(event)\n })\n }\n\n const { data } = event;\n const ringLevel = data.ringSource\n\n // Give the data to a handler depending on ring level\n if (ringLevel === -1) {\n this.error('Message sent directly from raw postMessage. Terminating worker...');\n console.debug(event);\n return this.terminate(true);\n } else {\n const handler = this.ringMessageHandlers[ringLevel];\n if (handler) {\n handler.call(this, data.value);\n } else {\n this.emit('warning', `No handler defined for message from ring ${ringLevel}`);\n console.debug(event);\n }\n }\n }\n\n /**\n * Error handler for the internal sandbox.\n * Emits error event that gets handled up in the Worker class.\n */\n onerror(event) {\n if (event instanceof Error) this.error('Sandbox emitted an error:', event);\n else this.error(`Sandbox emitted an error: ${event}`);\n this.terminate(true, true);\n }\n\n /**\n * Clears the timeout and terminates the sandbox and sometimes emits a reject event.\n *\n * @param {boolean} [reject = true] - if true emit reject event\n * @param {boolean} [immediate = false] - passed to terminate, used by standaloneWorker to immediately close the connection\n */\n terminate (reject = true, immediate = false) {\n debugging() && console.debug(`Sandbox.terminate ${this.identifier}, count: ${++sbCnter}`);\n\n if (this.isTerminated) return;\n this.terminated = true;\n\n clearTimeout(this.progressTimeout);\n clearTimeout(this.sliceTimeout);\n this.progressTimeout = this.sliceTimeout = null;\n \n if (this.evaluatorHandle && typeof this.evaluatorHandle.terminate === 'function') {\n try {\n this.evaluatorHandle.terminate(immediate);\n this.evaluatorHandle = null;\n } catch (e) {\n this.error(`Error terminating sandbox ${this.id}:`, e);\n } finally {\n this.emit('terminate', this);\n }\n }\n\n if (reject) {\n this.ee.emit('reject', new Error(`Sandbox ${this.id} was terminated.`));\n }\n\n this.emit('terminated');\n }\n\n /**\n * Attempts to stop the sandbox from doing completing its current\n * set of work without terminating the working. \n * \n *** Until stop is implemented properly, use terminate(false).\n *\n * @todo Properly implement stop\n */\n stop () {\n //throw new Error('Sandbox.stop is not yet implemented.')\n this.terminate(false);\n }\n\n /**\n * ringNPostMessage can send a `measurement` request and update these\n * totals.\n */\n updateTime (measurementEvent) {\n ['total', 'CPU', 'webGL'].forEach((key) => {\n if (measurementEvent[key]) this.sliceTimeReport[key] += measurementEvent[key];\n })\n }\n\n resetSliceTimeReport () {\n this.sliceTimeReport = {\n total: 0,\n CPU: 0,\n webGL: 0,\n }\n }\n}\n\nSandbox.idCounter = 1;\nSandbox.debugWork = false;\nSandbox.debugState = false;\nSandbox.debugEvents = false;\n\nexports.Sandbox = Sandbox;\nexports.SandboxError = SandboxError;\nexports.NoProgressError = NoProgressError;\nexports.SliceTooSlowError = SliceTooSlowError;\nexports.UncaughtExceptionError = UncaughtExceptionError;\nexports.RemoteFetchError = RemoteFetchError;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/sandbox2.js?");
4626
+ eval("/* eslint-disable require-await */\n// NOTE - need timeout/postmessage function\n/**\n * @file dcp-client/worker/supervisor2/sandbox.js\n *\n * A sandbox that when constructed and assigned can do work for\n * a distributed slice. A sandbox runs for a single slice at a time.\n *\n * Usage (simplified...):\n * const sandbox = new Sandbox(this, { ...this.options.sandboxOptions });\n * await sandbox.start(delayStartMs);\n * await sandbox.assign(this, delayAssignMs);\n * return sandbox.work(delayWorkMs)\n * .then((result) => { \n * slice.collectResult(result, true);\n * sandbox.changeWorkingToAssigned();\n * this.supervisor.recordResult(slice)\n * })\n * .catch((error) => {\n * slice.collectResult(error, false);\n * const reason = this.supervisor.handleSandboxWorkError(sandbox, slice, error);\n * this.supervisor.returnSlice(slice, reason);\n * this.returnSandbox(sandbox);\n * });\n *\n * Debug flags:\n * Sandbox.debugWork = true // - turns off 30 second timeout to let user debug sandbox innards more easily\n * Sandbox.debugState = true // - logs all state transitions for this sandbox\n * Sandbox.debugEvents = true // - logs all events received from the sandbox\n *\n * @author Matthew Palma, mpalma@kingsds.network\n * Ryan Rossiter, ryan@kingsds.network\n * Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * @date May 2019\n * May 2019\n * Decemeber 2020\n * June 2022\n * @module sandbox\n */\n\n/* global dcpConfig */ // eslint-disable-line no-redeclare\n// @ts-check\n\n\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { assert, assertEq3 } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst DCP_ENV = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst nanoid = (__webpack_require__(/*! nanoid */ \"./node_modules/nanoid/index.browser.js\").nanoid);\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { truncateAddress } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst kvin = __webpack_require__(/*! kvin */ \"./node_modules/kvin/kvin.js\");\nconst scopedKvin = new kvin.KVIN({Object: ({}).constructor,\n Array: ([]).constructor, \n Function: (()=>{}).constructor});\n\nlet timeDilation = 1;\nif (DCP_ENV.platform === 'nodejs') {\n const { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\n /** Make timers 10x slower when running in niim */\n timeDilation = (requireNative('module')._cache.niim instanceof requireNative('module').Module) ? 10 : 1;\n}\nconst addressTruncationLength = 20;\nconst workerTuning = dcpConfig.worker;\ntimeDilation = 1;\nlet sbCnter = 0; // Global counter of terminated sandboxes guarded by debugging().\n\n/**\n * Wraps console.debug to emulate debug module prefixing messages on npm.\n * @param {...any} args\n */\nconst debug = (...args) => {\n if (debugging()) {\n console.debug('Sandbox:', ...args);\n }\n};\n\n// Sandbox states\nconst UNREADY = 'UNREADY' // No Sandbox (web worker, saworker, etc) has been constructed yet\nconst READYING = 'READYING' // Sandbox is being constructed and environment (bravojs, env) is being set up\nconst READY_FOR_ASSIGN = 'READY_FOR_ASSIGN' // Sandbox is ready to be assigned\nconst ASSIGNED = 'ASSIGNED' // Sandbox is assigned but not working\nconst ASSIGNING = 'ASSIGNING' // Sandbox is in the process of being ASSIGNED\nconst WORKING = 'WORKING' // Sandbox is working\nconst BROKEN = 'BROKEN' // Sandbox is broken and should be terminated.\nconst EVAL_RESULT_PREFIX = 'evalResult::';\n\nclass SandboxError extends Error { constructor(errorCode, msg) { super(msg); this.errorCode = errorCode; }}\nclass NoProgressError extends SandboxError { constructor(msg) { super('ENOPROGRESS', msg); } }\nclass SliceTooSlowError extends SandboxError { constructor(msg) { super('ESLICETOOSLOW', msg); } }\nclass UncaughtExceptionError extends SandboxError { constructor(msg) { super('EUNCAUGHT', msg); } }\nclass RemoteFetchError extends SandboxError { constructor(msg) { super('EFETCH', msg); } }\n\n/** @typedef {import('dcp/common/dcp-events').EventEmitter} EventEmitter */\n/** @typedef {import('./index').Supervisor} Supervisor */\n/** @typedef {import('./slice2').Slice} Slice */\n/** @typedef {import('./job-manager').JobManager} JobManager */\n/** @typedef {import('./module-cache').ModuleCache} ModuleCache */\n\n/**\n * @access public\n * @typedef {object} SandboxOptions\n * @constructor {function} [SandboxConstructor]\n * @property {boolean} [ignoreNoProgress] - When true, the sandbox will not be stopped for not calling progress\n */\n\nclass Sandbox extends EventEmitter {\n /**\n * A Sandbox (i.e. a worker sandbox) which executes distributed slices.\n *\n * @constructor\n * @param {Supervisor} supervisor\n * @param {SandboxOptions} options\n */\n constructor (supervisor, options) {\n super('Sandbox');\n /** @type {Supervisor} */\n this.supervisor = supervisor; /** XXXpfr @todo Temporary for sandbox1 compat. Remove later. Directly pass in moduleCache. */\n /** @type {ModuleCache} */\n this.moduleCache = supervisor.moduleCache;\n /** @type {SandboxOptions} */\n this.options = {\n ignoreNoProgress: false,\n ...options,\n SandboxConstructor: options.SandboxConstructor || (__webpack_require__(/*! ../evaluators */ \"./src/dcp-client/worker/evaluators/index.js\").BrowserEvaluator),\n }\n /** @type {Synchronizer} */\n this.state = new Synchronizer(UNREADY, [ UNREADY, READYING, READY_FOR_ASSIGN, ASSIGNING, ASSIGNED, WORKING, BROKEN ]);\n\n /** @type {string} */\n this.jobAddress = null;\n /** @type {object} */\n this.evaluatorHandle = null;\n /** @type {object} */\n this.capabilities = null;\n /** @type {EventEmitter} */\n this.ee = new EventEmitter('SandboxInternal');\n\n /** @type {boolean} */\n this.terminated = false;\n /** @type {number?} */\n this.progress = 100;\n /** @type {object} */\n this.progressReports = null;\n /** @type {object} */\n this.progressTimeout = null;\n /** @type {object} */\n this.sliceTimeout = null;\n /** @type {object} */\n this.rejectionData = null;\n /** @type {Slice} */\n this.slice = null;\n /** @type {number?} */\n this.sliceStartTime = null;\n /** @type {number} */\n this.id = Sandbox.getNewId();\n\n this.ringMessageHandlers = [\n this.handleRing0Message,\n this.handleRing1Message,\n this.handleRing2Message,\n this.handleRing3Message,\n ];\n\n this.resetSliceTimeReport();\n }\n\n /** @type {string} - debug string that characterizes sandbox. */\n get identifier() {\n if (!this.jobAddress) return `${this.id}.${this.state}`;\n return `${this.id}.${truncateAddress(this.jobAddress, addressTruncationLength)}.${this.state}`;\n }\n\n static getNewId() {\n return Sandbox.idCounter++;\n }\n\n /** @type {boolean} */\n get isReadyForAssign () {\n return this.state.is(READY_FOR_ASSIGN);\n }\n /** @type {boolean} */\n get isAssigned () {\n return this.state.is(ASSIGNED);\n }\n /** @type {boolean} */\n get isWorking () {\n return this.state.is(WORKING);\n }\n /** @type {boolean} */\n get isBroken () {\n return this.state.is(BROKEN);\n }\n /** @type {boolean} */\n get isTerminated () {\n return this.terminated;\n }\n\n /**\n * Mark WORKING sandbox as ASSIGNED in preparation for possible reuse,\n */\n changeWorkingToAssigned () {\n this.state.testAndSet(WORKING, ASSIGNED);\n }\n \n /**\n * Remove from collection of ASSIGNED sandboxes in preparation for termination,\n */\n markAsUnready() {\n this.state.testAndSet(ASSIGNED, UNREADY);\n }\n \n /**\n * Transitions: ASSIGNED --> WORKING and assigns the slice.\n * @param {Slice} slice \n */\n markAsWorking (slice) {\n if (!this.isAssigned)\n throw new Error(`Sandbox ${this.identifier} is not ready to work`);\n this.state.set(ASSIGNED, WORKING);\n this.slice = slice;\n }\n \n /**\n * Fancy assert.\n */\n verifyWorking () {\n if (!this.isWorking) {\n throw new Error(`Sandbox ${this.identifier} is not working`);\n }\n }\n\n /**\n * Error feedback to user.\n * @param {string} message\n * @param {*} extra\n */\n error(message, extra)\n {\n const dcpError = new DCPError(message, extra);\n this.emit('error', dcpError);\n }\n\n /**\n * Readies the sandbox. This will result in the sandbox being ready and not assigned,\n * it will need to be assigned with a job before it is able to do work.\n *\n * @todo maybe preload specific modules or let the cache pass in what modules to load?\n * @param {number} [delay=0]\n * @returns {Promise<Sandbox>}\n * @throws on failure to ready\n */\n async start(delay = 0) {\n this.state.set(UNREADY, READYING);\n\n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay * timeDilation));\n\n try {\n // RING 0\n this.evaluatorHandle = new this.options.SandboxConstructor({\n name: `DCP Sandbox #${this.id}`,\n });\n this.evaluatorHandle.onerror = this.onerror.bind(this);\n\n const messageHandler = this.onmessage.bind(this);\n this.evaluatorHandle.onmessage = function onmessage(event)\n {\n let data;\n if (event.data.serialized)\n data = kvin.parse(event.data.message);\n else\n data = kvin.unmarshal(event.data);\n messageHandler({ data });\n }\n\n const evaluatorPostMessage = this.evaluatorHandle.postMessage.bind(this.evaluatorHandle);\n this.evaluatorHandle.postMessage = function postMessage(message)\n {\n evaluatorPostMessage(scopedKvin.marshal(message));\n }\n\n const that = this;\n this.evaluatorHandle.addEventListener('end', () => that.terminate(true));\n\n // Now in RING 1\n\n // Now in RING 2\n await this.describe();\n this.state.set(READYING, READY_FOR_ASSIGN);\n this.emit('ready', this);\n } catch (error) {\n this.error('Failed to start the sandbox.', error);\n this.state.set(READYING, BROKEN);\n this.terminate(false);\n throw error;\n }\n \n return this;\n }\n\n /**\n * This will assign the sandbox with a job, loading its sandbox code\n * into the sandbox.\n *\n * @param {JobManager} jobManager - The job manager that will be the owner of this sandbox.\n * @param {number} [delay=0]\n * @returns {Promise<Sandbox>}\n * @throws on initialization failure\n */\n async assign (jobManager, delay = 0) {\n \n if (delay > 0) await new Promise((resolve) => setTimeout(resolve, delay * timeDilation));\n \n this.state.set(READY_FOR_ASSIGN, ASSIGNING);\n this.jobAddress = jobManager.address;\n this.job = jobManager.jobMessage;\n \n assertEq3(this.job.address, this.jobAddress);\n assert(typeof this.job === 'object');\n assert(typeof this.job.requirements === 'object');\n assert(Array.isArray(this.job.dependencies));\n assert(Array.isArray(this.job.requirePath));\n\n // Extract public data from job, with defaults\n this.public = Object.assign({\n name: `Anonymous Job ${truncateAddress(this.jobAddress, addressTruncationLength)}`,\n description: 'Discreetly helping make the world smarter.',\n link: 'https://distributed.computer/about',\n }, this.job.public);\n\n // Future: We may want other filename tags for appliances // RR Nov 2019\n\n // Important: The order of applying requirements before loading the sandbox code\n // is important for modules and sandbox code to set globals over the whitelist.\n await this.applySandboxRequirements(this.job.requirements);\n await this.assignEvaluator();\n \n return this;\n }\n \n async assignEvaluator() {\n debug('Begin assigning job to evaluator');\n const that = this;\n\n return new Promise(function sandbox$$assignEvaluatorPromise(resolve, reject) {\n const message = {\n request: 'assign',\n job: that.job,\n sandboxConfig: workerTuning.sandbox,\n };\n\n /* note - onFailListener used for removal. This is necessary due to a bug in ee.once. /wg Feb 2022 */\n \n const onSuccess = (event) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener('reject', onFailListener);\n that.emit('assigned', event.jobAddress); /** XXXpfr @todo Who is listening to event 'assigned' ? */\n debug('Job assigned to evaluator');\n resolve();\n };\n\n const onFail = (error) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener('assigned', onSuccessListener);\n reject(error);\n };\n\n const onSuccessListener = that.ee.once('assigned', onSuccess); // Emitted by handleRing2Message.\n const onFailListener = that.ee.once('reject', onFail);\n that.evaluatorHandle.postMessage(message);\n }).then((event) => {\n that.state.set(ASSIGNING, ASSIGNED);\n return event;\n }).catch(error => {\n that.state.set(ASSIGNING, BROKEN);\n debug('Failure in assigning job to evaluator', error);\n throw error;\n });\n }\n\n /**\n * Evaluates a string inside the sandbox.\n *\n * @param {string} code - the code to evaluate in the sandbox\n * @param {string} filename - the name of the 'file' to help with debugging,\n * no longer working though?\n * @returns {Promise} - resolves with eval result on success, rejects\n * otherwise\n */\n eval(code, filename) {\n const that = this;\n \n return new Promise(function sandbox$$eval$Promise(resolve, reject) {\n let msgId = nanoid();\n let msg = {\n request: 'eval',\n data: code,\n filename,\n msgId, \n };\n\n const eventId = EVAL_RESULT_PREFIX + msgId;\n\n const onSuccess = (event) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener('reject', onFailListener);\n resolve(event);\n }\n\n const onFail = (error) => {\n // eslint-disable-next-line no-use-before-define\n that.ee.removeListener(eventId, onSuccessListener);\n reject(error);\n }\n\n const onSuccessListener = that.ee.once(eventId, onSuccess);\n const onFailListener = that.ee.once('reject', onFail);\n\n that.evaluatorHandle.postMessage(msg);\n })\n }\n\n /**\n * Resets the state of the bootstrap, without resetting the sandbox function if assigned.\n * Mostly used to reset the progress status before reusing a sandbox on another slice.\n * Must be called after @start.\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n resetSandboxState () {\n const that = this;\n\n return new Promise(function sandbox$resetSandboxStatePromise(resolve, reject) {\n let successCb, failTimeout;\n let msg = {\n request: 'resetState',\n };\n\n successCb = that.ee.once('resetStateDone', function sandbox$resetSandboxState$success () {\n if (failTimeout === false)\n return; /* already rejected */\n clearTimeout(failTimeout);\n failTimeout = false;\n resolve();\n });\n\n failTimeout = setTimeout(function sandbox$resetSandboxState$fail() {\n if (failTimeout === false)\n return; /* already resolved */\n \n that.ee.removeListener('resetStateDone', successCb);\n that.terminate(false);\n failTimeout = false;\n\n reject(new Error(`resetState never received resetStateDone event from sandbox: ${that.identifier}`));\n }, 3000 * timeDilation); /* XXXwg need tuneable */\n\n if (that.evaluatorHandle) // Sometimes \"terminate\" nulls out evaluatorHandle before getting here.\n that.evaluatorHandle.postMessage(msg);\n });\n }\n\n /**\n * Clear all timers that are set inside the sandbox (evaluator) environment.\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n clearSandboxTimers() {\n const that = this;\n \n return new Promise(function sandbox$clearSandboxTimersPromise(resolve, reject) {\n let successCb, failTimeout;\n let msg = {\n request: 'clearTimers',\n };\n\n successCb = that.ee.once('clearTimersDone', function sandbox$clearSandboxTimers$success() {\n if (failTimeout === false)\n return; /* already rejected */\n clearTimeout(failTimeout);\n failTimeout = false;\n resolve();\n });\n\n failTimeout = setTimeout(function sanbox$clearSandboxTimers$fail() {\n if (failTimeout === false)\n return; /* already resolved */\n \n that.ee.removeListener('clearTimersDone', successCb);\n that.terminate(false);\n failTimeout = false;\n \n reject(new Error(`clearTimers never received clearTimersDone event from sandbox: ${that.identifier}`));\n }, 3000 * timeDilation); /* XXXwg need tuneable */\n\n if (that.evaluatorHandle) // Sometimes \"terminate\" nulls out evaluatorHandle before getting here.\n that.evaluatorHandle.postMessage(msg);\n });\n }\n\n /**\n * Sends a post message to describe its capabilities.\n *\n * Side effect: Sets the capabilities property of the current sandbox.\n *\n * @returns {Promise} Resolves with the sandbox's capabilities. Rejects with\n * an error saying a response was not received.\n * @memberof Sandbox\n */\n describe() {\n debug('Beginning to describe evaluator');\n const that = this;\n \n return new Promise(function sandbox$describePromise(resolve, reject) {\n if (that.evaluatorHandle === null) {\n return reject(new Error(`Evaluator has not been initialized: ${that.identifier}`));\n }\n\n /**\n * Opted to create a flag for the describe response being received so that\n * we don't have to *hoist* the timeout's id to clear it in the response\n * handler.\n */\n let didReceiveDescribeResponse = false;\n const describeResponseHandler = that.ee.once('describe', (data) => { // Emitted by handleRing2Message.\n didReceiveDescribeResponse = true;\n const { capabilities } = data;\n if (typeof capabilities === 'undefined') {\n reject(new Error(`Did not receive capabilities from describe response: ${that.identifier}`));\n }\n that.capabilities = capabilities;\n\n // Currently only used in tests. May use the event in the future.\n that.emit('described', capabilities); /** XXXpfr @todo Who is listening to event 'described' ? */\n debug('Evaluator has been described');\n resolve(capabilities);\n });\n const describeResponseFailedHandler = () => {\n if (!didReceiveDescribeResponse) {\n that.ee.removeListener('describe', describeResponseHandler);\n that.terminate(false);\n reject(new Error( `Describe message timed-out. No describe response was received from the describe command: ${that.identifier}`));\n }\n };\n\n const message = {\n request: 'describe',\n };\n\n // Arbitrarily set the waiting time.\n setTimeout(describeResponseFailedHandler, 6000 * timeDilation); /* XXXwg need tuneable */\n if (that.evaluatorHandle) // Sometimes \"terminate\" nulls out evaluatorHandle before getting here.\n that.evaluatorHandle.postMessage(message);\n });\n }\n\n /**\n * Passes the job's requirements object into the sandbox so that the global\n * access lists can be updated accordingly.\n *\n * e.g. disallow access to OffscreenCanvas without\n * environment.offscreenCanvas=true present.\n *\n * Must be called after @start.\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n applySandboxRequirements(requirements) {\n const that = this;\n \n return new Promise(function sandbox$applySandboxRequirementsPromise(resolve, reject) {\n const message = {\n requirements,\n request: 'applyRequirements',\n };\n let wereRequirementsApplied = false;\n\n const successCb = that.ee.once(\n 'applyRequirementsDone',\n function sandbox$applyRequirements$success() {\n wereRequirementsApplied = true;\n resolve();\n },\n );\n\n assert(typeof message.requirements === 'object');\n that.evaluatorHandle.postMessage(message);\n\n setTimeout(function sandbox$finishApplySandboxRequirements() {\n if (!wereRequirementsApplied) {\n that.ee.removeListener('applyRequirementsDone', successCb);\n that.terminate(false);\n reject(new Error(`applyRequirements never received applyRequirementsDone response from sandbox: ${that.identifier}`));\n }\n }, 3000 * timeDilation); /* XXXwg needs tunable */\n });\n }\n\n /**\n * Executes a slice received from the supervisor.\n * Must be called after this.start, this.assign and this.markAsWorking .\n *\n * @param {number} [delay = 0] the delay that this method should wait before beginning work, used to avoid starting all sandboxes at once\n *\n * @returns {Promise} - resolves with result on success, rejects otherwise\n */\n\n async work (delay = 0) {\n const that = this;\n\n if (!this.slice)\n throw new Error(`The slice in Sandbox.work has not been set; be sure to call markAsWorking before: ${that.identifier}`);\n\n // cf. DCP-1720\n this.resetSliceTimeReport();\n \n // Now wait for the delay if provided, prevents many sandboxes starting at once from crashing the supervisor\n if (delay > 0) await new Promise(resolve => setTimeout(resolve, (delay + 1) * timeDilation));\n if (!this.isWorking) return; // sandbox.terminate could have been called during the delay timeout\n\n // Prepare the sandbox to begin work\n // will be replaced by `assign` message that should be called before emitting a `work` message\n if (this.jobAddress !== this.slice.jobAddress) {\n throw new Error(`Sandbox.run: Sandbox ${this.identifier} is already assigned and jobAddress doesn't match previous (${this.jobAddress} !== ${this.slice.jobAddress})`);\n }\n\n let sliceHnd = { job: this.public, sandbox: this };\n await this.resetSandboxState();\n if (!this.slice) {\n this.error(`Slice for job ${this.jobAddress} vanished during work initialization - aborting`);\n return;\n }\n\n const { datum: inputDatum, error: dataError } = this.slice;\n\n this.resetProgressTimeout();\n this.resetSliceTimeout();\n\n return new Promise(function sandbox$$workPromise(resolve, reject) {\n let onSuccess, onFail\n\n onSuccess = that.ee.once('resolve', function sandbox$$work$success (event) {\n that.ee.removeListener('reject', onFail)\n resolve(event)\n }.bind(that));\n\n onFail = that.ee.once('reject', function sandbox$$work$fail (err) {\n that.ee.removeListener('resolve', onSuccess)\n reject(err)\n }.bind(that))\n\n that.sliceStartTime = Date.now();\n that.progress = null;\n that.progressReports = {\n last: undefined,\n lastDeterministic: undefined,\n };\n\n that.resetProgressTimeout();\n that.resetSliceTimeout();\n that.emit('start', sliceHnd);\n \n if (dataError) {\n that.ee.removeListener('resolve', onSuccess);\n that.ee.removeListener('reject', onFail);\n that.emit('workEmit', {\n eventName: 'error',\n payload: {\n message: dataError.message,\n stack: dataError.stack,\n name: that.public.name\n }\n });\n setTimeout(() => reject(dataError), 0)\n\n } else {\n that.evaluatorHandle.postMessage({\n request: 'main',\n data: inputDatum,\n })\n }\n })\n .then(async function sandbox$$work$then(event) {\n // prevent any hanging timers from being fired\n await that.clearSandboxTimers();\n\n /** @todo Should sliceHnd just be replaced with that.public? */\n /** @todo Decide which event is right. */\n //that.emit('slice', sliceHnd); // Unused -- XXXpfr\n that.emit('sliceFinish', event);\n that.emit('complete', that.jobAddress);\n\n that.state.set(WORKING, ASSIGNED);\n that.slice = false;\n\n return event;\n })\n .catch((err) => { \n if (err.name === 'EWORKREJECT') {\n that.rejectionData = err;\n that.evaluatorHandle.postMessage({ request: 'resetAndGetCPUTime' })\n } else { // sandbox termination for rejected work happens in Supervisor.handleRejectedWork\n // Ceci is the reject callback for when the slice throws an error\n that.terminate(false);\n }\n\n that.emit('error', err, 'slice');\n\n if (err instanceof NoProgressError) {\n that.emit('workEmit', {\n eventName: 'noProgress',\n payload: {\n timestamp: Date.now() - that.sliceStartTime,\n data: that.slice.datumUri,\n progressReports: that.progressReports,\n }\n });\n }\n throw err;\n })\n .finally(function sandbox$$work$finally() {\n that.emit('end', sliceHnd); /** XXXpfr @todo Who is listening to event 'end' ? */\n });\n }\n\n resetProgressTimeout() {\n if (this.progressTimeout) {\n clearTimeout(this.progressTimeout);\n this.progressTimeout = null;\n }\n\n this.progressTimeout = setTimeout(() => {\n if (this.options.ignoreNoProgress)\n return this.emit('warning', \"ENOPROGRESS silenced by localExec: In a remote worker, this slice would be stopped for not calling progress frequently enough.\");\n\n this.ee.emit('reject', new NoProgressError(`No progress event was received in the last ${workerTuning.sandbox.progressTimeout / 1000} seconds.`));\n }, +workerTuning.sandbox.progressTimeout * timeDilation);\n }\n\n resetSliceTimeout() {\n if (this.sliceTimeout) clearTimeout(this.sliceTimeout);\n\n this.sliceTimeout = setTimeout(() => {\n if (Sandbox.debugWork) return this.emit('warning', 'Sandbox.debugWork: Ignoring slice timeout');\n\n this.ee.emit('reject', new SliceTooSlowError(`Slice took longer than ${workerTuning.sandbox.sliceTimeout / 1000} seconds.`));\n }, +workerTuning.sandbox.sliceTimeout * timeDilation);\n }\n \n async handleRing0Message(data) {\n debugging('event:ring-0') && debug('event:ring-0', data);\n //handling a true ring 0 message\n switch (data.request) {\n case 'sandboxLoaded':\n // emit externally\n this.emit('sandboxLoaded', this)\n break;\n\n case 'scriptLoaded':\n // emit externally\n this.emit('scriptLoaded', data);\n if(data.result !== \"success\") {\n this.onerror(data);\n }\n break;\n \n case 'clearTimersDone':\n this.ee.emit(data.request, data);\n break;\n case 'totalCPUTime':\n this.updateTime(data);\n if (this.ee.listenerCount('resolve') > 0) {\n this.completeData.timeReport = this.sliceTimeReport;\n this.ee.emit('resolve', this.completeData);\n delete this.completeData;\n } else {\n this.rejectionData.timeReport = this.sliceTimeReport\n this.emit('rejectedWorkMetrics', this.rejectionData) // If there is no internal listener for 'resolve', the slice was rejected and\n delete this.rejectionData; // we need to send the rejected metrics to the supervisor\n } \n break;\n case 'error': {\n // Warning: rejecting here with just event.data.error causes issues\n // where the reject handlers modify the object so it interferes with the\n // workEmit event payload, wrapping in an Error instance copies the values\n const wrappedError = this.fillInError(UncaughtExceptionError, data.error);\n\n if (this.ee.listenerCount('reject') > 0) {\n this.ee.emit('reject', wrappedError);\n } else {\n // This will happen if the error is thrown during initialization\n throw wrappedError;\n }\n break;\n }\n default: {\n this.error('Received unhandled request from sandbox: ' + data.request + '\\n\\t' + JSON.stringify(data));\n break;\n } \n }\n }\n\n async handleRing1Message(data) {\n switch (data.request) {\n case 'applyRequirementsDone':\n // emit internally\n this.ee.emit(data.request, data)\n break;\n default: {\n this.error('Received unhandled request from sandbox ring 1: ' + data.request + '\\n\\t' + JSON.stringify(data));\n break; \n }\n }\n }\n\n async handleRing2Message(data) {\n debugging('event:ring-2') && debug('event:ring-2', data);\n switch (data.request) {\n case 'dependency': {\n let moduleData;\n try {\n moduleData = await this.moduleCache.fetchModule(data.data, this.jobAddress);\n } catch (error) {\n /*\n * In the event of an error here, we want to let the client know there was a problem in\n * loading their module. However, there hasn't yet been an actual slice assigned to the sandbox.\n * Therefore, we assign 'slice 0' to the sandbox, a slice that will never exist, and is used\n * purely for this purpose. \n */\n this.slice = {\n jobAddress: this.jobAddress,\n sliceNumber: 0,\n };\n\n this.emit('workEmit', {\n eventName: 'error',\n payload: error,\n });\n this.ee.emit('reject', error);\n break;\n }\n this.evaluatorHandle.postMessage({\n request: 'moduleGroup',\n data: moduleData,\n id: data.id,\n });\n break;\n }\n case 'error':\n /*\n * Ring 2 error messages will only fire for problems inside of the worker that are separate from\n * the work function. In most cases there are other handlers for situations where 'error' may be emitted\n * such as timeouts if the expected message isn't recieved. Thus, we will output the error, but nothing else.\n */\n this.error('event:ring-2: some error happened.', data.error);\n break;\n case 'describe':\n case 'evalResult':\n case 'resetStateDone':\n case 'assigned':\n // emit internally\n this.ee.emit(data.request, data);\n break;\n case 'reject':\n // emit internally\n this.ee.emit(data.request, data.error);\n break;\n default: {\n this.error(`Received unhandled request from sandbox ring 2. Data: ${JSON.stringify(data, null, 2)}`);\n break;\n }\n }\n }\n\n async handleRing3Message(data) {\n switch (data.request) {\n case 'complete':\n clearTimeout(this.progressTimeout);\n clearTimeout(this.sliceTimeout);\n this.progressTimeout = this.sliceTimeout = null;\n\n if (this.progress === null) {\n if (this.options.ignoreNoProgress) {\n this.emit('warning', \"ENOPROGRESS silenced by localExec: Progress was not called during this slice's execution, in a remote sandbox this would cause the slice to be failed\");\n } else {\n // If a progress update was never received (progress === null) then reject\n this.ee.emit('reject', new NoProgressError('Sandbox never emitted a progress event.'));\n break;\n }\n }\n this.evaluatorHandle.postMessage({ request: 'resetAndGetCPUTime' })\n this.progress = 100;\n this.completeData = data;\n // The timing report and resolve will be emitted when the CPU time is received. \n break;\n case 'progress': {\n let { progress, indeterminate, throttledReports, value } = data;\n this.progress = progress;\n const progressReport = {\n timestamp: Date.now() - this.sliceStartTime,\n progress,\n value,\n throttledReports,\n }\n this.progressReports.last = progressReport;\n if (!indeterminate) {\n this.progressReports.lastDeterministic = progressReport;\n }\n\n this.resetProgressTimeout();\n this.emit('sliceProgress', data);\n break;\n }\n case 'noProgress':\n this.ee.emit('reject', new NoProgressError(data.message));\n break;\n case 'console':\n data.payload.message = scopedKvin.marshal(data.payload.message);\n this.emit('workEmit', {\n eventName: 'console',\n payload: data.payload,\n });\n break;\n case 'emitEvent':/* ad-hoc event from the sandbox (work.emit) */\n this.emit('workEmit', {\n eventName: 'custom',\n payload: data.payload\n });\n break;\n case 'measurement':\n this.updateTime(data);\n break;\n case 'sandboxError': /* the sandbox itself has an error condition */\n this.emit('sandboxError', data.error);\n break;\n case 'workError': { /* the work function threw/rejected */\n this.emit('workEmit', {\n eventName: 'error',\n payload: data.error,\n });\n\n // Warning: rejecting here with just .data.error causes issues\n // where the reject handlers modify the object so it interferes with the\n // workEmit payload, wrapping in an Error instance copies the values\n const wrappedError = this.fillInError(UncaughtExceptionError, data.error);\n\n if (this.ee.listenerCount('reject') > 0) {\n this.ee.emit('reject', wrappedError);\n } else {\n // This will happen if the error is thrown during initialization\n throw wrappedError;\n }\n break;\n }\n default: {\n this.error('Received unhandled request from sandbox ring 3: ' + data.request + '\\n\\t' + JSON.stringify(data));\n break; \n }\n }\n }\n\n /**\n * Handles progress and completion events from sandbox.\n * Unless explicitly returned out of this function will re-emit the event\n * on @this.ee where the name of the event is event.data.request.\n *\n * @param {object} event - event received from the sandbox\n */\n async onmessage(event) {\n debugging('event') && debug('event', event);\n if (Sandbox.debugEvents) {\n console.debug('sandbox - eventDebug:', {\n id: this.id,\n state: this.state.valueOf(),\n event: JSON.stringify(event)\n })\n }\n\n const { data } = event;\n const ringLevel = data.ringSource\n\n // Give the data to a handler depending on ring level\n if (ringLevel === -1) {\n this.error('Message sent directly from raw postMessage. Terminating worker...');\n console.debug(event);\n return this.terminate(true);\n } else {\n const handler = this.ringMessageHandlers[ringLevel];\n if (handler) {\n handler.call(this, data.value);\n } else {\n this.emit('warning', `No handler defined for message from ring ${ringLevel}`);\n console.debug(event);\n }\n }\n }\n\n /**\n * Error handler for the internal sandbox.\n * Emits error event that gets handled up in the Worker class.\n */\n onerror(event) {\n this.error('Sandbox emitted an error:', event);\n this.terminate(true, true);\n }\n\n /**\n * Clears the timeout and terminates the sandbox and sometimes emits a reject event.\n *\n * @param {boolean} [reject = true] - if true emit reject event\n * @param {boolean} [immediate = false] - passed to terminate, used by standaloneWorker to immediately close the connection\n */\n terminate (reject = true, immediate = false) {\n debugging() && console.debug(`Sandbox.terminate ${this.identifier}, count: ${++sbCnter}`);\n\n if (this.isTerminated) return;\n this.terminated = true;\n\n clearTimeout(this.progressTimeout);\n clearTimeout(this.sliceTimeout);\n this.progressTimeout = this.sliceTimeout = null;\n \n if (this.evaluatorHandle && typeof this.evaluatorHandle.terminate === 'function') {\n try {\n this.evaluatorHandle.terminate(immediate);\n this.evaluatorHandle = null;\n } catch (e) {\n this.error(`Error terminating sandbox ${this.id}:`, e);\n } finally {\n this.emit('terminate', this);\n }\n }\n\n if (reject) {\n this.ee.emit('reject', new Error(`Sandbox ${this.identifier} was terminated.`));\n }\n\n this.emit('terminated');\n }\n\n /**\n * Attempts to stop the sandbox from doing completing its current\n * set of work without terminating the working. \n * \n *** Until stop is implemented properly, use terminate(false).\n *\n * @todo Properly implement stop\n */\n stop () {\n //throw new Error('Sandbox.stop is not yet implemented.')\n this.terminate(false);\n }\n\n /**\n * ringNPostMessage can send a `measurement` request and update these\n * totals.\n */\n updateTime (measurementEvent) {\n ['total', 'CPU', 'webGL'].forEach((key) => {\n if (measurementEvent[key]) this.sliceTimeReport[key] += measurementEvent[key];\n })\n }\n\n /**\n * Start over sandbox work timers.\n */\n resetSliceTimeReport () {\n this.sliceTimeReport = {\n total: 0,\n CPU: 0,\n webGL: 0,\n }\n }\n\n /**\n * Clone the interesting part of an error.\n * @param {*} errorCtor\n * @param {Error} errorIn\n * @returns {Error}\n */\n fillInError(errorCtor, errorIn) {\n const errorOut = new errorCtor(errorIn.message);\n if (errorIn.name) errorOut.name = errorIn.name;\n if (errorIn.code) errorOut.code = errorIn.code;\n if (errorIn.fileName) errorOut.fileName = errorIn.fileName;\n if (errorIn.lineNumber) errorOut.lineNumber = errorIn.lineNumber;\n // Filter out the stack when we have more context\n if (errorIn.stack /*&& this.supervisor.debugBuild*/) errorOut.stack = errorIn.stack;\n\n return errorOut;\n }\n}\n\nSandbox.idCounter = 1;\nSandbox.debugWork = false;\nSandbox.debugState = false;\nSandbox.debugEvents = false;\n\nexports.Sandbox = Sandbox;\nexports.SandboxError = SandboxError;\nexports.NoProgressError = NoProgressError;\nexports.SliceTooSlowError = SliceTooSlowError;\nexports.UncaughtExceptionError = UncaughtExceptionError;\nexports.RemoteFetchError = RemoteFetchError;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/sandbox2.js?");
4581
4627
 
4582
4628
  /***/ }),
4583
4629
 
@@ -4588,7 +4634,7 @@ eval("/* eslint-disable require-await */\n// NOTE - need timeout/postmessage fun
4588
4634
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4589
4635
 
4590
4636
  "use strict";
4591
- eval("/**\n * @file dcp-client/worker/supervisor2/slice2.js\n *\n * A wrapper for the slice object returned from the scheduler's task distributor.\n *\n * @author Matthew Palma, mpalma@kingsds.network\n * Ryan Rossiter, ryan@kingsds.network\n * Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * @date May 2019\n * May 2019\n * Decemeber 2020\n * June 2022\n * @module slice\n */\n\n// @ts-check\n\n\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { truncateAddress } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n\nconst addressTruncationLength = 20;\n\nconst INITIAL = 'INITIAL';\nconst READY = 'READY';\nconst RESERVED = 'RESERVED';\nconst WORKING = 'WORKING';\nconst COMPLETE = 'COMPLETE';\nconst FAILED = 'FAILED';\nconst BROKEN = 'BROKEN';\nconst FINISHED = 'FINISHED';\n\n/** @typedef {import('./job-manager').JobManager} JobManager */\n/** @typedef {import('dcp/utils').SliceMessage} SliceMessage */\n/** @typedef {string} opaqueId */ // 22 character base64 string \n\n/** \n * Object use to represent a given slice inside the Supervisor. This object's shape \n * current inherits heavily from the message payload originating from the scheduler,\n * but should not /wg dec 2020.\n *\n * Caveat lector: this documentation was created without a 100% understanding of the code.\n * Please improve when possible.\n * \n * The read-only properties of this object are as follows:\n * - state INITIAL | READY | RESERVED | WORKING | COMPLETE | FAILED | BROKEN | FINISHED\n * - sliceNumber the number of the slice within this job\n * - jobAddress the address of the job that this slice belongs to\n * - isEstimation true when slice is used in estimation\n * - isLong true, when slice is estimated to take more than 300 seconds to execute\n * - datum input set element for this slice this slice of the job; could be a data: URI or it could\n * be a URL we need to fetch; note that fetches are limited to worker's allow list\n * - result\n * - result.request 'complete',...\n * - result.result return value from work function\n * - result.timeReport { total, idle } ms \n * - error error info when slice FAILED\n * - jobManager wrapper for the job that owns this slice\n * - resultStorageType 'values' => we are storing individual values,\n * which could be data: URIs or URLs\n * at the scheduler\n * 'pattern' => user-specified pattern for result URLs.\n * Data will be uploaded via POST to the\n * URL matching the pattern, provided the\n * worker is allowed to access it.\n * - resultStorageParams user-supplied POST parameters sent to the result \n * storage server when using resultStorageType = pattern.\n * - resultStorageDetails the pattern when using resultStorageType = pattern.\n * - authorizationMessage authorization from task distributor, sent to result submitter, etc...\n * \n * - isFinished true, when in state FINISHED <-- Indicates result submission has succeeded.\n * - hasFailed true, when in state FAILED, BROKEN\n * - isComplete true, when in state COMPLETED\n * - isReserved true, when in state RESERVED\n * - isActive true, when in state RESERVED, WORKING, COMPLETE\n * - isUnassigned true, when in state INITIAL\n * - isReady true, when in state READY\n * - isWorking true, when in state WORKING\n * - identifier string 'sliceNumber.jobAddress.state'\n * - timeReport accessor for this.result.timeReport that updates from this.rejectedTimeReport when appropriate\n *\n * The r/w properties of this object are as follows:\n * - startTime time when slice execution started\n * - rejectedTimeStamp slice has been rejected\n * - rejectedTimeReport rejected timeReport\n * \n * NOTE: If you ever use a property with a leading underscore you are probably making a mistake.\n * But if you must, please ask paul, yarn, bryan or eddie for a CR.\n */\nclass Slice\n{\n /**\n * @param {JobManager} jobManager\n * @param {SliceMessage} sliceMessage\n * @param {object} authorizationMessage\n */\n constructor (jobManager, sliceMessage, authorizationMessage)\n {\n /** @type {Synchronizer} */\n this._state = new Synchronizer(INITIAL, [ INITIAL, READY, RESERVED, WORKING, COMPLETE, FAILED, BROKEN, FINISHED ]);\n /** @type {JobManager} */\n this._jobManager = jobManager;\n /** @type {SliceMessage} */\n this._sliceMessage = { ...sliceMessage };\n this._authorizationMessage = authorizationMessage;\n this._datum = null;\n this._result = null;\n this._error = null;\n /** @type {number} */\n this.startTime = 0;\n /** @type {number} */\n this.rejectedTimeStamp = null;\n /** @type {{ total: number, CPU: number, webGL: number }} */\n this.rejectedTimeReport = null;\n \n assert(this.jobAddress === String(this._sliceMessage.jobAddress));\n if (!this.authorizationMessage && this.sliceNumber > 0)\n throw new Error(`Undefined authorization for slice ${this.identifier}.`);\n \n const that = this;\n \n /** \n * Start loading dependencies in the background. Once these are loaded, this.state will \n * transition to READY and the job will be ready to transition to WORKING.\n */\n (async function supervisor$$slice$$loadDatum() {\n ({ inputDatum: that._datum, dataError: that._error } = await that.jobManager.fetchSliceData(that.datumUri, that));\n }) (/* iife */)\n .then (() => {\n debugging('slice') && console.debug('Slice is transitioning to READY');\n this.state.set(INITIAL, READY);\n })\n .catch((error) => {\n debugging('slice') && console.debug('jobManager.fetchSliceData failed', error);\n this.state.set(INITIAL, BROKEN);\n })\n .finally(() => {\n debugging('slice') && console.debug('Slice.loadDatum completed.', this.identifier);\n });\n }\n\n /** @type {Synchronizer} */\n get state () { return this._state; }\n /** @type {number} */\n get sliceNumber () { return this._sliceMessage.sliceNumber; }\n /** @type {string} */\n get jobAddress () { return this._jobManager.address; }\n /** @type {boolean} */\n get isEstimation () { return this._sliceMessage.isEstimationSlice; }\n /** @type {boolean} */\n get isLong () { return this._sliceMessage.isLongSlice; }\n /** @type {string} */\n get datumUri () { return this._sliceMessage.datumUri; }\n /** @type {JobManager} */\n get jobManager () { return this._jobManager; }\n /** @type {string} */\n get resultStorageType () { return this._sliceMessage.resultStorageType; }\n /** @type {string} */\n get resultStorageDetails () { return this._sliceMessage.resultStorageDetails; }\n\n /** Read-only properties of type object. */\n get datum () { return this._datum; }\n get result () { return this._result; }\n get error () { return this._error; }\n get resultStorageParams () { return this._sliceMessage.resultStorageParams; }\n get authorizationMessage () { return this._authorizationMessage; }\n\n /** @type {boolean} */\n get isQueued () { return this.isUnassigned || this.isReady; }\n /** @type {boolean} */\n get isActive () { return this.isReserved || this.isWorking || this.isComplete; }\n /** @type {boolean} */\n get isFinished () { return this.state.is(FINISHED); }\n /** @type {boolean} */\n get hasFailed () { return this.state.is(FAILED) || this.state.is(BROKEN); }\n /** @type {boolean} */\n get isComplete () { return this.state.is(COMPLETE); }\n /** @type {boolean} */\n get isUnassigned () { return this.state.is(INITIAL); }\n /** @type {boolean} */\n get isReady () { return this.state.is(READY); }\n /** @type {boolean} */\n get isWorking () { return this.state.is(WORKING); }\n /**\n * Mark a slice as RESERVED to remove it from the ready list, yet still able to transition to WORKING.\n * @type {boolean}\n **/\n get isReserved () { return this.state.is(RESERVED); }\n\n // TEMPORARY: THIS IS ONLY USED FOR COMPATIBILITY WITH SUPERVISOR1.\n // WHEN SUPERVISOR1 IS PUT TO BED, REMOVE THESE TWO FUNCTIONS.\n /** DO NOT USE! @deprecated @type {boolean} */\n get failed () { return this.hasFailed; }\n /** DO NOT USE! @deprecated @type {boolean} */\n get completed () { return this.isComplete; }\n \n /** @type {string} */\n get identifier () { return `${this.sliceNumber}.${truncateAddress(this.jobAddress, addressTruncationLength)}.${this.state}`; }\n /** \n * timeReport accessor that optionally updates from this.rejectedTimeReport. \n * @type { { total, CPU, webGL } }\n **/\n get timeReport ()\n {\n if (this.result.timeReport && this.rejectedTimeReport && this.rejectedTimeReport.total > 0)\n {\n // Data collected from sandboxes that rejected this slice.\n ['total', 'CPU', 'webGL'].forEach((key) => {\n if (this.rejectedTimeReport[key]) this.result.timeReport[key] += this.rejectedTimeReport[key];\n });\n this.rejectedTimeReport = null;\n }\n return this.result.timeReport;\n }\n /**\n * Return the time interval to estimated slice completion time.\n * @type {number}\n **/\n get etaMs ()\n {\n if (this.startTime === null) return 0;\n let etaMs = this.jobManager.estimateWallMs;\n if (this.startTime) etaMs -= (Date.now() - this.startTime);\n return etaMs;\n }\n\n /** Start slice over, regardless of what state it is in. */\n resetState()\n {\n if (this.isReady || this.isUnassigned) return;\n this._state = new Synchronizer(READY, [ INITIAL, READY, RESERVED, WORKING, COMPLETE, FAILED, BROKEN, FINISHED ]);\n }\n\n /** \n * Sets the slice status to RESERVED, called to remove slice from the ready list,\n * yet still able to transition to WORKING.\n **/\n markAsReserved() { this.state.set(READY, RESERVED); }\n\n /** Sets the slice status to WORKING, called when the slice is getting ready to be handed to a worker. */\n markAsWorking() { this.state.set(RESERVED, WORKING); }\n\n /** Sets the slice status to FINISHED, called when the slice has completed and submitted results. */\n markAsFinished() { this.state.set(COMPLETE, FINISHED); }\n\n /**\n * Verifies slice status is working and assign sandbox, called when the slice is handed to a worker.\n */\n verifyWorking()\n {\n if (!this.isWorking) {\n throw new Error(`Slice ${this.identifier} is not working.`);\n }\n }\n\n /**\n * Receives a result from the scheduler.\n * It will then put the result in the appropriate place.\n * It could also eventually determine if the slice should be\n * retried before determining that it has failed.\n *\n * @param {object|Error} result - The result that came back from the worker sandbox.\n * @param {boolean} [success=true] - True if result is considered successful, false if error occurred.\n */\n collectResult(result, success = true)\n {\n if (this.result)\n throw new Error(`Slice ${this.identifier} received more than one result.`);\n \n if (success)\n {\n this.state.set(WORKING, COMPLETE);\n this._result = result;\n }\n else\n {\n this.state.set(WORKING, FAILED);\n this._error = result;\n }\n debugging('slice') && console.debug('collectResult', this.identifier);\n }\n\n /**\n * Create basic message object as part of the payload to send back to the result-submitter's status operation.\n * @param {string} status - The kind of status operation\n * @param {object} [extraProperties={}] - Extra properties for the paylaod object.\n * @returns {object}\n */\n getMessage(status, extraProperties = {})\n {\n return {\n sliceNumbers: [this.sliceNumber],\n job: this.jobAddress,\n authorizationMessage: this.authorizationMessage,\n status,\n ...extraProperties,\n }; \n }\n\n /**\n * Create basic payload object to send back to the result-submitter's status operation.\n * @param {opaqueId} worker - The current worker's opaqueId\n * @param {string} status - The kind of status operation\n * @param {object} [extraProperties={}] - Extra properties for the paylaod object.\n * @returns {object}\n */\n getMessagePayload(worker, status, extraProperties = {})\n {\n return {\n worker,\n slices: [ this.getMessage(status, extraProperties) ],\n }; \n }\n\n /**\n * Create slice-return payload object to send to the result-submitter's status operation.\n * @param {opaqueId} worker - The current worker's opaqueId\n * @param {string} [reason] - Optional reason for the return: 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @return {object}\n */\n getReturnMessagePayload(worker, reason)\n {\n delete this._result;\n\n if (!reason) reason = this.error ? 'EUNCAUGHT' : 'unknown';\n const extraProperties = {\n isEstimationSlice: this.isEstimation,\n error: this.error,\n reason,\n };\n\n return this.getMessagePayload(worker, 'return', extraProperties);\n }\n\n /**\n * @deprecated -- DO NOT USE!\n * TEMPORARY: THIS IS ONLY USED FOR COMPATIBILITY WITH SUPERVISOR1.\n * WHEN SUPERVISOR1 IS PUT TO BED, REMOVE THIS FUNCTION.\n * This function helps enforce the equivalence:\n * !this.authorizationMessage <==> sliceNumber === 0 .\n * @returns {object} this.authorizationMessage\n */\n getAuthorizationMessage () {\n if (!this.authorizationMessage && this.sliceNumber > 0)\n throw new Error(`Undefined authorization for slice ${this.identifier}.`);\n return this.authorizationMessage;\n }\n}\nexports.Slice = Slice;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/slice2.js?");
4637
+ eval("/**\n * @file dcp-client/worker/supervisor2/slice2.js\n *\n * A wrapper for the slice object returned from the scheduler's task distributor.\n *\n * @author Matthew Palma, mpalma@kingsds.network\n * Ryan Rossiter, ryan@kingsds.network\n * Wes Garland, wes@kingsds.network\n * Paul, paul@kingsds.network\n * @date May 2019\n * May 2019\n * Decemeber 2020\n * June 2022\n * @module slice\n */\n\n// @ts-check\n\n\nconst { Synchronizer } = __webpack_require__(/*! dcp/common/concurrency */ \"./src/common/concurrency.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('worker');\nconst { truncateAddress } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\n\nconst addressTruncationLength = 20;\n\nconst INITIAL = 'INITIAL';\nconst READY = 'READY';\nconst RESERVED = 'RESERVED';\nconst WORKING = 'WORKING';\nconst COMPLETE = 'COMPLETE';\nconst FAILED = 'FAILED';\nconst BROKEN = 'BROKEN';\nconst FINISHED = 'FINISHED';\n\n/** @typedef {import('./job-manager').JobManager} JobManager */\n/** @typedef {import('dcp/utils').SliceMessage} SliceMessage */\n/** @typedef {string} opaqueId */ // 22 character base64 string \n\n/** \n * Object use to represent a given slice inside the Supervisor. This object's shape \n * current inherits heavily from the message payload originating from the scheduler,\n * but should not /wg dec 2020.\n *\n * Caveat lector: this documentation was created without a 100% understanding of the code.\n * Please improve when possible.\n * \n * The read-only properties of this object are as follows:\n * - state INITIAL | READY | RESERVED | WORKING | COMPLETE | FAILED | BROKEN | FINISHED\n * - sliceNumber the number of the slice within this job\n * - jobAddress the address of the job that this slice belongs to\n * - isEstimation true when slice is used in estimation\n * - isLong true, when slice is estimated to take more than 300 seconds to execute\n * - datum input set element for this slice this slice of the job; could be a data: URI or it could\n * be a URL we need to fetch; note that fetches are limited to worker's allow list\n * - result\n * - result.request 'complete',...\n * - result.result return value from work function\n * - result.timeReport { total, idle } ms \n * - error error info when slice FAILED\n * - jobManager wrapper for the job that owns this slice\n * - resultStorageType 'values' => we are storing individual values,\n * which could be data: URIs or URLs\n * at the scheduler\n * 'pattern' => user-specified pattern for result URLs.\n * Data will be uploaded via POST to the\n * URL matching the pattern, provided the\n * worker is allowed to access it.\n * - resultStorageParams user-supplied POST parameters sent to the result \n * storage server when using resultStorageType = pattern.\n * - resultStorageDetails the pattern when using resultStorageType = pattern.\n * - authorizationMessage authorization from task distributor, sent to result submitter, etc...\n * \n * - isFinished true, when in state FINISHED <-- Indicates result submission has succeeded.\n * - hasFailed true, when in state FAILED, BROKEN\n * - isComplete true, when in state COMPLETED\n * - isReserved true, when in state RESERVED\n * - isActive true, when in state RESERVED, WORKING, COMPLETE\n * - isUnassigned true, when in state INITIAL\n * - isReady true, when in state READY\n * - isWorking true, when in state WORKING\n * - identifier string 'sliceNumber.jobAddress.state'\n * - timeReport accessor for this.result.timeReport that updates from this.rejectedTimeReport when appropriate\n *\n * The r/w properties of this object are as follows:\n * - startTime time when slice execution started\n * - rejectedTimeStamp slice has been rejected\n * - rejectedTimeReport rejected timeReport\n * \n * NOTE: If you ever use a property with a leading underscore you are probably making a mistake.\n * But if you must, please ask paul, yarn, bryan or eddie for a CR.\n */\nclass Slice\n{\n /**\n * @param {JobManager} jobManager\n * @param {SliceMessage} sliceMessage\n * @param {object} authorizationMessage\n */\n constructor (jobManager, sliceMessage, authorizationMessage)\n {\n /** @type {Synchronizer} */\n this._state = new Synchronizer(INITIAL, [ INITIAL, READY, RESERVED, WORKING, COMPLETE, FAILED, BROKEN, FINISHED ]);\n /** @type {JobManager} */\n this._jobManager = jobManager;\n /** @type {SliceMessage} */\n this._sliceMessage = { ...sliceMessage };\n this._authorizationMessage = authorizationMessage;\n this._datum = null;\n this._result = null;\n this._error = null;\n /** @type {number} */\n this.startTime = 0;\n /** @type {number} */\n this.rejectedTimeStamp = null;\n /** @type {{ total: number, CPU: number, webGL: number }} */\n this.rejectedTimeReport = null;\n \n assert(this.jobAddress === String(this._sliceMessage.jobAddress));\n if (!this.authorizationMessage && this.sliceNumber > 0)\n throw new Error(`Undefined authorization for slice ${this.identifier}.`);\n \n const that = this;\n \n /** \n * Start loading dependencies in the background. Once these are loaded, this.state will \n * transition to READY and the job will be ready to transition to WORKING.\n */\n (async function supervisor$$slice$$loadDatum() {\n ({ inputDatum: that._datum, dataError: that._error } = await that.jobManager.fetchSliceData(that.datumUri, that));\n }) (/* iife */)\n .then (() => {\n debugging('slice') && console.debug('Slice is transitioning to READY');\n this.state.set(INITIAL, READY);\n })\n .catch((error) => {\n debugging('slice') && console.debug('jobManager.fetchSliceData failed', error);\n this.state.set(INITIAL, BROKEN);\n })\n .finally(() => {\n debugging('slice') && console.debug('Slice.loadDatum completed.', this.identifier);\n });\n }\n\n /** @type {Synchronizer} */\n get state () { return this._state; }\n /** @type {number} */\n get sliceNumber () { return this._sliceMessage.sliceNumber; }\n /** @type {string} */\n get jobAddress () { return this._jobManager.address; }\n /** @type {boolean} */\n get isEstimation () { return this._sliceMessage.isEstimationSlice; }\n /** @type {boolean} */\n get isLong () { return this._sliceMessage.isLongSlice; }\n /** @type {string} */\n get datumUri () { return this._sliceMessage.datumUri; }\n /** @type {JobManager} */\n get jobManager () { return this._jobManager; }\n /** @type {string} */\n get resultStorageType () { return this._sliceMessage.resultStorageType; }\n /** @type {string} */\n get resultStorageDetails () { return this._sliceMessage.resultStorageDetails; }\n\n /** Read-only properties of type object. */\n get datum () { return this._datum; }\n get result () { return this._result; }\n get error () { return this._error; }\n get resultStorageParams () { return this._sliceMessage.resultStorageParams; }\n get authorizationMessage () { return this._authorizationMessage; }\n\n /** @type {boolean} */\n get isQueued () { return this.isUnassigned || this.isReady; }\n /** @type {boolean} */\n get isActive () { return this.isReserved || this.isWorking || this.isComplete; }\n /** @type {boolean} */\n get isFinished () { return this.state.is(FINISHED); }\n /** @type {boolean} */\n get hasFailed () { return this.state.is(FAILED) || this.state.is(BROKEN); }\n /** @type {boolean} */\n get isComplete () { return this.state.is(COMPLETE); }\n /** @type {boolean} */\n get isUnassigned () { return this.state.is(INITIAL); }\n /** @type {boolean} */\n get isReady () { return this.state.is(READY); }\n /** @type {boolean} */\n get isWorking () { return this.state.is(WORKING); }\n /**\n * Mark a slice as RESERVED to remove it from the ready list, yet still able to transition to WORKING.\n * @type {boolean}\n **/\n get isReserved () { return this.state.is(RESERVED); }\n\n /** @type {string} */\n get identifier () { return `${this.sliceNumber}.${truncateAddress(this.jobAddress, addressTruncationLength)}.${this.state}`; }\n /** \n * timeReport accessor that optionally updates from this.rejectedTimeReport. \n * @type { { total, CPU, webGL } }\n **/\n get timeReport ()\n {\n if (this.result.timeReport && this.rejectedTimeReport && this.rejectedTimeReport.total > 0)\n {\n // Data collected from sandboxes that rejected this slice.\n ['total', 'CPU', 'webGL'].forEach((key) => {\n if (this.rejectedTimeReport[key]) this.result.timeReport[key] += this.rejectedTimeReport[key];\n });\n this.rejectedTimeReport = null;\n }\n return this.result.timeReport;\n }\n /**\n * Return the time interval to estimated slice completion time.\n * @type {number}\n **/\n get etaMs ()\n {\n if (this.startTime === null) return 0;\n let etaMs = this.jobManager.estimateWallMs;\n if (this.startTime) etaMs -= (Date.now() - this.startTime);\n return etaMs;\n }\n\n /** Start slice over, regardless of what state it is in. */\n resetState()\n {\n if (this.isReady || this.isUnassigned) return;\n this._state = new Synchronizer(READY, [ INITIAL, READY, RESERVED, WORKING, COMPLETE, FAILED, BROKEN, FINISHED ]);\n }\n\n /** \n * Sets the slice status to RESERVED, called to remove slice from the ready list,\n * yet still able to transition to WORKING.\n **/\n markAsReserved() { this.state.set(READY, RESERVED); }\n\n /** Sets the slice status to WORKING, called when the slice is getting ready to be handed to a worker. */\n markAsWorking() { this.state.set(RESERVED, WORKING); }\n\n /** Sets the slice status to FINISHED, called when the slice has completed and submitted results. */\n markAsFinished() { this.state.set(COMPLETE, FINISHED); }\n\n /**\n * Verifies slice status is working and assign sandbox, called when the slice is handed to a worker.\n */\n verifyWorking()\n {\n if (!this.isWorking) {\n throw new Error(`Slice ${this.identifier} is not working.`);\n }\n }\n\n /**\n * Receives a result from the scheduler.\n * It will then put the result in the appropriate place.\n * It could also eventually determine if the slice should be\n * retried before determining that it has failed.\n *\n * @param {object|Error} result - The result that came back from the worker sandbox.\n * @param {boolean} [success=true] - True if result is considered successful, false if error occurred.\n */\n collectResult(result, success = true)\n {\n if (this.result)\n throw new Error(`Slice ${this.identifier} received more than one result.`);\n \n if (success)\n {\n this.state.set(WORKING, COMPLETE);\n this._result = result;\n }\n else\n {\n this.state.set(WORKING, FAILED);\n this._error = result;\n }\n debugging('slice') && console.debug('collectResult', this.identifier);\n }\n\n /**\n * Create basic message object as part of the payload to send back to the result-submitter's status operation.\n * @param {string} status - The kind of status operation\n * @param {object} [extraProperties={}] - Extra properties for the paylaod object.\n * @returns {object}\n */\n getMessage(status, extraProperties = {})\n {\n return {\n sliceNumbers: [this.sliceNumber],\n job: this.jobAddress,\n authorizationMessage: this.authorizationMessage,\n status,\n ...extraProperties,\n }; \n }\n\n /**\n * Create basic payload object to send back to the result-submitter's status operation.\n * @param {opaqueId} worker - The current worker's opaqueId\n * @param {string} status - The kind of status operation\n * @param {object} [extraProperties={}] - Extra properties for the paylaod object.\n * @returns {object}\n */\n getMessagePayload(worker, status, extraProperties = {})\n {\n return {\n worker,\n slices: [ this.getMessage(status, extraProperties) ],\n }; \n }\n\n /**\n * Create slice-return payload object to send to the result-submitter's status operation.\n * @param {opaqueId} worker - The current worker's opaqueId\n * @param {string} [reason] - Optional reason for the return: 'ENOPROGRESS', 'EUNCAUGHT', 'ESLICETOOSLOW', 'unknown'.\n * @return {object}\n */\n getReturnMessagePayload(worker, reason)\n {\n delete this._result;\n\n if (!reason) reason = this.error ? 'EUNCAUGHT' : 'unknown';\n const extraProperties = {\n isEstimationSlice: this.isEstimation,\n error: this.error,\n reason,\n };\n\n return this.getMessagePayload(worker, 'return', extraProperties);\n }\n}\nexports.Slice = Slice;\n\n\n//# sourceURL=webpack://dcp/./src/dcp-client/worker/supervisor2/slice2.js?");
4592
4638
 
4593
4639
  /***/ }),
4594
4640
 
@@ -4609,7 +4655,7 @@ eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_mod
4609
4655
  \****************************************/
4610
4656
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4611
4657
 
4612
- eval("/**\n * @file events/event-subscriber.js\n * @author Ryan Rossiter <ryan@kingsds.network>\n * @date March 2020\n * \n * This file is the client-side companion to the event-router.\n * It maintains a map of subscription tokens that the event router has provisioned\n * for it, and calls the associated callbacks when the event router emits a new event.\n */\n\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('event-subscriber');\nconst { v4: uuidv4 } = __webpack_require__(/*! uuid */ \"./node_modules/uuid/dist/esm-browser/index.js\");\nconst { leafMerge } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst dcpEnv = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\n\n/** @typedef {import('./common-types').DcpEvent} DcpEvent */\n/** @typedef {import('./common-types').SubscriptionToken} SubscriptionToken */\n/** @typedef {import('./common-types').EventLabel} EventLabel */\n/** @typedef {import('./common-types').SubscriptionOptions} SubscriptionOptions */\n\n\n/**\n * A container for callback and SubscriptionToken pairs\n * @typedef {Object} CallbackHandle\n * @property {SubscriptionToken} token\n * @property {function} callback\n */\n\n/**\n * @typedef {Object} Subscription\n * @property {EventLabel} label - Event label\n * @property {function[]} callbacks - The callbacks that will be invoked when an event is received for this subscription\n * @property {SubscriptionOptions} options - Additional options\n * @property {string} optionsStr - A string used to memoize stringifying the options object\n */\n\n/**\n * @constructor\n * @param {object} options configuration options for this instance of the EventSubscriber. Default\n * options are given via dcpConfig.eventSubscriber.\n */\nclass EventSubscriber\n{\n constructor(eventEmitter, options)\n {\n this.options = options = leafMerge(options, dcpConfig.eventSubscriber);\n this.eventEmitter = eventEmitter;\n this.subscriptions = new Map();\n this.seenEventIds = [];\n this.nextSeenEventIdSlot = 0;\n \n /**\n * Interval to keep the connection alive when no messages are being\n * received.\n * @type {NodeJS.Timer}\n */\n this._keepaliveIntervalHnd = null;\n\n this.onEventRouterConnectionInterrupted = () => {\n this.openEventRouterConn();\n this.setupEventRouterConnectionEvents();\n this.reestablishSubscriptions();\n }\n\n const ceci = this;\n\n this.eventRouterConnection = null;\n this.openEventRouterConn = function openEventRouterConn()\n {\n ceci.eventRouterConnection = new protocolV4.Connection(dcpConfig.scheduler.services.eventRouter);\n ceci.eventRouterConnection.on('close', ceci.onEventRouterConnectionInterrupted);\n }\n this.openEventRouterConn();\n this.setupEventRouterConnectionEvents();\n }\n\n async close() {\n debugging() && console.log(`event-subscriber: closing EventSubscriber connection`);\n\n let subs = this.subscriptions.entries();\n const ps = [];\n for (let [label] of subs) {\n ps.push(this.unsubscribe(label));\n }\n await Promise.all(ps);\n\n this.eventRouterConnection.off('close', this.onEventRouterConnectionInterrupted);\n\n this.setKeepalive(false);\n\n debugging() && console.log(`event-subscriber: closing event-router connection...`);\n this.eventRouterConnection.close('schedmsg closing');\n this.subscriptions.clear();\n this.hookedEventRouterConnectionEvents = false;\n }\n\n async reestablishSubscriptions() {\n let subs = this.subscriptions.entries();\n debugging() && console.log(`event-subscriber: reestablishing ${this.subscriptions.size} subscriptions`);\n\n const eventTypes = (__webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\").eventTypes);\n const reliableEvents = [];\n const optionalEvents = [];\n for (let [label, oldToken] of subs) {\n if (oldToken.options)\n {\n // schedmsg special case\n await this.provisionSubscriptionToken(label, oldToken.options);\n }\n else if (eventTypes[label] && eventTypes[label].reliable)\n {\n reliableEvents.push(label);\n }\n else\n {\n optionalEvents.push(label);\n }\n }\n try\n {\n if (reliableEvents.length || optionalEvents.length) {\n const message = {\n reliableEvents,\n optionalEvents,\n options: this.options\n }\n\n await this.provisionSubscriptionTokens(message);\n }\n \n }\n catch (error)\n {\n debugging() && console.warn(`event-subscriber: error establishing subscription (${sub.label}):`, error);\n }\n }\n\n setupEventRouterConnectionEvents() {\n debugging() && console.log(`event-subscriber: setupEventRouterConnectionEvents`)\n this.eventRouterConnection.on('request', (req) => {\n const { operation } = req.payload;\n if (operation === 'event') this.onEventRequest(req);\n else req.respond(new Error(`Unknown EventSubscriber operation: ${operation}`));\n });\n }\n\n async onEventRequest(req) {\n debugging('verbose') && console.log(`event-subscriber: onEventRequest`);\n // data = req.payload.data.event\n const { token, event: data } = req.payload.data;\n const event = data.data;\n const eventId = data.eventId;\n\n debugging() && console.debug(`131: event ${eventId} (${event.eventName}):`, req.payload.data);\n if (this.subscriptions.has(event.eventName))\n {\n const { token: eventToken } = this.subscriptions.get(event.eventName);\n debugging('verbose') && console.log(`event-subscriber: event: ${event.eventName}`);\n if (eventToken !== token)\n req.respond(new Error(`No subscription registered for token ${token}`));\n else if (eventId && this.seenEventIds.includes(eventId))\n req.respond(new Error(`This event has already been sent: ${eventId}`));\n else\n {\n const label = event.eventName;\n // on each request, we push the eventIds to keep track of events we have seen.\n // this will be filtered in the re-established events.\n if (eventId)\n {\n this.seenEventIds[this.nextSeenEventIdSlot] = eventId;\n this.nextSeenEventIdSlot = (this.nextSeenEventIdSlot + 1) % Number(this.options.seenListSize);\n }\n \n delete event.eventName;\n if (!this.eventEmitter.eventIntercepts)\n this.eventEmitter.emit(label, event);\n else\n {\n if (this.eventEmitter.eventIntercepts[label])\n this.eventEmitter.eventIntercepts[label](event)\n else\n this.eventEmitter.emit(label, event);\n }\n req.respond();\n }\n }\n else\n {\n console.warn(`No subscription registered for label ${event.eventName}`);\n req.respond(new Error(`No subscription registered for token ${event.eventName}`));\n }\n }\n\n /**\n * Registers this subscription with the event router, and saves the returned\n * subscription token into the this.subscriptions map.\n * @param {EventLabel} label\n * @param {SubscriptionOptions} options\n */\n async provisionSubscriptionToken(label, options) {\n debugging('verbose') && console.log(`event-subscriber: provisionSubscriptionToken(${label})`);\n\n if (this.eventRouterConnection.state.in(['closed','closing','close-wait']))\n {\n this.openEventRouterConn();\n this.setupEventRouterConnectionEvents();\n }\n let res = await this.eventRouterConnection.send('subscribe', {\n label, options,\n }).catch(async (error) => {\n debugging('verbose') && console.error(`Failed to send subscription request to scheduler for the event ${label}: ${error}. Will try again.`);\n this.eventRouterConnection.close();\n });\n \n if (res) {\n const token = res.payload.token;\n this.subscriptions.set(label, { token, options });\n }\n\n }\n\n async provisionSubscriptionTokens(message) {\n debugging('verbose') && console.log(`event-subscriber: provisionSubscriptionTokens(${JSON.stringify(message)})`);\n let res = await this.eventRouterConnection.send('subscribeMany', message).catch(async (e) => {\n debugging('verbose') && console.error(`Failed to send subscribeMany request to scheduler: ${e}. Will try again`);\n this.eventRouterConnection.close();\n });\n \n if (res && res.success) {\n const tokenEventPairs = res.payload.tokens;\n tokenEventPairs.forEach((tokenAndEvent) => {\n const label = tokenAndEvent.event;\n const token = tokenAndEvent.token;\n this.subscriptions.set(label, { token });\n })\n } else if (res && !res.success) {\n throw new Error(`Failed to subscribe to events, ${res.payload}`);\n }\n \n }\n\n /**\n * Unregisters a subscription from the event router\n * @param {EventLabel} label\n * @param {SubscriptionToken} token \n */\n async releaseSubscriptionToken(label, token) {\n debugging('verbose') && console.log(`event-subscriber: releaseSubscriptionToken(${label})`);\n await this.eventRouterConnection.send('unsubscribe', {\n label, token,\n }).catch(async (e) => {\n debugging('verbose') && console.error(`Failed to unsubscribe from event ${label}: ${e}. Will try again.`)\n this.eventRouterConnection.close();\n });\n }\n\n /**\n * @param {EventLabel} label - Event label\n * @param {function} callback\n * @param {SubscriptionOptions} options - Additional options\n */\n async subscribeManyEvents(reliableEvents, optionalEvents, options={}) {\n this.options = options;\n const noSubReliableEvents = [];\n reliableEvents.forEach((ev) => {\n for (let [t, sub] of this.subscriptions)\n {\n // search for a subscription with identical label,\n // to avoid making duplicate subscriptions\n if (ev === sub.label)\n {\n return;\n }\n }\n if (!noSubReliableEvents.includes(ev)) {\n noSubReliableEvents.push(ev);\n }\n })\n\n const noSubOptionalEvents = [];\n optionalEvents.forEach((ev) => {\n for (let [t, sub] of this.subscriptions)\n {\n // search for a subscription with identical label,\n // to avoid making duplicate subscriptions\n if (ev === sub.label)\n {\n return;\n }\n }\n if (!noSubOptionalEvents.includes(ev)) {\n noSubOptionalEvents.push(ev);\n }\n })\n\n if (reliableEvents.length || optionalEvents.length)\n {\n const message = {\n reliableEvents: noSubReliableEvents,\n optionalEvents: noSubOptionalEvents,\n options,\n }\n debugging() && console.log('event-subscriber: provisioning:', message);\n await this.provisionSubscriptionTokens(message);\n }\n \n // Start the keepalive interval\n this.setKeepalive();\n }\n \n async subscribe(label, options={}) {\n debugging() && console.log(`event-subscriber: subscribe(${label})`);\n let token;\n for (let [subLabel, t] of this.subscriptions) {\n // search for a subscription with identical label and filter,\n // to avoid making duplicate subscriptions\n if (label === subLabel) {\n token = t;\n break;\n }\n }\n\n if (!token) {\n await this.provisionSubscriptionToken(label, options);\n }\n\n // Start the keepalive interval\n this.setKeepalive();\n }\n\n /**\n * @param {string} label\n */\n async unsubscribe(label) {\n debugging() && console.log(`event-subscriber: unsubscribe)${label})`);\n if (!this.subscriptions.has(label)) {\n return;\n }\n \n const token = this.subscriptions.get(label)\n await this.releaseSubscriptionToken(label, token);\n this.subscriptions.delete(label);\n\n // If no subscriptions remain, disable the keepalive and allow the Connection\n // to expire\n if (this.subscriptions.size === 0)\n this.setKeepalive(false);\n }\n\n /**\n * De/activate an interval to send keepalives over the event-router connection\n *\n * @param {Boolean} keepalive If true, activate the interval. If false, remove it\n */\n setKeepalive(activate = true)\n {\n const that = this;\n const keepaliveInterval = Number(this.options.keepaliveInterval) || 3 * 60; /* seconds */\n\n if (!activate /* => deactivate */)\n {\n /* clear the watchdog interval, and neuter already-queued doKeepalive()s */\n if (this._keepaliveIntervalHnd)\n clearInterval(this._keepaliveIntervalHnd);\n this._keepaliveIntervalHnd = null;\n return;\n }\n\n if (activate && !keepaliveInterval)\n {\n debugging() && console.debug('event-subscriber: configured for no keepalive interval');\n return;\n }\n\n if (!this._keepaliveIntervalHnd)\n this._keepaliveIntervalHnd = setInterval(doKeepalive, keepaliveInterval * 1000)\n \n if (dcpEnv.platform === 'nodejs')\n this._keepaliveIntervalHnd.unref();\n\n async function doKeepalive()\n {\n if (that._keepaliveIntervalHnd)\n {\n try\n {\n that.eventRouterConnection.keepalive();\n }\n catch(error)\n {\n /* ignore errors if the watchdog was cancelled while were waiting for the\n * keepalive to complete; this implies the connection was purposefully closed.\n */\n if (!that._keepaliveIntervalHnd)\n return;\n\n debugging() && console.debug('Event subscriber watchdog detected connection in error state', error.code || error.message);\n debugging('verbose') && console.debug(error);\n\n that.eventRouterConnection.close(); /* trigger ER connection recycle */\n }\n }\n }\n }\n}\n\nexports.EventSubscriber = EventSubscriber;\n\n\n//# sourceURL=webpack://dcp/./src/events/event-subscriber.js?");
4658
+ eval("/**\n * @file events/event-subscriber.js\n * @author Ryan Rossiter <ryan@kingsds.network>\n * @date March 2020\n * \n * This file is the client-side companion to the event-router.\n * It maintains a map of subscription tokens that the event router has provisioned\n * for it, and calls the associated callbacks when the event router emits a new event.\n */\n\nconst protocolV4 = __webpack_require__(/*! dcp/protocol-v4 */ \"./src/protocol-v4/index.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('event-subscriber');\nconst { v4: uuidv4 } = __webpack_require__(/*! uuid */ \"./node_modules/uuid/dist/esm-browser/index.js\");\nconst { leafMerge } = __webpack_require__(/*! dcp/utils */ \"./src/utils/index.js\");\nconst dcpEnv = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\n\n/** @typedef {import('./common-types').DcpEvent} DcpEvent */\n/** @typedef {import('./common-types').SubscriptionToken} SubscriptionToken */\n/** @typedef {import('./common-types').EventLabel} EventLabel */\n/** @typedef {import('./common-types').SubscriptionOptions} SubscriptionOptions */\n\n\n/**\n * A container for callback and SubscriptionToken pairs\n * @typedef {Object} CallbackHandle\n * @property {SubscriptionToken} token\n * @property {function} callback\n */\n\n/**\n * @typedef {Object} Subscription\n * @property {EventLabel} label - Event label\n * @property {function[]} callbacks - The callbacks that will be invoked when an event is received for this subscription\n * @property {SubscriptionOptions} options - Additional options\n * @property {string} optionsStr - A string used to memoize stringifying the options object\n */\n\n/**\n * @constructor\n * @param {object} options configuration options for this instance of the EventSubscriber. Default\n * options are given via dcpConfig.eventSubscriber.\n */\nclass EventSubscriber\n{\n constructor(eventEmitter, options)\n {\n this.options = options = leafMerge(options, dcpConfig.eventSubscriber);\n this.eventEmitter = eventEmitter;\n this.subscriptions = new Map();\n this.seenEventIds = [];\n this.nextSeenEventIdSlot = 0;\n \n /**\n * Interval to keep the connection alive when no messages are being\n * received.\n * @type {NodeJS.Timer}\n */\n this._keepaliveIntervalHnd = null;\n\n this.onEventRouterConnectionInterrupted = () => {\n this.openEventRouterConn();\n this.setupEventRouterConnectionEvents();\n this.reestablishSubscriptions();\n }\n\n const ceci = this;\n\n this.eventRouterConnection = null;\n this.openEventRouterConn = function openEventRouterConn()\n {\n ceci.eventRouterConnection = new protocolV4.Connection(dcpConfig.scheduler.services.eventRouter);\n ceci.eventRouterConnection.on('close', ceci.onEventRouterConnectionInterrupted);\n }\n this.openEventRouterConn();\n this.setupEventRouterConnectionEvents();\n }\n\n async close() {\n debugging() && console.log(`event-subscriber: closing EventSubscriber connection`);\n\n let subs = this.subscriptions.entries();\n const ps = [];\n for (let [label] of subs) {\n ps.push(this.unsubscribe(label));\n }\n await Promise.all(ps);\n\n this.eventRouterConnection.off('close', this.onEventRouterConnectionInterrupted);\n\n this.setKeepalive(false);\n\n debugging() && console.log('event-subscriber: closing event-router connection...');\n this.eventRouterConnection.close('schedmsg closing');\n this.subscriptions.clear();\n this.hookedEventRouterConnectionEvents = false;\n }\n\n async reestablishSubscriptions() {\n let subs = this.subscriptions.entries();\n debugging() && console.log(`event-subscriber: reestablishing ${this.subscriptions.size} subscriptions`);\n\n const eventTypes = (__webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\").eventTypes);\n const reliableEvents = [];\n const optionalEvents = [];\n for (let [label, oldToken] of subs) {\n if (oldToken.options)\n {\n // schedmsg special case\n await this.provisionSubscriptionToken(label, oldToken.options);\n }\n else if (eventTypes[label] && eventTypes[label].reliable)\n {\n reliableEvents.push(label);\n }\n else\n {\n optionalEvents.push(label);\n }\n }\n try\n {\n if (reliableEvents.length || optionalEvents.length) {\n const message = {\n reliableEvents,\n optionalEvents,\n options: this.options\n }\n\n await this.provisionSubscriptionTokens(message);\n }\n \n }\n catch (error)\n {\n debugging() && console.warn(`event-subscriber: error establishing subscription (${sub.label}):`, error);\n }\n }\n\n setupEventRouterConnectionEvents() {\n debugging() && console.log(`event-subscriber: setupEventRouterConnectionEvents`)\n this.eventRouterConnection.on('request', (req) => {\n const { operation } = req.payload;\n if (operation === 'event') this.onEventRequest(req);\n else req.respond(new Error(`Unknown EventSubscriber operation: ${operation}`));\n });\n }\n\n async onEventRequest(req) {\n debugging('verbose') && console.log(`event-subscriber: onEventRequest`);\n // data = req.payload.data.event\n const { token, event: data } = req.payload.data;\n const event = data.data;\n const eventId = data.eventId;\n\n debugging() && console.debug(`131: event ${eventId} (${event.eventName}):`, req.payload.data);\n if (this.subscriptions.has(event.eventName))\n {\n const { token: eventToken } = this.subscriptions.get(event.eventName);\n debugging('verbose') && console.log(`event-subscriber: event: ${event.eventName}`);\n if (eventToken !== token)\n req.respond(new Error(`No subscription registered for token ${token}`));\n else if (eventId && this.seenEventIds.includes(eventId))\n req.respond(new Error(`This event has already been sent: ${eventId}`));\n else\n {\n const label = event.eventName;\n // on each request, we push the eventIds to keep track of events we have seen.\n // this will be filtered in the re-established events.\n if (eventId)\n {\n this.seenEventIds[this.nextSeenEventIdSlot] = eventId;\n this.nextSeenEventIdSlot = (this.nextSeenEventIdSlot + 1) % Number(this.options.seenListSize);\n }\n \n delete event.eventName;\n if (!this.eventEmitter.eventIntercepts)\n this.eventEmitter.emit(label, event);\n else\n {\n if (this.eventEmitter.eventIntercepts[label])\n this.eventEmitter.eventIntercepts[label](event)\n else\n this.eventEmitter.emit(label, event);\n }\n req.respond();\n }\n }\n else\n {\n console.warn(`No subscription registered for label ${event.eventName}`);\n req.respond(new Error(`No subscription registered for token ${event.eventName}`));\n }\n }\n\n /**\n * Registers this subscription with the event router, and saves the returned\n * subscription token into the this.subscriptions map.\n * @param {EventLabel} label\n * @param {SubscriptionOptions} options\n */\n async provisionSubscriptionToken(label, options) {\n debugging('verbose') && console.log(`event-subscriber: provisionSubscriptionToken(${label})`);\n\n if (this.eventRouterConnection.state.in(['closed','closing','close-wait']))\n {\n this.openEventRouterConn();\n this.setupEventRouterConnectionEvents();\n }\n let res = await this.eventRouterConnection.send('subscribe', {\n label, options,\n }).catch(async (error) => {\n debugging('verbose') && console.error(`Failed to send subscription request to scheduler for the event ${label}: ${error}. Will try again.`);\n this.eventRouterConnection.close();\n });\n \n if (res) {\n const token = res.payload.token;\n this.subscriptions.set(label, { token, options });\n }\n\n }\n\n async provisionSubscriptionTokens(message) {\n debugging('verbose') && console.log(`event-subscriber: provisionSubscriptionTokens(${JSON.stringify(message)})`);\n let res = await this.eventRouterConnection.send('subscribeMany', message).catch(async (e) => {\n debugging('verbose') && console.error(`Failed to send subscribeMany request to scheduler: ${e}. Will try again`);\n this.eventRouterConnection.close();\n });\n \n if (res && res.success) {\n const tokenEventPairs = res.payload.tokens;\n tokenEventPairs.forEach((tokenAndEvent) => {\n const label = tokenAndEvent.event;\n const token = tokenAndEvent.token;\n this.subscriptions.set(label, { token });\n })\n } else if (res && !res.success) {\n throw new Error(`Failed to subscribe to events, ${res.payload}`);\n }\n \n }\n\n /**\n * Unregisters a subscription from the event router\n * @param {EventLabel} label\n * @param {SubscriptionToken} token \n */\n async releaseSubscriptionToken(label, token) {\n debugging('verbose') && console.log(`event-subscriber: releaseSubscriptionToken(${label})`);\n await this.eventRouterConnection.send('unsubscribe', {\n label, token,\n }).catch(async (e) => {\n debugging('verbose') && console.error(`Failed to unsubscribe from event ${label}: ${e}. Will try again.`)\n this.eventRouterConnection.close();\n });\n }\n\n /**\n * @param {EventLabel} label - Event label\n * @param {function} callback\n * @param {SubscriptionOptions} options - Additional options\n */\n async subscribeManyEvents(reliableEvents, optionalEvents, options={}) {\n this.options = options;\n const noSubReliableEvents = [];\n reliableEvents.forEach((ev) => {\n for (let [t, sub] of this.subscriptions)\n {\n // search for a subscription with identical label,\n // to avoid making duplicate subscriptions\n if (ev === sub.label)\n {\n return;\n }\n }\n if (!noSubReliableEvents.includes(ev)) {\n noSubReliableEvents.push(ev);\n }\n })\n\n const noSubOptionalEvents = [];\n optionalEvents.forEach((ev) => {\n for (let [t, sub] of this.subscriptions)\n {\n // search for a subscription with identical label,\n // to avoid making duplicate subscriptions\n if (ev === sub.label)\n {\n return;\n }\n }\n if (!noSubOptionalEvents.includes(ev)) {\n noSubOptionalEvents.push(ev);\n }\n })\n\n if (reliableEvents.length || optionalEvents.length)\n {\n const message = {\n reliableEvents: noSubReliableEvents,\n optionalEvents: noSubOptionalEvents,\n options,\n }\n debugging() && console.log('event-subscriber: provisioning:', message);\n await this.provisionSubscriptionTokens(message);\n }\n \n // Start the keepalive interval\n this.setKeepalive();\n }\n \n async subscribe(label, options={}) {\n debugging() && console.log(`event-subscriber: subscribe(${label})`);\n let token;\n for (let [subLabel, t] of this.subscriptions) {\n // search for a subscription with identical label and filter,\n // to avoid making duplicate subscriptions\n if (label === subLabel) {\n token = t;\n break;\n }\n }\n\n if (!token) {\n await this.provisionSubscriptionToken(label, options);\n }\n\n // Start the keepalive interval\n this.setKeepalive();\n }\n\n /**\n * @param {string} label\n */\n async unsubscribe(label) {\n debugging() && console.log(`event-subscriber: unsubscribe)${label})`);\n if (!this.subscriptions.has(label)) {\n return;\n }\n \n const token = this.subscriptions.get(label)\n await this.releaseSubscriptionToken(label, token);\n this.subscriptions.delete(label);\n\n // If no subscriptions remain, disable the keepalive and allow the Connection\n // to expire\n if (this.subscriptions.size === 0)\n this.setKeepalive(false);\n }\n\n /**\n * De/activate an interval to send keepalives over the event-router connection\n *\n * @param {Boolean} keepalive If true, activate the interval. If false, remove it\n */\n setKeepalive(activate = true)\n {\n const that = this;\n const keepaliveInterval = Number(this.options.keepaliveInterval) || 3 * 60; /* seconds */\n\n if (!activate /* => deactivate */)\n {\n /* clear the watchdog interval, and neuter already-queued doKeepalive()s */\n if (this._keepaliveIntervalHnd)\n clearInterval(this._keepaliveIntervalHnd);\n this._keepaliveIntervalHnd = null;\n return;\n }\n\n if (activate && !keepaliveInterval)\n {\n debugging() && console.debug('event-subscriber: configured for no keepalive interval');\n return;\n }\n\n if (!this._keepaliveIntervalHnd)\n this._keepaliveIntervalHnd = setInterval(doKeepalive, keepaliveInterval * 1000)\n \n if (dcpEnv.platform === 'nodejs')\n this._keepaliveIntervalHnd.unref();\n\n async function doKeepalive()\n {\n if (that._keepaliveIntervalHnd)\n {\n try\n {\n that.eventRouterConnection.keepalive();\n }\n catch(error)\n {\n /* ignore errors if the watchdog was cancelled while were waiting for the\n * keepalive to complete; this implies the connection was purposefully closed.\n */\n if (!that._keepaliveIntervalHnd)\n return;\n\n debugging() && console.debug('Event subscriber watchdog detected connection in error state', error.code || error.message);\n debugging('verbose') && console.debug(error);\n\n that.eventRouterConnection.close(); /* trigger ER connection recycle */\n }\n }\n }\n }\n}\n\nexports.EventSubscriber = EventSubscriber;\n\n\n//# sourceURL=webpack://dcp/./src/events/event-subscriber.js?");
4613
4659
 
4614
4660
  /***/ }),
4615
4661
 
@@ -4869,7 +4915,7 @@ eval("/**\n * @file listener.js\n * Generic API for transpor
4869
4915
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
4870
4916
 
4871
4917
  "use strict";
4872
- eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_modules/process/browser.js */ \"./node_modules/process/browser.js\");\n/**\n * @file transport/socketio.js\n * @author Ryan Rossiter, ryan@kingsds.network\n * @author Wes Garland, wes@kingsds.network \n * @date January 2020, March 2022\n *\n * This module implements the SocketIO Transport that is\n * used by the protocol to connect and communicate with peers using\n * SocketIO connections.\n *\n * Transport can operate in either ack mode or non-ack-mode, depending on the value of this.ackMode.\n * Ack mode sends an extra socketio-layer packet back after each message is received, and theoretically\n * this can be use tod meter or measure bandwidth or potentially second-guess socketio's ability to\n * interleave its heartbeat messages OOB from the main traffic. This mode could potentially be enabled\n * on-demand as well, although it appears that it is not necessary to get the desired behaviour at this\n * point, so the feature is off-by-default (although reasonably well-tested).\n */\n\n\nconst { Transport } = __webpack_require__(/*! . */ \"./src/protocol-v4/transport/index.js\");\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { leafMerge } = __webpack_require__(/*! dcp/utils/obj-merge */ \"./src/utils/obj-merge.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst { setImmediateN } = __webpack_require__(/*! dcp/common/dcp-timers */ \"./src/common/dcp-timers.js\");\nconst { setImmediate } = __webpack_require__(/*! dcp/common/dcp-timers */ \"./src/common/dcp-timers.js\");\nconst { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\n\n// Note: trying to `require('process')` in the browser prevents the portal\n// from loading. ~ER 20220803\n// const process = requireNative ? requireNative('process') : require('process');\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('dcp');\nconst dcpEnv = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst bearer = __webpack_require__(/*! ./http-bearer */ \"./src/protocol-v4/transport/http-bearer.js\");\nconst semver = __webpack_require__(/*! semver */ \"./node_modules/semver/semver.js\");\n\nvar listenerSeq = 0;\nvar initiatorSeq = 0;\n\nif (debugging() && !process.env.DEBUG && dcpConfig.build === 'debug')\n{\n\n if (process.env.DEBUG)\n process.env.DEBUG += ',engine';\n else\n process.env.DEBUG = 'engine';\n\n if (debugging('socketio'))\n {\n /* DCP_DEBUG=dcp:socketio,dcp:verbose to see everything */\n process.env.DEBUG += ',socket.io-client:socket,socket.io:client,socket.io:server';\n if (debugging('verbose') && !debugging('all'))\n process.env.DEBUG += ',socket.io*,engine.io*'\n }\n}\n\nclass SocketIOTransport extends Transport\n{\n /**\n * @constructor\n * @param {socket} newSocket Target: an instance of socket.io Socket that represents\n * a new connection.\n * Initiator: unused\n */\n /**\n * @constructor create new instance of a socketio-flavoured Transport.\n *\n * @param {object} config dcpConfig fragment\n * @param {Url} url Initiator: has .location and optionally .friendLocation\n * Target: has .listen and .location\n *\n * @param {object} options The `socketio` property of a protocol-v4 connectionOptions \n * object, which was built from a combination of defaults and \n * various dcpConfig components.\n *\n * @returns instance suitable for target or initiator, depending on mode, but does not manage connections.\n */\n constructor(config, options={}, newSocket)\n {\n super('Protocol SocketIO Transport');\n const that = this;\n \n assert(DcpURL.isURL(config.location));\n assert(typeof options === 'object');\n \n this.name = 'socketio';\n this.url = config.location;\n this.msgSequence = 0;\n this.isClosed = false; /* true => connection finalization begun */\n this.isFinal = false; /* true => connection finalization completed */\n this.hasConnected = false; /* true => one 'connect' event has been processed */\n this.initTimeout = false; /* timeout when connecting to allow target to reply with their mode message + buffer size, crash if they don't reply */\n this.modeSet = false; /* true => received response mode message */\n this.bufferSizeSet = false; /* true => received target's buffer size */\n this.rxFragments = {}; /* messages we are receiving in fragments */\n this.rxPending = []; /* strings which are messages that are complete and ready to emit, or objects that in this.rxFragments */\n this.txPending = []; /* messages we are receiving in fragments */\n this.txNumInFlight = 0; /* number of txPending sent but not acknowleged */\n this.txMaxInFlight = 2; /* less than 2 causes bad perf when mode.ack */\n this.debugInfo = { remoteLabel: '<unknown>' };\n this.options = this.buildOptions(options);\n this.mode = {\n ack: this.options.enableModeAck ? true : false, /* Only turn on ack mode if specified */\n concat: this.options.enableModeConcat === false ? false : true, /* Only turn off concat mode if specified*/\n };\n\n\n if (!newSocket) /* Initiator */\n {\n this.debugLabel = `socketio(i:${++initiatorSeq}):`;\n this.rxReady = true; /* relax, said the night man */\n this.debugInfo.remoteLabel = config.location.href;\n if (this.options.disableModeAnnouncement !== false)\n this.sendModeList();\n this.sendMaxMessageInfo();\n\n bearer.a$isFriendlyUrl(config.friendLocation).then((useFriendLocation) => {\n const connectUrl = useFriendLocation ? config.friendLocation : config.location;\n\n debugging('socketio') && console.log(this.debugLabel, 'connecting to', connectUrl.href);\n this.socket = (__webpack_require__(/*! socket.io-client */ \"./node_modules/socket.io-client/build/cjs/index.js\").connect)(connectUrl.origin, this.options);\n this.socket.on('connect_error', (error) => !this.isClosed && this.handleConnectErrorEvent(error));\n this.socket.on('connect', () => !this.isClosed && this.handleConnectEvent());\n\n useSocket();\n }).catch((error) => {\n debugging() && console.error(this.debugLabel, error);\n this.emit('error', error);\n });\n }\n else /* Target - receives socketio instance from SocketIOListener.handleConnectionEvent */\n {\n if (dcpEnv.platform !== 'nodejs')\n throw new Error('socketio: target mode only supported in nodejs');\n\n this.debugLabel = 'socketio(t:<new>):';\n\n this.socket = newSocket;\n this.debugInfo.remoteLabel = this.socket.handshake.address.replace(/^::ffff:/,'');\n debugging('socketio') && console.debug(this.debugLabel, `new socket from ${this.debugInfo.remoteLabel}`\n + ` on ${this.socket.handshake && this.socket.handshake.url}, ${this.socket.id}`);\n\n useSocket();\n }\n\n function useSocket()\n {\n that.socket.compress(dcpConfig.build !== 'debug'); /* try to keep traffic sniffable in debug */\n that.socket.on('message', (msg) => !that.isFinal && that.handleMessageEvent(msg));\n that.socket.on('disconnect', (reason) => !that.isClosed && that.handleDisconnectEvent(reason));\n }\n }\n\n /**\n * API - determine if a transport is usable or not.\n * @returns true if the transport is not closed.\n */\n ready()\n {\n return !this.isClosed;\n }\n \n /**\n * Handle the socket.io disconnect event, which is fired upon disconnection.\n * For some reasons (explicit disconnection), the socket.io code will not try to reconnect, but\n * in all other cases, the socket.io client will normally wait for a small random delay and then \n * try to reconnect, but in our case, we simply tear the transport down and let the Connection\n * class handle reconnects, since it might prefer a different transport at this point anyhow.\n *\n * One tricky case here is ping timeout, since the ping timeout includes the time to transmit the\n * packet before it was sent.\n *\n * @param {string} reason Possible reasons for disconnection.\n */\n handleDisconnectEvent(reason)\n {\n debugging('socketio') && console.debug(this.debugLabel, `disconnected from ${this.debugInfo.remoteLabel}; reason=${reason}`);\n\n if (this.isClosed !== true)\n setImmediate(() => this.#close(reason));\n\n switch(reason)\n {\n case 'io client disconnect':\n return; /* we called socket.disconnect() from another \"thread\" */\n case 'io server disconnect':\n case 'ping timeout':\n case 'transport close':\n case 'transport error':\n this.emit('end', reason);\n /* fallthrough */\n default:\n }\n }\n\n /**\n * Handle a socketio message from the peer.\n *\n * Most of the time, a socketio message contains a DCP message, however we support other message\n * types as well, with a mechanism inspired by 3GPP TS 23.040 (GSM 03.40) message concatenation.\n */\n handleMessageEvent(msg)\n {\n if (typeof msg !== 'string')\n {\n debugging('socketio') && console.debug(this.debugLabel, `received ${typeof msg} message from peer`, this.debugInfo.remoteLabel);\n return;\n }\n\n try\n {\n switch (msg[0])\n {\n case '{': /* all DCP Messages are JSON */\n this.processMessage(msg);\n break;\n case 'E':\n this.processExtendedMessage(msg);\n break;\n case 'A':\n this.processAcknowledgement(msg);\n break;\n case 'T':\n debugging('socketio') && console.debug(this.debugLabel, 'Received debug message:', msg);\n break;\n default:\n /* adhoc messages not supported in this transport */\n throw new DCPError(this.debugLabel, `Unrecognized message type indicator from ${this.debugInfo.remoteLabel} (${msg.charCodeAt(0)})`, 'DCPC-1102');\n }\n }\n catch(error)\n {\n debugging('socketio') && console.debug(this.debugLabel, 'handleMessageEvent: transport error, closing\\n ', error);\n this.emit('error', error);\n setImmediate(() => this.#close(error.message));\n }\n }\n\n /**\n * Process an ordinary message. This is just a hunk of JSON that gets passed up to the connection.\n * @param {string} msg\n */\n processMessage(msg)\n {\n this.rxPending.push(msg);\n this.emitReadyMessages();\n if (this.mode.ack)\n this.socket.send('A0000-ACK'); /* Ack w/o header = normal message */\n }\n\n /**\n * Remote has acknowledged the receipt of a message fragment. When in ack mode, this\n * triggers us to send more messages from the txPending queue. We always ack event when \n * not in ack-mode. See processExtendedMessage() comment for header description.\n *\n * Future: this is where maxInFlight might be tuned, based on how fast we get here\n *\n * @param {string} _msg\n */\n processAcknowledgement(_msg)\n {\n if (this.txNumInFlight > 0)\n this.txNumInFlight--;\n else\n {\n if (this.options.strict || !this.mode.ack)\n throw new DCPError(this.debugLabel, `Synchronization error: received unexpected acknowledgement message from ${this.debugInfo.remoteLabel}`, 'DCPC-1109');\n else\n this.mode.ack = true;\n }\n \n this.drainTxPending_soon();\n }\n\n /**\n * Extended messages have the following format:\n * Octet(s) Description\n * 0 E (69) or A (65)\n * 1..4 Header size (N) in hexadecimal\n * 5 Header Type\n * C (67) => concatenation\n * 6..6 + N: Header\n *\n * Upon receipt, extended messages are acknowledged by retransmitting their header, but\n * with the first character changed from E to A.\n *\n * @param {string} msg\n */\n processExtendedMessage(msg)\n {\n const headerSize = parseInt(msg.slice(1,5), 16);\n const headerType = msg[5];\n const header = msg.slice(6, 6 + headerSize);\n\n switch(headerType)\n {\n case 'M':\n this.processExtendedMessage_mode(header, msg.slice(6 + headerSize));\n break;\n case 'B':\n this.processExtendedMessage_maxBufferSize(msg.slice(6 + headerSize));\n break;\n case 'C':\n this.processExtendedMessage_concatenated(header, msg.slice(6 + headerSize));\n break;\n default:\n if (this.options.strict)\n throw new DCPError(`Unrecognized extended message header type indicator (${msg.charCodeAt(5)})`, 'DCPC-1101');\n }\n\n if (this.mode.ack)\n this.socket.send('A' + msg.slice(1, 6 + headerSize));\n }\n\n /**\n * We have received a fragment of a concatenation extended message. Memoize the fragment, and\n * if this is the last fragment, transform the fragments list into a pending message string and\n * then emits all ready messages (pending message strings).\n *\n * This code is capable of handling messages whose fragments arrive out of order. Complete messages \n * are emitted in the order that their first parts are received. Socketio is supposed to be total\n * order-aware but I wanted a safety belt for intermingled messages without occupying the entire\n * socketio network buffer on the first pass of the event loop. Because the first message fragment\n * is emitted to socketio on the same event loop pass as their Connection::send calls, I believe\n * that are no cases where messages will be emitted out of order at the other end, even when \n * intermingled on the wire, and the Nth message is much longer than any (N+k)th messages.\n *\n * @param {object} header the extended message header\n * @param {string} payload the payload of the extended message, i.e. a message fragment\n */\n processExtendedMessage_concatenated(header, payload)\n {\n try\n {\n header = JSON.parse(header);\n }\n catch(e)\n {\n throw new DCPError(`invalid extended message header '${header}'`, 'DCPC-1103');\n }\n\n if (!this.options.strict && !this.mode.concat)\n this.mode.concat = true;\n \n if (!(header.total <= this.options.maxFragmentCount))\n throw new DCPError('excessive message fragmentation', 'DCPC-1107'); /* make it more difficult for attacker to force OOM us */\n\n if (!(header.seq < header.total))\n throw new DCPError(`corrupt header for part ${header.seq + 1} of ${header.total} of message ${header.msgId}`, 'DCPC-1108');\n \n /* First fragment received, initial data structures and memoize the message's arrival order via pending list */\n if (!this.rxFragments[header.msgId])\n {\n const fragments = [];\n this.rxFragments[header.msgId] = fragments;\n this.rxPending.push(fragments);\n }\n\n this.rxFragments[header.msgId][header.seq] = payload;\n debugging('socketio') && console.debug(this.debugLabel, 'received part', header.seq + 1, 'of', header.total, 'for message', header.msgId);\n\n if (header.total !== this.rxFragments[header.msgId].filter(el => el !== undefined).length)\n return;\n\n debugging('socketio') && console.debug(this.debugLabel, 'received all parts of message', header.msgId);\n \n const idx = this.rxPending.indexOf(this.rxFragments[header.msgId])\n this.rxPending[idx] = this.rxFragments[header.msgId].join('');\n delete this.rxFragments[header.msgId];\n this.emitReadyMessages();\n }\n\n processExtendedMessage_mode(_header, payload)\n {\n var modeList;\n \n try\n {\n modeList = JSON.parse(payload);\n }\n catch(e)\n {\n throw new DCPError(`invalid mode message payload '${payload}'`, 'DCPC-1105');\n }\n\n for (let mode of modeList)\n {\n debugging('socketio') && console.debug(this.debugLabel, ` - remote supports ${mode} mode`); \n switch(mode)\n {\n case 'ack':\n this.mode.ack = true;\n break;\n case 'concat':\n this.mode.concat = true;\n break;\n default:\n debugging('socketio') && console.debug(this.debugLabel, ' - remote requested unrecognized mode:', mode);\n break;\n }\n }\n\n // true if we are connecting a new transport, expecting the initial mode reply, and have received the target's buffer size\n if (this.initTimeout && this.bufferSizeSet)\n {\n clearTimeout(this.initTimeout);\n delete this.initTimeout;\n this.emit('connect');\n }\n this.modeSet = true;\n this.sendModeList();\n }\n \n processExtendedMessage_maxBufferSize(payload)\n {\n var receivedOptions = JSON.parse(payload); \n \n this.options.txMaxFragmentSize = Math.floor(receivedOptions.maxFragmentSize / 11); /* XXX EMERGENCY BUGFIX - Aug 2022 /wg */\n this.options.txMaxFragmentCount = receivedOptions.maxFragmentCount;\n\n debugging() && console.debug(this.debugLabel, 'Set max transmit fragment size to', this.options.txMaxFragmentSize);\n\n if (receivedOptions.txMaxInFlight < this.txMaxInFlight)\n this.txMaxInFlight = receivedOptions.txMaxInFlight;\n if (this.options.maxFragmentSize < 0)\n throw new DCPError(`maxFragmentSize was set to < 1, (${this.options.txMaxFragmentSize}). Server requested bad fragment size, cannot connect.`, 'DCPC-1111');\n if (this.txMaxInFlight < 1)\n throw new DCPError(`txMaxInFlight was set to < 1, (${this.txMaxInFlight}). Cannot send messages with fewer than 1 in flight.`, 'DCPC-1111');\n \n // true if we are connecting a new transport, expecting the initial buffer reply, and have received the target's mode designation\n if (this.initTimeout && this.modeSet)\n {\n clearTimeout(this.initTimeout);\n delete this.initTimeout;\n this.emit('connect');\n }\n this.bufferSizeSet = true;\n\n this.sendMaxMessageInfo(); \n }\n\n /* Internal method to tell peer what we can receive */\n sendModeList()\n {\n var rawMessage;\n var modeList = [];\n\n if (this.sentModeList)\n return;\n\n this.sentModeList = true;\n this.mode.ack && modeList.push('ack');\n this.mode.concat && modeList.push('concat');\n rawMessage = 'E0000M' + JSON.stringify(modeList);\n\n debugging('socketio') && console.debug(`${this.debugLabel} sending mode list to ${this.debugInfo.remoteLabel}`)\n if (this.socket)\n this.socket.send(rawMessage);\n else\n this.txPending.unshift({ message: rawMessage, msgSequence: ++this.msgSequence});\n }\n \n /* Internal method to exchange desired fragment size/max inflight messages/max fragments per message */\n sendMaxMessageInfo()\n {\n if (this.sentMessageInfo)\n return;\n\n /* Calculate the optimal maxFragmentSize based on maxHttpBufferSize.\n * Currently unsure of exactly what happens when socketio serializes our serialized\n * JSON. I suspect we may double-encode UTF-8 > 0x80. The largest UTF-8 character is\n * 5 bytes...so we ensure the maxFragmentSize is (maxHttpBufferSize / 5) - (fragmentOverhead * 5)\n * to allow for worst-case scenario\n */\n const fragmentOverhead = 1000 + 64 * 1024; /* derived from concatenation algorithm */\n\n let desiredMaxFragmentSize;\n if (this.options.maxHttpBufferSize)\n desiredMaxFragmentSize = (this.options.maxHttpBufferSize / 5) - (fragmentOverhead * 5);\n else\n desiredMaxFragmentSize = this.options.maxFragmentSize;\n const options = {\n maxFragmentSize: desiredMaxFragmentSize,\n maxFragments: this.options.maxFragmentCount,\n txMaxInFlight: this.txMaxInFlight,\n };\n\n const message = 'E0000B' + JSON.stringify(options);\n\n debugging('socketio') && console.debug(`${this.debugLabel} sending max http buffer size to ${this.debugInfo.remoteLabel}`);\n if (this.socket)\n this.socket.send(message);\n else\n this.txPending.push({ message: message, msgSequence: ++this.msgSequence });\n \n this.sentMessageInfo = true;\n }\n\n /**\n * Emit message events so that the connection can process them. The pending property is an\n * array which is used to order messages, so that they are emitted in the order they were\n * sent. There are edge cases in the concatenation code where a sender could theoretically\n * interleave multiple messages, and have a second, shorter, message fully received before \n * the first is fully transmitted.\n *\n * The pending property stores only strings or objects; objects are effectively positional\n * memoes which are turned into strings when they are ready to be emitted to the connection\n * for processing.\n */\n emitReadyMessages()\n {\n while (this.rxReady && this.rxPending.length && typeof this.rxPending[0] === 'string' && this.listenerCount('message') > 0)\n this.emit('message', this.rxPending.shift());\n }\n \n /** \n * Close the current instance of Socket.\n *\n * This function effectively finalizes the socket so that it can't be used any more and\n * (hopefully) does not entrain garbage. A 'close' event is emitted at the end of socket\n * finalization and should be hooked by things that might entrain this transport instance to free\n * up memory, like maybe a list of active transports, etc.\n *\n * We add few extra hops on/off the event loop here to try and clean up any messages halfway in/out\n * the door that we might want to process. That happens between the setting of the isClosed and \n * isFinal flags.\n *\n * @param {string} reason\n */\n #close(reason)\n {\n const that = this;\n debugging('socketio') && console.debug(this.debugLabel, 'closing connection' + (reason ? ` (${reason})` : ''));\n\n function finalize(socket)\n {\n if (that.isFinal)\n return;\n\n if (socket)\n socket.removeAllListeners();\n that.isFinal = true;\n\n /* Free up memory that might be huge in case something entrains us */\n that.rxPending.length = 0;\n that.txPending.length = 0;\n for (let msgId in that.rxFragments)\n that.rxFragments[msgId].length = 0;\n }\n\n function closeSocket()\n {\n const socket = that.socket;\n \n try\n {\n if (socket)\n socket.disconnect();\n that.isClosed = true;\n that.emit('close', reason);\n delete that.socket;\n }\n finally\n {\n finalize(socket);\n }\n }\n\n if (!this.isClosed)\n {\n /* If we called close, drain any pending messages and then disconnect */\n if (reason === 'api-call')\n {\n this.drainTxPending();\n setImmediate(()=>closeSocket());\n }\n else\n closeSocket();\n }\n }\n\n /* API - close the transport, free up memory. */\n close()\n {\n assert(arguments.length === 0)\n this.#close('api-call');\n }\n\n /**\n * Handle the socketio connect_error event, which is fired when:\n * - the low-level connection cannot be established\n * - the connection is denied by the server in a middleware function\n *\n * In the first case, socket.io will automatically try to reconnect, after a delay,\n * except that will cancel that reconnection attempt here by killing the transport\n * instance so that the Connection class can try failing over to an alternate transport.\n *\n * @param {Error} error optional instance of error describing the underlying reason for the\n * connect failure. If the underlying reason is expressible by an\n * HTTP status code, this code will be placed as a Number in the\n * description property.\n */\n handleConnectErrorEvent(error)\n {\n debugging('socketio') && console.debug(this.debugLabel, `unable to connect to ${this.url}`, error);\n if (error.type === 'TransportError' && typeof error.description === 'number')\n error.httpStatus = Number(error.description);\n\n this.emit('connect-failed', error);\n this.#close(error.message);\n }\n\n /** \n * Handle the socketio connect event, which is fired by the Socket instance upon initiator\n * connection and reconnection to a target.\n */\n handleConnectEvent()\n {\n if (this.hasConnected === true)\n {\n /* this transport is not supposed to reuse connections, and we call .close() on disconnect \n * to prevent this. However, the API does not guarantee that will be race-free.\n */\n debugging('socketio') && console.debug(this.debugLabel, `*** reconnected to ${this.debugInfo.remoteLabel} (ignoring)`);\n return;\n }\n\n /* initial connection */\n this.hasConnected = true;\n debugging('socketio') && console.debug(this.debugLabel, 'connected to', this.debugInfo.remoteLabel);\n\n this.initTimeout = setTimeout(() => this.#close('no mode message received'), this.options.establishTimeout) /* give target 10 second to reply with capabilities before assuming bad connection and crashing */\n if (dcpEnv.platform === 'nodejs')\n this.initTimeout.unref();\n \n this.drainTxPending();\n }\n\n /**\n * API: Send a message to the transport instance on the other side of this connection.\n *\n * @param {string} message the message to send\n */\n send(message)\n {\n const msgSequence = ++this.msgSequence;\n\n debugging('socketio') && console.debug(this.debugLabel, `sending message ${msgSequence} to`, this.debugInfo.remoteLabel);\n debugging('socketio') && debugging('verbose') && !debugging('all') && console.debug(this.debugLabel, message);\n\n if (!this.socket)\n throw new DCPError('SocketIOTransport.send: Not connected', 'DCPC-1110');\n \n if ( false\n || !(message.length > this.options.txMaxFragmentSize)\n || !this.mode.concat)\n this.txPending.push({ message, msgSequence });\n else\n { /* This is a large message. Use message concatenation to send in fragments. A failure of any\n * fragment is a failure of the entire message and will be handled at the connection layer.\n */\n let total = Math.ceil(message.length / this.options.txMaxFragmentSize);\n \n if (total > this.options.txMaxFragmentCount)\n throw new DCPError('extended message has more fragments than the peer will allow', 'DCP-1112');\n \n for (let seq=0; seq < total; seq++)\n {\n let start = seq * this.options.txMaxFragmentSize;\n let fragment = message.slice(start, start + this.options.txMaxFragmentSize);\n let header = JSON.stringify({ msgId: msgSequence, seq, total });\n let extMessage = 'E' + header.length.toString(16).padStart(4, '0') + 'C' + header + fragment;\n\n this.txPending.push({ message: extMessage, msgSequence, seq, total });\n\n /* This should be impossible, but it also effectively kills the connection. */\n if (header.length > 0xFFFF)\n throw new DCPError('extended message header too long', 'DCPC-1104');\n }\n }\n\n this.drainTxPending_soon();\n }\n\n drainTxPending_soon()\n {\n if (this.drainingSoon)\n return;\n this.drainingSoon = true;\n\n setImmediate(() => this.drainTxPending());\n }\n \n /**\n * Drain messages from the txPending queue out to socketio's network buffer. We throw away traffic\n * when it can't be sent; this is no different from packets already on the wire when the other end\n * has changed IP number, etc.\n *\n * In ack mode, this drain is controlled by acknowledgement messages so that socketio can have the\n * opportunity to insert ping/pong messages in the data stream while sending a message large enough\n * to need to be sent via concatenation. [ NOTE - it appears to send ping/pong OOB in version 4.4, \n * so currently ack mode is off by default / wg Mar 2022 ]\n *\n * The reason we put non-concat messages through here is to avoid special casing a bunch of nasty\n * things for the sake of a little tiny bit of performance. Sending message<-ACK and queueing through\n * the drain code is necessary if we wind up interleaving multiple message sends in some interesting\n * edge cases; notably, fast-teardown, but a future Connection class that ran without nonces (or multiple\n * nonces) would need this, too.\n *\n * In non-ack mode, the drain is mostly uncontrolled, but we throw each message on a separate pass of\n * the event loop, to try and keep huge message blasts one connection from overwhelming a busy server,\n * as well allowing socketio the chance to send its heartbeat messages and make it less likely that it\n * will hit its http buffer size limit, because that causes an immediate disconnect.\n *\n * Non-ack-mode is compatible with previous dcp5 versions and probably a little faster.\n */\n drainTxPending()\n {\n const that = this;\n\n if (this.txPending.length)\n debugging('socketio') && console.debug(this.debugLabel, `drain tx pending queue, ${this.txPending.length} ready`\n + (this.mode.ack ? `; ${this.txMaxInFlight - this.txNumInFlight} slots available` : ''));\n delete this.drainingSoon;\n\n if (this.isClosed)\n return;\n \n for (let i=0;\n !this.isClosed && this.txPending.length && (this.txNumInFlight < this.txMaxInFlight || !this.mode.ack);\n i++)\n {\n const pendingElement = this.txPending.shift();\n\n if (i === 0)\n writeToSocket(pendingElement);\n else\n setImmediateN(() => writeToSocket(pendingElement), i);\n \n if (this.mode.ack)\n this.txNumInFlight++;\n\n if (this.txPending.length === 0) /* just sent the last message */\n this.emit('drain');\n }\n\n if (this.txPending.length)\n this.drainTxPending_soon();\n \n function writeToSocket(pendingElement)\n {\n if (that.isClosed)\n return;\n\n const { message, msgSequence, seq, total } = pendingElement;\n that.socket.send(message);\n\n if (typeof seq !== 'undefined')\n debugging('socketio') && console.debug(that.debugLabel, `sent fragment ${seq + 1}/${total} of message ${msgSequence} (${message.length} bytes)`);\n else\n debugging('socketio') && console.debug(that.debugLabel, `sent message ${msgSequence} (${message.length} bytes)`);\n }\n }\n}\nSocketIOTransport.prototype.buildOptions = buildOptions;\n\n/** \n * Build the socketio options object, based on the passed-in options, dcp debug state, and internal\n * object state. Method shared between SocketIOTransport and SocketIOListener.\n */\nfunction buildOptions(options)\n{\n options = leafMerge(\n /* Baked-in Defaults */\n ({\n autoUnref: dcpEnv.platform === 'nodejs' ? true : undefined, /* socket.io + webpack 5 { node:global } work-around */\n perMessageDeflate: dcpConfig.build !== 'debug',\n maxFragmentCount: 1000, /* used to limit memory on receiver */\n maxFragmentSize: 1e6, /* bytes */\n txMaxFragmentCount:1000, /* peer's limit for fragments */\n txMaxFragmentSize: 1e6, /* peer's limit for fragment size */\n maxHttpBufferSize: false, /* bytes; false = auto */\n establishTimeout: 10 * 1000, /* 10 seconds */\n pingTimeout: 30 * 1e3, /* s */\n pingInterval: 90 * 1e3, /* s */\n transports: ['polling', 'websocket'],\n upgrade: true,\n rememberUpgrade: true,\n autoConnect: true,\n cors: {\n origin: '*',\n methods: ['GET', 'POST']\n },\n }),\n options,\n /* Absolutely mandatory */\n ({\n path: this.url.pathname // eslint-disable-line no-invalid-this\n }));\n\n /* draw out errors quickly in dev */\n if ((process.env.DCP_NETWORK_CONFIG_BUILD || dcpConfig.build) === 'debug')\n {\n /* short timeouts and debuggers don't get along well */\n if (dcpEnv.platform === 'nodejs' && !(requireNative('module')._cache.niim instanceof requireNative('module').Module))\n {\n options.pingTimeout /= 5;\n options.pingInterval /= 5;\n }\n }\n\n return options;\n}\n \n/**\n * @constructor\n * API - Factory which creates a new socketio listener, or throws. Emits 'listening' if the server \n * parameter is not provided and we create it, once it is ready for connections.\n * \n * @bug It is possible for a connection event to sneak in before the 'listening' event is fired, due to \n * the two-step event trigger. The right fix is to proxy the event, but dcp-events::event-emitter.proxy\n * itself currently has the same bug. For now, we just have callbacks in API. /wg apr 2022\n * \n * @param {object} config dcpConfig fragment - has .listen and .location\n * @param {object} target the instance of Target to bind to\n * @param {object} options [optional] A DCP config options variable (like dcpConfig.dcp.listenOptions)\n */\nfunction SocketIOListener(config, target, options)\n{\n var matchRegex;\n \n this.url = config.location;\n this.options = this.buildOptions(options);\n this.connSequence = 0;\n this.seq = ++listenerSeq;\n this.debugLabel = `socketio(l:${this.seq}):`;\n this.isShutdown = false;\n\n if (dcpEnv.platform !== 'nodejs')\n throw new Error('target mode only supported in nodejs');\n\n if (target.httpServer.listening)\n setImmediate(() => this.emit('listening', target.httpServer)); /* setImmediate to allow event listeners to be added before emitting */\n else\n target.httpServer.on('listening', () => this.emit('listening', target.httpServer));\n\n matchRegex = '^' + this.url.pathname;\n if (matchRegex.slice(-1) !== '/')\n matchRegex += '\\\\/';\n else\n matchRegex = matchRegex.slice(0, -1) + \"\\\\/\"\n\n matchRegex += '\\\\?EIO=[0-9]';\n debugging('socketio') && console.debug(this.debugLabel, `expecting requests to match regex ${matchRegex}`);\n\n target.all(new RegExp(matchRegex), (request) => {\n /* Socket.io does this intrinsically because it plays with the server's events directly,\n * but we \"handle\" it explicitly here so that we don't trip the 404 handler. \n */\n debugging('socketio') && console.debug(this.debugLabel, 'Handling', request.url);\n }, true);\n\n /* While we normally want to inherit options from parent to child socket via this.options,\n * we don't want this to happen for maxHttpBufferSize due to the way it is used and calculated\n * in the transport constructor. A buffer that is too small (eg false) will cause us to not\n * recognized socket.io SIDs.\n */\n \n this.options.maxHttpBufferSize = this.options.maxHttpBufferSize || 1e9;\n \n const socketOptions = this.options;\n\n this.socketServer = requireNative('socket.io')(target.httpServer, socketOptions);\n this.socketServer.on('connection', (socket) => !this.isShutdown && this.handleConnectionEvent(config, socket));\n debugging() && console.debug(this.debugLabel, 'Socketio listener initialized for path', this.options.path);\n}\nSocketIOListener.prototype = new EventEmitter('socketio(l)');\nSocketIOListener.prototype.buildOptions = buildOptions;\n\nSocketIOListener.prototype.close = function SocketIOListener$$close()\n{\n if (!this.isShutdown)\n this.shutdown();\n this.socketServer.close(() => this.emit('close'));\n}\n\n/** Stop accepting new connections */\nSocketIOListener.prototype.shutdown = function SocketIOListener$$shutdown()\n{\n this.isShutdown = true;\n}\n\n/** Remove any event loop references used by this transport instance */\nSocketIOListener.prototype.unref = function SocketIOListener$$unref()\n{\n void false;\n}\n\n/**\n * Handle the socketio connection event, which is fired upon a connection from client.\n * Used by Target->SocketIOListener to create transport instances.\n *\n * It is expected that code hooking the 'connection' event will hook the 'message' in\n * the same pass of the event loop as it is invoked; otherwise, messages may be missed.\n *\n * @param {object} socket a new connection emitted by the socket.io server\n */\nSocketIOListener.prototype.handleConnectionEvent = function SocketIOListener$$handleConnectionEvent(config, socket)\n{\n var transport = new SocketIOTransport(config, this.options, socket);\n\n this.url = config.location || config.listen;\n this.connSequence++;\n transport.debugLabel = `socketio(t:${this.seq}.${this.connSequence}):`\n\n /* Ensure we don't emit any message events until the application or Connection class\n * has had the chance to set up message handlers via the connection event.\n */\n transport.rxReady = false;\n this.on('connection', () => setImmediate(() => {\n transport.rxReady = true;\n transport.emitReadyMessages();\n }));\n\n this.emit('connection', transport);\n}\n\n/* Define API */\nexports.TransportClass = SocketIOTransport;\nexports.Listener = SocketIOListener;\n\n\n//# sourceURL=webpack://dcp/./src/protocol-v4/transport/socketio.js?");
4918
+ eval("/* provided dependency */ var process = __webpack_require__(/*! ./node_modules/process/browser.js */ \"./node_modules/process/browser.js\");\n/**\n * @file transport/socketio.js\n * @author Ryan Rossiter, ryan@kingsds.network\n * @author Wes Garland, wes@kingsds.network \n * @date January 2020, March 2022\n *\n * This module implements the SocketIO Transport that is\n * used by the protocol to connect and communicate with peers using\n * SocketIO connections.\n *\n * Transport can operate in either ack mode or non-ack-mode, depending on the value of this.ackMode.\n * Ack mode sends an extra socketio-layer packet back after each message is received, and theoretically\n * this can be use tod meter or measure bandwidth or potentially second-guess socketio's ability to\n * interleave its heartbeat messages OOB from the main traffic. This mode could potentially be enabled\n * on-demand as well, although it appears that it is not necessary to get the desired behaviour at this\n * point, so the feature is off-by-default (although reasonably well-tested).\n */\n\n\nconst { Transport } = __webpack_require__(/*! . */ \"./src/protocol-v4/transport/index.js\");\nconst { EventEmitter } = __webpack_require__(/*! dcp/common/dcp-events */ \"./src/common/dcp-events/index.js\");\nconst { leafMerge } = __webpack_require__(/*! dcp/utils/obj-merge */ \"./src/utils/obj-merge.js\");\nconst { DcpURL } = __webpack_require__(/*! dcp/common/dcp-url */ \"./src/common/dcp-url.js\");\nconst { DCPError } = __webpack_require__(/*! dcp/common/dcp-error */ \"./src/common/dcp-error.js\");\nconst { setImmediateN } = __webpack_require__(/*! dcp/common/dcp-timers */ \"./src/common/dcp-timers.js\");\nconst { setImmediate } = __webpack_require__(/*! dcp/common/dcp-timers */ \"./src/common/dcp-timers.js\");\nconst { requireNative } = __webpack_require__(/*! dcp/dcp-client/webpack-native-bridge */ \"./src/dcp-client/webpack-native-bridge.js\");\nconst { assert } = __webpack_require__(/*! dcp/common/dcp-assert */ \"./src/common/dcp-assert.js\");\n\n// Note: trying to `require('process')` in the browser prevents the portal\n// from loading. ~ER 20220803\n// const process = requireNative ? requireNative('process') : require('process');\nconst debugging = (__webpack_require__(/*! dcp/debugging */ \"./src/debugging.js\").scope)('dcp');\nconst dcpEnv = __webpack_require__(/*! dcp/common/dcp-env */ \"./src/common/dcp-env.js\");\nconst bearer = __webpack_require__(/*! ./http-bearer */ \"./src/protocol-v4/transport/http-bearer.js\");\nconst semver = __webpack_require__(/*! semver */ \"./node_modules/semver/semver.js\");\n\nvar listenerSeq = 0;\nvar initiatorSeq = 0;\n\nif (debugging() && !process.env.DEBUG && dcpConfig.build === 'debug')\n{\n\n if (process.env.DEBUG)\n process.env.DEBUG += ',engine';\n else\n process.env.DEBUG = 'engine';\n\n if (debugging('socketio'))\n {\n /* DCP_DEBUG=dcp:socketio,dcp:verbose to see everything */\n process.env.DEBUG += ',socket.io-client:socket,socket.io:client,socket.io:server';\n if (debugging('verbose') && !debugging('all'))\n process.env.DEBUG += ',socket.io*,engine.io*'\n }\n}\n\nclass SocketIOTransport extends Transport\n{\n /**\n * @constructor\n * @param {socket} newSocket Target: an instance of socket.io Socket that represents\n * a new connection.\n * Initiator: unused\n */\n /**\n * @constructor create new instance of a socketio-flavoured Transport.\n *\n * @param {object} config dcpConfig fragment\n * @param {Url} url Initiator: has .location and optionally .friendLocation\n * Target: has .listen and .location\n *\n * @param {object} options The `socketio` property of a protocol-v4 connectionOptions \n * object, which was built from a combination of defaults and \n * various dcpConfig components.\n *\n * @returns instance suitable for target or initiator, depending on mode, but does not manage connections.\n */\n constructor(config, options={}, newSocket)\n {\n super('Protocol SocketIO Transport');\n const that = this;\n \n assert(DcpURL.isURL(config.location));\n assert(typeof options === 'object');\n \n this.name = 'socketio';\n this.url = config.location;\n this.msgSequence = 0;\n this.isClosed = false; /* true => connection finalization begun */\n this.isFinal = false; /* true => connection finalization completed */\n this.hasConnected = false; /* true => one 'connect' event has been processed */\n this.initTimeout = false; /* timeout when connecting to allow target to reply with their mode message + buffer size, crash if they don't reply */\n this.modeSet = false; /* true => received response mode message */\n this.bufferSizeSet = false; /* true => received target's buffer size */\n this.rxFragments = {}; /* messages we are receiving in fragments */\n this.rxPending = []; /* strings which are messages that are complete and ready to emit, or objects that in this.rxFragments */\n this.txPending = []; /* messages we are receiving in fragments */\n this.txNumInFlight = 0; /* number of txPending sent but not acknowleged */\n this.txMaxInFlight = 2; /* less than 2 causes bad perf when mode.ack */\n this.debugInfo = { remoteLabel: '<unknown>' };\n this.options = this.buildOptions(options);\n this.mode = {\n ack: this.options.enableModeAck ? true : false, /* Only turn on ack mode if specified */\n concat: this.options.enableModeConcat === false ? false : true, /* Only turn off concat mode if specified*/\n };\n\n\n if (!newSocket) /* Initiator */\n {\n this.debugLabel = `socketio(i:${++initiatorSeq}):`;\n this.rxReady = true; /* relax, said the night man */\n this.debugInfo.remoteLabel = config.location.href;\n if (this.options.disableModeAnnouncement !== false)\n this.sendModeList();\n this.sendMaxMessageInfo();\n\n bearer.a$isFriendlyUrl(config.friendLocation).then((useFriendLocation) => {\n const connectUrl = useFriendLocation ? config.friendLocation : config.location;\n\n debugging('socketio') && console.log(this.debugLabel, 'connecting to', connectUrl.href);\n this.socket = (__webpack_require__(/*! @kingsds/socket.io-client */ \"./node_modules/@kingsds/socket.io-client/build/cjs/index.js\").connect)(connectUrl.origin, this.options);\n this.socket.on('connect_error', (error) => !this.isClosed && this.handleConnectErrorEvent(error));\n this.socket.on('connect', () => !this.isClosed && this.handleConnectEvent());\n\n useSocket();\n }).catch((error) => {\n debugging() && console.error(this.debugLabel, error);\n this.emit('error', error);\n });\n }\n else /* Target - receives socketio instance from SocketIOListener.handleConnectionEvent */\n {\n if (dcpEnv.platform !== 'nodejs')\n throw new Error('socketio: target mode only supported in nodejs');\n\n this.debugLabel = 'socketio(t:<new>):';\n\n this.socket = newSocket;\n this.debugInfo.remoteLabel = this.socket.handshake.address.replace(/^::ffff:/,'');\n debugging('socketio') && console.debug(this.debugLabel, `new socket from ${this.debugInfo.remoteLabel}`\n + ` on ${this.socket.handshake && this.socket.handshake.url}, ${this.socket.id}`);\n\n useSocket();\n }\n\n function useSocket()\n {\n that.socket.compress(dcpConfig.build !== 'debug'); /* try to keep traffic sniffable in debug */\n that.socket.on('message', (msg) => !that.isFinal && that.handleMessageEvent(msg));\n that.socket.on('disconnect', (reason) => !that.isClosed && that.handleDisconnectEvent(reason));\n }\n }\n\n /**\n * API - determine if a transport is usable or not.\n * @returns true if the transport is not closed.\n */\n ready()\n {\n return !this.isClosed;\n }\n \n /**\n * Handle the socket.io disconnect event, which is fired upon disconnection.\n * For some reasons (explicit disconnection), the socket.io code will not try to reconnect, but\n * in all other cases, the socket.io client will normally wait for a small random delay and then \n * try to reconnect, but in our case, we simply tear the transport down and let the Connection\n * class handle reconnects, since it might prefer a different transport at this point anyhow.\n *\n * One tricky case here is ping timeout, since the ping timeout includes the time to transmit the\n * packet before it was sent.\n *\n * @param {string} reason Possible reasons for disconnection.\n */\n handleDisconnectEvent(reason)\n {\n debugging('socketio') && console.debug(this.debugLabel, `disconnected from ${this.debugInfo.remoteLabel}; reason=${reason}`);\n\n if (this.isClosed !== true)\n setImmediate(() => this.#close(reason));\n\n switch(reason)\n {\n case 'io client disconnect':\n return; /* we called socket.disconnect() from another \"thread\" */\n case 'io server disconnect':\n case 'ping timeout':\n case 'transport close':\n case 'transport error':\n this.emit('end', reason);\n /* fallthrough */\n default:\n }\n }\n\n /**\n * Handle a socketio message from the peer.\n *\n * Most of the time, a socketio message contains a DCP message, however we support other message\n * types as well, with a mechanism inspired by 3GPP TS 23.040 (GSM 03.40) message concatenation.\n */\n handleMessageEvent(msg)\n {\n if (typeof msg !== 'string')\n {\n debugging('socketio') && console.debug(this.debugLabel, `received ${typeof msg} message from peer`, this.debugInfo.remoteLabel);\n return;\n }\n\n try\n {\n switch (msg[0])\n {\n case '{': /* all DCP Messages are JSON */\n this.processMessage(msg);\n break;\n case 'E':\n this.processExtendedMessage(msg);\n break;\n case 'A':\n this.processAcknowledgement(msg);\n break;\n case 'T':\n debugging('socketio') && console.debug(this.debugLabel, 'Received debug message:', msg);\n break;\n default:\n /* adhoc messages not supported in this transport */\n throw new DCPError(this.debugLabel, `Unrecognized message type indicator from ${this.debugInfo.remoteLabel} (${msg.charCodeAt(0)})`, 'DCPC-1102');\n }\n }\n catch(error)\n {\n debugging('socketio') && console.debug(this.debugLabel, 'handleMessageEvent: transport error, closing\\n ', error);\n this.emit('error', error);\n setImmediate(() => this.#close(error.message));\n }\n }\n\n /**\n * Process an ordinary message. This is just a hunk of JSON that gets passed up to the connection.\n * @param {string} msg\n */\n processMessage(msg)\n {\n this.rxPending.push(msg);\n this.emitReadyMessages();\n if (this.mode.ack)\n this.socket.send('A0000-ACK'); /* Ack w/o header = normal message */\n }\n\n /**\n * Remote has acknowledged the receipt of a message fragment. When in ack mode, this\n * triggers us to send more messages from the txPending queue. We always ack event when \n * not in ack-mode. See processExtendedMessage() comment for header description.\n *\n * Future: this is where maxInFlight might be tuned, based on how fast we get here\n *\n * @param {string} _msg\n */\n processAcknowledgement(_msg)\n {\n if (this.txNumInFlight > 0)\n this.txNumInFlight--;\n else\n {\n if (this.options.strict || !this.mode.ack)\n throw new DCPError(this.debugLabel, `Synchronization error: received unexpected acknowledgement message from ${this.debugInfo.remoteLabel}`, 'DCPC-1109');\n else\n this.mode.ack = true;\n }\n \n this.drainTxPending_soon();\n }\n\n /**\n * Extended messages have the following format:\n * Octet(s) Description\n * 0 E (69) or A (65)\n * 1..4 Header size (N) in hexadecimal\n * 5 Header Type\n * C (67) => concatenation\n * 6..6 + N: Header\n *\n * Upon receipt, extended messages are acknowledged by retransmitting their header, but\n * with the first character changed from E to A.\n *\n * @param {string} msg\n */\n processExtendedMessage(msg)\n {\n const headerSize = parseInt(msg.slice(1,5), 16);\n const headerType = msg[5];\n const header = msg.slice(6, 6 + headerSize);\n\n switch(headerType)\n {\n case 'M':\n this.processExtendedMessage_mode(header, msg.slice(6 + headerSize));\n break;\n case 'B':\n this.processExtendedMessage_maxBufferSize(msg.slice(6 + headerSize));\n break;\n case 'C':\n this.processExtendedMessage_concatenated(header, msg.slice(6 + headerSize));\n break;\n default:\n if (this.options.strict)\n throw new DCPError(`Unrecognized extended message header type indicator (${msg.charCodeAt(5)})`, 'DCPC-1101');\n }\n\n if (this.mode.ack)\n this.socket.send('A' + msg.slice(1, 6 + headerSize));\n }\n\n /**\n * We have received a fragment of a concatenation extended message. Memoize the fragment, and\n * if this is the last fragment, transform the fragments list into a pending message string and\n * then emits all ready messages (pending message strings).\n *\n * This code is capable of handling messages whose fragments arrive out of order. Complete messages \n * are emitted in the order that their first parts are received. Socketio is supposed to be total\n * order-aware but I wanted a safety belt for intermingled messages without occupying the entire\n * socketio network buffer on the first pass of the event loop. Because the first message fragment\n * is emitted to socketio on the same event loop pass as their Connection::send calls, I believe\n * that are no cases where messages will be emitted out of order at the other end, even when \n * intermingled on the wire, and the Nth message is much longer than any (N+k)th messages.\n *\n * @param {object} header the extended message header\n * @param {string} payload the payload of the extended message, i.e. a message fragment\n */\n processExtendedMessage_concatenated(header, payload)\n {\n try\n {\n header = JSON.parse(header);\n }\n catch(e)\n {\n throw new DCPError(`invalid extended message header '${header}'`, 'DCPC-1103');\n }\n\n if (!this.options.strict && !this.mode.concat)\n this.mode.concat = true;\n \n if (!(header.total <= this.options.maxFragmentCount))\n throw new DCPError('excessive message fragmentation', 'DCPC-1107'); /* make it more difficult for attacker to force OOM us */\n\n if (!(header.seq < header.total))\n throw new DCPError(`corrupt header for part ${header.seq + 1} of ${header.total} of message ${header.msgId}`, 'DCPC-1108');\n \n /* First fragment received, initial data structures and memoize the message's arrival order via pending list */\n if (!this.rxFragments[header.msgId])\n {\n const fragments = [];\n this.rxFragments[header.msgId] = fragments;\n this.rxPending.push(fragments);\n }\n\n this.rxFragments[header.msgId][header.seq] = payload;\n debugging('socketio') && console.debug(this.debugLabel, 'received part', header.seq + 1, 'of', header.total, 'for message', header.msgId);\n\n if (header.total !== this.rxFragments[header.msgId].filter(el => el !== undefined).length)\n return;\n\n debugging('socketio') && console.debug(this.debugLabel, 'received all parts of message', header.msgId);\n \n const idx = this.rxPending.indexOf(this.rxFragments[header.msgId])\n this.rxPending[idx] = this.rxFragments[header.msgId].join('');\n delete this.rxFragments[header.msgId];\n this.emitReadyMessages();\n }\n\n processExtendedMessage_mode(_header, payload)\n {\n var modeList;\n \n try\n {\n modeList = JSON.parse(payload);\n }\n catch(e)\n {\n throw new DCPError(`invalid mode message payload '${payload}'`, 'DCPC-1105');\n }\n\n for (let mode of modeList)\n {\n debugging('socketio') && console.debug(this.debugLabel, ` - remote supports ${mode} mode`); \n switch(mode)\n {\n case 'ack':\n this.mode.ack = true;\n break;\n case 'concat':\n this.mode.concat = true;\n break;\n default:\n debugging('socketio') && console.debug(this.debugLabel, ' - remote requested unrecognized mode:', mode);\n break;\n }\n }\n\n // true if we are connecting a new transport, expecting the initial mode reply, and have received the target's buffer size\n if (this.initTimeout && this.bufferSizeSet)\n {\n clearTimeout(this.initTimeout);\n delete this.initTimeout;\n this.emit('connect');\n }\n this.modeSet = true;\n this.sendModeList();\n }\n \n processExtendedMessage_maxBufferSize(payload)\n {\n var receivedOptions = JSON.parse(payload); \n \n this.options.txMaxFragmentSize = Math.floor(receivedOptions.maxFragmentSize / 11); /* XXX EMERGENCY BUGFIX - Aug 2022 /wg */\n this.options.txMaxFragmentCount = receivedOptions.maxFragmentCount;\n\n debugging() && console.debug(this.debugLabel, 'Set max transmit fragment size to', this.options.txMaxFragmentSize);\n\n if (receivedOptions.txMaxInFlight < this.txMaxInFlight)\n this.txMaxInFlight = receivedOptions.txMaxInFlight;\n if (this.options.maxFragmentSize < 0)\n throw new DCPError(`maxFragmentSize was set to < 1, (${this.options.txMaxFragmentSize}). Server requested bad fragment size, cannot connect.`, 'DCPC-1111');\n if (this.txMaxInFlight < 1)\n throw new DCPError(`txMaxInFlight was set to < 1, (${this.txMaxInFlight}). Cannot send messages with fewer than 1 in flight.`, 'DCPC-1111');\n \n // true if we are connecting a new transport, expecting the initial buffer reply, and have received the target's mode designation\n if (this.initTimeout && this.modeSet)\n {\n clearTimeout(this.initTimeout);\n delete this.initTimeout;\n this.emit('connect');\n }\n this.bufferSizeSet = true;\n\n this.sendMaxMessageInfo(); \n }\n\n /* Internal method to tell peer what we can receive */\n sendModeList()\n {\n var rawMessage;\n var modeList = [];\n\n if (this.sentModeList)\n return;\n\n this.sentModeList = true;\n this.mode.ack && modeList.push('ack');\n this.mode.concat && modeList.push('concat');\n rawMessage = 'E0000M' + JSON.stringify(modeList);\n\n debugging('socketio') && console.debug(`${this.debugLabel} sending mode list to ${this.debugInfo.remoteLabel}`)\n if (this.socket)\n this.socket.send(rawMessage);\n else\n this.txPending.unshift({ message: rawMessage, msgSequence: ++this.msgSequence});\n }\n \n /* Internal method to exchange desired fragment size/max inflight messages/max fragments per message */\n sendMaxMessageInfo()\n {\n if (this.sentMessageInfo)\n return;\n\n /* Calculate the optimal maxFragmentSize based on maxHttpBufferSize.\n * Currently unsure of exactly what happens when socketio serializes our serialized\n * JSON. I suspect we may double-encode UTF-8 > 0x80. The largest UTF-8 character is\n * 5 bytes...so we ensure the maxFragmentSize is (maxHttpBufferSize / 5) - (fragmentOverhead * 5)\n * to allow for worst-case scenario\n */\n const fragmentOverhead = 1000 + 64 * 1024; /* derived from concatenation algorithm */\n\n let desiredMaxFragmentSize;\n if (this.options.maxHttpBufferSize)\n desiredMaxFragmentSize = (this.options.maxHttpBufferSize / 5) - (fragmentOverhead * 5);\n else\n desiredMaxFragmentSize = this.options.maxFragmentSize;\n const options = {\n maxFragmentSize: desiredMaxFragmentSize,\n maxFragments: this.options.maxFragmentCount,\n txMaxInFlight: this.txMaxInFlight,\n };\n\n const message = 'E0000B' + JSON.stringify(options);\n\n debugging('socketio') && console.debug(`${this.debugLabel} sending max http buffer size to ${this.debugInfo.remoteLabel}`);\n if (this.socket)\n this.socket.send(message);\n else\n this.txPending.push({ message: message, msgSequence: ++this.msgSequence });\n \n this.sentMessageInfo = true;\n }\n\n /**\n * Emit message events so that the connection can process them. The pending property is an\n * array which is used to order messages, so that they are emitted in the order they were\n * sent. There are edge cases in the concatenation code where a sender could theoretically\n * interleave multiple messages, and have a second, shorter, message fully received before \n * the first is fully transmitted.\n *\n * The pending property stores only strings or objects; objects are effectively positional\n * memoes which are turned into strings when they are ready to be emitted to the connection\n * for processing.\n */\n emitReadyMessages()\n {\n while (this.rxReady && this.rxPending.length && typeof this.rxPending[0] === 'string' && this.listenerCount('message') > 0)\n this.emit('message', this.rxPending.shift());\n }\n \n /** \n * Close the current instance of Socket.\n *\n * This function effectively finalizes the socket so that it can't be used any more and\n * (hopefully) does not entrain garbage. A 'close' event is emitted at the end of socket\n * finalization and should be hooked by things that might entrain this transport instance to free\n * up memory, like maybe a list of active transports, etc.\n *\n * We add few extra hops on/off the event loop here to try and clean up any messages halfway in/out\n * the door that we might want to process. That happens between the setting of the isClosed and \n * isFinal flags.\n *\n * @param {string} reason\n */\n #close(reason)\n {\n const that = this;\n debugging('socketio') && console.debug(this.debugLabel, 'closing connection' + (reason ? ` (${reason})` : ''));\n\n function finalize(socket)\n {\n if (that.isFinal)\n return;\n\n if (socket)\n socket.removeAllListeners();\n that.isFinal = true;\n\n /* Free up memory that might be huge in case something entrains us */\n that.rxPending.length = 0;\n that.txPending.length = 0;\n for (let msgId in that.rxFragments)\n that.rxFragments[msgId].length = 0;\n }\n\n function closeSocket()\n {\n const socket = that.socket;\n \n try\n {\n if (socket)\n socket.disconnect();\n that.isClosed = true;\n that.emit('close', reason);\n delete that.socket;\n }\n finally\n {\n finalize(socket);\n }\n }\n\n if (!this.isClosed)\n {\n /* If we called close, drain any pending messages and then disconnect */\n if (reason === 'api-call')\n {\n this.drainTxPending();\n setImmediate(()=>closeSocket());\n }\n else\n closeSocket();\n }\n }\n\n /* API - close the transport, free up memory. */\n close()\n {\n assert(arguments.length === 0)\n this.#close('api-call');\n }\n\n /**\n * Handle the socketio connect_error event, which is fired when:\n * - the low-level connection cannot be established\n * - the connection is denied by the server in a middleware function\n *\n * In the first case, socket.io will automatically try to reconnect, after a delay,\n * except that will cancel that reconnection attempt here by killing the transport\n * instance so that the Connection class can try failing over to an alternate transport.\n *\n * @param {Error} error optional instance of error describing the underlying reason for the\n * connect failure. If the underlying reason is expressible by an\n * HTTP status code, this code will be placed as a Number in the\n * description property.\n */\n handleConnectErrorEvent(error)\n {\n debugging('socketio') && console.debug(this.debugLabel, `unable to connect to ${this.url}`, error);\n if (error.type === 'TransportError' && typeof error.description === 'number')\n error.httpStatus = Number(error.description);\n\n this.emit('connect-failed', error);\n this.#close(error.message);\n }\n\n /** \n * Handle the socketio connect event, which is fired by the Socket instance upon initiator\n * connection and reconnection to a target.\n */\n handleConnectEvent()\n {\n if (this.hasConnected === true)\n {\n /* this transport is not supposed to reuse connections, and we call .close() on disconnect \n * to prevent this. However, the API does not guarantee that will be race-free.\n */\n debugging('socketio') && console.debug(this.debugLabel, `*** reconnected to ${this.debugInfo.remoteLabel} (ignoring)`);\n return;\n }\n\n /* initial connection */\n this.hasConnected = true;\n debugging('socketio') && console.debug(this.debugLabel, 'connected to', this.debugInfo.remoteLabel);\n\n this.initTimeout = setTimeout(() => this.#close('no mode message received'), this.options.establishTimeout) /* give target 10 second to reply with capabilities before assuming bad connection and crashing */\n if (dcpEnv.platform === 'nodejs')\n this.initTimeout.unref();\n \n this.drainTxPending();\n }\n\n /**\n * API: Send a message to the transport instance on the other side of this connection.\n *\n * @param {string} message the message to send\n */\n send(message)\n {\n const msgSequence = ++this.msgSequence;\n\n debugging('socketio') && console.debug(this.debugLabel, `sending message ${msgSequence} to`, this.debugInfo.remoteLabel);\n debugging('socketio') && debugging('verbose') && !debugging('all') && console.debug(this.debugLabel, message);\n\n if (!this.socket)\n throw new DCPError('SocketIOTransport.send: Not connected', 'DCPC-1110');\n \n if ( false\n || !(message.length > this.options.txMaxFragmentSize)\n || !this.mode.concat)\n this.txPending.push({ message, msgSequence });\n else\n { /* This is a large message. Use message concatenation to send in fragments. A failure of any\n * fragment is a failure of the entire message and will be handled at the connection layer.\n */\n let total = Math.ceil(message.length / this.options.txMaxFragmentSize);\n \n if (total > this.options.txMaxFragmentCount)\n throw new DCPError('extended message has more fragments than the peer will allow', 'DCP-1112');\n \n for (let seq=0; seq < total; seq++)\n {\n let start = seq * this.options.txMaxFragmentSize;\n let fragment = message.slice(start, start + this.options.txMaxFragmentSize);\n let header = JSON.stringify({ msgId: msgSequence, seq, total });\n let extMessage = 'E' + header.length.toString(16).padStart(4, '0') + 'C' + header + fragment;\n\n this.txPending.push({ message: extMessage, msgSequence, seq, total });\n\n /* This should be impossible, but it also effectively kills the connection. */\n if (header.length > 0xFFFF)\n throw new DCPError('extended message header too long', 'DCPC-1104');\n }\n }\n\n this.drainTxPending_soon();\n }\n\n drainTxPending_soon()\n {\n if (this.drainingSoon)\n return;\n this.drainingSoon = true;\n\n setImmediate(() => this.drainTxPending());\n }\n \n /**\n * Drain messages from the txPending queue out to socketio's network buffer. We throw away traffic\n * when it can't be sent; this is no different from packets already on the wire when the other end\n * has changed IP number, etc.\n *\n * In ack mode, this drain is controlled by acknowledgement messages so that socketio can have the\n * opportunity to insert ping/pong messages in the data stream while sending a message large enough\n * to need to be sent via concatenation. [ NOTE - it appears to send ping/pong OOB in version 4.4, \n * so currently ack mode is off by default / wg Mar 2022 ]\n *\n * The reason we put non-concat messages through here is to avoid special casing a bunch of nasty\n * things for the sake of a little tiny bit of performance. Sending message<-ACK and queueing through\n * the drain code is necessary if we wind up interleaving multiple message sends in some interesting\n * edge cases; notably, fast-teardown, but a future Connection class that ran without nonces (or multiple\n * nonces) would need this, too.\n *\n * In non-ack mode, the drain is mostly uncontrolled, but we throw each message on a separate pass of\n * the event loop, to try and keep huge message blasts one connection from overwhelming a busy server,\n * as well allowing socketio the chance to send its heartbeat messages and make it less likely that it\n * will hit its http buffer size limit, because that causes an immediate disconnect.\n *\n * Non-ack-mode is compatible with previous dcp5 versions and probably a little faster.\n */\n drainTxPending()\n {\n const that = this;\n\n if (this.txPending.length)\n debugging('socketio') && console.debug(this.debugLabel, `drain tx pending queue, ${this.txPending.length} ready`\n + (this.mode.ack ? `; ${this.txMaxInFlight - this.txNumInFlight} slots available` : ''));\n delete this.drainingSoon;\n\n if (this.isClosed)\n return;\n \n for (let i=0;\n !this.isClosed && this.txPending.length && (this.txNumInFlight < this.txMaxInFlight || !this.mode.ack);\n i++)\n {\n const pendingElement = this.txPending.shift();\n\n if (i === 0)\n writeToSocket(pendingElement);\n else\n setImmediateN(() => writeToSocket(pendingElement), i);\n \n if (this.mode.ack)\n this.txNumInFlight++;\n\n if (this.txPending.length === 0) /* just sent the last message */\n this.emit('drain');\n }\n\n if (this.txPending.length)\n this.drainTxPending_soon();\n \n function writeToSocket(pendingElement)\n {\n if (that.isClosed)\n return;\n\n const { message, msgSequence, seq, total } = pendingElement;\n that.socket.send(message);\n\n if (typeof seq !== 'undefined')\n debugging('socketio') && console.debug(that.debugLabel, `sent fragment ${seq + 1}/${total} of message ${msgSequence} (${message.length} bytes)`);\n else\n debugging('socketio') && console.debug(that.debugLabel, `sent message ${msgSequence} (${message.length} bytes)`);\n }\n }\n}\nSocketIOTransport.prototype.buildOptions = buildOptions;\n\n/** \n * Build the socketio options object, based on the passed-in options, dcp debug state, and internal\n * object state. Method shared between SocketIOTransport and SocketIOListener.\n */\nfunction buildOptions(options)\n{\n options = leafMerge(\n /* Baked-in Defaults */\n ({\n autoUnref: dcpEnv.platform === 'nodejs' ? true : undefined, /* socket.io + webpack 5 { node:global } work-around */\n perMessageDeflate: dcpConfig.build !== 'debug',\n maxFragmentCount: 1000, /* used to limit memory on receiver */\n maxFragmentSize: 1e6, /* bytes */\n txMaxFragmentCount:1000, /* peer's limit for fragments */\n txMaxFragmentSize: 1e6, /* peer's limit for fragment size */\n maxHttpBufferSize: false, /* bytes; false = auto */\n establishTimeout: 10 * 1000, /* 10 seconds */\n pingTimeout: 30 * 1e3, /* s */\n pingInterval: 90 * 1e3, /* s */\n transports: ['polling', 'websocket'],\n upgrade: true,\n rememberUpgrade: true,\n autoConnect: true,\n cors: {\n origin: '*',\n methods: ['GET', 'POST']\n },\n }),\n options,\n /* Absolutely mandatory */\n ({\n path: this.url.pathname // eslint-disable-line no-invalid-this\n }));\n\n /* draw out errors quickly in dev */\n if ((process.env.DCP_NETWORK_CONFIG_BUILD || dcpConfig.build) === 'debug')\n {\n /* short timeouts and debuggers don't get along well */\n if (dcpEnv.platform === 'nodejs' && !(requireNative('module')._cache.niim instanceof requireNative('module').Module))\n {\n options.pingTimeout /= 5;\n options.pingInterval /= 5;\n }\n }\n\n return options;\n}\n \n/**\n * @constructor\n * API - Factory which creates a new socketio listener, or throws. Emits 'listening' if the server \n * parameter is not provided and we create it, once it is ready for connections.\n * \n * @bug It is possible for a connection event to sneak in before the 'listening' event is fired, due to \n * the two-step event trigger. The right fix is to proxy the event, but dcp-events::event-emitter.proxy\n * itself currently has the same bug. For now, we just have callbacks in API. /wg apr 2022\n * \n * @param {object} config dcpConfig fragment - has .listen and .location\n * @param {object} target the instance of Target to bind to\n * @param {object} options [optional] A DCP config options variable (like dcpConfig.dcp.listenOptions)\n */\nfunction SocketIOListener(config, target, options)\n{\n var matchRegex;\n \n this.url = config.location;\n this.options = this.buildOptions(options);\n this.connSequence = 0;\n this.seq = ++listenerSeq;\n this.debugLabel = `socketio(l:${this.seq}):`;\n this.isShutdown = false;\n\n if (dcpEnv.platform !== 'nodejs')\n throw new Error('target mode only supported in nodejs');\n\n if (target.httpServer.listening)\n setImmediate(() => this.emit('listening', target.httpServer)); /* setImmediate to allow event listeners to be added before emitting */\n else\n target.httpServer.on('listening', () => this.emit('listening', target.httpServer));\n\n matchRegex = '^' + this.url.pathname;\n if (matchRegex.slice(-1) !== '/')\n matchRegex += '\\\\/';\n else\n matchRegex = matchRegex.slice(0, -1) + \"\\\\/\"\n\n matchRegex += '\\\\?EIO=[0-9]';\n debugging('socketio') && console.debug(this.debugLabel, `expecting requests to match regex ${matchRegex}`);\n\n target.all(new RegExp(matchRegex), (request) => {\n /* Socket.io does this intrinsically because it plays with the server's events directly,\n * but we \"handle\" it explicitly here so that we don't trip the 404 handler. \n */\n debugging('socketio') && console.debug(this.debugLabel, 'Handling', request.url);\n }, true);\n\n /* While we normally want to inherit options from parent to child socket via this.options,\n * we don't want this to happen for maxHttpBufferSize due to the way it is used and calculated\n * in the transport constructor. A buffer that is too small (eg false) will cause us to not\n * recognized socket.io SIDs.\n */\n \n this.options.maxHttpBufferSize = this.options.maxHttpBufferSize || 1e9;\n \n const socketOptions = this.options;\n\n this.socketServer = requireNative('socket.io')(target.httpServer, socketOptions);\n this.socketServer.on('connection', (socket) => !this.isShutdown && this.handleConnectionEvent(config, socket));\n debugging() && console.debug(this.debugLabel, 'Socketio listener initialized for path', this.options.path);\n}\nSocketIOListener.prototype = new EventEmitter('socketio(l)');\nSocketIOListener.prototype.buildOptions = buildOptions;\n\nSocketIOListener.prototype.close = function SocketIOListener$$close()\n{\n if (!this.isShutdown)\n this.shutdown();\n this.socketServer.close(() => this.emit('close'));\n}\n\n/** Stop accepting new connections */\nSocketIOListener.prototype.shutdown = function SocketIOListener$$shutdown()\n{\n this.isShutdown = true;\n}\n\n/** Remove any event loop references used by this transport instance */\nSocketIOListener.prototype.unref = function SocketIOListener$$unref()\n{\n void false;\n}\n\n/**\n * Handle the socketio connection event, which is fired upon a connection from client.\n * Used by Target->SocketIOListener to create transport instances.\n *\n * It is expected that code hooking the 'connection' event will hook the 'message' in\n * the same pass of the event loop as it is invoked; otherwise, messages may be missed.\n *\n * @param {object} socket a new connection emitted by the socket.io server\n */\nSocketIOListener.prototype.handleConnectionEvent = function SocketIOListener$$handleConnectionEvent(config, socket)\n{\n var transport = new SocketIOTransport(config, this.options, socket);\n\n this.url = config.location || config.listen;\n this.connSequence++;\n transport.debugLabel = `socketio(t:${this.seq}.${this.connSequence}):`\n\n /* Ensure we don't emit any message events until the application or Connection class\n * has had the chance to set up message handlers via the connection event.\n */\n transport.rxReady = false;\n this.on('connection', () => setImmediate(() => {\n transport.rxReady = true;\n transport.emitReadyMessages();\n }));\n\n this.emit('connection', transport);\n}\n\n/* Define API */\nexports.TransportClass = SocketIOTransport;\nexports.Listener = SocketIOListener;\n\n\n//# sourceURL=webpack://dcp/./src/protocol-v4/transport/socketio.js?");
4873
4919
 
4874
4920
  /***/ }),
4875
4921
 
@@ -5473,179 +5519,245 @@ eval("/* (ignored) */\n\n//# sourceURL=webpack://dcp/util_(ignored)?");
5473
5519
 
5474
5520
  /***/ }),
5475
5521
 
5476
- /***/ "./node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.cjs":
5477
- /*!***********************************************************************!*\
5478
- !*** ./node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.cjs ***!
5479
- \***********************************************************************/
5480
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5522
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/contrib/has-cors.js":
5523
+ /*!******************************************************************************!*\
5524
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/contrib/has-cors.js ***!
5525
+ \******************************************************************************/
5526
+ /***/ ((__unused_webpack_module, exports) => {
5481
5527
 
5482
5528
  "use strict";
5483
- eval("\n\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\n\nvar domhandler = __webpack_require__(/*! domhandler */ \"./node_modules/@selderee/plugin-htmlparser2/node_modules/domhandler/lib/index.js\");\nvar selderee = __webpack_require__(/*! selderee */ \"./node_modules/selderee/lib/selderee.cjs\");\n\n/**\r\n * A {@link BuilderFunction} implementation.\r\n *\r\n * Creates a function (in a {@link Picker} wrapper) that can run\r\n * the decision tree against `htmlparser2` `Element` nodes.\r\n *\r\n * @typeParam V - the type of values associated with selectors.\r\n *\r\n * @param nodes - nodes ({@link DecisionTreeNode})\r\n * from the root level of the decision tree.\r\n *\r\n * @returns a {@link Picker} object.\r\n */\r\nfunction hp2Builder(nodes) {\r\n return new selderee.Picker(handleArray(nodes));\r\n}\r\n// ==============================================\r\nfunction handleArray(nodes) {\r\n const matchers = nodes.map(handleNode);\r\n return (el, ...tail) => flatMap(matchers, m => m(el, ...tail));\r\n}\r\nfunction handleNode(node) {\r\n switch (node.type) {\r\n case 'terminal': {\r\n const result = [node.valueContainer];\r\n return (el, ...tail) => result;\r\n }\r\n case 'tagName':\r\n return handleTagName(node);\r\n case 'attrValue':\r\n return handleAttrValueName(node);\r\n case 'attrPresence':\r\n return handleAttrPresenceName(node);\r\n case 'pushElement':\r\n return handlePushElementNode(node);\r\n case 'popElement':\r\n return handlePopElementNode(node);\r\n }\r\n}\r\nfunction handleTagName(node) {\r\n const variants = {};\r\n for (const variant of node.variants) {\r\n variants[variant.value] = handleArray(variant.cont);\r\n }\r\n return (el, ...tail) => {\r\n const continuation = variants[el.name];\r\n return (continuation) ? continuation(el, ...tail) : [];\r\n };\r\n}\r\nfunction handleAttrPresenceName(node) {\r\n const attrName = node.name;\r\n const continuation = handleArray(node.cont);\r\n return (el, ...tail) => (Object.prototype.hasOwnProperty.call(el.attribs, attrName))\r\n ? continuation(el, ...tail)\r\n : [];\r\n}\r\nfunction handleAttrValueName(node) {\r\n const callbacks = [];\r\n for (const matcher of node.matchers) {\r\n const predicate = matcher.predicate;\r\n const continuation = handleArray(matcher.cont);\r\n callbacks.push((attr, el, ...tail) => (predicate(attr) ? continuation(el, ...tail) : []));\r\n }\r\n const attrName = node.name;\r\n return (el, ...tail) => {\r\n const attr = el.attribs[attrName];\r\n return (attr || attr === '')\r\n ? flatMap(callbacks, cb => cb(attr, el, ...tail))\r\n : [];\r\n };\r\n}\r\nfunction handlePushElementNode(node) {\r\n const continuation = handleArray(node.cont);\r\n const leftElementGetter = (node.combinator === '+')\r\n ? getPrecedingElement\r\n : getParentElement;\r\n return (el, ...tail) => {\r\n const next = leftElementGetter(el);\r\n if (next === null) {\r\n return [];\r\n }\r\n return continuation(next, el, ...tail);\r\n };\r\n}\r\nconst getPrecedingElement = (el) => {\r\n const prev = el.prev;\r\n if (prev === null) {\r\n return null;\r\n }\r\n return (domhandler.isTag(prev)) ? prev : getPrecedingElement(prev);\r\n};\r\nconst getParentElement = (el) => {\r\n const parent = el.parent;\r\n return (parent && domhandler.isTag(parent)) ? parent : null;\r\n};\r\nfunction handlePopElementNode(node) {\r\n const continuation = handleArray(node.cont);\r\n return (el, next, ...tail) => continuation(next, ...tail);\r\n}\r\n// Can be removed after transition to Node 12.\r\nfunction flatMap(items, mapper) {\r\n return [].concat(...amap(items, mapper));\r\n}\r\nfunction amap(items, mapper) {\r\n const len = items.length;\r\n const res = new Array(len);\r\n for (let i = 0; i < len; i++) {\r\n res[i] = mapper(items[i]);\r\n }\r\n return res;\r\n}\n\nexports.hp2Builder = hp2Builder;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.cjs?");
5529
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.hasCORS = void 0;\n// imported from https://github.com/component/has-cors\nlet value = false;\ntry {\n value = typeof XMLHttpRequest !== 'undefined' &&\n 'withCredentials' in new XMLHttpRequest();\n}\ncatch (err) {\n // if XMLHttp support is disabled in IE then it will throw\n // when trying to create\n}\nexports.hasCORS = value;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/contrib/has-cors.js?");
5484
5530
 
5485
5531
  /***/ }),
5486
5532
 
5487
- /***/ "./node_modules/available-typed-arrays/index.js":
5488
- /*!******************************************************!*\
5489
- !*** ./node_modules/available-typed-arrays/index.js ***!
5490
- \******************************************************/
5491
- /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
5533
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseqs.js":
5534
+ /*!*****************************************************************************!*\
5535
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseqs.js ***!
5536
+ \*****************************************************************************/
5537
+ /***/ ((__unused_webpack_module, exports) => {
5492
5538
 
5493
5539
  "use strict";
5494
- eval("\n\nvar possibleNames = [\n\t'BigInt64Array',\n\t'BigUint64Array',\n\t'Float32Array',\n\t'Float64Array',\n\t'Int16Array',\n\t'Int32Array',\n\t'Int8Array',\n\t'Uint16Array',\n\t'Uint32Array',\n\t'Uint8Array',\n\t'Uint8ClampedArray'\n];\n\nvar g = typeof globalThis === 'undefined' ? __webpack_require__.g : globalThis;\n\nmodule.exports = function availableTypedArrays() {\n\tvar out = [];\n\tfor (var i = 0; i < possibleNames.length; i++) {\n\t\tif (typeof g[possibleNames[i]] === 'function') {\n\t\t\tout[out.length] = possibleNames[i];\n\t\t}\n\t}\n\treturn out;\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/available-typed-arrays/index.js?");
5540
+ eval("\n// imported from https://github.com/galkn/querystring\n/**\n * Compiles a querystring\n * Returns string representation of the object\n *\n * @param {Object}\n * @api private\n */\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.decode = exports.encode = void 0;\nfunction encode(obj) {\n let str = '';\n for (let i in obj) {\n if (obj.hasOwnProperty(i)) {\n if (str.length)\n str += '&';\n str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);\n }\n }\n return str;\n}\nexports.encode = encode;\n/**\n * Parses a simple querystring into an object\n *\n * @param {String} qs\n * @api private\n */\nfunction decode(qs) {\n let qry = {};\n let pairs = qs.split('&');\n for (let i = 0, l = pairs.length; i < l; i++) {\n let pair = pairs[i].split('=');\n qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n }\n return qry;\n}\nexports.decode = decode;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseqs.js?");
5495
5541
 
5496
5542
  /***/ }),
5497
5543
 
5498
- /***/ "./node_modules/engine.io-client/build/cjs/contrib/has-cors.js":
5499
- /*!*********************************************************************!*\
5500
- !*** ./node_modules/engine.io-client/build/cjs/contrib/has-cors.js ***!
5501
- \*********************************************************************/
5544
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseuri.js":
5545
+ /*!******************************************************************************!*\
5546
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseuri.js ***!
5547
+ \******************************************************************************/
5502
5548
  /***/ ((__unused_webpack_module, exports) => {
5503
5549
 
5504
5550
  "use strict";
5505
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.hasCORS = void 0;\n// imported from https://github.com/component/has-cors\nlet value = false;\ntry {\n value = typeof XMLHttpRequest !== 'undefined' &&\n 'withCredentials' in new XMLHttpRequest();\n}\ncatch (err) {\n // if XMLHttp support is disabled in IE then it will throw\n // when trying to create\n}\nexports.hasCORS = value;\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/contrib/has-cors.js?");
5551
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.parse = void 0;\n// imported from https://github.com/galkn/parseuri\n/**\n * Parses an URI\n *\n * @author Steven Levithan <stevenlevithan.com> (MIT license)\n * @api private\n */\nconst re = /^(?:(?![^:@]+:[^:@\\/]*@)(http|https|ws|wss):\\/\\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/;\nconst parts = [\n 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'\n];\nfunction parse(str) {\n const src = str, b = str.indexOf('['), e = str.indexOf(']');\n if (b != -1 && e != -1) {\n str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);\n }\n let m = re.exec(str || ''), uri = {}, i = 14;\n while (i--) {\n uri[parts[i]] = m[i] || '';\n }\n if (b != -1 && e != -1) {\n uri.source = src;\n uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');\n uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');\n uri.ipv6uri = true;\n }\n uri.pathNames = pathNames(uri, uri['path']);\n uri.queryKey = queryKey(uri, uri['query']);\n return uri;\n}\nexports.parse = parse;\nfunction pathNames(obj, path) {\n const regx = /\\/{2,9}/g, names = path.replace(regx, \"/\").split(\"/\");\n if (path.slice(0, 1) == '/' || path.length === 0) {\n names.splice(0, 1);\n }\n if (path.slice(-1) == '/') {\n names.splice(names.length - 1, 1);\n }\n return names;\n}\nfunction queryKey(uri, query) {\n const data = {};\n query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ($0, $1, $2) {\n if ($1) {\n data[$1] = $2;\n }\n });\n return data;\n}\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseuri.js?");
5506
5552
 
5507
5553
  /***/ }),
5508
5554
 
5509
- /***/ "./node_modules/engine.io-client/build/cjs/contrib/parseqs.js":
5510
- /*!********************************************************************!*\
5511
- !*** ./node_modules/engine.io-client/build/cjs/contrib/parseqs.js ***!
5512
- \********************************************************************/
5555
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/contrib/yeast.js":
5556
+ /*!***************************************************************************!*\
5557
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/contrib/yeast.js ***!
5558
+ \***************************************************************************/
5513
5559
  /***/ ((__unused_webpack_module, exports) => {
5514
5560
 
5515
5561
  "use strict";
5516
- eval("\n// imported from https://github.com/galkn/querystring\n/**\n * Compiles a querystring\n * Returns string representation of the object\n *\n * @param {Object}\n * @api private\n */\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.decode = exports.encode = void 0;\nfunction encode(obj) {\n let str = '';\n for (let i in obj) {\n if (obj.hasOwnProperty(i)) {\n if (str.length)\n str += '&';\n str += encodeURIComponent(i) + '=' + encodeURIComponent(obj[i]);\n }\n }\n return str;\n}\nexports.encode = encode;\n/**\n * Parses a simple querystring into an object\n *\n * @param {String} qs\n * @api private\n */\nfunction decode(qs) {\n let qry = {};\n let pairs = qs.split('&');\n for (let i = 0, l = pairs.length; i < l; i++) {\n let pair = pairs[i].split('=');\n qry[decodeURIComponent(pair[0])] = decodeURIComponent(pair[1]);\n }\n return qry;\n}\nexports.decode = decode;\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/contrib/parseqs.js?");
5562
+ eval("// imported from https://github.com/unshiftio/yeast\n\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.yeast = exports.decode = exports.encode = void 0;\nconst alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split(''), length = 64, map = {};\nlet seed = 0, i = 0, prev;\n/**\n * Return a string representing the specified number.\n *\n * @param {Number} num The number to convert.\n * @returns {String} The string representation of the number.\n * @api public\n */\nfunction encode(num) {\n let encoded = '';\n do {\n encoded = alphabet[num % length] + encoded;\n num = Math.floor(num / length);\n } while (num > 0);\n return encoded;\n}\nexports.encode = encode;\n/**\n * Return the integer value specified by the given string.\n *\n * @param {String} str The string to convert.\n * @returns {Number} The integer value represented by the string.\n * @api public\n */\nfunction decode(str) {\n let decoded = 0;\n for (i = 0; i < str.length; i++) {\n decoded = decoded * length + map[str.charAt(i)];\n }\n return decoded;\n}\nexports.decode = decode;\n/**\n * Yeast: A tiny growing id generator.\n *\n * @returns {String} A unique id.\n * @api public\n */\nfunction yeast() {\n const now = encode(+new Date());\n if (now !== prev)\n return seed = 0, prev = now;\n return now + '.' + encode(seed++);\n}\nexports.yeast = yeast;\n//\n// Map each character to its index.\n//\nfor (; i < length; i++)\n map[alphabet[i]] = i;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/contrib/yeast.js?");
5517
5563
 
5518
5564
  /***/ }),
5519
5565
 
5520
- /***/ "./node_modules/engine.io-client/build/cjs/contrib/parseuri.js":
5521
- /*!*********************************************************************!*\
5522
- !*** ./node_modules/engine.io-client/build/cjs/contrib/parseuri.js ***!
5523
- \*********************************************************************/
5566
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/globalThis.browser.js":
5567
+ /*!********************************************************************************!*\
5568
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/globalThis.browser.js ***!
5569
+ \********************************************************************************/
5524
5570
  /***/ ((__unused_webpack_module, exports) => {
5525
5571
 
5526
5572
  "use strict";
5527
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.parse = void 0;\n// imported from https://github.com/galkn/parseuri\n/**\n * Parses an URI\n *\n * @author Steven Levithan <stevenlevithan.com> (MIT license)\n * @api private\n */\nconst re = /^(?:(?![^:@]+:[^:@\\/]*@)(http|https|ws|wss):\\/\\/)?((?:(([^:@]*)(?::([^:@]*))?)?@)?((?:[a-f0-9]{0,4}:){2,7}[a-f0-9]{0,4}|[^:\\/?#]*)(?::(\\d*))?)(((\\/(?:[^?#](?![^?#\\/]*\\.[^?#\\/.]+(?:[?#]|$)))*\\/?)?([^?#\\/]*))(?:\\?([^#]*))?(?:#(.*))?)/;\nconst parts = [\n 'source', 'protocol', 'authority', 'userInfo', 'user', 'password', 'host', 'port', 'relative', 'path', 'directory', 'file', 'query', 'anchor'\n];\nfunction parse(str) {\n const src = str, b = str.indexOf('['), e = str.indexOf(']');\n if (b != -1 && e != -1) {\n str = str.substring(0, b) + str.substring(b, e).replace(/:/g, ';') + str.substring(e, str.length);\n }\n let m = re.exec(str || ''), uri = {}, i = 14;\n while (i--) {\n uri[parts[i]] = m[i] || '';\n }\n if (b != -1 && e != -1) {\n uri.source = src;\n uri.host = uri.host.substring(1, uri.host.length - 1).replace(/;/g, ':');\n uri.authority = uri.authority.replace('[', '').replace(']', '').replace(/;/g, ':');\n uri.ipv6uri = true;\n }\n uri.pathNames = pathNames(uri, uri['path']);\n uri.queryKey = queryKey(uri, uri['query']);\n return uri;\n}\nexports.parse = parse;\nfunction pathNames(obj, path) {\n const regx = /\\/{2,9}/g, names = path.replace(regx, \"/\").split(\"/\");\n if (path.slice(0, 1) == '/' || path.length === 0) {\n names.splice(0, 1);\n }\n if (path.slice(-1) == '/') {\n names.splice(names.length - 1, 1);\n }\n return names;\n}\nfunction queryKey(uri, query) {\n const data = {};\n query.replace(/(?:^|&)([^&=]*)=?([^&]*)/g, function ($0, $1, $2) {\n if ($1) {\n data[$1] = $2;\n }\n });\n return data;\n}\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/contrib/parseuri.js?");
5573
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.globalThisShim = void 0;\nexports.globalThisShim = (() => {\n if (typeof self !== \"undefined\") {\n return self;\n }\n else if (typeof window !== \"undefined\") {\n return window;\n }\n else {\n return Function(\"return this\")();\n }\n})();\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/globalThis.browser.js?");
5528
5574
 
5529
5575
  /***/ }),
5530
5576
 
5531
- /***/ "./node_modules/engine.io-client/build/cjs/contrib/yeast.js":
5532
- /*!******************************************************************!*\
5533
- !*** ./node_modules/engine.io-client/build/cjs/contrib/yeast.js ***!
5534
- \******************************************************************/
5535
- /***/ ((__unused_webpack_module, exports) => {
5577
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/index.js":
5578
+ /*!*******************************************************************!*\
5579
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/index.js ***!
5580
+ \*******************************************************************/
5581
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5582
+
5583
+ "use strict";
5584
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.nextTick = exports.parse = exports.installTimerFunctions = exports.transports = exports.Transport = exports.protocol = exports.Socket = void 0;\nconst socket_js_1 = __webpack_require__(/*! ./socket.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/socket.js\");\nObject.defineProperty(exports, \"Socket\", ({ enumerable: true, get: function () { return socket_js_1.Socket; } }));\nexports.protocol = socket_js_1.Socket.protocol;\nvar transport_js_1 = __webpack_require__(/*! ./transport.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transport.js\");\nObject.defineProperty(exports, \"Transport\", ({ enumerable: true, get: function () { return transport_js_1.Transport; } }));\nvar index_js_1 = __webpack_require__(/*! ./transports/index.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transports/index.js\");\nObject.defineProperty(exports, \"transports\", ({ enumerable: true, get: function () { return index_js_1.transports; } }));\nvar util_js_1 = __webpack_require__(/*! ./util.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/util.js\");\nObject.defineProperty(exports, \"installTimerFunctions\", ({ enumerable: true, get: function () { return util_js_1.installTimerFunctions; } }));\nvar parseuri_js_1 = __webpack_require__(/*! ./contrib/parseuri.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseuri.js\");\nObject.defineProperty(exports, \"parse\", ({ enumerable: true, get: function () { return parseuri_js_1.parse; } }));\nvar websocket_constructor_js_1 = __webpack_require__(/*! ./transports/websocket-constructor.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket-constructor.browser.js\");\nObject.defineProperty(exports, \"nextTick\", ({ enumerable: true, get: function () { return websocket_constructor_js_1.nextTick; } }));\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/index.js?");
5585
+
5586
+ /***/ }),
5587
+
5588
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/socket.js":
5589
+ /*!********************************************************************!*\
5590
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/socket.js ***!
5591
+ \********************************************************************/
5592
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5536
5593
 
5537
5594
  "use strict";
5538
- eval("// imported from https://github.com/unshiftio/yeast\n\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.yeast = exports.decode = exports.encode = void 0;\nconst alphabet = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_'.split(''), length = 64, map = {};\nlet seed = 0, i = 0, prev;\n/**\n * Return a string representing the specified number.\n *\n * @param {Number} num The number to convert.\n * @returns {String} The string representation of the number.\n * @api public\n */\nfunction encode(num) {\n let encoded = '';\n do {\n encoded = alphabet[num % length] + encoded;\n num = Math.floor(num / length);\n } while (num > 0);\n return encoded;\n}\nexports.encode = encode;\n/**\n * Return the integer value specified by the given string.\n *\n * @param {String} str The string to convert.\n * @returns {Number} The integer value represented by the string.\n * @api public\n */\nfunction decode(str) {\n let decoded = 0;\n for (i = 0; i < str.length; i++) {\n decoded = decoded * length + map[str.charAt(i)];\n }\n return decoded;\n}\nexports.decode = decode;\n/**\n * Yeast: A tiny growing id generator.\n *\n * @returns {String} A unique id.\n * @api public\n */\nfunction yeast() {\n const now = encode(+new Date());\n if (now !== prev)\n return seed = 0, prev = now;\n return now + '.' + encode(seed++);\n}\nexports.yeast = yeast;\n//\n// Map each character to its index.\n//\nfor (; i < length; i++)\n map[alphabet[i]] = i;\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/contrib/yeast.js?");
5595
+ eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Socket = void 0;\nconst index_js_1 = __webpack_require__(/*! ./transports/index.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transports/index.js\");\nconst util_js_1 = __webpack_require__(/*! ./util.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/util.js\");\nconst parseqs_js_1 = __webpack_require__(/*! ./contrib/parseqs.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseqs.js\");\nconst parseuri_js_1 = __webpack_require__(/*! ./contrib/parseuri.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseuri.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst debug = (0, debug_1.default)(\"engine.io-client:socket\"); // debug()\nclass Socket extends component_emitter_1.Emitter {\n /**\n * Socket constructor.\n *\n * @param {String|Object} uri or options\n * @param {Object} opts - options\n * @api public\n */\n constructor(uri, opts = {}) {\n super();\n if (uri && \"object\" === typeof uri) {\n opts = uri;\n uri = null;\n }\n if (uri) {\n uri = (0, parseuri_js_1.parse)(uri);\n opts.hostname = uri.host;\n opts.secure = uri.protocol === \"https\" || uri.protocol === \"wss\";\n opts.port = uri.port;\n if (uri.query)\n opts.query = uri.query;\n }\n else if (opts.host) {\n opts.hostname = (0, parseuri_js_1.parse)(opts.host).host;\n }\n (0, util_js_1.installTimerFunctions)(this, opts);\n this.secure =\n null != opts.secure\n ? opts.secure\n : typeof location !== \"undefined\" && \"https:\" === location.protocol;\n if (opts.hostname && !opts.port) {\n // if no port is specified manually, use the protocol default\n opts.port = this.secure ? \"443\" : \"80\";\n }\n this.hostname =\n opts.hostname ||\n (typeof location !== \"undefined\" ? location.hostname : \"localhost\");\n this.port =\n opts.port ||\n (typeof location !== \"undefined\" && location.port\n ? location.port\n : this.secure\n ? \"443\"\n : \"80\");\n this.transports = opts.transports || [\"polling\", \"websocket\"];\n this.readyState = \"\";\n this.writeBuffer = [];\n this.prevBufferLen = 0;\n this.opts = Object.assign({\n path: \"/engine.io\",\n agent: false,\n withCredentials: false,\n upgrade: true,\n timestampParam: \"t\",\n rememberUpgrade: false,\n rejectUnauthorized: true,\n perMessageDeflate: {\n threshold: 1024\n },\n transportOptions: {},\n closeOnBeforeunload: true\n }, opts);\n this.opts.path = this.opts.path.replace(/\\/$/, \"\") + \"/\";\n if (typeof this.opts.query === \"string\") {\n this.opts.query = (0, parseqs_js_1.decode)(this.opts.query);\n }\n // set on handshake\n this.id = null;\n this.upgrades = null;\n this.pingInterval = null;\n this.pingTimeout = null;\n // set on heartbeat\n this.pingTimeoutTimer = null;\n if (typeof addEventListener === \"function\") {\n if (this.opts.closeOnBeforeunload) {\n // Firefox closes the connection when the \"beforeunload\" event is emitted but not Chrome. This event listener\n // ensures every browser behaves the same (no \"disconnect\" event at the Socket.IO level when the page is\n // closed/reloaded)\n this.beforeunloadEventListener = () => {\n if (this.transport) {\n // silently close the transport\n this.transport.removeAllListeners();\n this.transport.close();\n }\n };\n addEventListener(\"beforeunload\", this.beforeunloadEventListener, false);\n }\n if (this.hostname !== \"localhost\") {\n this.offlineEventListener = () => {\n this.onClose(\"transport close\", {\n description: \"network connection lost\"\n });\n };\n addEventListener(\"offline\", this.offlineEventListener, false);\n }\n }\n this.open();\n }\n /**\n * Creates transport of the given type.\n *\n * @param {String} transport name\n * @return {Transport}\n * @api private\n */\n createTransport(name) {\n debug('creating transport \"%s\"', name);\n const query = Object.assign({}, this.opts.query);\n // append engine.io protocol identifier\n query.EIO = engine_io_parser_1.protocol;\n // transport name\n query.transport = name;\n // session id if we already have one\n if (this.id)\n query.sid = this.id;\n const opts = Object.assign({}, this.opts.transportOptions[name], this.opts, {\n query,\n socket: this,\n hostname: this.hostname,\n secure: this.secure,\n port: this.port\n });\n debug(\"options: %j\", opts);\n return new index_js_1.transports[name](opts);\n }\n /**\n * Initializes transport to use and starts probe.\n *\n * @api private\n */\n open() {\n let transport;\n if (this.opts.rememberUpgrade &&\n Socket.priorWebsocketSuccess &&\n this.transports.indexOf(\"websocket\") !== -1) {\n transport = \"websocket\";\n }\n else if (0 === this.transports.length) {\n // Emit error on next tick so it can be listened to\n this.setTimeoutFn(() => {\n this.emitReserved(\"error\", \"No transports available\");\n }, 0);\n return;\n }\n else {\n transport = this.transports[0];\n }\n this.readyState = \"opening\";\n // Retry with the next transport if the transport is disabled (jsonp: false)\n try {\n transport = this.createTransport(transport);\n }\n catch (e) {\n debug(\"error while creating transport: %s\", e);\n this.transports.shift();\n this.open();\n return;\n }\n transport.open();\n this.setTransport(transport);\n }\n /**\n * Sets the current transport. Disables the existing one (if any).\n *\n * @api private\n */\n setTransport(transport) {\n debug(\"setting transport %s\", transport.name);\n if (this.transport) {\n debug(\"clearing existing transport %s\", this.transport.name);\n this.transport.removeAllListeners();\n }\n // set up transport\n this.transport = transport;\n // set up transport listeners\n transport\n .on(\"drain\", this.onDrain.bind(this))\n .on(\"packet\", this.onPacket.bind(this))\n .on(\"error\", this.onError.bind(this))\n .on(\"close\", reason => this.onClose(\"transport close\", reason));\n }\n /**\n * Probes a transport.\n *\n * @param {String} transport name\n * @api private\n */\n probe(name) {\n debug('probing transport \"%s\"', name);\n let transport = this.createTransport(name);\n let failed = false;\n Socket.priorWebsocketSuccess = false;\n const onTransportOpen = () => {\n if (failed)\n return;\n debug('probe transport \"%s\" opened', name);\n transport.send([{ type: \"ping\", data: \"probe\" }]);\n transport.once(\"packet\", msg => {\n if (failed)\n return;\n if (\"pong\" === msg.type && \"probe\" === msg.data) {\n debug('probe transport \"%s\" pong', name);\n this.upgrading = true;\n this.emitReserved(\"upgrading\", transport);\n if (!transport)\n return;\n Socket.priorWebsocketSuccess = \"websocket\" === transport.name;\n debug('pausing current transport \"%s\"', this.transport.name);\n this.transport.pause(() => {\n if (failed)\n return;\n if (\"closed\" === this.readyState)\n return;\n debug(\"changing transport and sending upgrade packet\");\n cleanup();\n this.setTransport(transport);\n transport.send([{ type: \"upgrade\" }]);\n this.emitReserved(\"upgrade\", transport);\n transport = null;\n this.upgrading = false;\n this.flush();\n });\n }\n else {\n debug('probe transport \"%s\" failed', name);\n const err = new Error(\"probe error\");\n // @ts-ignore\n err.transport = transport.name;\n this.emitReserved(\"upgradeError\", err);\n }\n });\n };\n function freezeTransport() {\n if (failed)\n return;\n // Any callback called by transport should be ignored since now\n failed = true;\n cleanup();\n transport.close();\n transport = null;\n }\n // Handle any error that happens while probing\n const onerror = err => {\n const error = new Error(\"probe error: \" + err);\n // @ts-ignore\n error.transport = transport.name;\n freezeTransport();\n debug('probe transport \"%s\" failed because of error: %s', name, err);\n this.emitReserved(\"upgradeError\", error);\n };\n function onTransportClose() {\n onerror(\"transport closed\");\n }\n // When the socket is closed while we're probing\n function onclose() {\n onerror(\"socket closed\");\n }\n // When the socket is upgraded while we're probing\n function onupgrade(to) {\n if (transport && to.name !== transport.name) {\n debug('\"%s\" works - aborting \"%s\"', to.name, transport.name);\n freezeTransport();\n }\n }\n // Remove all listeners on the transport and on self\n const cleanup = () => {\n transport.removeListener(\"open\", onTransportOpen);\n transport.removeListener(\"error\", onerror);\n transport.removeListener(\"close\", onTransportClose);\n this.off(\"close\", onclose);\n this.off(\"upgrading\", onupgrade);\n };\n transport.once(\"open\", onTransportOpen);\n transport.once(\"error\", onerror);\n transport.once(\"close\", onTransportClose);\n this.once(\"close\", onclose);\n this.once(\"upgrading\", onupgrade);\n transport.open();\n }\n /**\n * Called when connection is deemed open.\n *\n * @api private\n */\n onOpen() {\n debug(\"socket open\");\n this.readyState = \"open\";\n Socket.priorWebsocketSuccess = \"websocket\" === this.transport.name;\n this.emitReserved(\"open\");\n this.flush();\n // we check for `readyState` in case an `open`\n // listener already closed the socket\n if (\"open\" === this.readyState &&\n this.opts.upgrade &&\n this.transport.pause) {\n debug(\"starting upgrade probes\");\n let i = 0;\n const l = this.upgrades.length;\n for (; i < l; i++) {\n this.probe(this.upgrades[i]);\n }\n }\n }\n /**\n * Handles a packet.\n *\n * @api private\n */\n onPacket(packet) {\n if (\"opening\" === this.readyState ||\n \"open\" === this.readyState ||\n \"closing\" === this.readyState) {\n debug('socket receive: type \"%s\", data \"%s\"', packet.type, packet.data);\n this.emitReserved(\"packet\", packet);\n // Socket is live - any packet counts\n this.emitReserved(\"heartbeat\");\n switch (packet.type) {\n case \"open\":\n this.onHandshake(JSON.parse(packet.data));\n break;\n case \"ping\":\n this.resetPingTimeout();\n this.sendPacket(\"pong\");\n this.emitReserved(\"ping\");\n this.emitReserved(\"pong\");\n break;\n case \"error\":\n const err = new Error(\"server error\");\n // @ts-ignore\n err.code = packet.data;\n this.onError(err);\n break;\n case \"message\":\n this.emitReserved(\"data\", packet.data);\n this.emitReserved(\"message\", packet.data);\n break;\n }\n }\n else {\n debug('packet received with socket readyState \"%s\"', this.readyState);\n }\n }\n /**\n * Called upon handshake completion.\n *\n * @param {Object} data - handshake obj\n * @api private\n */\n onHandshake(data) {\n this.emitReserved(\"handshake\", data);\n this.id = data.sid;\n this.transport.query.sid = data.sid;\n this.upgrades = this.filterUpgrades(data.upgrades);\n this.pingInterval = data.pingInterval;\n this.pingTimeout = data.pingTimeout;\n this.maxPayload = data.maxPayload;\n this.onOpen();\n // In case open handler closes socket\n if (\"closed\" === this.readyState)\n return;\n this.resetPingTimeout();\n }\n /**\n * Sets and resets ping timeout timer based on server pings.\n *\n * @api private\n */\n resetPingTimeout() {\n this.clearTimeoutFn(this.pingTimeoutTimer);\n this.pingTimeoutTimer = this.setTimeoutFn(() => {\n this.onClose(\"ping timeout\");\n }, this.pingInterval + this.pingTimeout);\n if (this.opts.autoUnref) {\n this.pingTimeoutTimer.unref();\n }\n }\n /**\n * Called on `drain` event\n *\n * @api private\n */\n onDrain() {\n this.writeBuffer.splice(0, this.prevBufferLen);\n // setting prevBufferLen = 0 is very important\n // for example, when upgrading, upgrade packet is sent over,\n // and a nonzero prevBufferLen could cause problems on `drain`\n this.prevBufferLen = 0;\n if (0 === this.writeBuffer.length) {\n this.emitReserved(\"drain\");\n }\n else {\n this.flush();\n }\n }\n /**\n * Flush write buffers.\n *\n * @api private\n */\n flush() {\n if (\"closed\" !== this.readyState &&\n this.transport.writable &&\n !this.upgrading &&\n this.writeBuffer.length) {\n const packets = this.getWritablePackets();\n debug(\"flushing %d packets in socket\", packets.length);\n this.transport.send(packets);\n // keep track of current length of writeBuffer\n // splice writeBuffer and callbackBuffer on `drain`\n this.prevBufferLen = packets.length;\n this.emitReserved(\"flush\");\n }\n }\n /**\n * Ensure the encoded size of the writeBuffer is below the maxPayload value sent by the server (only for HTTP\n * long-polling)\n *\n * @private\n */\n getWritablePackets() {\n const shouldCheckPayloadSize = this.maxPayload &&\n this.transport.name === \"polling\" &&\n this.writeBuffer.length > 1;\n if (!shouldCheckPayloadSize) {\n return this.writeBuffer;\n }\n let payloadSize = 1; // first packet type\n for (let i = 0; i < this.writeBuffer.length; i++) {\n const data = this.writeBuffer[i].data;\n if (data) {\n payloadSize += (0, util_js_1.byteLength)(data);\n }\n if (i > 0 && payloadSize > this.maxPayload) {\n debug(\"only send %d out of %d packets\", i, this.writeBuffer.length);\n return this.writeBuffer.slice(0, i);\n }\n payloadSize += 2; // separator + packet type\n }\n debug(\"payload size is %d (max: %d)\", payloadSize, this.maxPayload);\n return this.writeBuffer;\n }\n /**\n * Sends a message.\n *\n * @param {String} message.\n * @param {Function} callback function.\n * @param {Object} options.\n * @return {Socket} for chaining.\n * @api public\n */\n write(msg, options, fn) {\n this.sendPacket(\"message\", msg, options, fn);\n return this;\n }\n send(msg, options, fn) {\n this.sendPacket(\"message\", msg, options, fn);\n return this;\n }\n /**\n * Sends a packet.\n *\n * @param {String} packet type.\n * @param {String} data.\n * @param {Object} options.\n * @param {Function} callback function.\n * @api private\n */\n sendPacket(type, data, options, fn) {\n if (\"function\" === typeof data) {\n fn = data;\n data = undefined;\n }\n if (\"function\" === typeof options) {\n fn = options;\n options = null;\n }\n if (\"closing\" === this.readyState || \"closed\" === this.readyState) {\n return;\n }\n options = options || {};\n options.compress = false !== options.compress;\n const packet = {\n type: type,\n data: data,\n options: options\n };\n this.emitReserved(\"packetCreate\", packet);\n this.writeBuffer.push(packet);\n if (fn)\n this.once(\"flush\", fn);\n this.flush();\n }\n /**\n * Closes the connection.\n *\n * @api public\n */\n close() {\n const close = () => {\n this.onClose(\"forced close\");\n debug(\"socket closing - telling transport to close\");\n this.transport.close();\n };\n const cleanupAndClose = () => {\n this.off(\"upgrade\", cleanupAndClose);\n this.off(\"upgradeError\", cleanupAndClose);\n close();\n };\n const waitForUpgrade = () => {\n // wait for upgrade to finish since we can't send packets while pausing a transport\n this.once(\"upgrade\", cleanupAndClose);\n this.once(\"upgradeError\", cleanupAndClose);\n };\n if (\"opening\" === this.readyState || \"open\" === this.readyState) {\n this.readyState = \"closing\";\n if (this.writeBuffer.length) {\n this.once(\"drain\", () => {\n if (this.upgrading) {\n waitForUpgrade();\n }\n else {\n close();\n }\n });\n }\n else if (this.upgrading) {\n waitForUpgrade();\n }\n else {\n close();\n }\n }\n return this;\n }\n /**\n * Called upon transport error\n *\n * @api private\n */\n onError(err) {\n debug(\"socket error %j\", err);\n Socket.priorWebsocketSuccess = false;\n this.emitReserved(\"error\", err);\n this.onClose(\"transport error\", err);\n }\n /**\n * Called upon transport close.\n *\n * @api private\n */\n onClose(reason, description) {\n if (\"opening\" === this.readyState ||\n \"open\" === this.readyState ||\n \"closing\" === this.readyState) {\n debug('socket close with reason: \"%s\"', reason);\n // clear timers\n this.clearTimeoutFn(this.pingTimeoutTimer);\n // stop event from firing again for transport\n this.transport.removeAllListeners(\"close\");\n // ensure transport won't stay open\n this.transport.close();\n // ignore further transport communication\n this.transport.removeAllListeners();\n if (typeof removeEventListener === \"function\") {\n removeEventListener(\"beforeunload\", this.beforeunloadEventListener, false);\n removeEventListener(\"offline\", this.offlineEventListener, false);\n }\n // set ready state\n this.readyState = \"closed\";\n // clear session id\n this.id = null;\n // emit close event\n this.emitReserved(\"close\", reason, description);\n // clean buffers after, so users can still\n // grab the buffers on `close` event\n this.writeBuffer = [];\n this.prevBufferLen = 0;\n }\n }\n /**\n * Filters upgrades, returning only those matching client transports.\n *\n * @param {Array} server upgrades\n * @api private\n *\n */\n filterUpgrades(upgrades) {\n const filteredUpgrades = [];\n let i = 0;\n const j = upgrades.length;\n for (; i < j; i++) {\n if (~this.transports.indexOf(upgrades[i]))\n filteredUpgrades.push(upgrades[i]);\n }\n return filteredUpgrades;\n }\n}\nexports.Socket = Socket;\nSocket.protocol = engine_io_parser_1.protocol;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/socket.js?");
5539
5596
 
5540
5597
  /***/ }),
5541
5598
 
5542
- /***/ "./node_modules/engine.io-client/build/cjs/globalThis.browser.js":
5599
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/transport.js":
5543
5600
  /*!***********************************************************************!*\
5544
- !*** ./node_modules/engine.io-client/build/cjs/globalThis.browser.js ***!
5601
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/transport.js ***!
5545
5602
  \***********************************************************************/
5546
- /***/ ((__unused_webpack_module, exports) => {
5603
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5547
5604
 
5548
5605
  "use strict";
5549
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.globalThisShim = void 0;\nexports.globalThisShim = (() => {\n if (typeof self !== \"undefined\") {\n return self;\n }\n else if (typeof window !== \"undefined\") {\n return window;\n }\n else {\n return Function(\"return this\")();\n }\n})();\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/globalThis.browser.js?");
5606
+ eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Transport = void 0;\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst util_js_1 = __webpack_require__(/*! ./util.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/util.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst debug = (0, debug_1.default)(\"engine.io-client:transport\"); // debug()\nclass TransportError extends Error {\n constructor(reason, description, context) {\n super(reason);\n this.description = description;\n this.context = context;\n this.type = \"TransportError\";\n }\n}\nclass Transport extends component_emitter_1.Emitter {\n /**\n * Transport abstract constructor.\n *\n * @param {Object} options.\n * @api private\n */\n constructor(opts) {\n super();\n this.writable = false;\n (0, util_js_1.installTimerFunctions)(this, opts);\n this.opts = opts;\n this.query = opts.query;\n this.readyState = \"\";\n this.socket = opts.socket;\n }\n /**\n * Emits an error.\n *\n * @param {String} reason\n * @param description\n * @param context - the error context\n * @return {Transport} for chaining\n * @api protected\n */\n onError(reason, description, context) {\n super.emitReserved(\"error\", new TransportError(reason, description, context));\n return this;\n }\n /**\n * Opens the transport.\n *\n * @api public\n */\n open() {\n if (\"closed\" === this.readyState || \"\" === this.readyState) {\n this.readyState = \"opening\";\n this.doOpen();\n }\n return this;\n }\n /**\n * Closes the transport.\n *\n * @api public\n */\n close() {\n if (\"opening\" === this.readyState || \"open\" === this.readyState) {\n this.doClose();\n this.onClose();\n }\n return this;\n }\n /**\n * Sends multiple packets.\n *\n * @param {Array} packets\n * @api public\n */\n send(packets) {\n if (\"open\" === this.readyState) {\n this.write(packets);\n }\n else {\n // this might happen if the transport was silently closed in the beforeunload event handler\n debug(\"transport is not open, discarding packets\");\n }\n }\n /**\n * Called upon open\n *\n * @api protected\n */\n onOpen() {\n this.readyState = \"open\";\n this.writable = true;\n super.emitReserved(\"open\");\n }\n /**\n * Called with data.\n *\n * @param {String} data\n * @api protected\n */\n onData(data) {\n const packet = (0, engine_io_parser_1.decodePacket)(data, this.socket.binaryType);\n this.onPacket(packet);\n }\n /**\n * Called with a decoded packet.\n *\n * @api protected\n */\n onPacket(packet) {\n super.emitReserved(\"packet\", packet);\n }\n /**\n * Called upon close.\n *\n * @api protected\n */\n onClose(details) {\n this.readyState = \"closed\";\n super.emitReserved(\"close\", details);\n }\n}\nexports.Transport = Transport;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/transport.js?");
5550
5607
 
5551
5608
  /***/ }),
5552
5609
 
5553
- /***/ "./node_modules/engine.io-client/build/cjs/index.js":
5554
- /*!**********************************************************!*\
5555
- !*** ./node_modules/engine.io-client/build/cjs/index.js ***!
5556
- \**********************************************************/
5610
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/transports/index.js":
5611
+ /*!******************************************************************************!*\
5612
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/transports/index.js ***!
5613
+ \******************************************************************************/
5557
5614
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5558
5615
 
5559
5616
  "use strict";
5560
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.nextTick = exports.parse = exports.installTimerFunctions = exports.transports = exports.Transport = exports.protocol = exports.Socket = void 0;\nconst socket_js_1 = __webpack_require__(/*! ./socket.js */ \"./node_modules/engine.io-client/build/cjs/socket.js\");\nObject.defineProperty(exports, \"Socket\", ({ enumerable: true, get: function () { return socket_js_1.Socket; } }));\nexports.protocol = socket_js_1.Socket.protocol;\nvar transport_js_1 = __webpack_require__(/*! ./transport.js */ \"./node_modules/engine.io-client/build/cjs/transport.js\");\nObject.defineProperty(exports, \"Transport\", ({ enumerable: true, get: function () { return transport_js_1.Transport; } }));\nvar index_js_1 = __webpack_require__(/*! ./transports/index.js */ \"./node_modules/engine.io-client/build/cjs/transports/index.js\");\nObject.defineProperty(exports, \"transports\", ({ enumerable: true, get: function () { return index_js_1.transports; } }));\nvar util_js_1 = __webpack_require__(/*! ./util.js */ \"./node_modules/engine.io-client/build/cjs/util.js\");\nObject.defineProperty(exports, \"installTimerFunctions\", ({ enumerable: true, get: function () { return util_js_1.installTimerFunctions; } }));\nvar parseuri_js_1 = __webpack_require__(/*! ./contrib/parseuri.js */ \"./node_modules/engine.io-client/build/cjs/contrib/parseuri.js\");\nObject.defineProperty(exports, \"parse\", ({ enumerable: true, get: function () { return parseuri_js_1.parse; } }));\nvar websocket_constructor_js_1 = __webpack_require__(/*! ./transports/websocket-constructor.js */ \"./node_modules/engine.io-client/build/cjs/transports/websocket-constructor.browser.js\");\nObject.defineProperty(exports, \"nextTick\", ({ enumerable: true, get: function () { return websocket_constructor_js_1.nextTick; } }));\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/index.js?");
5617
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.transports = void 0;\nconst polling_js_1 = __webpack_require__(/*! ./polling.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transports/polling.js\");\nconst websocket_js_1 = __webpack_require__(/*! ./websocket.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket.js\");\nexports.transports = {\n websocket: websocket_js_1.WS,\n polling: polling_js_1.Polling\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/transports/index.js?");
5561
5618
 
5562
5619
  /***/ }),
5563
5620
 
5564
- /***/ "./node_modules/engine.io-client/build/cjs/socket.js":
5565
- /*!***********************************************************!*\
5566
- !*** ./node_modules/engine.io-client/build/cjs/socket.js ***!
5567
- \***********************************************************/
5621
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/transports/polling.js":
5622
+ /*!********************************************************************************!*\
5623
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/transports/polling.js ***!
5624
+ \********************************************************************************/
5568
5625
  /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5569
5626
 
5570
5627
  "use strict";
5571
- eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Socket = void 0;\nconst index_js_1 = __webpack_require__(/*! ./transports/index.js */ \"./node_modules/engine.io-client/build/cjs/transports/index.js\");\nconst util_js_1 = __webpack_require__(/*! ./util.js */ \"./node_modules/engine.io-client/build/cjs/util.js\");\nconst parseqs_js_1 = __webpack_require__(/*! ./contrib/parseqs.js */ \"./node_modules/engine.io-client/build/cjs/contrib/parseqs.js\");\nconst parseuri_js_1 = __webpack_require__(/*! ./contrib/parseuri.js */ \"./node_modules/engine.io-client/build/cjs/contrib/parseuri.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst debug = (0, debug_1.default)(\"engine.io-client:socket\"); // debug()\nclass Socket extends component_emitter_1.Emitter {\n /**\n * Socket constructor.\n *\n * @param {String|Object} uri or options\n * @param {Object} opts - options\n * @api public\n */\n constructor(uri, opts = {}) {\n super();\n if (uri && \"object\" === typeof uri) {\n opts = uri;\n uri = null;\n }\n if (uri) {\n uri = (0, parseuri_js_1.parse)(uri);\n opts.hostname = uri.host;\n opts.secure = uri.protocol === \"https\" || uri.protocol === \"wss\";\n opts.port = uri.port;\n if (uri.query)\n opts.query = uri.query;\n }\n else if (opts.host) {\n opts.hostname = (0, parseuri_js_1.parse)(opts.host).host;\n }\n (0, util_js_1.installTimerFunctions)(this, opts);\n this.secure =\n null != opts.secure\n ? opts.secure\n : typeof location !== \"undefined\" && \"https:\" === location.protocol;\n if (opts.hostname && !opts.port) {\n // if no port is specified manually, use the protocol default\n opts.port = this.secure ? \"443\" : \"80\";\n }\n this.hostname =\n opts.hostname ||\n (typeof location !== \"undefined\" ? location.hostname : \"localhost\");\n this.port =\n opts.port ||\n (typeof location !== \"undefined\" && location.port\n ? location.port\n : this.secure\n ? \"443\"\n : \"80\");\n this.transports = opts.transports || [\"polling\", \"websocket\"];\n this.readyState = \"\";\n this.writeBuffer = [];\n this.prevBufferLen = 0;\n this.opts = Object.assign({\n path: \"/engine.io\",\n agent: false,\n withCredentials: false,\n upgrade: true,\n timestampParam: \"t\",\n rememberUpgrade: false,\n rejectUnauthorized: true,\n perMessageDeflate: {\n threshold: 1024\n },\n transportOptions: {},\n closeOnBeforeunload: true\n }, opts);\n this.opts.path = this.opts.path.replace(/\\/$/, \"\") + \"/\";\n if (typeof this.opts.query === \"string\") {\n this.opts.query = (0, parseqs_js_1.decode)(this.opts.query);\n }\n // set on handshake\n this.id = null;\n this.upgrades = null;\n this.pingInterval = null;\n this.pingTimeout = null;\n // set on heartbeat\n this.pingTimeoutTimer = null;\n if (typeof addEventListener === \"function\") {\n if (this.opts.closeOnBeforeunload) {\n // Firefox closes the connection when the \"beforeunload\" event is emitted but not Chrome. This event listener\n // ensures every browser behaves the same (no \"disconnect\" event at the Socket.IO level when the page is\n // closed/reloaded)\n this.beforeunloadEventListener = () => {\n if (this.transport) {\n // silently close the transport\n this.transport.removeAllListeners();\n this.transport.close();\n }\n };\n addEventListener(\"beforeunload\", this.beforeunloadEventListener, false);\n }\n if (this.hostname !== \"localhost\") {\n this.offlineEventListener = () => {\n this.onClose(\"transport close\", {\n description: \"network connection lost\"\n });\n };\n addEventListener(\"offline\", this.offlineEventListener, false);\n }\n }\n this.open();\n }\n /**\n * Creates transport of the given type.\n *\n * @param {String} transport name\n * @return {Transport}\n * @api private\n */\n createTransport(name) {\n debug('creating transport \"%s\"', name);\n const query = Object.assign({}, this.opts.query);\n // append engine.io protocol identifier\n query.EIO = engine_io_parser_1.protocol;\n // transport name\n query.transport = name;\n // session id if we already have one\n if (this.id)\n query.sid = this.id;\n const opts = Object.assign({}, this.opts.transportOptions[name], this.opts, {\n query,\n socket: this,\n hostname: this.hostname,\n secure: this.secure,\n port: this.port\n });\n debug(\"options: %j\", opts);\n return new index_js_1.transports[name](opts);\n }\n /**\n * Initializes transport to use and starts probe.\n *\n * @api private\n */\n open() {\n let transport;\n if (this.opts.rememberUpgrade &&\n Socket.priorWebsocketSuccess &&\n this.transports.indexOf(\"websocket\") !== -1) {\n transport = \"websocket\";\n }\n else if (0 === this.transports.length) {\n // Emit error on next tick so it can be listened to\n this.setTimeoutFn(() => {\n this.emitReserved(\"error\", \"No transports available\");\n }, 0);\n return;\n }\n else {\n transport = this.transports[0];\n }\n this.readyState = \"opening\";\n // Retry with the next transport if the transport is disabled (jsonp: false)\n try {\n transport = this.createTransport(transport);\n }\n catch (e) {\n debug(\"error while creating transport: %s\", e);\n this.transports.shift();\n this.open();\n return;\n }\n transport.open();\n this.setTransport(transport);\n }\n /**\n * Sets the current transport. Disables the existing one (if any).\n *\n * @api private\n */\n setTransport(transport) {\n debug(\"setting transport %s\", transport.name);\n if (this.transport) {\n debug(\"clearing existing transport %s\", this.transport.name);\n this.transport.removeAllListeners();\n }\n // set up transport\n this.transport = transport;\n // set up transport listeners\n transport\n .on(\"drain\", this.onDrain.bind(this))\n .on(\"packet\", this.onPacket.bind(this))\n .on(\"error\", this.onError.bind(this))\n .on(\"close\", reason => this.onClose(\"transport close\", reason));\n }\n /**\n * Probes a transport.\n *\n * @param {String} transport name\n * @api private\n */\n probe(name) {\n debug('probing transport \"%s\"', name);\n let transport = this.createTransport(name);\n let failed = false;\n Socket.priorWebsocketSuccess = false;\n const onTransportOpen = () => {\n if (failed)\n return;\n debug('probe transport \"%s\" opened', name);\n transport.send([{ type: \"ping\", data: \"probe\" }]);\n transport.once(\"packet\", msg => {\n if (failed)\n return;\n if (\"pong\" === msg.type && \"probe\" === msg.data) {\n debug('probe transport \"%s\" pong', name);\n this.upgrading = true;\n this.emitReserved(\"upgrading\", transport);\n if (!transport)\n return;\n Socket.priorWebsocketSuccess = \"websocket\" === transport.name;\n debug('pausing current transport \"%s\"', this.transport.name);\n this.transport.pause(() => {\n if (failed)\n return;\n if (\"closed\" === this.readyState)\n return;\n debug(\"changing transport and sending upgrade packet\");\n cleanup();\n this.setTransport(transport);\n transport.send([{ type: \"upgrade\" }]);\n this.emitReserved(\"upgrade\", transport);\n transport = null;\n this.upgrading = false;\n this.flush();\n });\n }\n else {\n debug('probe transport \"%s\" failed', name);\n const err = new Error(\"probe error\");\n // @ts-ignore\n err.transport = transport.name;\n this.emitReserved(\"upgradeError\", err);\n }\n });\n };\n function freezeTransport() {\n if (failed)\n return;\n // Any callback called by transport should be ignored since now\n failed = true;\n cleanup();\n transport.close();\n transport = null;\n }\n // Handle any error that happens while probing\n const onerror = err => {\n const error = new Error(\"probe error: \" + err);\n // @ts-ignore\n error.transport = transport.name;\n freezeTransport();\n debug('probe transport \"%s\" failed because of error: %s', name, err);\n this.emitReserved(\"upgradeError\", error);\n };\n function onTransportClose() {\n onerror(\"transport closed\");\n }\n // When the socket is closed while we're probing\n function onclose() {\n onerror(\"socket closed\");\n }\n // When the socket is upgraded while we're probing\n function onupgrade(to) {\n if (transport && to.name !== transport.name) {\n debug('\"%s\" works - aborting \"%s\"', to.name, transport.name);\n freezeTransport();\n }\n }\n // Remove all listeners on the transport and on self\n const cleanup = () => {\n transport.removeListener(\"open\", onTransportOpen);\n transport.removeListener(\"error\", onerror);\n transport.removeListener(\"close\", onTransportClose);\n this.off(\"close\", onclose);\n this.off(\"upgrading\", onupgrade);\n };\n transport.once(\"open\", onTransportOpen);\n transport.once(\"error\", onerror);\n transport.once(\"close\", onTransportClose);\n this.once(\"close\", onclose);\n this.once(\"upgrading\", onupgrade);\n transport.open();\n }\n /**\n * Called when connection is deemed open.\n *\n * @api private\n */\n onOpen() {\n debug(\"socket open\");\n this.readyState = \"open\";\n Socket.priorWebsocketSuccess = \"websocket\" === this.transport.name;\n this.emitReserved(\"open\");\n this.flush();\n // we check for `readyState` in case an `open`\n // listener already closed the socket\n if (\"open\" === this.readyState &&\n this.opts.upgrade &&\n this.transport.pause) {\n debug(\"starting upgrade probes\");\n let i = 0;\n const l = this.upgrades.length;\n for (; i < l; i++) {\n this.probe(this.upgrades[i]);\n }\n }\n }\n /**\n * Handles a packet.\n *\n * @api private\n */\n onPacket(packet) {\n if (\"opening\" === this.readyState ||\n \"open\" === this.readyState ||\n \"closing\" === this.readyState) {\n debug('socket receive: type \"%s\", data \"%s\"', packet.type, packet.data);\n this.emitReserved(\"packet\", packet);\n // Socket is live - any packet counts\n this.emitReserved(\"heartbeat\");\n switch (packet.type) {\n case \"open\":\n this.onHandshake(JSON.parse(packet.data));\n break;\n case \"ping\":\n this.resetPingTimeout();\n this.sendPacket(\"pong\");\n this.emitReserved(\"ping\");\n this.emitReserved(\"pong\");\n break;\n case \"error\":\n const err = new Error(\"server error\");\n // @ts-ignore\n err.code = packet.data;\n this.onError(err);\n break;\n case \"message\":\n this.emitReserved(\"data\", packet.data);\n this.emitReserved(\"message\", packet.data);\n break;\n }\n }\n else {\n debug('packet received with socket readyState \"%s\"', this.readyState);\n }\n }\n /**\n * Called upon handshake completion.\n *\n * @param {Object} data - handshake obj\n * @api private\n */\n onHandshake(data) {\n this.emitReserved(\"handshake\", data);\n this.id = data.sid;\n this.transport.query.sid = data.sid;\n this.upgrades = this.filterUpgrades(data.upgrades);\n this.pingInterval = data.pingInterval;\n this.pingTimeout = data.pingTimeout;\n this.maxPayload = data.maxPayload;\n this.onOpen();\n // In case open handler closes socket\n if (\"closed\" === this.readyState)\n return;\n this.resetPingTimeout();\n }\n /**\n * Sets and resets ping timeout timer based on server pings.\n *\n * @api private\n */\n resetPingTimeout() {\n this.clearTimeoutFn(this.pingTimeoutTimer);\n this.pingTimeoutTimer = this.setTimeoutFn(() => {\n this.onClose(\"ping timeout\");\n }, this.pingInterval + this.pingTimeout);\n if (this.opts.autoUnref) {\n this.pingTimeoutTimer.unref();\n }\n }\n /**\n * Called on `drain` event\n *\n * @api private\n */\n onDrain() {\n this.writeBuffer.splice(0, this.prevBufferLen);\n // setting prevBufferLen = 0 is very important\n // for example, when upgrading, upgrade packet is sent over,\n // and a nonzero prevBufferLen could cause problems on `drain`\n this.prevBufferLen = 0;\n if (0 === this.writeBuffer.length) {\n this.emitReserved(\"drain\");\n }\n else {\n this.flush();\n }\n }\n /**\n * Flush write buffers.\n *\n * @api private\n */\n flush() {\n if (\"closed\" !== this.readyState &&\n this.transport.writable &&\n !this.upgrading &&\n this.writeBuffer.length) {\n const packets = this.getWritablePackets();\n debug(\"flushing %d packets in socket\", packets.length);\n this.transport.send(packets);\n // keep track of current length of writeBuffer\n // splice writeBuffer and callbackBuffer on `drain`\n this.prevBufferLen = packets.length;\n this.emitReserved(\"flush\");\n }\n }\n /**\n * Ensure the encoded size of the writeBuffer is below the maxPayload value sent by the server (only for HTTP\n * long-polling)\n *\n * @private\n */\n getWritablePackets() {\n const shouldCheckPayloadSize = this.maxPayload &&\n this.transport.name === \"polling\" &&\n this.writeBuffer.length > 1;\n if (!shouldCheckPayloadSize) {\n return this.writeBuffer;\n }\n let payloadSize = 1; // first packet type\n for (let i = 0; i < this.writeBuffer.length; i++) {\n const data = this.writeBuffer[i].data;\n if (data) {\n payloadSize += (0, util_js_1.byteLength)(data);\n }\n if (i > 0 && payloadSize > this.maxPayload) {\n debug(\"only send %d out of %d packets\", i, this.writeBuffer.length);\n return this.writeBuffer.slice(0, i);\n }\n payloadSize += 2; // separator + packet type\n }\n debug(\"payload size is %d (max: %d)\", payloadSize, this.maxPayload);\n return this.writeBuffer;\n }\n /**\n * Sends a message.\n *\n * @param {String} message.\n * @param {Function} callback function.\n * @param {Object} options.\n * @return {Socket} for chaining.\n * @api public\n */\n write(msg, options, fn) {\n this.sendPacket(\"message\", msg, options, fn);\n return this;\n }\n send(msg, options, fn) {\n this.sendPacket(\"message\", msg, options, fn);\n return this;\n }\n /**\n * Sends a packet.\n *\n * @param {String} packet type.\n * @param {String} data.\n * @param {Object} options.\n * @param {Function} callback function.\n * @api private\n */\n sendPacket(type, data, options, fn) {\n if (\"function\" === typeof data) {\n fn = data;\n data = undefined;\n }\n if (\"function\" === typeof options) {\n fn = options;\n options = null;\n }\n if (\"closing\" === this.readyState || \"closed\" === this.readyState) {\n return;\n }\n options = options || {};\n options.compress = false !== options.compress;\n const packet = {\n type: type,\n data: data,\n options: options\n };\n this.emitReserved(\"packetCreate\", packet);\n this.writeBuffer.push(packet);\n if (fn)\n this.once(\"flush\", fn);\n this.flush();\n }\n /**\n * Closes the connection.\n *\n * @api public\n */\n close() {\n const close = () => {\n this.onClose(\"forced close\");\n debug(\"socket closing - telling transport to close\");\n this.transport.close();\n };\n const cleanupAndClose = () => {\n this.off(\"upgrade\", cleanupAndClose);\n this.off(\"upgradeError\", cleanupAndClose);\n close();\n };\n const waitForUpgrade = () => {\n // wait for upgrade to finish since we can't send packets while pausing a transport\n this.once(\"upgrade\", cleanupAndClose);\n this.once(\"upgradeError\", cleanupAndClose);\n };\n if (\"opening\" === this.readyState || \"open\" === this.readyState) {\n this.readyState = \"closing\";\n if (this.writeBuffer.length) {\n this.once(\"drain\", () => {\n if (this.upgrading) {\n waitForUpgrade();\n }\n else {\n close();\n }\n });\n }\n else if (this.upgrading) {\n waitForUpgrade();\n }\n else {\n close();\n }\n }\n return this;\n }\n /**\n * Called upon transport error\n *\n * @api private\n */\n onError(err) {\n debug(\"socket error %j\", err);\n Socket.priorWebsocketSuccess = false;\n this.emitReserved(\"error\", err);\n this.onClose(\"transport error\", err);\n }\n /**\n * Called upon transport close.\n *\n * @api private\n */\n onClose(reason, description) {\n if (\"opening\" === this.readyState ||\n \"open\" === this.readyState ||\n \"closing\" === this.readyState) {\n debug('socket close with reason: \"%s\"', reason);\n // clear timers\n this.clearTimeoutFn(this.pingTimeoutTimer);\n // stop event from firing again for transport\n this.transport.removeAllListeners(\"close\");\n // ensure transport won't stay open\n this.transport.close();\n // ignore further transport communication\n this.transport.removeAllListeners();\n if (typeof removeEventListener === \"function\") {\n removeEventListener(\"beforeunload\", this.beforeunloadEventListener, false);\n removeEventListener(\"offline\", this.offlineEventListener, false);\n }\n // set ready state\n this.readyState = \"closed\";\n // clear session id\n this.id = null;\n // emit close event\n this.emitReserved(\"close\", reason, description);\n // clean buffers after, so users can still\n // grab the buffers on `close` event\n this.writeBuffer = [];\n this.prevBufferLen = 0;\n }\n }\n /**\n * Filters upgrades, returning only those matching client transports.\n *\n * @param {Array} server upgrades\n * @api private\n *\n */\n filterUpgrades(upgrades) {\n const filteredUpgrades = [];\n let i = 0;\n const j = upgrades.length;\n for (; i < j; i++) {\n if (~this.transports.indexOf(upgrades[i]))\n filteredUpgrades.push(upgrades[i]);\n }\n return filteredUpgrades;\n }\n}\nexports.Socket = Socket;\nSocket.protocol = engine_io_parser_1.protocol;\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/socket.js?");
5628
+ eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Request = exports.Polling = void 0;\nconst transport_js_1 = __webpack_require__(/*! ../transport.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transport.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst yeast_js_1 = __webpack_require__(/*! ../contrib/yeast.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/yeast.js\");\nconst parseqs_js_1 = __webpack_require__(/*! ../contrib/parseqs.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseqs.js\");\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst xmlhttprequest_js_1 = __webpack_require__(/*! ./xmlhttprequest.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst util_js_1 = __webpack_require__(/*! ../util.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/util.js\");\nconst globalThis_js_1 = __webpack_require__(/*! ../globalThis.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/globalThis.browser.js\");\nconst debug = (0, debug_1.default)(\"engine.io-client:polling\"); // debug()\nfunction empty() { }\nconst hasXHR2 = (function () {\n const xhr = new xmlhttprequest_js_1.XHR({\n xdomain: false\n });\n return null != xhr.responseType;\n})();\nclass Polling extends transport_js_1.Transport {\n /**\n * XHR Polling constructor.\n *\n * @param {Object} opts\n * @api public\n */\n constructor(opts) {\n super(opts);\n this.polling = false;\n if (typeof location !== \"undefined\") {\n const isSSL = \"https:\" === location.protocol;\n let port = location.port;\n // some user agents have empty `location.port`\n if (!port) {\n port = isSSL ? \"443\" : \"80\";\n }\n this.xd =\n (typeof location !== \"undefined\" &&\n opts.hostname !== location.hostname) ||\n port !== opts.port;\n this.xs = opts.secure !== isSSL;\n }\n /**\n * XHR supports binary\n */\n const forceBase64 = opts && opts.forceBase64;\n this.supportsBinary = hasXHR2 && !forceBase64;\n }\n /**\n * Transport name.\n */\n get name() {\n return \"polling\";\n }\n /**\n * Opens the socket (triggers polling). We write a PING message to determine\n * when the transport is open.\n *\n * @api private\n */\n doOpen() {\n this.poll();\n }\n /**\n * Pauses polling.\n *\n * @param {Function} callback upon buffers are flushed and transport is paused\n * @api private\n */\n pause(onPause) {\n this.readyState = \"pausing\";\n const pause = () => {\n debug(\"paused\");\n this.readyState = \"paused\";\n onPause();\n };\n if (this.polling || !this.writable) {\n let total = 0;\n if (this.polling) {\n debug(\"we are currently polling - waiting to pause\");\n total++;\n this.once(\"pollComplete\", function () {\n debug(\"pre-pause polling complete\");\n --total || pause();\n });\n }\n if (!this.writable) {\n debug(\"we are currently writing - waiting to pause\");\n total++;\n this.once(\"drain\", function () {\n debug(\"pre-pause writing complete\");\n --total || pause();\n });\n }\n }\n else {\n pause();\n }\n }\n /**\n * Starts polling cycle.\n *\n * @api public\n */\n poll() {\n debug(\"polling\");\n this.polling = true;\n this.doPoll();\n this.emitReserved(\"poll\");\n }\n /**\n * Overloads onData to detect payloads.\n *\n * @api private\n */\n onData(data) {\n debug(\"polling got data %s\", data);\n const callback = packet => {\n // if its the first message we consider the transport open\n if (\"opening\" === this.readyState && packet.type === \"open\") {\n this.onOpen();\n }\n // if its a close packet, we close the ongoing requests\n if (\"close\" === packet.type) {\n this.onClose({ description: \"transport closed by the server\" });\n return false;\n }\n // otherwise bypass onData and handle the message\n this.onPacket(packet);\n };\n // decode payload\n (0, engine_io_parser_1.decodePayload)(data, this.socket.binaryType).forEach(callback);\n // if an event did not trigger closing\n if (\"closed\" !== this.readyState) {\n // if we got data we're not polling\n this.polling = false;\n this.emitReserved(\"pollComplete\");\n if (\"open\" === this.readyState) {\n this.poll();\n }\n else {\n debug('ignoring poll - transport state \"%s\"', this.readyState);\n }\n }\n }\n /**\n * For polling, send a close packet.\n *\n * @api private\n */\n doClose() {\n const close = () => {\n debug(\"writing close packet\");\n this.write([{ type: \"close\" }]);\n };\n if (\"open\" === this.readyState) {\n debug(\"transport open - closing\");\n close();\n }\n else {\n // in case we're trying to close while\n // handshaking is in progress (GH-164)\n debug(\"transport not open - deferring close\");\n this.once(\"open\", close);\n }\n }\n /**\n * Writes a packets payload.\n *\n * @param {Array} data packets\n * @param {Function} drain callback\n * @api private\n */\n write(packets) {\n this.writable = false;\n (0, engine_io_parser_1.encodePayload)(packets, data => {\n this.doWrite(data, () => {\n this.writable = true;\n this.emitReserved(\"drain\");\n });\n });\n }\n /**\n * Generates uri for connection.\n *\n * @api private\n */\n uri() {\n let query = this.query || {};\n const schema = this.opts.secure ? \"https\" : \"http\";\n let port = \"\";\n // cache busting is forced\n if (false !== this.opts.timestampRequests) {\n query[this.opts.timestampParam] = (0, yeast_js_1.yeast)();\n }\n if (!this.supportsBinary && !query.sid) {\n query.b64 = 1;\n }\n // avoid port if default for schema\n if (this.opts.port &&\n ((\"https\" === schema && Number(this.opts.port) !== 443) ||\n (\"http\" === schema && Number(this.opts.port) !== 80))) {\n port = \":\" + this.opts.port;\n }\n const encodedQuery = (0, parseqs_js_1.encode)(query);\n const ipv6 = this.opts.hostname.indexOf(\":\") !== -1;\n return (schema +\n \"://\" +\n (ipv6 ? \"[\" + this.opts.hostname + \"]\" : this.opts.hostname) +\n port +\n this.opts.path +\n (encodedQuery.length ? \"?\" + encodedQuery : \"\"));\n }\n /**\n * Creates a request.\n *\n * @param {String} method\n * @api private\n */\n request(opts = {}) {\n Object.assign(opts, { xd: this.xd, xs: this.xs }, this.opts);\n return new Request(this.uri(), opts);\n }\n /**\n * Sends data.\n *\n * @param {String} data to send.\n * @param {Function} called upon flush.\n * @api private\n */\n doWrite(data, fn) {\n const req = this.request({\n method: \"POST\",\n data: data\n });\n req.on(\"success\", fn);\n req.on(\"error\", (xhrStatus, context) => {\n this.onError(\"xhr post error\", xhrStatus, context);\n });\n }\n /**\n * Starts a poll cycle.\n *\n * @api private\n */\n doPoll() {\n debug(\"xhr poll\");\n const req = this.request();\n req.on(\"data\", this.onData.bind(this));\n req.on(\"error\", (xhrStatus, context) => {\n this.onError(\"xhr poll error\", xhrStatus, context);\n });\n this.pollXhr = req;\n }\n}\nexports.Polling = Polling;\nclass Request extends component_emitter_1.Emitter {\n /**\n * Request constructor\n *\n * @param {Object} options\n * @api public\n */\n constructor(uri, opts) {\n super();\n (0, util_js_1.installTimerFunctions)(this, opts);\n this.opts = opts;\n this.method = opts.method || \"GET\";\n this.uri = uri;\n this.async = false !== opts.async;\n this.data = undefined !== opts.data ? opts.data : null;\n this.create();\n }\n /**\n * Creates the XHR object and sends the request.\n *\n * @api private\n */\n create() {\n const opts = (0, util_js_1.pick)(this.opts, \"agent\", \"pfx\", \"key\", \"passphrase\", \"cert\", \"ca\", \"ciphers\", \"rejectUnauthorized\", \"autoUnref\");\n opts.xdomain = !!this.opts.xd;\n opts.xscheme = !!this.opts.xs;\n const xhr = (this.xhr = new xmlhttprequest_js_1.XHR(opts));\n try {\n debug(\"xhr open %s: %s\", this.method, this.uri);\n xhr.open(this.method, this.uri, this.async);\n try {\n if (this.opts.extraHeaders) {\n xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true);\n for (let i in this.opts.extraHeaders) {\n if (this.opts.extraHeaders.hasOwnProperty(i)) {\n xhr.setRequestHeader(i, this.opts.extraHeaders[i]);\n }\n }\n }\n }\n catch (e) { }\n if (\"POST\" === this.method) {\n try {\n xhr.setRequestHeader(\"Content-type\", \"text/plain;charset=UTF-8\");\n }\n catch (e) { }\n }\n try {\n xhr.setRequestHeader(\"Accept\", \"*/*\");\n }\n catch (e) { }\n // ie6 check\n if (\"withCredentials\" in xhr) {\n xhr.withCredentials = this.opts.withCredentials;\n }\n if (this.opts.requestTimeout) {\n xhr.timeout = this.opts.requestTimeout;\n }\n xhr.onreadystatechange = () => {\n if (4 !== xhr.readyState)\n return;\n if (200 === xhr.status || 1223 === xhr.status) {\n this.onLoad();\n }\n else {\n // make sure the `error` event handler that's user-set\n // does not throw in the same tick and gets caught here\n this.setTimeoutFn(() => {\n this.onError(typeof xhr.status === \"number\" ? xhr.status : 0);\n }, 0);\n }\n };\n debug(\"xhr data %s\", this.data);\n xhr.send(this.data);\n }\n catch (e) {\n // Need to defer since .create() is called directly from the constructor\n // and thus the 'error' event can only be only bound *after* this exception\n // occurs. Therefore, also, we cannot throw here at all.\n this.setTimeoutFn(() => {\n this.onError(e);\n }, 0);\n return;\n }\n if (typeof document !== \"undefined\") {\n this.index = Request.requestsCount++;\n Request.requests[this.index] = this;\n }\n }\n /**\n * Called upon error.\n *\n * @api private\n */\n onError(err) {\n this.emitReserved(\"error\", err, this.xhr);\n this.cleanup(true);\n }\n /**\n * Cleans up house.\n *\n * @api private\n */\n cleanup(fromError) {\n if (\"undefined\" === typeof this.xhr || null === this.xhr) {\n return;\n }\n this.xhr.onreadystatechange = empty;\n if (fromError) {\n try {\n this.xhr.abort();\n }\n catch (e) { }\n }\n if (typeof document !== \"undefined\") {\n delete Request.requests[this.index];\n }\n this.xhr = null;\n }\n /**\n * Called upon load.\n *\n * @api private\n */\n onLoad() {\n const data = this.xhr.responseText;\n if (data !== null) {\n this.emitReserved(\"data\", data);\n this.emitReserved(\"success\");\n this.cleanup();\n }\n }\n /**\n * Aborts the request.\n *\n * @api public\n */\n abort() {\n this.cleanup();\n }\n}\nexports.Request = Request;\nRequest.requestsCount = 0;\nRequest.requests = {};\n/**\n * Aborts pending requests when unloading the window. This is needed to prevent\n * memory leaks (e.g. when using IE) and to ensure that no spurious error is\n * emitted.\n */\nif (typeof document !== \"undefined\") {\n // @ts-ignore\n if (typeof attachEvent === \"function\") {\n // @ts-ignore\n attachEvent(\"onunload\", unloadHandler);\n }\n else if (typeof addEventListener === \"function\") {\n const terminationEvent = \"onpagehide\" in globalThis_js_1.globalThisShim ? \"pagehide\" : \"unload\";\n addEventListener(terminationEvent, unloadHandler, false);\n }\n}\nfunction unloadHandler() {\n for (let i in Request.requests) {\n if (Request.requests.hasOwnProperty(i)) {\n Request.requests[i].abort();\n }\n }\n}\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/transports/polling.js?");
5572
5629
 
5573
5630
  /***/ }),
5574
5631
 
5575
- /***/ "./node_modules/engine.io-client/build/cjs/transport.js":
5576
- /*!**************************************************************!*\
5577
- !*** ./node_modules/engine.io-client/build/cjs/transport.js ***!
5578
- \**************************************************************/
5632
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket-constructor.browser.js":
5633
+ /*!******************************************************************************************************!*\
5634
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket-constructor.browser.js ***!
5635
+ \******************************************************************************************************/
5636
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5637
+
5638
+ "use strict";
5639
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.defaultBinaryType = exports.usingBrowserWebSocket = exports.WebSocket = exports.nextTick = void 0;\nconst globalThis_js_1 = __webpack_require__(/*! ../globalThis.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/globalThis.browser.js\");\nexports.nextTick = (() => {\n const isPromiseAvailable = typeof Promise === \"function\" && typeof Promise.resolve === \"function\";\n if (isPromiseAvailable) {\n return cb => Promise.resolve().then(cb);\n }\n else {\n return (cb, setTimeoutFn) => setTimeoutFn(cb, 0);\n }\n})();\nexports.WebSocket = globalThis_js_1.globalThisShim.WebSocket || globalThis_js_1.globalThisShim.MozWebSocket;\nexports.usingBrowserWebSocket = true;\nexports.defaultBinaryType = \"arraybuffer\";\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket-constructor.browser.js?");
5640
+
5641
+ /***/ }),
5642
+
5643
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket.js":
5644
+ /*!**********************************************************************************!*\
5645
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket.js ***!
5646
+ \**********************************************************************************/
5579
5647
  /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5580
5648
 
5581
5649
  "use strict";
5582
- eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Transport = void 0;\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst util_js_1 = __webpack_require__(/*! ./util.js */ \"./node_modules/engine.io-client/build/cjs/util.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst debug = (0, debug_1.default)(\"engine.io-client:transport\"); // debug()\nclass TransportError extends Error {\n constructor(reason, description, context) {\n super(reason);\n this.description = description;\n this.context = context;\n this.type = \"TransportError\";\n }\n}\nclass Transport extends component_emitter_1.Emitter {\n /**\n * Transport abstract constructor.\n *\n * @param {Object} options.\n * @api private\n */\n constructor(opts) {\n super();\n this.writable = false;\n (0, util_js_1.installTimerFunctions)(this, opts);\n this.opts = opts;\n this.query = opts.query;\n this.readyState = \"\";\n this.socket = opts.socket;\n }\n /**\n * Emits an error.\n *\n * @param {String} reason\n * @param description\n * @param context - the error context\n * @return {Transport} for chaining\n * @api protected\n */\n onError(reason, description, context) {\n super.emitReserved(\"error\", new TransportError(reason, description, context));\n return this;\n }\n /**\n * Opens the transport.\n *\n * @api public\n */\n open() {\n if (\"closed\" === this.readyState || \"\" === this.readyState) {\n this.readyState = \"opening\";\n this.doOpen();\n }\n return this;\n }\n /**\n * Closes the transport.\n *\n * @api public\n */\n close() {\n if (\"opening\" === this.readyState || \"open\" === this.readyState) {\n this.doClose();\n this.onClose();\n }\n return this;\n }\n /**\n * Sends multiple packets.\n *\n * @param {Array} packets\n * @api public\n */\n send(packets) {\n if (\"open\" === this.readyState) {\n this.write(packets);\n }\n else {\n // this might happen if the transport was silently closed in the beforeunload event handler\n debug(\"transport is not open, discarding packets\");\n }\n }\n /**\n * Called upon open\n *\n * @api protected\n */\n onOpen() {\n this.readyState = \"open\";\n this.writable = true;\n super.emitReserved(\"open\");\n }\n /**\n * Called with data.\n *\n * @param {String} data\n * @api protected\n */\n onData(data) {\n const packet = (0, engine_io_parser_1.decodePacket)(data, this.socket.binaryType);\n this.onPacket(packet);\n }\n /**\n * Called with a decoded packet.\n *\n * @api protected\n */\n onPacket(packet) {\n super.emitReserved(\"packet\", packet);\n }\n /**\n * Called upon close.\n *\n * @api protected\n */\n onClose(details) {\n this.readyState = \"closed\";\n super.emitReserved(\"close\", details);\n }\n}\nexports.Transport = Transport;\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/transport.js?");
5650
+ eval("/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js */ \"./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js\")[\"Buffer\"];\n\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.WS = void 0;\nconst transport_js_1 = __webpack_require__(/*! ../transport.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transport.js\");\nconst parseqs_js_1 = __webpack_require__(/*! ../contrib/parseqs.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/parseqs.js\");\nconst yeast_js_1 = __webpack_require__(/*! ../contrib/yeast.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/yeast.js\");\nconst util_js_1 = __webpack_require__(/*! ../util.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/util.js\");\nconst websocket_constructor_js_1 = __webpack_require__(/*! ./websocket-constructor.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket-constructor.browser.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst debug = (0, debug_1.default)(\"engine.io-client:websocket\"); // debug()\n// detect ReactNative environment\nconst isReactNative = typeof navigator !== \"undefined\" &&\n typeof navigator.product === \"string\" &&\n navigator.product.toLowerCase() === \"reactnative\";\nclass WS extends transport_js_1.Transport {\n /**\n * WebSocket transport constructor.\n *\n * @api {Object} connection options\n * @api public\n */\n constructor(opts) {\n super(opts);\n this.supportsBinary = !opts.forceBase64;\n }\n /**\n * Transport name.\n *\n * @api public\n */\n get name() {\n return \"websocket\";\n }\n /**\n * Opens socket.\n *\n * @api private\n */\n doOpen() {\n if (!this.check()) {\n // let probe timeout\n return;\n }\n const uri = this.uri();\n const protocols = this.opts.protocols;\n // React Native only supports the 'headers' option, and will print a warning if anything else is passed\n const opts = isReactNative\n ? {}\n : (0, util_js_1.pick)(this.opts, \"agent\", \"perMessageDeflate\", \"pfx\", \"key\", \"passphrase\", \"cert\", \"ca\", \"ciphers\", \"rejectUnauthorized\", \"localAddress\", \"protocolVersion\", \"origin\", \"maxPayload\", \"family\", \"checkServerIdentity\");\n if (this.opts.extraHeaders) {\n opts.headers = this.opts.extraHeaders;\n }\n try {\n this.ws =\n websocket_constructor_js_1.usingBrowserWebSocket && !isReactNative\n ? protocols\n ? new websocket_constructor_js_1.WebSocket(uri, protocols)\n : new websocket_constructor_js_1.WebSocket(uri)\n : new websocket_constructor_js_1.WebSocket(uri, protocols, opts);\n }\n catch (err) {\n return this.emitReserved(\"error\", err);\n }\n this.ws.binaryType = this.socket.binaryType || websocket_constructor_js_1.defaultBinaryType;\n this.addEventListeners();\n }\n /**\n * Adds event listeners to the socket\n *\n * @api private\n */\n addEventListeners() {\n this.ws.onopen = () => {\n if (this.opts.autoUnref) {\n this.ws._socket.unref();\n }\n this.onOpen();\n };\n this.ws.onclose = closeEvent => this.onClose({\n description: \"websocket connection closed\",\n context: closeEvent\n });\n this.ws.onmessage = ev => this.onData(ev.data);\n this.ws.onerror = e => this.onError(\"websocket error\", e);\n }\n /**\n * Writes data to socket.\n *\n * @param {Array} array of packets.\n * @api private\n */\n write(packets) {\n this.writable = false;\n // encodePacket efficient as it uses WS framing\n // no need for encodePayload\n for (let i = 0; i < packets.length; i++) {\n const packet = packets[i];\n const lastPacket = i === packets.length - 1;\n (0, engine_io_parser_1.encodePacket)(packet, this.supportsBinary, data => {\n // always create a new object (GH-437)\n const opts = {};\n if (!websocket_constructor_js_1.usingBrowserWebSocket) {\n if (packet.options) {\n opts.compress = packet.options.compress;\n }\n if (this.opts.perMessageDeflate) {\n const len = \n // @ts-ignore\n \"string\" === typeof data ? Buffer.byteLength(data) : data.length;\n if (len < this.opts.perMessageDeflate.threshold) {\n opts.compress = false;\n }\n }\n }\n // Sometimes the websocket has already been closed but the browser didn't\n // have a chance of informing us about it yet, in that case send will\n // throw an error\n try {\n if (websocket_constructor_js_1.usingBrowserWebSocket) {\n // TypeError is thrown when passing the second argument on Safari\n this.ws.send(data);\n }\n else {\n this.ws.send(data, opts);\n }\n }\n catch (e) {\n debug(\"websocket closed before onclose event\");\n }\n if (lastPacket) {\n // fake drain\n // defer to next tick to allow Socket to clear writeBuffer\n (0, websocket_constructor_js_1.nextTick)(() => {\n this.writable = true;\n this.emitReserved(\"drain\");\n }, this.setTimeoutFn);\n }\n });\n }\n }\n /**\n * Closes socket.\n *\n * @api private\n */\n doClose() {\n if (typeof this.ws !== \"undefined\") {\n this.ws.close();\n this.ws = null;\n }\n }\n /**\n * Generates uri for connection.\n *\n * @api private\n */\n uri() {\n let query = this.query || {};\n const schema = this.opts.secure ? \"wss\" : \"ws\";\n let port = \"\";\n // avoid port if default for schema\n if (this.opts.port &&\n ((\"wss\" === schema && Number(this.opts.port) !== 443) ||\n (\"ws\" === schema && Number(this.opts.port) !== 80))) {\n port = \":\" + this.opts.port;\n }\n // append timestamp to URI\n if (this.opts.timestampRequests) {\n query[this.opts.timestampParam] = (0, yeast_js_1.yeast)();\n }\n // communicate binary support capabilities\n if (!this.supportsBinary) {\n query.b64 = 1;\n }\n const encodedQuery = (0, parseqs_js_1.encode)(query);\n const ipv6 = this.opts.hostname.indexOf(\":\") !== -1;\n return (schema +\n \"://\" +\n (ipv6 ? \"[\" + this.opts.hostname + \"]\" : this.opts.hostname) +\n port +\n this.opts.path +\n (encodedQuery.length ? \"?\" + encodedQuery : \"\"));\n }\n /**\n * Feature detection for WebSocket.\n *\n * @return {Boolean} whether this transport is available.\n * @api public\n */\n check() {\n return !!websocket_constructor_js_1.WebSocket;\n }\n}\nexports.WS = WS;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/transports/websocket.js?");
5583
5651
 
5584
5652
  /***/ }),
5585
5653
 
5586
- /***/ "./node_modules/engine.io-client/build/cjs/transports/index.js":
5587
- /*!*********************************************************************!*\
5588
- !*** ./node_modules/engine.io-client/build/cjs/transports/index.js ***!
5589
- \*********************************************************************/
5654
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js":
5655
+ /*!***********************************************************************************************!*\
5656
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js ***!
5657
+ \***********************************************************************************************/
5590
5658
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5591
5659
 
5592
5660
  "use strict";
5593
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.transports = void 0;\nconst polling_js_1 = __webpack_require__(/*! ./polling.js */ \"./node_modules/engine.io-client/build/cjs/transports/polling.js\");\nconst websocket_js_1 = __webpack_require__(/*! ./websocket.js */ \"./node_modules/engine.io-client/build/cjs/transports/websocket.js\");\nexports.transports = {\n websocket: websocket_js_1.WS,\n polling: polling_js_1.Polling\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/transports/index.js?");
5661
+ eval("\n// browser shim for xmlhttprequest module\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.XHR = void 0;\nconst has_cors_js_1 = __webpack_require__(/*! ../contrib/has-cors.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/contrib/has-cors.js\");\nconst globalThis_js_1 = __webpack_require__(/*! ../globalThis.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/globalThis.browser.js\");\nfunction XHR(opts) {\n const xdomain = opts.xdomain;\n // XMLHttpRequest can be disabled on IE\n try {\n if (\"undefined\" !== typeof XMLHttpRequest && (!xdomain || has_cors_js_1.hasCORS)) {\n return new XMLHttpRequest();\n }\n }\n catch (e) { }\n if (!xdomain) {\n try {\n return new globalThis_js_1.globalThisShim[[\"Active\"].concat(\"Object\").join(\"X\")](\"Microsoft.XMLHTTP\");\n }\n catch (e) { }\n }\n}\nexports.XHR = XHR;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js?");
5594
5662
 
5595
5663
  /***/ }),
5596
5664
 
5597
- /***/ "./node_modules/engine.io-client/build/cjs/transports/polling.js":
5598
- /*!***********************************************************************!*\
5599
- !*** ./node_modules/engine.io-client/build/cjs/transports/polling.js ***!
5600
- \***********************************************************************/
5665
+ /***/ "./node_modules/@kingsds/engine.io-client/build/cjs/util.js":
5666
+ /*!******************************************************************!*\
5667
+ !*** ./node_modules/@kingsds/engine.io-client/build/cjs/util.js ***!
5668
+ \******************************************************************/
5669
+ /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5670
+
5671
+ "use strict";
5672
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.byteLength = exports.installTimerFunctions = exports.pick = void 0;\nconst globalThis_js_1 = __webpack_require__(/*! ./globalThis.js */ \"./node_modules/@kingsds/engine.io-client/build/cjs/globalThis.browser.js\");\nfunction pick(obj, ...attr) {\n return attr.reduce((acc, k) => {\n if (obj.hasOwnProperty(k)) {\n acc[k] = obj[k];\n }\n return acc;\n }, {});\n}\nexports.pick = pick;\n// Keep a reference to the real timeout functions so they can be used when overridden\nconst NATIVE_SET_TIMEOUT = setTimeout;\nconst NATIVE_CLEAR_TIMEOUT = clearTimeout;\nfunction installTimerFunctions(obj, opts) {\n if (opts.useNativeTimers) {\n obj.setTimeoutFn = NATIVE_SET_TIMEOUT.bind(globalThis_js_1.globalThisShim);\n obj.clearTimeoutFn = NATIVE_CLEAR_TIMEOUT.bind(globalThis_js_1.globalThisShim);\n }\n else {\n obj.setTimeoutFn = setTimeout.bind(globalThis_js_1.globalThisShim);\n obj.clearTimeoutFn = clearTimeout.bind(globalThis_js_1.globalThisShim);\n }\n}\nexports.installTimerFunctions = installTimerFunctions;\n// base64 encoded buffers are about 33% bigger (https://en.wikipedia.org/wiki/Base64)\nconst BASE64_OVERHEAD = 1.33;\n// we could also have used `new Blob([obj]).size`, but it isn't supported in IE9\nfunction byteLength(obj) {\n if (typeof obj === \"string\") {\n return utf8Length(obj);\n }\n // arraybuffer or blob\n return Math.ceil((obj.byteLength || obj.size) * BASE64_OVERHEAD);\n}\nexports.byteLength = byteLength;\nfunction utf8Length(str) {\n let c = 0, length = 0;\n for (let i = 0, l = str.length; i < l; i++) {\n c = str.charCodeAt(i);\n if (c < 0x80) {\n length += 1;\n }\n else if (c < 0x800) {\n length += 2;\n }\n else if (c < 0xd800 || c >= 0xe000) {\n length += 3;\n }\n else {\n i++;\n length += 4;\n }\n }\n return length;\n}\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/engine.io-client/build/cjs/util.js?");
5673
+
5674
+ /***/ }),
5675
+
5676
+ /***/ "./node_modules/@kingsds/socket.io-client/build/cjs/contrib/backo2.js":
5677
+ /*!****************************************************************************!*\
5678
+ !*** ./node_modules/@kingsds/socket.io-client/build/cjs/contrib/backo2.js ***!
5679
+ \****************************************************************************/
5680
+ /***/ ((__unused_webpack_module, exports) => {
5681
+
5682
+ "use strict";
5683
+ eval("\n/**\n * Initialize backoff timer with `opts`.\n *\n * - `min` initial timeout in milliseconds [100]\n * - `max` max timeout [10000]\n * - `jitter` [0]\n * - `factor` [2]\n *\n * @param {Object} opts\n * @api public\n */\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Backoff = void 0;\nfunction Backoff(opts) {\n opts = opts || {};\n this.ms = opts.min || 100;\n this.max = opts.max || 10000;\n this.factor = opts.factor || 2;\n this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0;\n this.attempts = 0;\n}\nexports.Backoff = Backoff;\n/**\n * Return the backoff duration.\n *\n * @return {Number}\n * @api public\n */\nBackoff.prototype.duration = function () {\n var ms = this.ms * Math.pow(this.factor, this.attempts++);\n if (this.jitter) {\n var rand = Math.random();\n var deviation = Math.floor(rand * this.jitter * ms);\n ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation;\n }\n return Math.min(ms, this.max) | 0;\n};\n/**\n * Reset the number of attempts.\n *\n * @api public\n */\nBackoff.prototype.reset = function () {\n this.attempts = 0;\n};\n/**\n * Set the minimum duration\n *\n * @api public\n */\nBackoff.prototype.setMin = function (min) {\n this.ms = min;\n};\n/**\n * Set the maximum duration\n *\n * @api public\n */\nBackoff.prototype.setMax = function (max) {\n this.max = max;\n};\n/**\n * Set the jitter\n *\n * @api public\n */\nBackoff.prototype.setJitter = function (jitter) {\n this.jitter = jitter;\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/build/cjs/contrib/backo2.js?");
5684
+
5685
+ /***/ }),
5686
+
5687
+ /***/ "./node_modules/@kingsds/socket.io-client/build/cjs/index.js":
5688
+ /*!*******************************************************************!*\
5689
+ !*** ./node_modules/@kingsds/socket.io-client/build/cjs/index.js ***!
5690
+ \*******************************************************************/
5691
+ /***/ (function(module, exports, __webpack_require__) {
5692
+
5693
+ "use strict";
5694
+ eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports[\"default\"] = exports.connect = exports.io = exports.Socket = exports.Manager = exports.protocol = void 0;\nconst url_js_1 = __webpack_require__(/*! ./url.js */ \"./node_modules/@kingsds/socket.io-client/build/cjs/url.js\");\nconst manager_js_1 = __webpack_require__(/*! ./manager.js */ \"./node_modules/@kingsds/socket.io-client/build/cjs/manager.js\");\nObject.defineProperty(exports, \"Manager\", ({ enumerable: true, get: function () { return manager_js_1.Manager; } }));\nconst socket_js_1 = __webpack_require__(/*! ./socket.js */ \"./node_modules/@kingsds/socket.io-client/build/cjs/socket.js\");\nObject.defineProperty(exports, \"Socket\", ({ enumerable: true, get: function () { return socket_js_1.Socket; } }));\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/@kingsds/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = (0, debug_1.default)(\"socket.io-client\"); // debug()\n/**\n * Managers cache.\n */\nconst cache = {};\nfunction lookup(uri, opts) {\n if (typeof uri === \"object\") {\n opts = uri;\n uri = undefined;\n }\n opts = opts || {};\n const parsed = (0, url_js_1.url)(uri, opts.path || \"/socket.io\");\n const source = parsed.source;\n const id = parsed.id;\n const path = parsed.path;\n const sameNamespace = cache[id] && path in cache[id][\"nsps\"];\n const newConnection = opts.forceNew ||\n opts[\"force new connection\"] ||\n false === opts.multiplex ||\n sameNamespace;\n let io;\n if (newConnection) {\n debug(\"ignoring socket cache for %s\", source);\n io = new manager_js_1.Manager(source, opts);\n }\n else {\n if (!cache[id]) {\n debug(\"new io instance for %s\", source);\n cache[id] = new manager_js_1.Manager(source, opts);\n }\n io = cache[id];\n }\n if (parsed.query && !opts.query) {\n opts.query = parsed.queryKey;\n }\n return io.socket(parsed.path, opts);\n}\nexports.io = lookup;\nexports.connect = lookup;\nexports[\"default\"] = lookup;\n// so that \"lookup\" can be used both as a function (e.g. `io(...)`) and as a\n// namespace (e.g. `io.connect(...)`), for backward compatibility\nObject.assign(lookup, {\n Manager: manager_js_1.Manager,\n Socket: socket_js_1.Socket,\n io: lookup,\n connect: lookup,\n});\n/**\n * Protocol version.\n *\n * @public\n */\nvar socket_io_parser_1 = __webpack_require__(/*! socket.io-parser */ \"./node_modules/socket.io-parser/build/cjs/index.js\");\nObject.defineProperty(exports, \"protocol\", ({ enumerable: true, get: function () { return socket_io_parser_1.protocol; } }));\n\nmodule.exports = lookup;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/build/cjs/index.js?");
5695
+
5696
+ /***/ }),
5697
+
5698
+ /***/ "./node_modules/@kingsds/socket.io-client/build/cjs/manager.js":
5699
+ /*!*********************************************************************!*\
5700
+ !*** ./node_modules/@kingsds/socket.io-client/build/cjs/manager.js ***!
5701
+ \*********************************************************************/
5601
5702
  /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5602
5703
 
5603
5704
  "use strict";
5604
- eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Request = exports.Polling = void 0;\nconst transport_js_1 = __webpack_require__(/*! ../transport.js */ \"./node_modules/engine.io-client/build/cjs/transport.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst yeast_js_1 = __webpack_require__(/*! ../contrib/yeast.js */ \"./node_modules/engine.io-client/build/cjs/contrib/yeast.js\");\nconst parseqs_js_1 = __webpack_require__(/*! ../contrib/parseqs.js */ \"./node_modules/engine.io-client/build/cjs/contrib/parseqs.js\");\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst xmlhttprequest_js_1 = __webpack_require__(/*! ./xmlhttprequest.js */ \"./node_modules/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst util_js_1 = __webpack_require__(/*! ../util.js */ \"./node_modules/engine.io-client/build/cjs/util.js\");\nconst globalThis_js_1 = __webpack_require__(/*! ../globalThis.js */ \"./node_modules/engine.io-client/build/cjs/globalThis.browser.js\");\nconst debug = (0, debug_1.default)(\"engine.io-client:polling\"); // debug()\nfunction empty() { }\nconst hasXHR2 = (function () {\n const xhr = new xmlhttprequest_js_1.XHR({\n xdomain: false\n });\n return null != xhr.responseType;\n})();\nclass Polling extends transport_js_1.Transport {\n /**\n * XHR Polling constructor.\n *\n * @param {Object} opts\n * @api public\n */\n constructor(opts) {\n super(opts);\n this.polling = false;\n if (typeof location !== \"undefined\") {\n const isSSL = \"https:\" === location.protocol;\n let port = location.port;\n // some user agents have empty `location.port`\n if (!port) {\n port = isSSL ? \"443\" : \"80\";\n }\n this.xd =\n (typeof location !== \"undefined\" &&\n opts.hostname !== location.hostname) ||\n port !== opts.port;\n this.xs = opts.secure !== isSSL;\n }\n /**\n * XHR supports binary\n */\n const forceBase64 = opts && opts.forceBase64;\n this.supportsBinary = hasXHR2 && !forceBase64;\n }\n /**\n * Transport name.\n */\n get name() {\n return \"polling\";\n }\n /**\n * Opens the socket (triggers polling). We write a PING message to determine\n * when the transport is open.\n *\n * @api private\n */\n doOpen() {\n this.poll();\n }\n /**\n * Pauses polling.\n *\n * @param {Function} callback upon buffers are flushed and transport is paused\n * @api private\n */\n pause(onPause) {\n this.readyState = \"pausing\";\n const pause = () => {\n debug(\"paused\");\n this.readyState = \"paused\";\n onPause();\n };\n if (this.polling || !this.writable) {\n let total = 0;\n if (this.polling) {\n debug(\"we are currently polling - waiting to pause\");\n total++;\n this.once(\"pollComplete\", function () {\n debug(\"pre-pause polling complete\");\n --total || pause();\n });\n }\n if (!this.writable) {\n debug(\"we are currently writing - waiting to pause\");\n total++;\n this.once(\"drain\", function () {\n debug(\"pre-pause writing complete\");\n --total || pause();\n });\n }\n }\n else {\n pause();\n }\n }\n /**\n * Starts polling cycle.\n *\n * @api public\n */\n poll() {\n debug(\"polling\");\n this.polling = true;\n this.doPoll();\n this.emitReserved(\"poll\");\n }\n /**\n * Overloads onData to detect payloads.\n *\n * @api private\n */\n onData(data) {\n debug(\"polling got data %s\", data);\n const callback = packet => {\n // if its the first message we consider the transport open\n if (\"opening\" === this.readyState && packet.type === \"open\") {\n this.onOpen();\n }\n // if its a close packet, we close the ongoing requests\n if (\"close\" === packet.type) {\n this.onClose({ description: \"transport closed by the server\" });\n return false;\n }\n // otherwise bypass onData and handle the message\n this.onPacket(packet);\n };\n // decode payload\n (0, engine_io_parser_1.decodePayload)(data, this.socket.binaryType).forEach(callback);\n // if an event did not trigger closing\n if (\"closed\" !== this.readyState) {\n // if we got data we're not polling\n this.polling = false;\n this.emitReserved(\"pollComplete\");\n if (\"open\" === this.readyState) {\n this.poll();\n }\n else {\n debug('ignoring poll - transport state \"%s\"', this.readyState);\n }\n }\n }\n /**\n * For polling, send a close packet.\n *\n * @api private\n */\n doClose() {\n const close = () => {\n debug(\"writing close packet\");\n this.write([{ type: \"close\" }]);\n };\n if (\"open\" === this.readyState) {\n debug(\"transport open - closing\");\n close();\n }\n else {\n // in case we're trying to close while\n // handshaking is in progress (GH-164)\n debug(\"transport not open - deferring close\");\n this.once(\"open\", close);\n }\n }\n /**\n * Writes a packets payload.\n *\n * @param {Array} data packets\n * @param {Function} drain callback\n * @api private\n */\n write(packets) {\n this.writable = false;\n (0, engine_io_parser_1.encodePayload)(packets, data => {\n this.doWrite(data, () => {\n this.writable = true;\n this.emitReserved(\"drain\");\n });\n });\n }\n /**\n * Generates uri for connection.\n *\n * @api private\n */\n uri() {\n let query = this.query || {};\n const schema = this.opts.secure ? \"https\" : \"http\";\n let port = \"\";\n // cache busting is forced\n if (false !== this.opts.timestampRequests) {\n query[this.opts.timestampParam] = (0, yeast_js_1.yeast)();\n }\n if (!this.supportsBinary && !query.sid) {\n query.b64 = 1;\n }\n // avoid port if default for schema\n if (this.opts.port &&\n ((\"https\" === schema && Number(this.opts.port) !== 443) ||\n (\"http\" === schema && Number(this.opts.port) !== 80))) {\n port = \":\" + this.opts.port;\n }\n const encodedQuery = (0, parseqs_js_1.encode)(query);\n const ipv6 = this.opts.hostname.indexOf(\":\") !== -1;\n return (schema +\n \"://\" +\n (ipv6 ? \"[\" + this.opts.hostname + \"]\" : this.opts.hostname) +\n port +\n this.opts.path +\n (encodedQuery.length ? \"?\" + encodedQuery : \"\"));\n }\n /**\n * Creates a request.\n *\n * @param {String} method\n * @api private\n */\n request(opts = {}) {\n Object.assign(opts, { xd: this.xd, xs: this.xs }, this.opts);\n return new Request(this.uri(), opts);\n }\n /**\n * Sends data.\n *\n * @param {String} data to send.\n * @param {Function} called upon flush.\n * @api private\n */\n doWrite(data, fn) {\n const req = this.request({\n method: \"POST\",\n data: data\n });\n req.on(\"success\", fn);\n req.on(\"error\", (xhrStatus, context) => {\n this.onError(\"xhr post error\", xhrStatus, context);\n });\n }\n /**\n * Starts a poll cycle.\n *\n * @api private\n */\n doPoll() {\n debug(\"xhr poll\");\n const req = this.request();\n req.on(\"data\", this.onData.bind(this));\n req.on(\"error\", (xhrStatus, context) => {\n this.onError(\"xhr poll error\", xhrStatus, context);\n });\n this.pollXhr = req;\n }\n}\nexports.Polling = Polling;\nclass Request extends component_emitter_1.Emitter {\n /**\n * Request constructor\n *\n * @param {Object} options\n * @api public\n */\n constructor(uri, opts) {\n super();\n (0, util_js_1.installTimerFunctions)(this, opts);\n this.opts = opts;\n this.method = opts.method || \"GET\";\n this.uri = uri;\n this.async = false !== opts.async;\n this.data = undefined !== opts.data ? opts.data : null;\n this.create();\n }\n /**\n * Creates the XHR object and sends the request.\n *\n * @api private\n */\n create() {\n const opts = (0, util_js_1.pick)(this.opts, \"agent\", \"pfx\", \"key\", \"passphrase\", \"cert\", \"ca\", \"ciphers\", \"rejectUnauthorized\", \"autoUnref\");\n opts.xdomain = !!this.opts.xd;\n opts.xscheme = !!this.opts.xs;\n const xhr = (this.xhr = new xmlhttprequest_js_1.XHR(opts));\n try {\n debug(\"xhr open %s: %s\", this.method, this.uri);\n xhr.open(this.method, this.uri, this.async);\n try {\n if (this.opts.extraHeaders) {\n xhr.setDisableHeaderCheck && xhr.setDisableHeaderCheck(true);\n for (let i in this.opts.extraHeaders) {\n if (this.opts.extraHeaders.hasOwnProperty(i)) {\n xhr.setRequestHeader(i, this.opts.extraHeaders[i]);\n }\n }\n }\n }\n catch (e) { }\n if (\"POST\" === this.method) {\n try {\n xhr.setRequestHeader(\"Content-type\", \"text/plain;charset=UTF-8\");\n }\n catch (e) { }\n }\n try {\n xhr.setRequestHeader(\"Accept\", \"*/*\");\n }\n catch (e) { }\n // ie6 check\n if (\"withCredentials\" in xhr) {\n xhr.withCredentials = this.opts.withCredentials;\n }\n if (this.opts.requestTimeout) {\n xhr.timeout = this.opts.requestTimeout;\n }\n xhr.onreadystatechange = () => {\n if (4 !== xhr.readyState)\n return;\n if (200 === xhr.status || 1223 === xhr.status) {\n this.onLoad();\n }\n else {\n // make sure the `error` event handler that's user-set\n // does not throw in the same tick and gets caught here\n this.setTimeoutFn(() => {\n this.onError(typeof xhr.status === \"number\" ? xhr.status : 0);\n }, 0);\n }\n };\n debug(\"xhr data %s\", this.data);\n xhr.send(this.data);\n }\n catch (e) {\n // Need to defer since .create() is called directly from the constructor\n // and thus the 'error' event can only be only bound *after* this exception\n // occurs. Therefore, also, we cannot throw here at all.\n this.setTimeoutFn(() => {\n this.onError(e);\n }, 0);\n return;\n }\n if (typeof document !== \"undefined\") {\n this.index = Request.requestsCount++;\n Request.requests[this.index] = this;\n }\n }\n /**\n * Called upon error.\n *\n * @api private\n */\n onError(err) {\n this.emitReserved(\"error\", err, this.xhr);\n this.cleanup(true);\n }\n /**\n * Cleans up house.\n *\n * @api private\n */\n cleanup(fromError) {\n if (\"undefined\" === typeof this.xhr || null === this.xhr) {\n return;\n }\n this.xhr.onreadystatechange = empty;\n if (fromError) {\n try {\n this.xhr.abort();\n }\n catch (e) { }\n }\n if (typeof document !== \"undefined\") {\n delete Request.requests[this.index];\n }\n this.xhr = null;\n }\n /**\n * Called upon load.\n *\n * @api private\n */\n onLoad() {\n const data = this.xhr.responseText;\n if (data !== null) {\n this.emitReserved(\"data\", data);\n this.emitReserved(\"success\");\n this.cleanup();\n }\n }\n /**\n * Aborts the request.\n *\n * @api public\n */\n abort() {\n this.cleanup();\n }\n}\nexports.Request = Request;\nRequest.requestsCount = 0;\nRequest.requests = {};\n/**\n * Aborts pending requests when unloading the window. This is needed to prevent\n * memory leaks (e.g. when using IE) and to ensure that no spurious error is\n * emitted.\n */\nif (typeof document !== \"undefined\") {\n // @ts-ignore\n if (typeof attachEvent === \"function\") {\n // @ts-ignore\n attachEvent(\"onunload\", unloadHandler);\n }\n else if (typeof addEventListener === \"function\") {\n const terminationEvent = \"onpagehide\" in globalThis_js_1.globalThisShim ? \"pagehide\" : \"unload\";\n addEventListener(terminationEvent, unloadHandler, false);\n }\n}\nfunction unloadHandler() {\n for (let i in Request.requests) {\n if (Request.requests.hasOwnProperty(i)) {\n Request.requests[i].abort();\n }\n }\n}\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/transports/polling.js?");
5705
+ eval("\nvar __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n var desc = Object.getOwnPropertyDescriptor(m, k);\n if (!desc || (\"get\" in desc ? !m.__esModule : desc.writable || desc.configurable)) {\n desc = { enumerable: true, get: function() { return m[k]; } };\n }\n Object.defineProperty(o, k2, desc);\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n}));\nvar __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n});\nvar __importStar = (this && this.__importStar) || function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Manager = void 0;\nconst engine_io_client_1 = __webpack_require__(/*! @kingsds/engine.io-client */ \"./node_modules/@kingsds/engine.io-client/build/cjs/index.js\");\nconst socket_js_1 = __webpack_require__(/*! ./socket.js */ \"./node_modules/@kingsds/socket.io-client/build/cjs/socket.js\");\nconst parser = __importStar(__webpack_require__(/*! socket.io-parser */ \"./node_modules/socket.io-parser/build/cjs/index.js\"));\nconst on_js_1 = __webpack_require__(/*! ./on.js */ \"./node_modules/@kingsds/socket.io-client/build/cjs/on.js\");\nconst backo2_js_1 = __webpack_require__(/*! ./contrib/backo2.js */ \"./node_modules/@kingsds/socket.io-client/build/cjs/contrib/backo2.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/@kingsds/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = (0, debug_1.default)(\"socket.io-client:manager\"); // debug()\nclass Manager extends component_emitter_1.Emitter {\n constructor(uri, opts) {\n var _a;\n super();\n this.nsps = {};\n this.subs = [];\n if (uri && \"object\" === typeof uri) {\n opts = uri;\n uri = undefined;\n }\n opts = opts || {};\n opts.path = opts.path || \"/socket.io\";\n this.opts = opts;\n (0, engine_io_client_1.installTimerFunctions)(this, opts);\n this.reconnection(opts.reconnection !== false);\n this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);\n this.reconnectionDelay(opts.reconnectionDelay || 1000);\n this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);\n this.randomizationFactor((_a = opts.randomizationFactor) !== null && _a !== void 0 ? _a : 0.5);\n this.backoff = new backo2_js_1.Backoff({\n min: this.reconnectionDelay(),\n max: this.reconnectionDelayMax(),\n jitter: this.randomizationFactor(),\n });\n this.timeout(null == opts.timeout ? 20000 : opts.timeout);\n this._readyState = \"closed\";\n this.uri = uri;\n const _parser = opts.parser || parser;\n this.encoder = new _parser.Encoder();\n this.decoder = new _parser.Decoder();\n this._autoConnect = opts.autoConnect !== false;\n if (this._autoConnect)\n this.open();\n }\n reconnection(v) {\n if (!arguments.length)\n return this._reconnection;\n this._reconnection = !!v;\n return this;\n }\n reconnectionAttempts(v) {\n if (v === undefined)\n return this._reconnectionAttempts;\n this._reconnectionAttempts = v;\n return this;\n }\n reconnectionDelay(v) {\n var _a;\n if (v === undefined)\n return this._reconnectionDelay;\n this._reconnectionDelay = v;\n (_a = this.backoff) === null || _a === void 0 ? void 0 : _a.setMin(v);\n return this;\n }\n randomizationFactor(v) {\n var _a;\n if (v === undefined)\n return this._randomizationFactor;\n this._randomizationFactor = v;\n (_a = this.backoff) === null || _a === void 0 ? void 0 : _a.setJitter(v);\n return this;\n }\n reconnectionDelayMax(v) {\n var _a;\n if (v === undefined)\n return this._reconnectionDelayMax;\n this._reconnectionDelayMax = v;\n (_a = this.backoff) === null || _a === void 0 ? void 0 : _a.setMax(v);\n return this;\n }\n timeout(v) {\n if (!arguments.length)\n return this._timeout;\n this._timeout = v;\n return this;\n }\n /**\n * Starts trying to reconnect if reconnection is enabled and we have not\n * started reconnecting yet\n *\n * @private\n */\n maybeReconnectOnOpen() {\n // Only try to reconnect if it's the first time we're connecting\n if (!this._reconnecting &&\n this._reconnection &&\n this.backoff.attempts === 0) {\n // keeps reconnection from firing twice for the same reconnection loop\n this.reconnect();\n }\n }\n /**\n * Sets the current transport `socket`.\n *\n * @param {Function} fn - optional, callback\n * @return self\n * @public\n */\n open(fn) {\n debug(\"readyState %s\", this._readyState);\n if (~this._readyState.indexOf(\"open\"))\n return this;\n debug(\"opening %s\", this.uri);\n this.engine = new engine_io_client_1.Socket(this.uri, this.opts);\n const socket = this.engine;\n const self = this;\n this._readyState = \"opening\";\n this.skipReconnect = false;\n // emit `open`\n const openSubDestroy = (0, on_js_1.on)(socket, \"open\", function () {\n self.onopen();\n fn && fn();\n });\n // emit `error`\n const errorSub = (0, on_js_1.on)(socket, \"error\", (err) => {\n debug(\"error\");\n self.cleanup();\n self._readyState = \"closed\";\n this.emitReserved(\"error\", err);\n if (fn) {\n fn(err);\n }\n else {\n // Only do this if there is no fn to handle the error\n self.maybeReconnectOnOpen();\n }\n });\n if (false !== this._timeout) {\n const timeout = this._timeout;\n debug(\"connect attempt will timeout after %d\", timeout);\n if (timeout === 0) {\n openSubDestroy(); // prevents a race condition with the 'open' event\n }\n // set timer\n const timer = this.setTimeoutFn(() => {\n debug(\"connect attempt timed out after %d\", timeout);\n openSubDestroy();\n socket.close();\n // @ts-ignore\n socket.emit(\"error\", new Error(\"timeout\"));\n }, timeout);\n if (this.opts.autoUnref) {\n timer.unref();\n }\n this.subs.push(function subDestroy() {\n clearTimeout(timer);\n });\n }\n this.subs.push(openSubDestroy);\n this.subs.push(errorSub);\n return this;\n }\n /**\n * Alias for open()\n *\n * @return self\n * @public\n */\n connect(fn) {\n return this.open(fn);\n }\n /**\n * Called upon transport open.\n *\n * @private\n */\n onopen() {\n debug(\"open\");\n // clear old subs\n this.cleanup();\n // mark as open\n this._readyState = \"open\";\n this.emitReserved(\"open\");\n // add new subs\n const socket = this.engine;\n this.subs.push((0, on_js_1.on)(socket, \"ping\", this.onping.bind(this)), (0, on_js_1.on)(socket, \"data\", this.ondata.bind(this)), (0, on_js_1.on)(socket, \"error\", this.onerror.bind(this)), (0, on_js_1.on)(socket, \"close\", this.onclose.bind(this)), (0, on_js_1.on)(this.decoder, \"decoded\", this.ondecoded.bind(this)));\n }\n /**\n * Called upon a ping.\n *\n * @private\n */\n onping() {\n this.emitReserved(\"ping\");\n }\n /**\n * Called with data.\n *\n * @private\n */\n ondata(data) {\n try {\n this.decoder.add(data);\n }\n catch (e) {\n this.onclose(\"parse error\", e);\n }\n }\n /**\n * Called when parser fully decodes a packet.\n *\n * @private\n */\n ondecoded(packet) {\n // the nextTick call prevents an exception in a user-provided event listener from triggering a disconnection due to a \"parse error\"\n (0, engine_io_client_1.nextTick)(() => {\n this.emitReserved(\"packet\", packet);\n }, this.setTimeoutFn);\n }\n /**\n * Called upon socket error.\n *\n * @private\n */\n onerror(err) {\n debug(\"error\", err);\n this.emitReserved(\"error\", err);\n }\n /**\n * Creates a new socket for the given `nsp`.\n *\n * @return {Socket}\n * @public\n */\n socket(nsp, opts) {\n let socket = this.nsps[nsp];\n if (!socket) {\n socket = new socket_js_1.Socket(this, nsp, opts);\n this.nsps[nsp] = socket;\n }\n return socket;\n }\n /**\n * Called upon a socket close.\n *\n * @param socket\n * @private\n */\n _destroy(socket) {\n const nsps = Object.keys(this.nsps);\n for (const nsp of nsps) {\n const socket = this.nsps[nsp];\n if (socket.active) {\n debug(\"socket %s is still active, skipping close\", nsp);\n return;\n }\n }\n this._close();\n }\n /**\n * Writes a packet.\n *\n * @param packet\n * @private\n */\n _packet(packet) {\n debug(\"writing packet %j\", packet);\n const encodedPackets = this.encoder.encode(packet);\n for (let i = 0; i < encodedPackets.length; i++) {\n this.engine.write(encodedPackets[i], packet.options);\n }\n }\n /**\n * Clean up transport subscriptions and packet buffer.\n *\n * @private\n */\n cleanup() {\n debug(\"cleanup\");\n this.subs.forEach((subDestroy) => subDestroy());\n this.subs.length = 0;\n this.decoder.destroy();\n }\n /**\n * Close the current socket.\n *\n * @private\n */\n _close() {\n debug(\"disconnect\");\n this.skipReconnect = true;\n this._reconnecting = false;\n this.onclose(\"forced close\");\n if (this.engine)\n this.engine.close();\n }\n /**\n * Alias for close()\n *\n * @private\n */\n disconnect() {\n return this._close();\n }\n /**\n * Called upon engine close.\n *\n * @private\n */\n onclose(reason, description) {\n debug(\"closed due to %s\", reason);\n this.cleanup();\n this.backoff.reset();\n this._readyState = \"closed\";\n this.emitReserved(\"close\", reason, description);\n if (this._reconnection && !this.skipReconnect) {\n this.reconnect();\n }\n }\n /**\n * Attempt a reconnection.\n *\n * @private\n */\n reconnect() {\n if (this._reconnecting || this.skipReconnect)\n return this;\n const self = this;\n if (this.backoff.attempts >= this._reconnectionAttempts) {\n debug(\"reconnect failed\");\n this.backoff.reset();\n this.emitReserved(\"reconnect_failed\");\n this._reconnecting = false;\n }\n else {\n const delay = this.backoff.duration();\n debug(\"will wait %dms before reconnect attempt\", delay);\n this._reconnecting = true;\n const timer = this.setTimeoutFn(() => {\n if (self.skipReconnect)\n return;\n debug(\"attempting reconnect\");\n this.emitReserved(\"reconnect_attempt\", self.backoff.attempts);\n // check again for the case socket closed in above events\n if (self.skipReconnect)\n return;\n self.open((err) => {\n if (err) {\n debug(\"reconnect attempt error\");\n self._reconnecting = false;\n self.reconnect();\n this.emitReserved(\"reconnect_error\", err);\n }\n else {\n debug(\"reconnect success\");\n self.onreconnect();\n }\n });\n }, delay);\n if (this.opts.autoUnref) {\n timer.unref();\n }\n this.subs.push(function subDestroy() {\n clearTimeout(timer);\n });\n }\n }\n /**\n * Called upon successful reconnect.\n *\n * @private\n */\n onreconnect() {\n const attempt = this.backoff.attempts;\n this._reconnecting = false;\n this.backoff.reset();\n this.emitReserved(\"reconnect\", attempt);\n }\n}\nexports.Manager = Manager;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/build/cjs/manager.js?");
5605
5706
 
5606
5707
  /***/ }),
5607
5708
 
5608
- /***/ "./node_modules/engine.io-client/build/cjs/transports/websocket-constructor.browser.js":
5609
- /*!*********************************************************************************************!*\
5610
- !*** ./node_modules/engine.io-client/build/cjs/transports/websocket-constructor.browser.js ***!
5611
- \*********************************************************************************************/
5612
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5709
+ /***/ "./node_modules/@kingsds/socket.io-client/build/cjs/on.js":
5710
+ /*!****************************************************************!*\
5711
+ !*** ./node_modules/@kingsds/socket.io-client/build/cjs/on.js ***!
5712
+ \****************************************************************/
5713
+ /***/ ((__unused_webpack_module, exports) => {
5613
5714
 
5614
5715
  "use strict";
5615
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.defaultBinaryType = exports.usingBrowserWebSocket = exports.WebSocket = exports.nextTick = void 0;\nconst globalThis_js_1 = __webpack_require__(/*! ../globalThis.js */ \"./node_modules/engine.io-client/build/cjs/globalThis.browser.js\");\nexports.nextTick = (() => {\n const isPromiseAvailable = typeof Promise === \"function\" && typeof Promise.resolve === \"function\";\n if (isPromiseAvailable) {\n return cb => Promise.resolve().then(cb);\n }\n else {\n return (cb, setTimeoutFn) => setTimeoutFn(cb, 0);\n }\n})();\nexports.WebSocket = globalThis_js_1.globalThisShim.WebSocket || globalThis_js_1.globalThisShim.MozWebSocket;\nexports.usingBrowserWebSocket = true;\nexports.defaultBinaryType = \"arraybuffer\";\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/transports/websocket-constructor.browser.js?");
5716
+ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.on = void 0;\nfunction on(obj, ev, fn) {\n obj.on(ev, fn);\n return function subDestroy() {\n obj.off(ev, fn);\n };\n}\nexports.on = on;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/build/cjs/on.js?");
5616
5717
 
5617
5718
  /***/ }),
5618
5719
 
5619
- /***/ "./node_modules/engine.io-client/build/cjs/transports/websocket.js":
5620
- /*!*************************************************************************!*\
5621
- !*** ./node_modules/engine.io-client/build/cjs/transports/websocket.js ***!
5622
- \*************************************************************************/
5720
+ /***/ "./node_modules/@kingsds/socket.io-client/build/cjs/socket.js":
5721
+ /*!********************************************************************!*\
5722
+ !*** ./node_modules/@kingsds/socket.io-client/build/cjs/socket.js ***!
5723
+ \********************************************************************/
5623
5724
  /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5624
5725
 
5625
5726
  "use strict";
5626
- eval("/* provided dependency */ var Buffer = __webpack_require__(/*! ./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js */ \"./node_modules/node-polyfill-webpack-plugin/node_modules/buffer/index.js\")[\"Buffer\"];\n\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.WS = void 0;\nconst transport_js_1 = __webpack_require__(/*! ../transport.js */ \"./node_modules/engine.io-client/build/cjs/transport.js\");\nconst parseqs_js_1 = __webpack_require__(/*! ../contrib/parseqs.js */ \"./node_modules/engine.io-client/build/cjs/contrib/parseqs.js\");\nconst yeast_js_1 = __webpack_require__(/*! ../contrib/yeast.js */ \"./node_modules/engine.io-client/build/cjs/contrib/yeast.js\");\nconst util_js_1 = __webpack_require__(/*! ../util.js */ \"./node_modules/engine.io-client/build/cjs/util.js\");\nconst websocket_constructor_js_1 = __webpack_require__(/*! ./websocket-constructor.js */ \"./node_modules/engine.io-client/build/cjs/transports/websocket-constructor.browser.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/debug/src/browser.js\")); // debug()\nconst engine_io_parser_1 = __webpack_require__(/*! engine.io-parser */ \"./node_modules/engine.io-parser/build/cjs/index.js\");\nconst debug = (0, debug_1.default)(\"engine.io-client:websocket\"); // debug()\n// detect ReactNative environment\nconst isReactNative = typeof navigator !== \"undefined\" &&\n typeof navigator.product === \"string\" &&\n navigator.product.toLowerCase() === \"reactnative\";\nclass WS extends transport_js_1.Transport {\n /**\n * WebSocket transport constructor.\n *\n * @api {Object} connection options\n * @api public\n */\n constructor(opts) {\n super(opts);\n this.supportsBinary = !opts.forceBase64;\n }\n /**\n * Transport name.\n *\n * @api public\n */\n get name() {\n return \"websocket\";\n }\n /**\n * Opens socket.\n *\n * @api private\n */\n doOpen() {\n if (!this.check()) {\n // let probe timeout\n return;\n }\n const uri = this.uri();\n const protocols = this.opts.protocols;\n // React Native only supports the 'headers' option, and will print a warning if anything else is passed\n const opts = isReactNative\n ? {}\n : (0, util_js_1.pick)(this.opts, \"agent\", \"perMessageDeflate\", \"pfx\", \"key\", \"passphrase\", \"cert\", \"ca\", \"ciphers\", \"rejectUnauthorized\", \"localAddress\", \"protocolVersion\", \"origin\", \"maxPayload\", \"family\", \"checkServerIdentity\");\n if (this.opts.extraHeaders) {\n opts.headers = this.opts.extraHeaders;\n }\n try {\n this.ws =\n websocket_constructor_js_1.usingBrowserWebSocket && !isReactNative\n ? protocols\n ? new websocket_constructor_js_1.WebSocket(uri, protocols)\n : new websocket_constructor_js_1.WebSocket(uri)\n : new websocket_constructor_js_1.WebSocket(uri, protocols, opts);\n }\n catch (err) {\n return this.emitReserved(\"error\", err);\n }\n this.ws.binaryType = this.socket.binaryType || websocket_constructor_js_1.defaultBinaryType;\n this.addEventListeners();\n }\n /**\n * Adds event listeners to the socket\n *\n * @api private\n */\n addEventListeners() {\n this.ws.onopen = () => {\n if (this.opts.autoUnref) {\n this.ws._socket.unref();\n }\n this.onOpen();\n };\n this.ws.onclose = closeEvent => this.onClose({\n description: \"websocket connection closed\",\n context: closeEvent\n });\n this.ws.onmessage = ev => this.onData(ev.data);\n this.ws.onerror = e => this.onError(\"websocket error\", e);\n }\n /**\n * Writes data to socket.\n *\n * @param {Array} array of packets.\n * @api private\n */\n write(packets) {\n this.writable = false;\n // encodePacket efficient as it uses WS framing\n // no need for encodePayload\n for (let i = 0; i < packets.length; i++) {\n const packet = packets[i];\n const lastPacket = i === packets.length - 1;\n (0, engine_io_parser_1.encodePacket)(packet, this.supportsBinary, data => {\n // always create a new object (GH-437)\n const opts = {};\n if (!websocket_constructor_js_1.usingBrowserWebSocket) {\n if (packet.options) {\n opts.compress = packet.options.compress;\n }\n if (this.opts.perMessageDeflate) {\n const len = \n // @ts-ignore\n \"string\" === typeof data ? Buffer.byteLength(data) : data.length;\n if (len < this.opts.perMessageDeflate.threshold) {\n opts.compress = false;\n }\n }\n }\n // Sometimes the websocket has already been closed but the browser didn't\n // have a chance of informing us about it yet, in that case send will\n // throw an error\n try {\n if (websocket_constructor_js_1.usingBrowserWebSocket) {\n // TypeError is thrown when passing the second argument on Safari\n this.ws.send(data);\n }\n else {\n this.ws.send(data, opts);\n }\n }\n catch (e) {\n debug(\"websocket closed before onclose event\");\n }\n if (lastPacket) {\n // fake drain\n // defer to next tick to allow Socket to clear writeBuffer\n (0, websocket_constructor_js_1.nextTick)(() => {\n this.writable = true;\n this.emitReserved(\"drain\");\n }, this.setTimeoutFn);\n }\n });\n }\n }\n /**\n * Closes socket.\n *\n * @api private\n */\n doClose() {\n if (typeof this.ws !== \"undefined\") {\n this.ws.close();\n this.ws = null;\n }\n }\n /**\n * Generates uri for connection.\n *\n * @api private\n */\n uri() {\n let query = this.query || {};\n const schema = this.opts.secure ? \"wss\" : \"ws\";\n let port = \"\";\n // avoid port if default for schema\n if (this.opts.port &&\n ((\"wss\" === schema && Number(this.opts.port) !== 443) ||\n (\"ws\" === schema && Number(this.opts.port) !== 80))) {\n port = \":\" + this.opts.port;\n }\n // append timestamp to URI\n if (this.opts.timestampRequests) {\n query[this.opts.timestampParam] = (0, yeast_js_1.yeast)();\n }\n // communicate binary support capabilities\n if (!this.supportsBinary) {\n query.b64 = 1;\n }\n const encodedQuery = (0, parseqs_js_1.encode)(query);\n const ipv6 = this.opts.hostname.indexOf(\":\") !== -1;\n return (schema +\n \"://\" +\n (ipv6 ? \"[\" + this.opts.hostname + \"]\" : this.opts.hostname) +\n port +\n this.opts.path +\n (encodedQuery.length ? \"?\" + encodedQuery : \"\"));\n }\n /**\n * Feature detection for WebSocket.\n *\n * @return {Boolean} whether this transport is available.\n * @api public\n */\n check() {\n return !!websocket_constructor_js_1.WebSocket;\n }\n}\nexports.WS = WS;\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/transports/websocket.js?");
5727
+ eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Socket = void 0;\nconst socket_io_parser_1 = __webpack_require__(/*! socket.io-parser */ \"./node_modules/socket.io-parser/build/cjs/index.js\");\nconst on_js_1 = __webpack_require__(/*! ./on.js */ \"./node_modules/@kingsds/socket.io-client/build/cjs/on.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/@kingsds/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = (0, debug_1.default)(\"socket.io-client:socket\"); // debug()\n/**\n * Internal events.\n * These events can't be emitted by the user.\n */\nconst RESERVED_EVENTS = Object.freeze({\n connect: 1,\n connect_error: 1,\n disconnect: 1,\n disconnecting: 1,\n // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener\n newListener: 1,\n removeListener: 1,\n});\n/**\n * A Socket is the fundamental class for interacting with the server.\n *\n * A Socket belongs to a certain Namespace (by default /) and uses an underlying {@link Manager} to communicate.\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"connect\", () => {\n * console.log(\"connected\");\n * });\n *\n * // send an event to the server\n * socket.emit(\"foo\", \"bar\");\n *\n * socket.on(\"foobar\", () => {\n * // an event was received from the server\n * });\n *\n * // upon disconnection\n * socket.on(\"disconnect\", (reason) => {\n * console.log(`disconnected due to ${reason}`);\n * });\n */\nclass Socket extends component_emitter_1.Emitter {\n /**\n * `Socket` constructor.\n */\n constructor(io, nsp, opts) {\n super();\n /**\n * Whether the socket is currently connected to the server.\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"connect\", () => {\n * console.log(socket.connected); // true\n * });\n *\n * socket.on(\"disconnect\", () => {\n * console.log(socket.connected); // false\n * });\n */\n this.connected = false;\n /**\n * Buffer for packets received before the CONNECT packet\n */\n this.receiveBuffer = [];\n /**\n * Buffer for packets that will be sent once the socket is connected\n */\n this.sendBuffer = [];\n this.ids = 0;\n this.acks = {};\n this.flags = {};\n this.io = io;\n this.nsp = nsp;\n if (opts && opts.auth) {\n this.auth = opts.auth;\n }\n if (this.io._autoConnect)\n this.open();\n }\n /**\n * Whether the socket is currently disconnected\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"connect\", () => {\n * console.log(socket.disconnected); // false\n * });\n *\n * socket.on(\"disconnect\", () => {\n * console.log(socket.disconnected); // true\n * });\n */\n get disconnected() {\n return !this.connected;\n }\n /**\n * Subscribe to open, close and packet events\n *\n * @private\n */\n subEvents() {\n if (this.subs)\n return;\n const io = this.io;\n this.subs = [\n (0, on_js_1.on)(io, \"open\", this.onopen.bind(this)),\n (0, on_js_1.on)(io, \"packet\", this.onpacket.bind(this)),\n (0, on_js_1.on)(io, \"error\", this.onerror.bind(this)),\n (0, on_js_1.on)(io, \"close\", this.onclose.bind(this)),\n ];\n }\n /**\n * Whether the Socket will try to reconnect when its Manager connects or reconnects.\n *\n * @example\n * const socket = io();\n *\n * console.log(socket.active); // true\n *\n * socket.on(\"disconnect\", (reason) => {\n * if (reason === \"io server disconnect\") {\n * // the disconnection was initiated by the server, you need to manually reconnect\n * console.log(socket.active); // false\n * }\n * // else the socket will automatically try to reconnect\n * console.log(socket.active); // true\n * });\n */\n get active() {\n return !!this.subs;\n }\n /**\n * \"Opens\" the socket.\n *\n * @example\n * const socket = io({\n * autoConnect: false\n * });\n *\n * socket.connect();\n */\n connect() {\n if (this.connected)\n return this;\n this.subEvents();\n if (!this.io[\"_reconnecting\"])\n this.io.open(); // ensure open\n if (\"open\" === this.io._readyState)\n this.onopen();\n return this;\n }\n /**\n * Alias for {@link connect()}.\n */\n open() {\n return this.connect();\n }\n /**\n * Sends a `message` event.\n *\n * This method mimics the WebSocket.send() method.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send\n *\n * @example\n * socket.send(\"hello\");\n *\n * // this is equivalent to\n * socket.emit(\"message\", \"hello\");\n *\n * @return self\n */\n send(...args) {\n args.unshift(\"message\");\n this.emit.apply(this, args);\n return this;\n }\n /**\n * Override `emit`.\n * If the event is in `events`, it's emitted normally.\n *\n * @example\n * socket.emit(\"hello\", \"world\");\n *\n * // all serializable datastructures are supported (no need to call JSON.stringify)\n * socket.emit(\"hello\", 1, \"2\", { 3: [\"4\"], 5: Uint8Array.from([6]) });\n *\n * // with an acknowledgement from the server\n * socket.emit(\"hello\", \"world\", (val) => {\n * // ...\n * });\n *\n * @return self\n */\n emit(ev, ...args) {\n if (RESERVED_EVENTS.hasOwnProperty(ev)) {\n throw new Error('\"' + ev.toString() + '\" is a reserved event name');\n }\n args.unshift(ev);\n const packet = {\n type: socket_io_parser_1.PacketType.EVENT,\n data: args,\n };\n packet.options = {};\n packet.options.compress = this.flags.compress !== false;\n // event ack callback\n if (\"function\" === typeof args[args.length - 1]) {\n const id = this.ids++;\n debug(\"emitting packet with ack id %d\", id);\n const ack = args.pop();\n this._registerAckCallback(id, ack);\n packet.id = id;\n }\n const isTransportWritable = this.io.engine &&\n this.io.engine.transport &&\n this.io.engine.transport.writable;\n const discardPacket = this.flags.volatile && (!isTransportWritable || !this.connected);\n if (discardPacket) {\n debug(\"discard packet as the transport is not currently writable\");\n }\n else if (this.connected) {\n this.notifyOutgoingListeners(packet);\n this.packet(packet);\n }\n else {\n this.sendBuffer.push(packet);\n }\n this.flags = {};\n return this;\n }\n /**\n * @private\n */\n _registerAckCallback(id, ack) {\n const timeout = this.flags.timeout;\n if (timeout === undefined) {\n this.acks[id] = ack;\n return;\n }\n // @ts-ignore\n const timer = this.io.setTimeoutFn(() => {\n delete this.acks[id];\n for (let i = 0; i < this.sendBuffer.length; i++) {\n if (this.sendBuffer[i].id === id) {\n debug(\"removing packet with ack id %d from the buffer\", id);\n this.sendBuffer.splice(i, 1);\n }\n }\n debug(\"event with ack id %d has timed out after %d ms\", id, timeout);\n ack.call(this, new Error(\"operation has timed out\"));\n }, timeout);\n this.acks[id] = (...args) => {\n // @ts-ignore\n this.io.clearTimeoutFn(timer);\n ack.apply(this, [null, ...args]);\n };\n }\n /**\n * Sends a packet.\n *\n * @param packet\n * @private\n */\n packet(packet) {\n packet.nsp = this.nsp;\n this.io._packet(packet);\n }\n /**\n * Called upon engine `open`.\n *\n * @private\n */\n onopen() {\n debug(\"transport is open - connecting\");\n if (typeof this.auth == \"function\") {\n this.auth((data) => {\n this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data });\n });\n }\n else {\n this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data: this.auth });\n }\n }\n /**\n * Called upon engine or manager `error`.\n *\n * @param err\n * @private\n */\n onerror(err) {\n if (!this.connected) {\n this.emitReserved(\"connect_error\", err);\n }\n }\n /**\n * Called upon engine `close`.\n *\n * @param reason\n * @param description\n * @private\n */\n onclose(reason, description) {\n debug(\"close (%s)\", reason);\n this.connected = false;\n delete this.id;\n this.emitReserved(\"disconnect\", reason, description);\n }\n /**\n * Called with socket packet.\n *\n * @param packet\n * @private\n */\n onpacket(packet) {\n const sameNamespace = packet.nsp === this.nsp;\n if (!sameNamespace)\n return;\n switch (packet.type) {\n case socket_io_parser_1.PacketType.CONNECT:\n if (packet.data && packet.data.sid) {\n const id = packet.data.sid;\n this.onconnect(id);\n }\n else {\n this.emitReserved(\"connect_error\", new Error(\"It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)\"));\n }\n break;\n case socket_io_parser_1.PacketType.EVENT:\n case socket_io_parser_1.PacketType.BINARY_EVENT:\n this.onevent(packet);\n break;\n case socket_io_parser_1.PacketType.ACK:\n case socket_io_parser_1.PacketType.BINARY_ACK:\n this.onack(packet);\n break;\n case socket_io_parser_1.PacketType.DISCONNECT:\n this.ondisconnect();\n break;\n case socket_io_parser_1.PacketType.CONNECT_ERROR:\n this.destroy();\n const err = new Error(packet.data.message);\n // @ts-ignore\n err.data = packet.data.data;\n this.emitReserved(\"connect_error\", err);\n break;\n }\n }\n /**\n * Called upon a server event.\n *\n * @param packet\n * @private\n */\n onevent(packet) {\n const args = packet.data || [];\n debug(\"emitting event %j\", args);\n if (null != packet.id) {\n debug(\"attaching ack callback to event\");\n args.push(this.ack(packet.id));\n }\n if (this.connected) {\n this.emitEvent(args);\n }\n else {\n this.receiveBuffer.push(Object.freeze(args));\n }\n }\n emitEvent(args) {\n if (this._anyListeners && this._anyListeners.length) {\n const listeners = this._anyListeners.slice();\n for (const listener of listeners) {\n listener.apply(this, args);\n }\n }\n super.emit.apply(this, args);\n }\n /**\n * Produces an ack callback to emit with an event.\n *\n * @private\n */\n ack(id) {\n const self = this;\n let sent = false;\n return function (...args) {\n // prevent double callbacks\n if (sent)\n return;\n sent = true;\n debug(\"sending ack %j\", args);\n self.packet({\n type: socket_io_parser_1.PacketType.ACK,\n id: id,\n data: args,\n });\n };\n }\n /**\n * Called upon a server acknowlegement.\n *\n * @param packet\n * @private\n */\n onack(packet) {\n const ack = this.acks[packet.id];\n if (\"function\" === typeof ack) {\n debug(\"calling ack %s with %j\", packet.id, packet.data);\n ack.apply(this, packet.data);\n delete this.acks[packet.id];\n }\n else {\n debug(\"bad ack %s\", packet.id);\n }\n }\n /**\n * Called upon server connect.\n *\n * @private\n */\n onconnect(id) {\n debug(\"socket connected with id %s\", id);\n this.id = id;\n this.connected = true;\n this.emitBuffered();\n this.emitReserved(\"connect\");\n }\n /**\n * Emit buffered events (received and emitted).\n *\n * @private\n */\n emitBuffered() {\n this.receiveBuffer.forEach((args) => this.emitEvent(args));\n this.receiveBuffer = [];\n this.sendBuffer.forEach((packet) => {\n this.notifyOutgoingListeners(packet);\n this.packet(packet);\n });\n this.sendBuffer = [];\n }\n /**\n * Called upon server disconnect.\n *\n * @private\n */\n ondisconnect() {\n debug(\"server disconnect (%s)\", this.nsp);\n this.destroy();\n this.onclose(\"io server disconnect\");\n }\n /**\n * Called upon forced client/server side disconnections,\n * this method ensures the manager stops tracking us and\n * that reconnections don't get triggered for this.\n *\n * @private\n */\n destroy() {\n if (this.subs) {\n // clean subscriptions to avoid reconnections\n this.subs.forEach((subDestroy) => subDestroy());\n this.subs = undefined;\n }\n this.io[\"_destroy\"](this);\n }\n /**\n * Disconnects the socket manually. In that case, the socket will not try to reconnect.\n *\n * If this is the last active Socket instance of the {@link Manager}, the low-level connection will be closed.\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"disconnect\", (reason) => {\n * // console.log(reason); prints \"io client disconnect\"\n * });\n *\n * socket.disconnect();\n *\n * @return self\n */\n disconnect() {\n if (this.connected) {\n debug(\"performing disconnect (%s)\", this.nsp);\n this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT });\n }\n // remove socket from pool\n this.destroy();\n if (this.connected) {\n // fire events\n this.onclose(\"io client disconnect\");\n }\n return this;\n }\n /**\n * Alias for {@link disconnect()}.\n *\n * @return self\n */\n close() {\n return this.disconnect();\n }\n /**\n * Sets the compress flag.\n *\n * @example\n * socket.compress(false).emit(\"hello\");\n *\n * @param compress - if `true`, compresses the sending data\n * @return self\n */\n compress(compress) {\n this.flags.compress = compress;\n return this;\n }\n /**\n * Sets a modifier for a subsequent event emission that the event message will be dropped when this socket is not\n * ready to send messages.\n *\n * @example\n * socket.volatile.emit(\"hello\"); // the server may or may not receive it\n *\n * @returns self\n */\n get volatile() {\n this.flags.volatile = true;\n return this;\n }\n /**\n * Sets a modifier for a subsequent event emission that the callback will be called with an error when the\n * given number of milliseconds have elapsed without an acknowledgement from the server:\n *\n * @example\n * socket.timeout(5000).emit(\"my-event\", (err) => {\n * if (err) {\n * // the server did not acknowledge the event in the given delay\n * }\n * });\n *\n * @returns self\n */\n timeout(timeout) {\n this.flags.timeout = timeout;\n return this;\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback.\n *\n * @example\n * socket.onAny((event, ...args) => {\n * console.log(`got ${event}`);\n * });\n *\n * @param listener\n */\n onAny(listener) {\n this._anyListeners = this._anyListeners || [];\n this._anyListeners.push(listener);\n return this;\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback. The listener is added to the beginning of the listeners array.\n *\n * @example\n * socket.prependAny((event, ...args) => {\n * console.log(`got event ${event}`);\n * });\n *\n * @param listener\n */\n prependAny(listener) {\n this._anyListeners = this._anyListeners || [];\n this._anyListeners.unshift(listener);\n return this;\n }\n /**\n * Removes the listener that will be fired when any event is emitted.\n *\n * @example\n * const catchAllListener = (event, ...args) => {\n * console.log(`got event ${event}`);\n * }\n *\n * socket.onAny(catchAllListener);\n *\n * // remove a specific listener\n * socket.offAny(catchAllListener);\n *\n * // or remove all listeners\n * socket.offAny();\n *\n * @param listener\n */\n offAny(listener) {\n if (!this._anyListeners) {\n return this;\n }\n if (listener) {\n const listeners = this._anyListeners;\n for (let i = 0; i < listeners.length; i++) {\n if (listener === listeners[i]) {\n listeners.splice(i, 1);\n return this;\n }\n }\n }\n else {\n this._anyListeners = [];\n }\n return this;\n }\n /**\n * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,\n * e.g. to remove listeners.\n */\n listenersAny() {\n return this._anyListeners || [];\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback.\n *\n * Note: acknowledgements sent to the server are not included.\n *\n * @example\n * socket.onAnyOutgoing((event, ...args) => {\n * console.log(`sent event ${event}`);\n * });\n *\n * @param listener\n */\n onAnyOutgoing(listener) {\n this._anyOutgoingListeners = this._anyOutgoingListeners || [];\n this._anyOutgoingListeners.push(listener);\n return this;\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback. The listener is added to the beginning of the listeners array.\n *\n * Note: acknowledgements sent to the server are not included.\n *\n * @example\n * socket.prependAnyOutgoing((event, ...args) => {\n * console.log(`sent event ${event}`);\n * });\n *\n * @param listener\n */\n prependAnyOutgoing(listener) {\n this._anyOutgoingListeners = this._anyOutgoingListeners || [];\n this._anyOutgoingListeners.unshift(listener);\n return this;\n }\n /**\n * Removes the listener that will be fired when any event is emitted.\n *\n * @example\n * const catchAllListener = (event, ...args) => {\n * console.log(`sent event ${event}`);\n * }\n *\n * socket.onAnyOutgoing(catchAllListener);\n *\n * // remove a specific listener\n * socket.offAnyOutgoing(catchAllListener);\n *\n * // or remove all listeners\n * socket.offAnyOutgoing();\n *\n * @param [listener] - the catch-all listener (optional)\n */\n offAnyOutgoing(listener) {\n if (!this._anyOutgoingListeners) {\n return this;\n }\n if (listener) {\n const listeners = this._anyOutgoingListeners;\n for (let i = 0; i < listeners.length; i++) {\n if (listener === listeners[i]) {\n listeners.splice(i, 1);\n return this;\n }\n }\n }\n else {\n this._anyOutgoingListeners = [];\n }\n return this;\n }\n /**\n * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,\n * e.g. to remove listeners.\n */\n listenersAnyOutgoing() {\n return this._anyOutgoingListeners || [];\n }\n /**\n * Notify the listeners for each packet sent\n *\n * @param packet\n *\n * @private\n */\n notifyOutgoingListeners(packet) {\n if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {\n const listeners = this._anyOutgoingListeners.slice();\n for (const listener of listeners) {\n listener.apply(this, packet.data);\n }\n }\n }\n}\nexports.Socket = Socket;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/build/cjs/socket.js?");
5627
5728
 
5628
5729
  /***/ }),
5629
5730
 
5630
- /***/ "./node_modules/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js":
5631
- /*!**************************************************************************************!*\
5632
- !*** ./node_modules/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js ***!
5633
- \**************************************************************************************/
5634
- /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5731
+ /***/ "./node_modules/@kingsds/socket.io-client/build/cjs/url.js":
5732
+ /*!*****************************************************************!*\
5733
+ !*** ./node_modules/@kingsds/socket.io-client/build/cjs/url.js ***!
5734
+ \*****************************************************************/
5735
+ /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5635
5736
 
5636
5737
  "use strict";
5637
- eval("\n// browser shim for xmlhttprequest module\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.XHR = void 0;\nconst has_cors_js_1 = __webpack_require__(/*! ../contrib/has-cors.js */ \"./node_modules/engine.io-client/build/cjs/contrib/has-cors.js\");\nconst globalThis_js_1 = __webpack_require__(/*! ../globalThis.js */ \"./node_modules/engine.io-client/build/cjs/globalThis.browser.js\");\nfunction XHR(opts) {\n const xdomain = opts.xdomain;\n // XMLHttpRequest can be disabled on IE\n try {\n if (\"undefined\" !== typeof XMLHttpRequest && (!xdomain || has_cors_js_1.hasCORS)) {\n return new XMLHttpRequest();\n }\n }\n catch (e) { }\n if (!xdomain) {\n try {\n return new globalThis_js_1.globalThisShim[[\"Active\"].concat(\"Object\").join(\"X\")](\"Microsoft.XMLHTTP\");\n }\n catch (e) { }\n }\n}\nexports.XHR = XHR;\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/transports/xmlhttprequest.browser.js?");
5738
+ eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.url = void 0;\nconst engine_io_client_1 = __webpack_require__(/*! @kingsds/engine.io-client */ \"./node_modules/@kingsds/engine.io-client/build/cjs/index.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/@kingsds/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = (0, debug_1.default)(\"socket.io-client:url\"); // debug()\n/**\n * URL parser.\n *\n * @param uri - url\n * @param path - the request path of the connection\n * @param loc - An object meant to mimic window.location.\n * Defaults to window.location.\n * @public\n */\nfunction url(uri, path = \"\", loc) {\n let obj = uri;\n // default to window.location\n loc = loc || (typeof location !== \"undefined\" && location);\n if (null == uri)\n uri = loc.protocol + \"//\" + loc.host;\n // relative path support\n if (typeof uri === \"string\") {\n if (\"/\" === uri.charAt(0)) {\n if (\"/\" === uri.charAt(1)) {\n uri = loc.protocol + uri;\n }\n else {\n uri = loc.host + uri;\n }\n }\n if (!/^(https?|wss?):\\/\\//.test(uri)) {\n debug(\"protocol-less url %s\", uri);\n if (\"undefined\" !== typeof loc) {\n uri = loc.protocol + \"//\" + uri;\n }\n else {\n uri = \"https://\" + uri;\n }\n }\n // parse\n debug(\"parse %s\", uri);\n obj = (0, engine_io_client_1.parse)(uri);\n }\n // make sure we treat `localhost:80` and `localhost` equally\n if (!obj.port) {\n if (/^(http|ws)$/.test(obj.protocol)) {\n obj.port = \"80\";\n }\n else if (/^(http|ws)s$/.test(obj.protocol)) {\n obj.port = \"443\";\n }\n }\n obj.path = obj.path || \"/\";\n const ipv6 = obj.host.indexOf(\":\") !== -1;\n const host = ipv6 ? \"[\" + obj.host + \"]\" : obj.host;\n // define unique id\n obj.id = obj.protocol + \"://\" + host + \":\" + obj.port + path;\n // define href\n obj.href =\n obj.protocol +\n \"://\" +\n host +\n (loc && loc.port === obj.port ? \"\" : \":\" + obj.port);\n return obj;\n}\nexports.url = url;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@kingsds/socket.io-client/build/cjs/url.js?");
5638
5739
 
5639
5740
  /***/ }),
5640
5741
 
5641
- /***/ "./node_modules/engine.io-client/build/cjs/util.js":
5642
- /*!*********************************************************!*\
5643
- !*** ./node_modules/engine.io-client/build/cjs/util.js ***!
5644
- \*********************************************************/
5742
+ /***/ "./node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.cjs":
5743
+ /*!***********************************************************************!*\
5744
+ !*** ./node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.cjs ***!
5745
+ \***********************************************************************/
5645
5746
  /***/ ((__unused_webpack_module, exports, __webpack_require__) => {
5646
5747
 
5647
5748
  "use strict";
5648
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.byteLength = exports.installTimerFunctions = exports.pick = void 0;\nconst globalThis_js_1 = __webpack_require__(/*! ./globalThis.js */ \"./node_modules/engine.io-client/build/cjs/globalThis.browser.js\");\nfunction pick(obj, ...attr) {\n return attr.reduce((acc, k) => {\n if (obj.hasOwnProperty(k)) {\n acc[k] = obj[k];\n }\n return acc;\n }, {});\n}\nexports.pick = pick;\n// Keep a reference to the real timeout functions so they can be used when overridden\nconst NATIVE_SET_TIMEOUT = setTimeout;\nconst NATIVE_CLEAR_TIMEOUT = clearTimeout;\nfunction installTimerFunctions(obj, opts) {\n if (opts.useNativeTimers) {\n obj.setTimeoutFn = NATIVE_SET_TIMEOUT.bind(globalThis_js_1.globalThisShim);\n obj.clearTimeoutFn = NATIVE_CLEAR_TIMEOUT.bind(globalThis_js_1.globalThisShim);\n }\n else {\n obj.setTimeoutFn = setTimeout.bind(globalThis_js_1.globalThisShim);\n obj.clearTimeoutFn = clearTimeout.bind(globalThis_js_1.globalThisShim);\n }\n}\nexports.installTimerFunctions = installTimerFunctions;\n// base64 encoded buffers are about 33% bigger (https://en.wikipedia.org/wiki/Base64)\nconst BASE64_OVERHEAD = 1.33;\n// we could also have used `new Blob([obj]).size`, but it isn't supported in IE9\nfunction byteLength(obj) {\n if (typeof obj === \"string\") {\n return utf8Length(obj);\n }\n // arraybuffer or blob\n return Math.ceil((obj.byteLength || obj.size) * BASE64_OVERHEAD);\n}\nexports.byteLength = byteLength;\nfunction utf8Length(str) {\n let c = 0, length = 0;\n for (let i = 0, l = str.length; i < l; i++) {\n c = str.charCodeAt(i);\n if (c < 0x80) {\n length += 1;\n }\n else if (c < 0x800) {\n length += 2;\n }\n else if (c < 0xd800 || c >= 0xe000) {\n length += 3;\n }\n else {\n i++;\n length += 4;\n }\n }\n return length;\n}\n\n\n//# sourceURL=webpack://dcp/./node_modules/engine.io-client/build/cjs/util.js?");
5749
+ eval("\n\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\n\nvar domhandler = __webpack_require__(/*! domhandler */ \"./node_modules/@selderee/plugin-htmlparser2/node_modules/domhandler/lib/index.js\");\nvar selderee = __webpack_require__(/*! selderee */ \"./node_modules/selderee/lib/selderee.cjs\");\n\n/**\r\n * A {@link BuilderFunction} implementation.\r\n *\r\n * Creates a function (in a {@link Picker} wrapper) that can run\r\n * the decision tree against `htmlparser2` `Element` nodes.\r\n *\r\n * @typeParam V - the type of values associated with selectors.\r\n *\r\n * @param nodes - nodes ({@link DecisionTreeNode})\r\n * from the root level of the decision tree.\r\n *\r\n * @returns a {@link Picker} object.\r\n */\r\nfunction hp2Builder(nodes) {\r\n return new selderee.Picker(handleArray(nodes));\r\n}\r\n// ==============================================\r\nfunction handleArray(nodes) {\r\n const matchers = nodes.map(handleNode);\r\n return (el, ...tail) => flatMap(matchers, m => m(el, ...tail));\r\n}\r\nfunction handleNode(node) {\r\n switch (node.type) {\r\n case 'terminal': {\r\n const result = [node.valueContainer];\r\n return (el, ...tail) => result;\r\n }\r\n case 'tagName':\r\n return handleTagName(node);\r\n case 'attrValue':\r\n return handleAttrValueName(node);\r\n case 'attrPresence':\r\n return handleAttrPresenceName(node);\r\n case 'pushElement':\r\n return handlePushElementNode(node);\r\n case 'popElement':\r\n return handlePopElementNode(node);\r\n }\r\n}\r\nfunction handleTagName(node) {\r\n const variants = {};\r\n for (const variant of node.variants) {\r\n variants[variant.value] = handleArray(variant.cont);\r\n }\r\n return (el, ...tail) => {\r\n const continuation = variants[el.name];\r\n return (continuation) ? continuation(el, ...tail) : [];\r\n };\r\n}\r\nfunction handleAttrPresenceName(node) {\r\n const attrName = node.name;\r\n const continuation = handleArray(node.cont);\r\n return (el, ...tail) => (Object.prototype.hasOwnProperty.call(el.attribs, attrName))\r\n ? continuation(el, ...tail)\r\n : [];\r\n}\r\nfunction handleAttrValueName(node) {\r\n const callbacks = [];\r\n for (const matcher of node.matchers) {\r\n const predicate = matcher.predicate;\r\n const continuation = handleArray(matcher.cont);\r\n callbacks.push((attr, el, ...tail) => (predicate(attr) ? continuation(el, ...tail) : []));\r\n }\r\n const attrName = node.name;\r\n return (el, ...tail) => {\r\n const attr = el.attribs[attrName];\r\n return (attr || attr === '')\r\n ? flatMap(callbacks, cb => cb(attr, el, ...tail))\r\n : [];\r\n };\r\n}\r\nfunction handlePushElementNode(node) {\r\n const continuation = handleArray(node.cont);\r\n const leftElementGetter = (node.combinator === '+')\r\n ? getPrecedingElement\r\n : getParentElement;\r\n return (el, ...tail) => {\r\n const next = leftElementGetter(el);\r\n if (next === null) {\r\n return [];\r\n }\r\n return continuation(next, el, ...tail);\r\n };\r\n}\r\nconst getPrecedingElement = (el) => {\r\n const prev = el.prev;\r\n if (prev === null) {\r\n return null;\r\n }\r\n return (domhandler.isTag(prev)) ? prev : getPrecedingElement(prev);\r\n};\r\nconst getParentElement = (el) => {\r\n const parent = el.parent;\r\n return (parent && domhandler.isTag(parent)) ? parent : null;\r\n};\r\nfunction handlePopElementNode(node) {\r\n const continuation = handleArray(node.cont);\r\n return (el, next, ...tail) => continuation(next, ...tail);\r\n}\r\n// Can be removed after transition to Node 12.\r\nfunction flatMap(items, mapper) {\r\n return [].concat(...amap(items, mapper));\r\n}\r\nfunction amap(items, mapper) {\r\n const len = items.length;\r\n const res = new Array(len);\r\n for (let i = 0; i < len; i++) {\r\n res[i] = mapper(items[i]);\r\n }\r\n return res;\r\n}\n\nexports.hp2Builder = hp2Builder;\n\n\n//# sourceURL=webpack://dcp/./node_modules/@selderee/plugin-htmlparser2/lib/hp2-builder.cjs?");
5750
+
5751
+ /***/ }),
5752
+
5753
+ /***/ "./node_modules/available-typed-arrays/index.js":
5754
+ /*!******************************************************!*\
5755
+ !*** ./node_modules/available-typed-arrays/index.js ***!
5756
+ \******************************************************/
5757
+ /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
5758
+
5759
+ "use strict";
5760
+ eval("\n\nvar possibleNames = [\n\t'BigInt64Array',\n\t'BigUint64Array',\n\t'Float32Array',\n\t'Float64Array',\n\t'Int16Array',\n\t'Int32Array',\n\t'Int8Array',\n\t'Uint16Array',\n\t'Uint32Array',\n\t'Uint8Array',\n\t'Uint8ClampedArray'\n];\n\nvar g = typeof globalThis === 'undefined' ? __webpack_require__.g : globalThis;\n\nmodule.exports = function availableTypedArrays() {\n\tvar out = [];\n\tfor (var i = 0; i < possibleNames.length; i++) {\n\t\tif (typeof g[possibleNames[i]] === 'function') {\n\t\t\tout[out.length] = possibleNames[i];\n\t\t}\n\t}\n\treturn out;\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/available-typed-arrays/index.js?");
5649
5761
 
5650
5762
  /***/ }),
5651
5763
 
@@ -5711,7 +5823,7 @@ eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexpo
5711
5823
  /***/ ((module, __unused_webpack_exports, __webpack_require__) => {
5712
5824
 
5713
5825
  "use strict";
5714
- eval("\n\nvar GetIntrinsic = __webpack_require__(/*! get-intrinsic */ \"./node_modules/get-intrinsic/index.js\");\n\nvar $gOPD = GetIntrinsic('%Object.getOwnPropertyDescriptor%', true);\nif ($gOPD) {\n\ttry {\n\t\t$gOPD([], 'length');\n\t} catch (e) {\n\t\t// IE 8 has a broken gOPD\n\t\t$gOPD = null;\n\t}\n}\n\nmodule.exports = $gOPD;\n\n\n//# sourceURL=webpack://dcp/./node_modules/es-abstract/helpers/getOwnPropertyDescriptor.js?");
5826
+ eval("\n\n// TODO: remove, semver-major\n\nmodule.exports = __webpack_require__(/*! gopd */ \"./node_modules/gopd/index.js\");\n\n\n//# sourceURL=webpack://dcp/./node_modules/es-abstract/helpers/getOwnPropertyDescriptor.js?");
5715
5827
 
5716
5828
  /***/ }),
5717
5829
 
@@ -5737,72 +5849,6 @@ eval("\n\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\n\n
5737
5849
 
5738
5850
  /***/ }),
5739
5851
 
5740
- /***/ "./node_modules/socket.io-client/build/cjs/contrib/backo2.js":
5741
- /*!*******************************************************************!*\
5742
- !*** ./node_modules/socket.io-client/build/cjs/contrib/backo2.js ***!
5743
- \*******************************************************************/
5744
- /***/ ((__unused_webpack_module, exports) => {
5745
-
5746
- "use strict";
5747
- eval("\n/**\n * Initialize backoff timer with `opts`.\n *\n * - `min` initial timeout in milliseconds [100]\n * - `max` max timeout [10000]\n * - `jitter` [0]\n * - `factor` [2]\n *\n * @param {Object} opts\n * @api public\n */\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Backoff = void 0;\nfunction Backoff(opts) {\n opts = opts || {};\n this.ms = opts.min || 100;\n this.max = opts.max || 10000;\n this.factor = opts.factor || 2;\n this.jitter = opts.jitter > 0 && opts.jitter <= 1 ? opts.jitter : 0;\n this.attempts = 0;\n}\nexports.Backoff = Backoff;\n/**\n * Return the backoff duration.\n *\n * @return {Number}\n * @api public\n */\nBackoff.prototype.duration = function () {\n var ms = this.ms * Math.pow(this.factor, this.attempts++);\n if (this.jitter) {\n var rand = Math.random();\n var deviation = Math.floor(rand * this.jitter * ms);\n ms = (Math.floor(rand * 10) & 1) == 0 ? ms - deviation : ms + deviation;\n }\n return Math.min(ms, this.max) | 0;\n};\n/**\n * Reset the number of attempts.\n *\n * @api public\n */\nBackoff.prototype.reset = function () {\n this.attempts = 0;\n};\n/**\n * Set the minimum duration\n *\n * @api public\n */\nBackoff.prototype.setMin = function (min) {\n this.ms = min;\n};\n/**\n * Set the maximum duration\n *\n * @api public\n */\nBackoff.prototype.setMax = function (max) {\n this.max = max;\n};\n/**\n * Set the jitter\n *\n * @api public\n */\nBackoff.prototype.setJitter = function (jitter) {\n this.jitter = jitter;\n};\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/build/cjs/contrib/backo2.js?");
5748
-
5749
- /***/ }),
5750
-
5751
- /***/ "./node_modules/socket.io-client/build/cjs/index.js":
5752
- /*!**********************************************************!*\
5753
- !*** ./node_modules/socket.io-client/build/cjs/index.js ***!
5754
- \**********************************************************/
5755
- /***/ (function(module, exports, __webpack_require__) {
5756
-
5757
- "use strict";
5758
- eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports[\"default\"] = exports.connect = exports.io = exports.Socket = exports.Manager = exports.protocol = void 0;\nconst url_js_1 = __webpack_require__(/*! ./url.js */ \"./node_modules/socket.io-client/build/cjs/url.js\");\nconst manager_js_1 = __webpack_require__(/*! ./manager.js */ \"./node_modules/socket.io-client/build/cjs/manager.js\");\nObject.defineProperty(exports, \"Manager\", ({ enumerable: true, get: function () { return manager_js_1.Manager; } }));\nconst socket_js_1 = __webpack_require__(/*! ./socket.js */ \"./node_modules/socket.io-client/build/cjs/socket.js\");\nObject.defineProperty(exports, \"Socket\", ({ enumerable: true, get: function () { return socket_js_1.Socket; } }));\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = debug_1.default(\"socket.io-client\"); // debug()\n/**\n * Managers cache.\n */\nconst cache = {};\nfunction lookup(uri, opts) {\n if (typeof uri === \"object\") {\n opts = uri;\n uri = undefined;\n }\n opts = opts || {};\n const parsed = url_js_1.url(uri, opts.path || \"/socket.io\");\n const source = parsed.source;\n const id = parsed.id;\n const path = parsed.path;\n const sameNamespace = cache[id] && path in cache[id][\"nsps\"];\n const newConnection = opts.forceNew ||\n opts[\"force new connection\"] ||\n false === opts.multiplex ||\n sameNamespace;\n let io;\n if (newConnection) {\n debug(\"ignoring socket cache for %s\", source);\n io = new manager_js_1.Manager(source, opts);\n }\n else {\n if (!cache[id]) {\n debug(\"new io instance for %s\", source);\n cache[id] = new manager_js_1.Manager(source, opts);\n }\n io = cache[id];\n }\n if (parsed.query && !opts.query) {\n opts.query = parsed.queryKey;\n }\n return io.socket(parsed.path, opts);\n}\nexports.io = lookup;\nexports.connect = lookup;\nexports[\"default\"] = lookup;\n// so that \"lookup\" can be used both as a function (e.g. `io(...)`) and as a\n// namespace (e.g. `io.connect(...)`), for backward compatibility\nObject.assign(lookup, {\n Manager: manager_js_1.Manager,\n Socket: socket_js_1.Socket,\n io: lookup,\n connect: lookup,\n});\n/**\n * Protocol version.\n *\n * @public\n */\nvar socket_io_parser_1 = __webpack_require__(/*! socket.io-parser */ \"./node_modules/socket.io-parser/build/cjs/index.js\");\nObject.defineProperty(exports, \"protocol\", ({ enumerable: true, get: function () { return socket_io_parser_1.protocol; } }));\n\nmodule.exports = lookup;\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/build/cjs/index.js?");
5759
-
5760
- /***/ }),
5761
-
5762
- /***/ "./node_modules/socket.io-client/build/cjs/manager.js":
5763
- /*!************************************************************!*\
5764
- !*** ./node_modules/socket.io-client/build/cjs/manager.js ***!
5765
- \************************************************************/
5766
- /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5767
-
5768
- "use strict";
5769
- eval("\nvar __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });\n}) : (function(o, m, k, k2) {\n if (k2 === undefined) k2 = k;\n o[k2] = m[k];\n}));\nvar __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {\n Object.defineProperty(o, \"default\", { enumerable: true, value: v });\n}) : function(o, v) {\n o[\"default\"] = v;\n});\nvar __importStar = (this && this.__importStar) || function (mod) {\n if (mod && mod.__esModule) return mod;\n var result = {};\n if (mod != null) for (var k in mod) if (k !== \"default\" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);\n __setModuleDefault(result, mod);\n return result;\n};\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Manager = void 0;\nconst engine_io_client_1 = __webpack_require__(/*! engine.io-client */ \"./node_modules/engine.io-client/build/cjs/index.js\");\nconst socket_js_1 = __webpack_require__(/*! ./socket.js */ \"./node_modules/socket.io-client/build/cjs/socket.js\");\nconst parser = __importStar(__webpack_require__(/*! socket.io-parser */ \"./node_modules/socket.io-parser/build/cjs/index.js\"));\nconst on_js_1 = __webpack_require__(/*! ./on.js */ \"./node_modules/socket.io-client/build/cjs/on.js\");\nconst backo2_js_1 = __webpack_require__(/*! ./contrib/backo2.js */ \"./node_modules/socket.io-client/build/cjs/contrib/backo2.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = debug_1.default(\"socket.io-client:manager\"); // debug()\nclass Manager extends component_emitter_1.Emitter {\n constructor(uri, opts) {\n var _a;\n super();\n this.nsps = {};\n this.subs = [];\n if (uri && \"object\" === typeof uri) {\n opts = uri;\n uri = undefined;\n }\n opts = opts || {};\n opts.path = opts.path || \"/socket.io\";\n this.opts = opts;\n engine_io_client_1.installTimerFunctions(this, opts);\n this.reconnection(opts.reconnection !== false);\n this.reconnectionAttempts(opts.reconnectionAttempts || Infinity);\n this.reconnectionDelay(opts.reconnectionDelay || 1000);\n this.reconnectionDelayMax(opts.reconnectionDelayMax || 5000);\n this.randomizationFactor((_a = opts.randomizationFactor) !== null && _a !== void 0 ? _a : 0.5);\n this.backoff = new backo2_js_1.Backoff({\n min: this.reconnectionDelay(),\n max: this.reconnectionDelayMax(),\n jitter: this.randomizationFactor(),\n });\n this.timeout(null == opts.timeout ? 20000 : opts.timeout);\n this._readyState = \"closed\";\n this.uri = uri;\n const _parser = opts.parser || parser;\n this.encoder = new _parser.Encoder();\n this.decoder = new _parser.Decoder();\n this._autoConnect = opts.autoConnect !== false;\n if (this._autoConnect)\n this.open();\n }\n reconnection(v) {\n if (!arguments.length)\n return this._reconnection;\n this._reconnection = !!v;\n return this;\n }\n reconnectionAttempts(v) {\n if (v === undefined)\n return this._reconnectionAttempts;\n this._reconnectionAttempts = v;\n return this;\n }\n reconnectionDelay(v) {\n var _a;\n if (v === undefined)\n return this._reconnectionDelay;\n this._reconnectionDelay = v;\n (_a = this.backoff) === null || _a === void 0 ? void 0 : _a.setMin(v);\n return this;\n }\n randomizationFactor(v) {\n var _a;\n if (v === undefined)\n return this._randomizationFactor;\n this._randomizationFactor = v;\n (_a = this.backoff) === null || _a === void 0 ? void 0 : _a.setJitter(v);\n return this;\n }\n reconnectionDelayMax(v) {\n var _a;\n if (v === undefined)\n return this._reconnectionDelayMax;\n this._reconnectionDelayMax = v;\n (_a = this.backoff) === null || _a === void 0 ? void 0 : _a.setMax(v);\n return this;\n }\n timeout(v) {\n if (!arguments.length)\n return this._timeout;\n this._timeout = v;\n return this;\n }\n /**\n * Starts trying to reconnect if reconnection is enabled and we have not\n * started reconnecting yet\n *\n * @private\n */\n maybeReconnectOnOpen() {\n // Only try to reconnect if it's the first time we're connecting\n if (!this._reconnecting &&\n this._reconnection &&\n this.backoff.attempts === 0) {\n // keeps reconnection from firing twice for the same reconnection loop\n this.reconnect();\n }\n }\n /**\n * Sets the current transport `socket`.\n *\n * @param {Function} fn - optional, callback\n * @return self\n * @public\n */\n open(fn) {\n debug(\"readyState %s\", this._readyState);\n if (~this._readyState.indexOf(\"open\"))\n return this;\n debug(\"opening %s\", this.uri);\n this.engine = new engine_io_client_1.Socket(this.uri, this.opts);\n const socket = this.engine;\n const self = this;\n this._readyState = \"opening\";\n this.skipReconnect = false;\n // emit `open`\n const openSubDestroy = on_js_1.on(socket, \"open\", function () {\n self.onopen();\n fn && fn();\n });\n // emit `error`\n const errorSub = on_js_1.on(socket, \"error\", (err) => {\n debug(\"error\");\n self.cleanup();\n self._readyState = \"closed\";\n this.emitReserved(\"error\", err);\n if (fn) {\n fn(err);\n }\n else {\n // Only do this if there is no fn to handle the error\n self.maybeReconnectOnOpen();\n }\n });\n if (false !== this._timeout) {\n const timeout = this._timeout;\n debug(\"connect attempt will timeout after %d\", timeout);\n if (timeout === 0) {\n openSubDestroy(); // prevents a race condition with the 'open' event\n }\n // set timer\n const timer = this.setTimeoutFn(() => {\n debug(\"connect attempt timed out after %d\", timeout);\n openSubDestroy();\n socket.close();\n // @ts-ignore\n socket.emit(\"error\", new Error(\"timeout\"));\n }, timeout);\n if (this.opts.autoUnref) {\n timer.unref();\n }\n this.subs.push(function subDestroy() {\n clearTimeout(timer);\n });\n }\n this.subs.push(openSubDestroy);\n this.subs.push(errorSub);\n return this;\n }\n /**\n * Alias for open()\n *\n * @return self\n * @public\n */\n connect(fn) {\n return this.open(fn);\n }\n /**\n * Called upon transport open.\n *\n * @private\n */\n onopen() {\n debug(\"open\");\n // clear old subs\n this.cleanup();\n // mark as open\n this._readyState = \"open\";\n this.emitReserved(\"open\");\n // add new subs\n const socket = this.engine;\n this.subs.push(on_js_1.on(socket, \"ping\", this.onping.bind(this)), on_js_1.on(socket, \"data\", this.ondata.bind(this)), on_js_1.on(socket, \"error\", this.onerror.bind(this)), on_js_1.on(socket, \"close\", this.onclose.bind(this)), on_js_1.on(this.decoder, \"decoded\", this.ondecoded.bind(this)));\n }\n /**\n * Called upon a ping.\n *\n * @private\n */\n onping() {\n this.emitReserved(\"ping\");\n }\n /**\n * Called with data.\n *\n * @private\n */\n ondata(data) {\n try {\n this.decoder.add(data);\n }\n catch (e) {\n this.onclose(\"parse error\", e);\n }\n }\n /**\n * Called when parser fully decodes a packet.\n *\n * @private\n */\n ondecoded(packet) {\n // the nextTick call prevents an exception in a user-provided event listener from triggering a disconnection due to a \"parse error\"\n engine_io_client_1.nextTick(() => {\n this.emitReserved(\"packet\", packet);\n }, this.setTimeoutFn);\n }\n /**\n * Called upon socket error.\n *\n * @private\n */\n onerror(err) {\n debug(\"error\", err);\n this.emitReserved(\"error\", err);\n }\n /**\n * Creates a new socket for the given `nsp`.\n *\n * @return {Socket}\n * @public\n */\n socket(nsp, opts) {\n let socket = this.nsps[nsp];\n if (!socket) {\n socket = new socket_js_1.Socket(this, nsp, opts);\n this.nsps[nsp] = socket;\n }\n return socket;\n }\n /**\n * Called upon a socket close.\n *\n * @param socket\n * @private\n */\n _destroy(socket) {\n const nsps = Object.keys(this.nsps);\n for (const nsp of nsps) {\n const socket = this.nsps[nsp];\n if (socket.active) {\n debug(\"socket %s is still active, skipping close\", nsp);\n return;\n }\n }\n this._close();\n }\n /**\n * Writes a packet.\n *\n * @param packet\n * @private\n */\n _packet(packet) {\n debug(\"writing packet %j\", packet);\n const encodedPackets = this.encoder.encode(packet);\n for (let i = 0; i < encodedPackets.length; i++) {\n this.engine.write(encodedPackets[i], packet.options);\n }\n }\n /**\n * Clean up transport subscriptions and packet buffer.\n *\n * @private\n */\n cleanup() {\n debug(\"cleanup\");\n this.subs.forEach((subDestroy) => subDestroy());\n this.subs.length = 0;\n this.decoder.destroy();\n }\n /**\n * Close the current socket.\n *\n * @private\n */\n _close() {\n debug(\"disconnect\");\n this.skipReconnect = true;\n this._reconnecting = false;\n this.onclose(\"forced close\");\n if (this.engine)\n this.engine.close();\n }\n /**\n * Alias for close()\n *\n * @private\n */\n disconnect() {\n return this._close();\n }\n /**\n * Called upon engine close.\n *\n * @private\n */\n onclose(reason, description) {\n debug(\"closed due to %s\", reason);\n this.cleanup();\n this.backoff.reset();\n this._readyState = \"closed\";\n this.emitReserved(\"close\", reason, description);\n if (this._reconnection && !this.skipReconnect) {\n this.reconnect();\n }\n }\n /**\n * Attempt a reconnection.\n *\n * @private\n */\n reconnect() {\n if (this._reconnecting || this.skipReconnect)\n return this;\n const self = this;\n if (this.backoff.attempts >= this._reconnectionAttempts) {\n debug(\"reconnect failed\");\n this.backoff.reset();\n this.emitReserved(\"reconnect_failed\");\n this._reconnecting = false;\n }\n else {\n const delay = this.backoff.duration();\n debug(\"will wait %dms before reconnect attempt\", delay);\n this._reconnecting = true;\n const timer = this.setTimeoutFn(() => {\n if (self.skipReconnect)\n return;\n debug(\"attempting reconnect\");\n this.emitReserved(\"reconnect_attempt\", self.backoff.attempts);\n // check again for the case socket closed in above events\n if (self.skipReconnect)\n return;\n self.open((err) => {\n if (err) {\n debug(\"reconnect attempt error\");\n self._reconnecting = false;\n self.reconnect();\n this.emitReserved(\"reconnect_error\", err);\n }\n else {\n debug(\"reconnect success\");\n self.onreconnect();\n }\n });\n }, delay);\n if (this.opts.autoUnref) {\n timer.unref();\n }\n this.subs.push(function subDestroy() {\n clearTimeout(timer);\n });\n }\n }\n /**\n * Called upon successful reconnect.\n *\n * @private\n */\n onreconnect() {\n const attempt = this.backoff.attempts;\n this._reconnecting = false;\n this.backoff.reset();\n this.emitReserved(\"reconnect\", attempt);\n }\n}\nexports.Manager = Manager;\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/build/cjs/manager.js?");
5770
-
5771
- /***/ }),
5772
-
5773
- /***/ "./node_modules/socket.io-client/build/cjs/on.js":
5774
- /*!*******************************************************!*\
5775
- !*** ./node_modules/socket.io-client/build/cjs/on.js ***!
5776
- \*******************************************************/
5777
- /***/ ((__unused_webpack_module, exports) => {
5778
-
5779
- "use strict";
5780
- eval("\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.on = void 0;\nfunction on(obj, ev, fn) {\n obj.on(ev, fn);\n return function subDestroy() {\n obj.off(ev, fn);\n };\n}\nexports.on = on;\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/build/cjs/on.js?");
5781
-
5782
- /***/ }),
5783
-
5784
- /***/ "./node_modules/socket.io-client/build/cjs/socket.js":
5785
- /*!***********************************************************!*\
5786
- !*** ./node_modules/socket.io-client/build/cjs/socket.js ***!
5787
- \***********************************************************/
5788
- /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5789
-
5790
- "use strict";
5791
- eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.Socket = void 0;\nconst socket_io_parser_1 = __webpack_require__(/*! socket.io-parser */ \"./node_modules/socket.io-parser/build/cjs/index.js\");\nconst on_js_1 = __webpack_require__(/*! ./on.js */ \"./node_modules/socket.io-client/build/cjs/on.js\");\nconst component_emitter_1 = __webpack_require__(/*! @socket.io/component-emitter */ \"./node_modules/@socket.io/component-emitter/index.mjs\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = debug_1.default(\"socket.io-client:socket\"); // debug()\n/**\n * Internal events.\n * These events can't be emitted by the user.\n */\nconst RESERVED_EVENTS = Object.freeze({\n connect: 1,\n connect_error: 1,\n disconnect: 1,\n disconnecting: 1,\n // EventEmitter reserved events: https://nodejs.org/api/events.html#events_event_newlistener\n newListener: 1,\n removeListener: 1,\n});\n/**\n * A Socket is the fundamental class for interacting with the server.\n *\n * A Socket belongs to a certain Namespace (by default /) and uses an underlying {@link Manager} to communicate.\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"connect\", () => {\n * console.log(\"connected\");\n * });\n *\n * // send an event to the server\n * socket.emit(\"foo\", \"bar\");\n *\n * socket.on(\"foobar\", () => {\n * // an event was received from the server\n * });\n *\n * // upon disconnection\n * socket.on(\"disconnect\", (reason) => {\n * console.log(`disconnected due to ${reason}`);\n * });\n */\nclass Socket extends component_emitter_1.Emitter {\n /**\n * `Socket` constructor.\n */\n constructor(io, nsp, opts) {\n super();\n /**\n * Whether the socket is currently connected to the server.\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"connect\", () => {\n * console.log(socket.connected); // true\n * });\n *\n * socket.on(\"disconnect\", () => {\n * console.log(socket.connected); // false\n * });\n */\n this.connected = false;\n /**\n * Buffer for packets received before the CONNECT packet\n */\n this.receiveBuffer = [];\n /**\n * Buffer for packets that will be sent once the socket is connected\n */\n this.sendBuffer = [];\n this.ids = 0;\n this.acks = {};\n this.flags = {};\n this.io = io;\n this.nsp = nsp;\n if (opts && opts.auth) {\n this.auth = opts.auth;\n }\n if (this.io._autoConnect)\n this.open();\n }\n /**\n * Whether the socket is currently disconnected\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"connect\", () => {\n * console.log(socket.disconnected); // false\n * });\n *\n * socket.on(\"disconnect\", () => {\n * console.log(socket.disconnected); // true\n * });\n */\n get disconnected() {\n return !this.connected;\n }\n /**\n * Subscribe to open, close and packet events\n *\n * @private\n */\n subEvents() {\n if (this.subs)\n return;\n const io = this.io;\n this.subs = [\n on_js_1.on(io, \"open\", this.onopen.bind(this)),\n on_js_1.on(io, \"packet\", this.onpacket.bind(this)),\n on_js_1.on(io, \"error\", this.onerror.bind(this)),\n on_js_1.on(io, \"close\", this.onclose.bind(this)),\n ];\n }\n /**\n * Whether the Socket will try to reconnect when its Manager connects or reconnects.\n *\n * @example\n * const socket = io();\n *\n * console.log(socket.active); // true\n *\n * socket.on(\"disconnect\", (reason) => {\n * if (reason === \"io server disconnect\") {\n * // the disconnection was initiated by the server, you need to manually reconnect\n * console.log(socket.active); // false\n * }\n * // else the socket will automatically try to reconnect\n * console.log(socket.active); // true\n * });\n */\n get active() {\n return !!this.subs;\n }\n /**\n * \"Opens\" the socket.\n *\n * @example\n * const socket = io({\n * autoConnect: false\n * });\n *\n * socket.connect();\n */\n connect() {\n if (this.connected)\n return this;\n this.subEvents();\n if (!this.io[\"_reconnecting\"])\n this.io.open(); // ensure open\n if (\"open\" === this.io._readyState)\n this.onopen();\n return this;\n }\n /**\n * Alias for {@link connect()}.\n */\n open() {\n return this.connect();\n }\n /**\n * Sends a `message` event.\n *\n * This method mimics the WebSocket.send() method.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/WebSocket/send\n *\n * @example\n * socket.send(\"hello\");\n *\n * // this is equivalent to\n * socket.emit(\"message\", \"hello\");\n *\n * @return self\n */\n send(...args) {\n args.unshift(\"message\");\n this.emit.apply(this, args);\n return this;\n }\n /**\n * Override `emit`.\n * If the event is in `events`, it's emitted normally.\n *\n * @example\n * socket.emit(\"hello\", \"world\");\n *\n * // all serializable datastructures are supported (no need to call JSON.stringify)\n * socket.emit(\"hello\", 1, \"2\", { 3: [\"4\"], 5: Uint8Array.from([6]) });\n *\n * // with an acknowledgement from the server\n * socket.emit(\"hello\", \"world\", (val) => {\n * // ...\n * });\n *\n * @return self\n */\n emit(ev, ...args) {\n if (RESERVED_EVENTS.hasOwnProperty(ev)) {\n throw new Error('\"' + ev.toString() + '\" is a reserved event name');\n }\n args.unshift(ev);\n const packet = {\n type: socket_io_parser_1.PacketType.EVENT,\n data: args,\n };\n packet.options = {};\n packet.options.compress = this.flags.compress !== false;\n // event ack callback\n if (\"function\" === typeof args[args.length - 1]) {\n const id = this.ids++;\n debug(\"emitting packet with ack id %d\", id);\n const ack = args.pop();\n this._registerAckCallback(id, ack);\n packet.id = id;\n }\n const isTransportWritable = this.io.engine &&\n this.io.engine.transport &&\n this.io.engine.transport.writable;\n const discardPacket = this.flags.volatile && (!isTransportWritable || !this.connected);\n if (discardPacket) {\n debug(\"discard packet as the transport is not currently writable\");\n }\n else if (this.connected) {\n this.notifyOutgoingListeners(packet);\n this.packet(packet);\n }\n else {\n this.sendBuffer.push(packet);\n }\n this.flags = {};\n return this;\n }\n /**\n * @private\n */\n _registerAckCallback(id, ack) {\n const timeout = this.flags.timeout;\n if (timeout === undefined) {\n this.acks[id] = ack;\n return;\n }\n // @ts-ignore\n const timer = this.io.setTimeoutFn(() => {\n delete this.acks[id];\n for (let i = 0; i < this.sendBuffer.length; i++) {\n if (this.sendBuffer[i].id === id) {\n debug(\"removing packet with ack id %d from the buffer\", id);\n this.sendBuffer.splice(i, 1);\n }\n }\n debug(\"event with ack id %d has timed out after %d ms\", id, timeout);\n ack.call(this, new Error(\"operation has timed out\"));\n }, timeout);\n this.acks[id] = (...args) => {\n // @ts-ignore\n this.io.clearTimeoutFn(timer);\n ack.apply(this, [null, ...args]);\n };\n }\n /**\n * Sends a packet.\n *\n * @param packet\n * @private\n */\n packet(packet) {\n packet.nsp = this.nsp;\n this.io._packet(packet);\n }\n /**\n * Called upon engine `open`.\n *\n * @private\n */\n onopen() {\n debug(\"transport is open - connecting\");\n if (typeof this.auth == \"function\") {\n this.auth((data) => {\n this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data });\n });\n }\n else {\n this.packet({ type: socket_io_parser_1.PacketType.CONNECT, data: this.auth });\n }\n }\n /**\n * Called upon engine or manager `error`.\n *\n * @param err\n * @private\n */\n onerror(err) {\n if (!this.connected) {\n this.emitReserved(\"connect_error\", err);\n }\n }\n /**\n * Called upon engine `close`.\n *\n * @param reason\n * @param description\n * @private\n */\n onclose(reason, description) {\n debug(\"close (%s)\", reason);\n this.connected = false;\n delete this.id;\n this.emitReserved(\"disconnect\", reason, description);\n }\n /**\n * Called with socket packet.\n *\n * @param packet\n * @private\n */\n onpacket(packet) {\n const sameNamespace = packet.nsp === this.nsp;\n if (!sameNamespace)\n return;\n switch (packet.type) {\n case socket_io_parser_1.PacketType.CONNECT:\n if (packet.data && packet.data.sid) {\n const id = packet.data.sid;\n this.onconnect(id);\n }\n else {\n this.emitReserved(\"connect_error\", new Error(\"It seems you are trying to reach a Socket.IO server in v2.x with a v3.x client, but they are not compatible (more information here: https://socket.io/docs/v3/migrating-from-2-x-to-3-0/)\"));\n }\n break;\n case socket_io_parser_1.PacketType.EVENT:\n case socket_io_parser_1.PacketType.BINARY_EVENT:\n this.onevent(packet);\n break;\n case socket_io_parser_1.PacketType.ACK:\n case socket_io_parser_1.PacketType.BINARY_ACK:\n this.onack(packet);\n break;\n case socket_io_parser_1.PacketType.DISCONNECT:\n this.ondisconnect();\n break;\n case socket_io_parser_1.PacketType.CONNECT_ERROR:\n this.destroy();\n const err = new Error(packet.data.message);\n // @ts-ignore\n err.data = packet.data.data;\n this.emitReserved(\"connect_error\", err);\n break;\n }\n }\n /**\n * Called upon a server event.\n *\n * @param packet\n * @private\n */\n onevent(packet) {\n const args = packet.data || [];\n debug(\"emitting event %j\", args);\n if (null != packet.id) {\n debug(\"attaching ack callback to event\");\n args.push(this.ack(packet.id));\n }\n if (this.connected) {\n this.emitEvent(args);\n }\n else {\n this.receiveBuffer.push(Object.freeze(args));\n }\n }\n emitEvent(args) {\n if (this._anyListeners && this._anyListeners.length) {\n const listeners = this._anyListeners.slice();\n for (const listener of listeners) {\n listener.apply(this, args);\n }\n }\n super.emit.apply(this, args);\n }\n /**\n * Produces an ack callback to emit with an event.\n *\n * @private\n */\n ack(id) {\n const self = this;\n let sent = false;\n return function (...args) {\n // prevent double callbacks\n if (sent)\n return;\n sent = true;\n debug(\"sending ack %j\", args);\n self.packet({\n type: socket_io_parser_1.PacketType.ACK,\n id: id,\n data: args,\n });\n };\n }\n /**\n * Called upon a server acknowlegement.\n *\n * @param packet\n * @private\n */\n onack(packet) {\n const ack = this.acks[packet.id];\n if (\"function\" === typeof ack) {\n debug(\"calling ack %s with %j\", packet.id, packet.data);\n ack.apply(this, packet.data);\n delete this.acks[packet.id];\n }\n else {\n debug(\"bad ack %s\", packet.id);\n }\n }\n /**\n * Called upon server connect.\n *\n * @private\n */\n onconnect(id) {\n debug(\"socket connected with id %s\", id);\n this.id = id;\n this.connected = true;\n this.emitBuffered();\n this.emitReserved(\"connect\");\n }\n /**\n * Emit buffered events (received and emitted).\n *\n * @private\n */\n emitBuffered() {\n this.receiveBuffer.forEach((args) => this.emitEvent(args));\n this.receiveBuffer = [];\n this.sendBuffer.forEach((packet) => {\n this.notifyOutgoingListeners(packet);\n this.packet(packet);\n });\n this.sendBuffer = [];\n }\n /**\n * Called upon server disconnect.\n *\n * @private\n */\n ondisconnect() {\n debug(\"server disconnect (%s)\", this.nsp);\n this.destroy();\n this.onclose(\"io server disconnect\");\n }\n /**\n * Called upon forced client/server side disconnections,\n * this method ensures the manager stops tracking us and\n * that reconnections don't get triggered for this.\n *\n * @private\n */\n destroy() {\n if (this.subs) {\n // clean subscriptions to avoid reconnections\n this.subs.forEach((subDestroy) => subDestroy());\n this.subs = undefined;\n }\n this.io[\"_destroy\"](this);\n }\n /**\n * Disconnects the socket manually. In that case, the socket will not try to reconnect.\n *\n * If this is the last active Socket instance of the {@link Manager}, the low-level connection will be closed.\n *\n * @example\n * const socket = io();\n *\n * socket.on(\"disconnect\", (reason) => {\n * // console.log(reason); prints \"io client disconnect\"\n * });\n *\n * socket.disconnect();\n *\n * @return self\n */\n disconnect() {\n if (this.connected) {\n debug(\"performing disconnect (%s)\", this.nsp);\n this.packet({ type: socket_io_parser_1.PacketType.DISCONNECT });\n }\n // remove socket from pool\n this.destroy();\n if (this.connected) {\n // fire events\n this.onclose(\"io client disconnect\");\n }\n return this;\n }\n /**\n * Alias for {@link disconnect()}.\n *\n * @return self\n */\n close() {\n return this.disconnect();\n }\n /**\n * Sets the compress flag.\n *\n * @example\n * socket.compress(false).emit(\"hello\");\n *\n * @param compress - if `true`, compresses the sending data\n * @return self\n */\n compress(compress) {\n this.flags.compress = compress;\n return this;\n }\n /**\n * Sets a modifier for a subsequent event emission that the event message will be dropped when this socket is not\n * ready to send messages.\n *\n * @example\n * socket.volatile.emit(\"hello\"); // the server may or may not receive it\n *\n * @returns self\n */\n get volatile() {\n this.flags.volatile = true;\n return this;\n }\n /**\n * Sets a modifier for a subsequent event emission that the callback will be called with an error when the\n * given number of milliseconds have elapsed without an acknowledgement from the server:\n *\n * @example\n * socket.timeout(5000).emit(\"my-event\", (err) => {\n * if (err) {\n * // the server did not acknowledge the event in the given delay\n * }\n * });\n *\n * @returns self\n */\n timeout(timeout) {\n this.flags.timeout = timeout;\n return this;\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback.\n *\n * @example\n * socket.onAny((event, ...args) => {\n * console.log(`got ${event}`);\n * });\n *\n * @param listener\n */\n onAny(listener) {\n this._anyListeners = this._anyListeners || [];\n this._anyListeners.push(listener);\n return this;\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback. The listener is added to the beginning of the listeners array.\n *\n * @example\n * socket.prependAny((event, ...args) => {\n * console.log(`got event ${event}`);\n * });\n *\n * @param listener\n */\n prependAny(listener) {\n this._anyListeners = this._anyListeners || [];\n this._anyListeners.unshift(listener);\n return this;\n }\n /**\n * Removes the listener that will be fired when any event is emitted.\n *\n * @example\n * const catchAllListener = (event, ...args) => {\n * console.log(`got event ${event}`);\n * }\n *\n * socket.onAny(catchAllListener);\n *\n * // remove a specific listener\n * socket.offAny(catchAllListener);\n *\n * // or remove all listeners\n * socket.offAny();\n *\n * @param listener\n */\n offAny(listener) {\n if (!this._anyListeners) {\n return this;\n }\n if (listener) {\n const listeners = this._anyListeners;\n for (let i = 0; i < listeners.length; i++) {\n if (listener === listeners[i]) {\n listeners.splice(i, 1);\n return this;\n }\n }\n }\n else {\n this._anyListeners = [];\n }\n return this;\n }\n /**\n * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,\n * e.g. to remove listeners.\n */\n listenersAny() {\n return this._anyListeners || [];\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback.\n *\n * Note: acknowledgements sent to the server are not included.\n *\n * @example\n * socket.onAnyOutgoing((event, ...args) => {\n * console.log(`sent event ${event}`);\n * });\n *\n * @param listener\n */\n onAnyOutgoing(listener) {\n this._anyOutgoingListeners = this._anyOutgoingListeners || [];\n this._anyOutgoingListeners.push(listener);\n return this;\n }\n /**\n * Adds a listener that will be fired when any event is emitted. The event name is passed as the first argument to the\n * callback. The listener is added to the beginning of the listeners array.\n *\n * Note: acknowledgements sent to the server are not included.\n *\n * @example\n * socket.prependAnyOutgoing((event, ...args) => {\n * console.log(`sent event ${event}`);\n * });\n *\n * @param listener\n */\n prependAnyOutgoing(listener) {\n this._anyOutgoingListeners = this._anyOutgoingListeners || [];\n this._anyOutgoingListeners.unshift(listener);\n return this;\n }\n /**\n * Removes the listener that will be fired when any event is emitted.\n *\n * @example\n * const catchAllListener = (event, ...args) => {\n * console.log(`sent event ${event}`);\n * }\n *\n * socket.onAnyOutgoing(catchAllListener);\n *\n * // remove a specific listener\n * socket.offAnyOutgoing(catchAllListener);\n *\n * // or remove all listeners\n * socket.offAnyOutgoing();\n *\n * @param [listener] - the catch-all listener (optional)\n */\n offAnyOutgoing(listener) {\n if (!this._anyOutgoingListeners) {\n return this;\n }\n if (listener) {\n const listeners = this._anyOutgoingListeners;\n for (let i = 0; i < listeners.length; i++) {\n if (listener === listeners[i]) {\n listeners.splice(i, 1);\n return this;\n }\n }\n }\n else {\n this._anyOutgoingListeners = [];\n }\n return this;\n }\n /**\n * Returns an array of listeners that are listening for any event that is specified. This array can be manipulated,\n * e.g. to remove listeners.\n */\n listenersAnyOutgoing() {\n return this._anyOutgoingListeners || [];\n }\n /**\n * Notify the listeners for each packet sent\n *\n * @param packet\n *\n * @private\n */\n notifyOutgoingListeners(packet) {\n if (this._anyOutgoingListeners && this._anyOutgoingListeners.length) {\n const listeners = this._anyOutgoingListeners.slice();\n for (const listener of listeners) {\n listener.apply(this, packet.data);\n }\n }\n }\n}\nexports.Socket = Socket;\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/build/cjs/socket.js?");
5792
-
5793
- /***/ }),
5794
-
5795
- /***/ "./node_modules/socket.io-client/build/cjs/url.js":
5796
- /*!********************************************************!*\
5797
- !*** ./node_modules/socket.io-client/build/cjs/url.js ***!
5798
- \********************************************************/
5799
- /***/ (function(__unused_webpack_module, exports, __webpack_require__) {
5800
-
5801
- "use strict";
5802
- eval("\nvar __importDefault = (this && this.__importDefault) || function (mod) {\n return (mod && mod.__esModule) ? mod : { \"default\": mod };\n};\nObject.defineProperty(exports, \"__esModule\", ({ value: true }));\nexports.url = void 0;\nconst engine_io_client_1 = __webpack_require__(/*! engine.io-client */ \"./node_modules/engine.io-client/build/cjs/index.js\");\nconst debug_1 = __importDefault(__webpack_require__(/*! debug */ \"./node_modules/socket.io-client/node_modules/debug/src/browser.js\")); // debug()\nconst debug = debug_1.default(\"socket.io-client:url\"); // debug()\n/**\n * URL parser.\n *\n * @param uri - url\n * @param path - the request path of the connection\n * @param loc - An object meant to mimic window.location.\n * Defaults to window.location.\n * @public\n */\nfunction url(uri, path = \"\", loc) {\n let obj = uri;\n // default to window.location\n loc = loc || (typeof location !== \"undefined\" && location);\n if (null == uri)\n uri = loc.protocol + \"//\" + loc.host;\n // relative path support\n if (typeof uri === \"string\") {\n if (\"/\" === uri.charAt(0)) {\n if (\"/\" === uri.charAt(1)) {\n uri = loc.protocol + uri;\n }\n else {\n uri = loc.host + uri;\n }\n }\n if (!/^(https?|wss?):\\/\\//.test(uri)) {\n debug(\"protocol-less url %s\", uri);\n if (\"undefined\" !== typeof loc) {\n uri = loc.protocol + \"//\" + uri;\n }\n else {\n uri = \"https://\" + uri;\n }\n }\n // parse\n debug(\"parse %s\", uri);\n obj = engine_io_client_1.parse(uri);\n }\n // make sure we treat `localhost:80` and `localhost` equally\n if (!obj.port) {\n if (/^(http|ws)$/.test(obj.protocol)) {\n obj.port = \"80\";\n }\n else if (/^(http|ws)s$/.test(obj.protocol)) {\n obj.port = \"443\";\n }\n }\n obj.path = obj.path || \"/\";\n const ipv6 = obj.host.indexOf(\":\") !== -1;\n const host = ipv6 ? \"[\" + obj.host + \"]\" : obj.host;\n // define unique id\n obj.id = obj.protocol + \"://\" + host + \":\" + obj.port + path;\n // define href\n obj.href =\n obj.protocol +\n \"://\" +\n host +\n (loc && loc.port === obj.port ? \"\" : \":\" + obj.port);\n return obj;\n}\nexports.url = url;\n\n\n//# sourceURL=webpack://dcp/./node_modules/socket.io-client/build/cjs/url.js?");
5803
-
5804
- /***/ }),
5805
-
5806
5852
  /***/ "./node_modules/socket.io-parser/build/cjs/binary.js":
5807
5853
  /*!***********************************************************!*\
5808
5854
  !*** ./node_modules/socket.io-parser/build/cjs/binary.js ***!