@jbrowse/cli 3.6.3 → 3.6.4

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.
package/bundle/index.js CHANGED
@@ -3163,7 +3163,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
3163
3163
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3164
3164
 
3165
3165
  "use strict";
3166
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ generateKey: () => (/* binding */ generateKey),\n/* harmony export */ isValidPort: () => (/* binding */ isValidPort),\n/* harmony export */ parsePort: () => (/* binding */ parsePort),\n/* harmony export */ setupConfigFile: () => (/* binding */ setupConfigFile),\n/* harmony export */ setupRoutes: () => (/* binding */ setupRoutes),\n/* harmony export */ setupServer: () => (/* binding */ setupServer),\n/* harmony export */ startServer: () => (/* binding */ startServer)\n/* harmony export */ });\n/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! crypto */ \"crypto\");\n/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ \"fs\");\n/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! os */ \"os\");\n/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(os__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! path */ \"path\");\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var cors__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! cors */ \"../../node_modules/cors/lib/index.js\");\n/* harmony import */ var cors__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(cors__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var express__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! express */ \"../../node_modules/express/index.js\");\n/* harmony import */ var express__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(express__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils */ \"./src/utils.ts\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { if (r) i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n;else { var o = function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); }; o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2); } }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\n\n\n\n\n\n\n\n/**\n * Validates if a port number is in the valid range\n */\nfunction isValidPort(port) {\n return port > 0 && port < 65535;\n}\n\n/**\n * Parses and validates a port string\n */\nfunction parsePort(_ref) {\n var portStr = _ref.portStr,\n _ref$defaultPort = _ref.defaultPort,\n defaultPort = _ref$defaultPort === void 0 ? 9090 : _ref$defaultPort;\n if (!portStr) {\n return defaultPort;\n }\n var parsedPort = Number.parseInt(portStr, 10);\n if (!isValidPort(parsedPort)) {\n throw new Error(\"\".concat(portStr, \" is not a valid port\"));\n }\n return parsedPort;\n}\n\n/**\n * Generates a random alphanumeric string to serve as admin key\n */\nfunction generateKey() {\n return crypto__WEBPACK_IMPORTED_MODULE_0___default().randomBytes(5).toString('hex');\n}\n\n/**\n * Sets up the configuration file\n */\nfunction setupConfigFile() {\n return _setupConfigFile.apply(this, arguments);\n}\n\n/**\n * Validates admin key and extracts config path from request\n */\nfunction _setupConfigFile() {\n _setupConfigFile = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var _ref2,\n _ref2$root,\n root,\n output,\n isDir,\n outFile,\n baseDir,\n _args = arguments;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.n) {\n case 0:\n _ref2 = _args.length > 0 && _args[0] !== undefined ? _args[0] : {}, _ref2$root = _ref2.root, root = _ref2$root === void 0 ? '.' : _ref2$root;\n output = root;\n isDir = fs__WEBPACK_IMPORTED_MODULE_1___default().lstatSync(output).isDirectory();\n outFile = isDir ? \"\".concat(output, \"/config.json\") : output;\n baseDir = path__WEBPACK_IMPORTED_MODULE_3___default().dirname(outFile);\n if (!fs__WEBPACK_IMPORTED_MODULE_1___default().existsSync(outFile)) {\n _context.n = 1;\n break;\n }\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Found existing config file \".concat(outFile));\n _context.n = 2;\n break;\n case 1:\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Creating config file \".concat(outFile));\n _context.n = 2;\n return (0,_utils__WEBPACK_IMPORTED_MODULE_6__.writeJsonFile)(outFile, {\n assemblies: [],\n configuration: {},\n connections: [],\n defaultSession: {\n name: 'New Session'\n },\n tracks: []\n });\n case 2:\n return _context.a(2, {\n outFile: outFile,\n baseDir: baseDir\n });\n }\n }, _callee);\n }));\n return _setupConfigFile.apply(this, arguments);\n}\nfunction validateAndExtractParams(_ref3) {\n var req = _ref3.req,\n key = _ref3.key,\n baseDir = _ref3.baseDir,\n outFile = _ref3.outFile;\n var body = req.body;\n // Check for adminKey in both query parameters and request body for backward compatibility\n var adminKeyFromQuery = req.query.adminKey;\n var adminKeyFromBody = body === null || body === void 0 ? void 0 : body.adminKey;\n var adminKey = adminKeyFromBody || adminKeyFromQuery;\n if (adminKey !== key) {\n return {\n isValid: false,\n error: 'Invalid admin key'\n };\n }\n\n // Get configPath from either body or query parameters\n var configPathParam = (body === null || body === void 0 ? void 0 : body.configPath) || req.query.config;\n try {\n // Normalize the config path\n var configPath = configPathParam ? path__WEBPACK_IMPORTED_MODULE_3___default().normalize(path__WEBPACK_IMPORTED_MODULE_3___default().join(baseDir, configPathParam)) : outFile;\n\n // Check for directory traversal attempts\n var normalizedBaseDir = path__WEBPACK_IMPORTED_MODULE_3___default().normalize(baseDir);\n var relPath = path__WEBPACK_IMPORTED_MODULE_3___default().relative(normalizedBaseDir, configPath);\n\n // Ensure the config path is within the base directory and doesn't contain path traversal\n if (relPath.startsWith('..') || path__WEBPACK_IMPORTED_MODULE_3___default().isAbsolute(relPath)) {\n return {\n isValid: false,\n error: 'Cannot perform directory traversal'\n };\n }\n return {\n isValid: true,\n configPath: configPath\n };\n } catch (error) {\n return {\n isValid: false,\n error: 'Failed to validate config path'\n };\n }\n}\n\n/**\n * Sets up the API routes for the server\n */\nfunction setupRoutes(_ref4) {\n var app = _ref4.app,\n baseDir = _ref4.baseDir,\n outFile = _ref4.outFile,\n key = _ref4.key;\n // Root route\n app.get('/', function (_req, res) {\n res.setHeader('Content-Type', 'text/plain');\n res.send('JBrowse Admin Server');\n });\n\n // Update config route\n app.post('/updateConfig', function (req, res) {\n var body = req.body;\n var config = body.config;\n var validation = validateAndExtractParams({\n req: req,\n key: key,\n baseDir: baseDir,\n outFile: outFile\n });\n if (!validation.isValid) {\n res.status(401).setHeader('Content-Type', 'text/plain');\n res.send(\"Error: \".concat(validation.error));\n return;\n }\n\n // Remove adminKey from body before saving to config\n if (body.adminKey) {\n delete body.adminKey;\n }\n try {\n fs__WEBPACK_IMPORTED_MODULE_1___default().writeFileSync(validation.configPath, JSON.stringify(config, null, 2));\n res.setHeader('Content-Type', 'text/plain');\n res.send('Config updated successfully');\n } catch (error) {\n res.status(500).setHeader('Content-Type', 'text/plain');\n res.send('Error: Failed to update config');\n }\n });\n\n // Get config route\n app.get('/config', function (req, res) {\n var validation = validateAndExtractParams({\n req: req,\n key: key,\n baseDir: baseDir,\n outFile: outFile\n });\n if (!validation.isValid) {\n res.status(401).setHeader('Content-Type', 'text/plain');\n res.send(\"Error: \".concat(validation.error));\n return;\n }\n try {\n if (fs__WEBPACK_IMPORTED_MODULE_1___default().existsSync(validation.configPath)) {\n var config = fs__WEBPACK_IMPORTED_MODULE_1___default().readFileSync(validation.configPath, 'utf8');\n res.setHeader('Content-Type', 'text/plain');\n res.send(config);\n } else {\n res.status(404).setHeader('Content-Type', 'text/plain');\n res.send('Error: Config file not found');\n }\n } catch (error) {\n console.error('Error reading config:', error);\n res.status(500).setHeader('Content-Type', 'text/plain');\n res.send('Error: Failed to read config');\n }\n });\n}\n\n/**\n * Sets up the Express server with routes\n */\nfunction setupServer(_ref5) {\n var baseDir = _ref5.baseDir,\n outFile = _ref5.outFile,\n bodySizeLimit = _ref5.bodySizeLimit;\n // Create Express application\n var app = express__WEBPACK_IMPORTED_MODULE_5___default()();\n\n // Configure middleware\n app.use(express__WEBPACK_IMPORTED_MODULE_5___default()[\"static\"](baseDir));\n app.use(cors__WEBPACK_IMPORTED_MODULE_4___default()());\n app.use(express__WEBPACK_IMPORTED_MODULE_5___default().json({\n limit: bodySizeLimit\n }));\n\n // Add error handling middleware\n app.use(function (err, _req, res, next) {\n if (err) {\n console.error('Server error:', err);\n res.status(500).setHeader('Content-Type', 'text/plain');\n res.send('Internal Server Error');\n } else {\n next();\n }\n });\n\n // Generate admin key and store it\n var key = generateKey();\n var keyPath = path__WEBPACK_IMPORTED_MODULE_3___default().join(os__WEBPACK_IMPORTED_MODULE_2___default().tmpdir(), \"jbrowse-admin-\".concat(key));\n try {\n fs__WEBPACK_IMPORTED_MODULE_1___default().writeFileSync(keyPath, key);\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Admin key stored at \".concat(keyPath));\n } catch (error) {\n console.error(\"Failed to write admin key to \".concat(keyPath, \":\"), error.message);\n // Continue anyway, as this is not critical\n }\n\n // Set up routes\n setupRoutes({\n app: app,\n baseDir: baseDir,\n outFile: outFile,\n key: key\n });\n return {\n app: app,\n key: key,\n keyPath: keyPath\n };\n}\n\n/**\n * Starts the server and sets up shutdown handlers\n */\nfunction startServer(_ref6) {\n var app = _ref6.app,\n port = _ref6.port,\n key = _ref6.key,\n outFile = _ref6.outFile,\n keyPath = _ref6.keyPath;\n // Start the server\n var server = app.listen(port, function () {\n console.log(\"Admin server started on port \".concat(port, \"\\n\\n\") + \"To access the admin interface, open your browser to:\\n\" + \"http://localhost:\".concat(port, \"?adminKey=\").concat(key, \"\\n\\n\") + \"Admin key: \".concat(key, \"\\n\") + \"Config file: \".concat(outFile, \"\\n\\n\") + \"To stop the server, press Ctrl+C\");\n });\n\n // Handle server errors\n server.on('error', function (error) {\n if (error.code === 'EADDRINUSE') {\n console.error(\"Error: Port \".concat(port, \" is already in use\"));\n } else {\n console.error('Server error:', error.message);\n }\n process.exit(1);\n });\n\n // Common shutdown handler\n var shutdownHandler = function shutdownHandler() {\n console.log('\\nShutting down admin server...');\n server.close(function () {\n // Clean up admin key file\n try {\n fs__WEBPACK_IMPORTED_MODULE_1___default().unlinkSync(keyPath);\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Removed admin key file: \".concat(keyPath));\n } catch (error) {\n // Ignore errors when cleaning up\n }\n process.exit(0);\n });\n };\n\n // Handle server shutdown\n process.on('SIGINT', shutdownHandler);\n process.on('SIGTERM', shutdownHandler);\n}\n\n//# sourceURL=webpack://@jbrowse/cli/./src/commands/admin-server-utils.ts?\n}");
3166
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ generateKey: () => (/* binding */ generateKey),\n/* harmony export */ isValidPort: () => (/* binding */ isValidPort),\n/* harmony export */ parsePort: () => (/* binding */ parsePort),\n/* harmony export */ setupConfigFile: () => (/* binding */ setupConfigFile),\n/* harmony export */ setupRoutes: () => (/* binding */ setupRoutes),\n/* harmony export */ setupServer: () => (/* binding */ setupServer),\n/* harmony export */ startServer: () => (/* binding */ startServer)\n/* harmony export */ });\n/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! crypto */ \"crypto\");\n/* harmony import */ var crypto__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(crypto__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! fs */ \"fs\");\n/* harmony import */ var fs__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(fs__WEBPACK_IMPORTED_MODULE_1__);\n/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! os */ \"os\");\n/* harmony import */ var os__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__webpack_require__.n(os__WEBPACK_IMPORTED_MODULE_2__);\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3__ = __webpack_require__(/*! path */ \"path\");\n/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_3___default = /*#__PURE__*/__webpack_require__.n(path__WEBPACK_IMPORTED_MODULE_3__);\n/* harmony import */ var cors__WEBPACK_IMPORTED_MODULE_4__ = __webpack_require__(/*! cors */ \"../../node_modules/cors/lib/index.js\");\n/* harmony import */ var cors__WEBPACK_IMPORTED_MODULE_4___default = /*#__PURE__*/__webpack_require__.n(cors__WEBPACK_IMPORTED_MODULE_4__);\n/* harmony import */ var express__WEBPACK_IMPORTED_MODULE_5__ = __webpack_require__(/*! express */ \"../../node_modules/express/index.js\");\n/* harmony import */ var express__WEBPACK_IMPORTED_MODULE_5___default = /*#__PURE__*/__webpack_require__.n(express__WEBPACK_IMPORTED_MODULE_5__);\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_6__ = __webpack_require__(/*! ../utils */ \"./src/utils.ts\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { if (r) i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n;else { var o = function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); }; o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2); } }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\n\n\n\n\n\n\n\n/**\n * Validates if a port number is in the valid range\n */\nfunction isValidPort(port) {\n return port > 0 && port < 65535;\n}\n\n/**\n * Parses and validates a port string\n */\nfunction parsePort(_ref) {\n var portStr = _ref.portStr,\n _ref$defaultPort = _ref.defaultPort,\n defaultPort = _ref$defaultPort === void 0 ? 9090 : _ref$defaultPort;\n if (!portStr) {\n return defaultPort;\n }\n var parsedPort = Number.parseInt(portStr, 10);\n if (!isValidPort(parsedPort)) {\n throw new Error(\"\".concat(portStr, \" is not a valid port\"));\n }\n return parsedPort;\n}\n\n/**\n * Generates a random alphanumeric string to serve as admin key\n */\nfunction generateKey() {\n return crypto__WEBPACK_IMPORTED_MODULE_0___default().randomBytes(5).toString('hex');\n}\n\n/**\n * Sets up the configuration file\n */\nfunction setupConfigFile() {\n return _setupConfigFile.apply(this, arguments);\n}\n\n/**\n * Validates admin key and extracts config path from request\n */\nfunction _setupConfigFile() {\n _setupConfigFile = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee() {\n var _ref2,\n _ref2$root,\n root,\n output,\n isDir,\n outFile,\n baseDir,\n _args = arguments;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.n) {\n case 0:\n _ref2 = _args.length > 0 && _args[0] !== undefined ? _args[0] : {}, _ref2$root = _ref2.root, root = _ref2$root === void 0 ? '.' : _ref2$root;\n output = root;\n isDir = fs__WEBPACK_IMPORTED_MODULE_1___default().lstatSync(output).isDirectory();\n outFile = isDir ? \"\".concat(output, \"/config.json\") : output;\n baseDir = path__WEBPACK_IMPORTED_MODULE_3___default().dirname(outFile);\n if (!fs__WEBPACK_IMPORTED_MODULE_1___default().existsSync(outFile)) {\n _context.n = 1;\n break;\n }\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Found existing config file \".concat(outFile));\n _context.n = 2;\n break;\n case 1:\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Creating config file \".concat(outFile));\n _context.n = 2;\n return (0,_utils__WEBPACK_IMPORTED_MODULE_6__.writeJsonFile)(outFile, {\n assemblies: [],\n configuration: {},\n connections: [],\n defaultSession: {\n name: 'New Session'\n },\n tracks: []\n });\n case 2:\n return _context.a(2, {\n outFile: outFile,\n baseDir: baseDir\n });\n }\n }, _callee);\n }));\n return _setupConfigFile.apply(this, arguments);\n}\nfunction validateAndExtractParams(_ref3) {\n var req = _ref3.req,\n key = _ref3.key,\n baseDir = _ref3.baseDir,\n outFile = _ref3.outFile;\n var body = req.body;\n // Check for adminKey in both query parameters and request body for backward compatibility\n var adminKeyFromQuery = req.query.adminKey;\n var adminKeyFromBody = body === null || body === void 0 ? void 0 : body.adminKey;\n var adminKey = adminKeyFromBody || adminKeyFromQuery;\n if (adminKey !== key) {\n return {\n isValid: false,\n error: 'Invalid admin key'\n };\n }\n\n // Get configPath from either body or query parameters\n var configPathParam = (body === null || body === void 0 ? void 0 : body.configPath) || req.query.config;\n try {\n // Normalize the config path\n var configPath = configPathParam ? path__WEBPACK_IMPORTED_MODULE_3___default().normalize(path__WEBPACK_IMPORTED_MODULE_3___default().join(baseDir, configPathParam)) : outFile;\n\n // Check for directory traversal attempts\n var normalizedBaseDir = path__WEBPACK_IMPORTED_MODULE_3___default().normalize(baseDir);\n var relPath = path__WEBPACK_IMPORTED_MODULE_3___default().relative(normalizedBaseDir, configPath);\n\n // Ensure the config path is within the base directory and doesn't contain path traversal\n if (relPath.startsWith('..') || path__WEBPACK_IMPORTED_MODULE_3___default().isAbsolute(relPath)) {\n return {\n isValid: false,\n error: 'Cannot perform directory traversal'\n };\n }\n return {\n isValid: true,\n configPath: configPath\n };\n } catch (error) {\n return {\n isValid: false,\n error: 'Failed to validate config path'\n };\n }\n}\n\n/**\n * Sets up the API routes for the server\n */\nfunction setupRoutes(_ref4) {\n var app = _ref4.app,\n baseDir = _ref4.baseDir,\n outFile = _ref4.outFile,\n key = _ref4.key,\n serverRef = _ref4.serverRef;\n // Root route\n app.get('/', function (_req, res) {\n res.setHeader('Content-Type', 'text/plain');\n res.send('JBrowse Admin Server');\n });\n\n // Update config route\n app.post('/updateConfig', function (req, res) {\n var body = req.body;\n var config = body.config;\n var validation = validateAndExtractParams({\n req: req,\n key: key,\n baseDir: baseDir,\n outFile: outFile\n });\n if (!validation.isValid) {\n res.status(401).setHeader('Content-Type', 'text/plain');\n res.send(\"Error: \".concat(validation.error));\n return;\n }\n\n // Remove adminKey from body before saving to config\n if (body.adminKey) {\n delete body.adminKey;\n }\n try {\n fs__WEBPACK_IMPORTED_MODULE_1___default().writeFileSync(validation.configPath, JSON.stringify(config, null, 2));\n res.setHeader('Content-Type', 'text/plain');\n res.send('Config updated successfully');\n } catch (error) {\n res.status(500).setHeader('Content-Type', 'text/plain');\n res.send('Error: Failed to update config');\n }\n });\n\n // Get config route\n app.get('/config', function (req, res) {\n var validation = validateAndExtractParams({\n req: req,\n key: key,\n baseDir: baseDir,\n outFile: outFile\n });\n if (!validation.isValid) {\n res.status(401).setHeader('Content-Type', 'text/plain');\n res.send(\"Error: \".concat(validation.error));\n return;\n }\n try {\n if (fs__WEBPACK_IMPORTED_MODULE_1___default().existsSync(validation.configPath)) {\n var config = fs__WEBPACK_IMPORTED_MODULE_1___default().readFileSync(validation.configPath, 'utf8');\n res.setHeader('Content-Type', 'text/plain');\n res.send(config);\n } else {\n res.status(404).setHeader('Content-Type', 'text/plain');\n res.send('Error: Config file not found');\n }\n } catch (error) {\n console.error('Error reading config:', error);\n res.status(500).setHeader('Content-Type', 'text/plain');\n res.send('Error: Failed to read config');\n }\n });\n\n // Shutdown route for testing\n app.post('/shutdown', function (req, res) {\n var body = req.body;\n var adminKey = body === null || body === void 0 ? void 0 : body.adminKey;\n if (adminKey !== key) {\n res.status(401).setHeader('Content-Type', 'text/plain');\n res.send('Error: Invalid admin key');\n return;\n }\n res.setHeader('Content-Type', 'text/plain');\n res.send('Server shutting down');\n\n // Shutdown the server after sending response\n setImmediate(function () {\n if (serverRef.current) {\n serverRef.current.close();\n }\n });\n });\n}\n\n/**\n * Sets up the Express server with routes\n */\nfunction setupServer(_ref5) {\n var baseDir = _ref5.baseDir,\n outFile = _ref5.outFile,\n bodySizeLimit = _ref5.bodySizeLimit;\n // Create Express application\n var app = express__WEBPACK_IMPORTED_MODULE_5___default()();\n\n // Configure middleware\n app.use(express__WEBPACK_IMPORTED_MODULE_5___default()[\"static\"](baseDir));\n app.use(cors__WEBPACK_IMPORTED_MODULE_4___default()());\n app.use(express__WEBPACK_IMPORTED_MODULE_5___default().json({\n limit: bodySizeLimit\n }));\n\n // Add error handling middleware\n app.use(function (err, _req, res, next) {\n if (err) {\n console.error('Server error:', err);\n res.status(500).setHeader('Content-Type', 'text/plain');\n res.send('Internal Server Error');\n } else {\n next();\n }\n });\n\n // Generate admin key and store it\n var key = generateKey();\n var keyPath = path__WEBPACK_IMPORTED_MODULE_3___default().join(os__WEBPACK_IMPORTED_MODULE_2___default().tmpdir(), \"jbrowse-admin-\".concat(key));\n try {\n fs__WEBPACK_IMPORTED_MODULE_1___default().writeFileSync(keyPath, key);\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Admin key stored at \".concat(keyPath));\n } catch (error) {\n console.error(\"Failed to write admin key to \".concat(keyPath, \":\"), error.message);\n // Continue anyway, as this is not critical\n }\n\n // Create server reference for shutdown route\n var serverRef = {\n current: null\n };\n\n // Set up routes\n setupRoutes({\n app: app,\n baseDir: baseDir,\n outFile: outFile,\n key: key,\n serverRef: serverRef\n });\n return {\n app: app,\n key: key,\n keyPath: keyPath,\n serverRef: serverRef\n };\n}\n\n/**\n * Starts the server and sets up shutdown handlers\n */\nfunction startServer(_ref6) {\n var app = _ref6.app,\n port = _ref6.port,\n key = _ref6.key,\n outFile = _ref6.outFile,\n keyPath = _ref6.keyPath,\n serverRef = _ref6.serverRef;\n // Start the server\n var server = app.listen(port, function () {\n console.log(\"Admin server started on port \".concat(port, \"\\n\\n\") + \"To access the admin interface, open your browser to:\\n\" + \"http://localhost:\".concat(port, \"?adminKey=\").concat(key, \"\\n\\n\") + \"Admin key: \".concat(key, \"\\n\") + \"Config file: \".concat(outFile, \"\\n\\n\") + \"To stop the server, press Ctrl+C\");\n });\n\n // Store server reference for shutdown route\n serverRef.current = server;\n\n // Handle server errors\n server.on('error', function (error) {\n if (error.code === 'EADDRINUSE') {\n console.error(\"Error: Port \".concat(port, \" is already in use\"));\n } else {\n console.error('Server error:', error.message);\n }\n process.exit(1);\n });\n\n // Common shutdown handler\n var shutdownHandler = function shutdownHandler() {\n console.log('\\nShutting down admin server...');\n server.close(function () {\n // Clean up admin key file\n try {\n fs__WEBPACK_IMPORTED_MODULE_1___default().unlinkSync(keyPath);\n (0,_utils__WEBPACK_IMPORTED_MODULE_6__.debug)(\"Removed admin key file: \".concat(keyPath));\n } catch (error) {\n // Ignore errors when cleaning up\n }\n process.exit(0);\n });\n };\n\n // Handle server shutdown\n process.on('SIGINT', shutdownHandler);\n process.on('SIGTERM', shutdownHandler);\n}\n\n//# sourceURL=webpack://@jbrowse/cli/./src/commands/admin-server-utils.ts?\n}");
3167
3167
 
3168
3168
  /***/ }),
3169
3169
 
@@ -3174,7 +3174,7 @@ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpa
3174
3174
  /***/ ((__unused_webpack_module, __webpack_exports__, __webpack_require__) => {
3175
3175
 
3176
3176
  "use strict";
3177
- eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ run: () => (/* binding */ run)\n/* harmony export */ });\n/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! util */ \"util\");\n/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils */ \"./src/utils.ts\");\n/* harmony import */ var _admin_server_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./admin-server-utils */ \"./src/commands/admin-server-utils.ts\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { if (r) i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n;else { var o = function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); }; o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2); } }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\n\n\n\nfunction run(_x) {\n return _run.apply(this, arguments);\n}\nfunction _run() {\n _run = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(args) {\n var options, _parseArgs, flags, description, examples, root, _flags$bodySizeLimit, bodySizeLimit, _yield$setupConfigFil, outFile, baseDir, port, _setupServer, app, key, keyPath;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.n) {\n case 0:\n options = {\n help: {\n type: 'boolean',\n \"short\": 'h'\n },\n port: {\n type: 'string',\n \"short\": 'p',\n description: 'Specified port to start the server on (default: 9090)'\n },\n root: {\n type: 'string',\n description: 'Path to the root of the JB2 installation'\n },\n bodySizeLimit: {\n type: 'string',\n description: 'Size limit of the update message (default: 25mb)'\n }\n };\n _parseArgs = (0,util__WEBPACK_IMPORTED_MODULE_0__.parseArgs)({\n args: args,\n options: options,\n allowPositionals: true\n }), flags = _parseArgs.values;\n description = 'Start up a small admin server for JBrowse configuration';\n examples = ['$ jbrowse admin-server', '$ jbrowse admin-server -p 8888'];\n if (!flags.help) {\n _context.n = 1;\n break;\n }\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.printHelp)({\n description: description,\n examples: examples,\n usage: 'jbrowse admin-server [options]',\n options: options\n });\n return _context.a(2);\n case 1:\n root = flags.root, _flags$bodySizeLimit = flags.bodySizeLimit, bodySizeLimit = _flags$bodySizeLimit === void 0 ? '25mb' : _flags$bodySizeLimit;\n _context.n = 2;\n return (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.setupConfigFile)({\n root: root\n });\n case 2:\n _yield$setupConfigFil = _context.v;\n outFile = _yield$setupConfigFil.outFile;\n baseDir = _yield$setupConfigFil.baseDir;\n // Parse and validate port\n port = (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.parsePort)({\n portStr: flags.port\n }); // Set up the Express server\n _setupServer = (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.setupServer)({\n baseDir: baseDir,\n outFile: outFile,\n bodySizeLimit: bodySizeLimit\n }), app = _setupServer.app, key = _setupServer.key, keyPath = _setupServer.keyPath; // Start the server and set up shutdown handlers\n (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.startServer)({\n app: app,\n port: port,\n key: key,\n outFile: outFile,\n keyPath: keyPath\n });\n case 3:\n return _context.a(2);\n }\n }, _callee);\n }));\n return _run.apply(this, arguments);\n}\n\n//# sourceURL=webpack://@jbrowse/cli/./src/commands/admin-server.ts?\n}");
3177
+ eval("{__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ run: () => (/* binding */ run)\n/* harmony export */ });\n/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! util */ \"util\");\n/* harmony import */ var util__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(util__WEBPACK_IMPORTED_MODULE_0__);\n/* harmony import */ var _utils__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ../utils */ \"./src/utils.ts\");\n/* harmony import */ var _admin_server_utils__WEBPACK_IMPORTED_MODULE_2__ = __webpack_require__(/*! ./admin-server-utils */ \"./src/commands/admin-server-utils.ts\");\nfunction _regenerator() { /*! regenerator-runtime -- Copyright (c) 2014-present, Facebook, Inc. -- license (MIT): https://github.com/babel/babel/blob/main/packages/babel-helpers/LICENSE */ var e, t, r = \"function\" == typeof Symbol ? Symbol : {}, n = r.iterator || \"@@iterator\", o = r.toStringTag || \"@@toStringTag\"; function i(r, n, o, i) { var c = n && n.prototype instanceof Generator ? n : Generator, u = Object.create(c.prototype); return _regeneratorDefine2(u, \"_invoke\", function (r, n, o) { var i, c, u, f = 0, p = o || [], y = !1, G = { p: 0, n: 0, v: e, a: d, f: d.bind(e, 4), d: function d(t, r) { return i = t, c = 0, u = e, G.n = r, a; } }; function d(r, n) { for (c = r, u = n, t = 0; !y && f && !o && t < p.length; t++) { var o, i = p[t], d = G.p, l = i[2]; r > 3 ? (o = l === n) && (u = i[(c = i[4]) ? 5 : (c = 3, 3)], i[4] = i[5] = e) : i[0] <= d && ((o = r < 2 && d < i[1]) ? (c = 0, G.v = n, G.n = i[1]) : d < l && (o = r < 3 || i[0] > n || n > l) && (i[4] = r, i[5] = n, G.n = l, c = 0)); } if (o || r > 1) return a; throw y = !0, n; } return function (o, p, l) { if (f > 1) throw TypeError(\"Generator is already running\"); for (y && 1 === p && d(p, l), c = p, u = l; (t = c < 2 ? e : u) || !y;) { i || (c ? c < 3 ? (c > 1 && (G.n = -1), d(c, u)) : G.n = u : G.v = u); try { if (f = 2, i) { if (c || (o = \"next\"), t = i[o]) { if (!(t = t.call(i, u))) throw TypeError(\"iterator result is not an object\"); if (!t.done) return t; u = t.value, c < 2 && (c = 0); } else 1 === c && (t = i[\"return\"]) && t.call(i), c < 2 && (u = TypeError(\"The iterator does not provide a '\" + o + \"' method\"), c = 1); i = e; } else if ((t = (y = G.n < 0) ? u : r.call(n, G)) !== a) break; } catch (t) { i = e, c = 1, u = t; } finally { f = 1; } } return { value: t, done: y }; }; }(r, o, i), !0), u; } var a = {}; function Generator() {} function GeneratorFunction() {} function GeneratorFunctionPrototype() {} t = Object.getPrototypeOf; var c = [][n] ? t(t([][n]())) : (_regeneratorDefine2(t = {}, n, function () { return this; }), t), u = GeneratorFunctionPrototype.prototype = Generator.prototype = Object.create(c); function f(e) { return Object.setPrototypeOf ? Object.setPrototypeOf(e, GeneratorFunctionPrototype) : (e.__proto__ = GeneratorFunctionPrototype, _regeneratorDefine2(e, o, \"GeneratorFunction\")), e.prototype = Object.create(u), e; } return GeneratorFunction.prototype = GeneratorFunctionPrototype, _regeneratorDefine2(u, \"constructor\", GeneratorFunctionPrototype), _regeneratorDefine2(GeneratorFunctionPrototype, \"constructor\", GeneratorFunction), GeneratorFunction.displayName = \"GeneratorFunction\", _regeneratorDefine2(GeneratorFunctionPrototype, o, \"GeneratorFunction\"), _regeneratorDefine2(u), _regeneratorDefine2(u, o, \"Generator\"), _regeneratorDefine2(u, n, function () { return this; }), _regeneratorDefine2(u, \"toString\", function () { return \"[object Generator]\"; }), (_regenerator = function _regenerator() { return { w: i, m: f }; })(); }\nfunction _regeneratorDefine2(e, r, n, t) { var i = Object.defineProperty; try { i({}, \"\", {}); } catch (e) { i = 0; } _regeneratorDefine2 = function _regeneratorDefine(e, r, n, t) { if (r) i ? i(e, r, { value: n, enumerable: !t, configurable: !t, writable: !t }) : e[r] = n;else { var o = function o(r, n) { _regeneratorDefine2(e, r, function (e) { return this._invoke(r, n, e); }); }; o(\"next\", 0), o(\"throw\", 1), o(\"return\", 2); } }, _regeneratorDefine2(e, r, n, t); }\nfunction asyncGeneratorStep(n, t, e, r, o, a, c) { try { var i = n[a](c), u = i.value; } catch (n) { return void e(n); } i.done ? t(u) : Promise.resolve(u).then(r, o); }\nfunction _asyncToGenerator(n) { return function () { var t = this, e = arguments; return new Promise(function (r, o) { var a = n.apply(t, e); function _next(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"next\", n); } function _throw(n) { asyncGeneratorStep(a, r, o, _next, _throw, \"throw\", n); } _next(void 0); }); }; }\n\n\n\nfunction run(_x) {\n return _run.apply(this, arguments);\n}\nfunction _run() {\n _run = _asyncToGenerator(/*#__PURE__*/_regenerator().m(function _callee(args) {\n var options, _parseArgs, flags, description, examples, root, _flags$bodySizeLimit, bodySizeLimit, _yield$setupConfigFil, outFile, baseDir, port, _setupServer, app, key, keyPath, serverRef;\n return _regenerator().w(function (_context) {\n while (1) switch (_context.n) {\n case 0:\n options = {\n help: {\n type: 'boolean',\n \"short\": 'h'\n },\n port: {\n type: 'string',\n \"short\": 'p',\n description: 'Specified port to start the server on (default: 9090)'\n },\n root: {\n type: 'string',\n description: 'Path to the root of the JB2 installation'\n },\n bodySizeLimit: {\n type: 'string',\n description: 'Size limit of the update message (default: 25mb)'\n }\n };\n _parseArgs = (0,util__WEBPACK_IMPORTED_MODULE_0__.parseArgs)({\n args: args,\n options: options,\n allowPositionals: true\n }), flags = _parseArgs.values;\n description = 'Start up a small admin server for JBrowse configuration';\n examples = ['$ jbrowse admin-server', '$ jbrowse admin-server -p 8888'];\n if (!flags.help) {\n _context.n = 1;\n break;\n }\n (0,_utils__WEBPACK_IMPORTED_MODULE_1__.printHelp)({\n description: description,\n examples: examples,\n usage: 'jbrowse admin-server [options]',\n options: options\n });\n return _context.a(2);\n case 1:\n root = flags.root, _flags$bodySizeLimit = flags.bodySizeLimit, bodySizeLimit = _flags$bodySizeLimit === void 0 ? '25mb' : _flags$bodySizeLimit;\n _context.n = 2;\n return (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.setupConfigFile)({\n root: root\n });\n case 2:\n _yield$setupConfigFil = _context.v;\n outFile = _yield$setupConfigFil.outFile;\n baseDir = _yield$setupConfigFil.baseDir;\n // Parse and validate port\n port = (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.parsePort)({\n portStr: flags.port\n }); // Set up the Express server\n _setupServer = (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.setupServer)({\n baseDir: baseDir,\n outFile: outFile,\n bodySizeLimit: bodySizeLimit\n }), app = _setupServer.app, key = _setupServer.key, keyPath = _setupServer.keyPath, serverRef = _setupServer.serverRef; // Start the server and set up shutdown handlers\n (0,_admin_server_utils__WEBPACK_IMPORTED_MODULE_2__.startServer)({\n app: app,\n port: port,\n key: key,\n outFile: outFile,\n keyPath: keyPath,\n serverRef: serverRef\n });\n case 3:\n return _context.a(2);\n }\n }, _callee);\n }));\n return _run.apply(this, arguments);\n}\n\n//# sourceURL=webpack://@jbrowse/cli/./src/commands/admin-server.ts?\n}");
3178
3178
 
3179
3179
  /***/ }),
3180
3180
 
@@ -102,7 +102,7 @@ function validateAndExtractParams({ req, key, baseDir, outFile, }) {
102
102
  /**
103
103
  * Sets up the API routes for the server
104
104
  */
105
- function setupRoutes({ app, baseDir, outFile, key, }) {
105
+ function setupRoutes({ app, baseDir, outFile, key, serverRef, }) {
106
106
  // Root route
107
107
  app.get('/', (_req, res) => {
108
108
  res.setHeader('Content-Type', 'text/plain');
@@ -157,6 +157,24 @@ function setupRoutes({ app, baseDir, outFile, key, }) {
157
157
  res.send('Error: Failed to read config');
158
158
  }
159
159
  });
160
+ // Shutdown route for testing
161
+ app.post('/shutdown', (req, res) => {
162
+ const { body } = req;
163
+ const adminKey = body?.adminKey;
164
+ if (adminKey !== key) {
165
+ res.status(401).setHeader('Content-Type', 'text/plain');
166
+ res.send('Error: Invalid admin key');
167
+ return;
168
+ }
169
+ res.setHeader('Content-Type', 'text/plain');
170
+ res.send('Server shutting down');
171
+ // Shutdown the server after sending response
172
+ setImmediate(() => {
173
+ if (serverRef.current) {
174
+ serverRef.current.close();
175
+ }
176
+ });
177
+ });
160
178
  }
161
179
  /**
162
180
  * Sets up the Express server with routes
@@ -190,14 +208,16 @@ function setupServer({ baseDir, outFile, bodySizeLimit, }) {
190
208
  console.error(`Failed to write admin key to ${keyPath}:`, error.message);
191
209
  // Continue anyway, as this is not critical
192
210
  }
211
+ // Create server reference for shutdown route
212
+ const serverRef = { current: null };
193
213
  // Set up routes
194
- setupRoutes({ app, baseDir, outFile, key });
195
- return { app, key, keyPath };
214
+ setupRoutes({ app, baseDir, outFile, key, serverRef });
215
+ return { app, key, keyPath, serverRef };
196
216
  }
197
217
  /**
198
218
  * Starts the server and sets up shutdown handlers
199
219
  */
200
- function startServer({ app, port, key, outFile, keyPath, }) {
220
+ function startServer({ app, port, key, outFile, keyPath, serverRef, }) {
201
221
  // Start the server
202
222
  const server = app.listen(port, () => {
203
223
  console.log(`Admin server started on port ${port}\n\n` +
@@ -207,6 +227,8 @@ function startServer({ app, port, key, outFile, keyPath, }) {
207
227
  `Config file: ${outFile}\n\n` +
208
228
  `To stop the server, press Ctrl+C`);
209
229
  });
230
+ // Store server reference for shutdown route
231
+ serverRef.current = server;
210
232
  // Handle server errors
211
233
  server.on('error', (error) => {
212
234
  if (error.code === 'EADDRINUSE') {
@@ -45,7 +45,11 @@ async function run(args) {
45
45
  // Parse and validate port
46
46
  const port = (0, admin_server_utils_1.parsePort)({ portStr: flags.port });
47
47
  // Set up the Express server
48
- const { app, key, keyPath } = (0, admin_server_utils_1.setupServer)({ baseDir, outFile, bodySizeLimit });
48
+ const { app, key, keyPath, serverRef } = (0, admin_server_utils_1.setupServer)({
49
+ baseDir,
50
+ outFile,
51
+ bodySizeLimit,
52
+ });
49
53
  // Start the server and set up shutdown handlers
50
- (0, admin_server_utils_1.startServer)({ app, port, key, outFile, keyPath });
54
+ (0, admin_server_utils_1.startServer)({ app, port, key, outFile, keyPath, serverRef });
51
55
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jbrowse/cli",
3
- "version": "3.6.3",
3
+ "version": "3.6.4",
4
4
  "description": "A command line tool for working with JBrowse 2",
5
5
  "keywords": [
6
6
  "jbrowse",
@@ -48,5 +48,5 @@
48
48
  "publishConfig": {
49
49
  "access": "public"
50
50
  },
51
- "gitHead": "cf3dd1c895b4f3f7367093d57a0b607f54b8d7db"
51
+ "gitHead": "3db8e50ce2bd9c081efbf6c2e7ae5f342380a25a"
52
52
  }