{"version":3,"sources":["../../src/cli-utils.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport http, { Server } from \"node:http\";\nimport https from \"node:https\";\nimport path from \"node:path\";\nimport { Command } from \"commander\";\nimport { DefaultWebSocketServerPort } from \"@mocky-balboa/shared-config\";\nimport express from \"express\";\nimport { mockyBalboaMiddleware, startServer } from \"@mocky-balboa/server\";\nimport { logger } from \"./logger.js\";\nimport {\n  createSelfSignedCertificate,\n  type SelfSignedCertificate,\n} from \"./mkcert.js\";\n\ninterface CommonCLIOptions {\n  port: string;\n  websocketPort: string;\n  hostname: string;\n  timeout: string;\n  https: boolean;\n  httpsCert?: string;\n  httpsKey?: string;\n  httpsCa?: string;\n}\n\nexport type CLIOptions<TOptions> = CommonCLIOptions & TOptions;\n\nexport const createCommand = (name: string, description: string) => {\n  const cli = new Command();\n\n  cli.name(name).description(description);\n\n  cli.option(\"-p, --port [port]\", \"Port to run the server on\", \"3000\");\n  cli.option(\n    \"--websocket-port [websocketPort]\",\n    \"Port to run the WebSocket server on\",\n    `${DefaultWebSocketServerPort}`,\n  );\n  cli.option(\n    \"-h, --hostname [hostname]\",\n    \"Hostname to bind the server to\",\n    \"0.0.0.0\",\n  );\n  cli.option(\n    \"-t, --timeout [timeout]\",\n    \"Timeout in milliseconds for the mock server to receive a response from the client\",\n    \"5000\",\n  );\n\n  // https options\n  cli.option(\n    \"--https\",\n    \"Enable https server. Either https or http server is run, not both. When no --https-cert and --https-key are provided, a self-signed certificate will be automatically generated.\",\n  );\n  cli.option(\n    \"--https-cert [certPath]\",\n    \"Optional path to the https certificate file\",\n  );\n  cli.option(\n    \"--https-ca [caPath]\",\n    \"Optional path to the https Certificate Authority file\",\n  );\n  cli.option(\"--https-key [keyPath]\", \"Optional path to the https key file\");\n\n  return cli;\n};\n\nexport const parseCLIOptions = <TOptions = Record<string, never>>(\n  cli: Command,\n) => {\n  cli.parse();\n  const cliOptions = cli.opts<CLIOptions<TOptions>>();\n  return cliOptions;\n};\n\nexport const createExpressServer = (): express.Express => {\n  const app = express();\n  app.use(mockyBalboaMiddleware());\n\n  return app;\n};\n\nexport const loadCertificateFiles = async (\n  certificate: SelfSignedCertificate,\n) => {\n  const [cert, key, ca] = await Promise.all([\n    fs.readFile(certificate.cert),\n    fs.readFile(certificate.key),\n    certificate.rootCA\n      ? fs.readFile(certificate.rootCA)\n      : Promise.resolve(undefined),\n  ]);\n\n  return { cert, key, ca };\n};\n\nexport const getSelfSignedCertificate = async ({\n  hostname,\n  https,\n  httpsCa,\n  httpsCert,\n  httpsKey,\n}: Pick<\n  CommonCLIOptions,\n  \"hostname\" | \"https\" | \"httpsCa\" | \"httpsCert\" | \"httpsKey\"\n>): Promise<SelfSignedCertificate | undefined> => {\n  if (!https) return undefined;\n\n  let certificate: SelfSignedCertificate | undefined;\n  if (httpsKey && httpsCert) {\n    certificate = {\n      key: path.resolve(httpsKey),\n      cert: path.resolve(httpsCert),\n    };\n\n    if (httpsCa) {\n      certificate.rootCA = path.resolve(httpsCa);\n    }\n\n    return certificate;\n  } else {\n    return createSelfSignedCertificate(hostname);\n  }\n};\n\nconst getServerUrls = (\n  protocol: \"http\" | \"https\",\n  hostname: string,\n  port: number,\n): string[] => {\n  const hosts: string[] = [];\n  if (hostname === \"0.0.0.0\") {\n    hosts.push(\"localhost\", \"127.0.0.1\", \"0.0.0.0\");\n  } else if ([\"localhost\", \"127.0.0.1\"].includes(hostname)) {\n    hosts.push(\"127.0.0.1\", \"localhost\");\n  } else {\n    hosts.push(hostname);\n  }\n\n  return hosts.map((host) => `${protocol}://${host}:${port}`);\n};\n\nexport const getServerStartedOnString = (\n  protocol: \"http\" | \"https\",\n  hostname: string,\n  port: number,\n) => {\n  return `Server started available at:\\n${getServerUrls(\n    protocol,\n    hostname,\n    port,\n  )\n    .map((url) => `  - ${url}`)\n    .join(\"\\n\")}`;\n};\n\nexport const startServers = async <TCLIOptions extends CommonCLIOptions>(\n  app: express.Express,\n  cliOptions: TCLIOptions,\n) => {\n  const certificate = await getSelfSignedCertificate(cliOptions);\n\n  // Start Mocky Balboa server\n  await startServer({\n    webSocketServerOptions: {\n      port: parseInt(cliOptions.websocketPort, 10),\n    },\n    mockServerOptions: {\n      timeout: parseInt(cliOptions.timeout, 10),\n    },\n  });\n\n  await new Promise<void>(async (resolve, reject) => {\n    let server: Server;\n    if (certificate) {\n      try {\n        const { key, cert, ca } = await loadCertificateFiles(certificate);\n        server = https.createServer({ key, cert, ca }, app);\n      } catch (error) {\n        reject(error);\n        return;\n      }\n    } else {\n      server = http.createServer(app);\n    }\n\n    const port = parseInt(cliOptions.port, 10);\n    server.listen(port, cliOptions.hostname, (error?: Error) => {\n      if (error) {\n        reject(error);\n      } else {\n        logger.info(\n          getServerStartedOnString(\n            certificate ? \"https\" : \"http\",\n            cliOptions.hostname,\n            port,\n          ),\n        );\n        resolve();\n      }\n    });\n  });\n};\n\nconst getServerEntry = async (\n  cwd: string,\n  distDir: string,\n  expectedRelativePaths: string[],\n): Promise<string> => {\n  const possibleFiles = expectedRelativePaths.flatMap((relativePath) => {\n    const entryPath = path.join(distDir, relativePath);\n    return [\n      entryPath,\n      `${entryPath}.mjs`,\n      `${entryPath}.cjs`,\n      `${entryPath}.js`,\n    ];\n  });\n\n  try {\n    const serverEntryPath = await Promise.any(\n      possibleFiles.map(async (file) => {\n        const fullPath = path.resolve(cwd, file);\n        const stats = await fs.stat(fullPath);\n        if (stats.isFile()) {\n          return fullPath;\n        } else {\n          throw new Error(`File ${fullPath} is not a file`);\n        }\n      }),\n    );\n\n    return serverEntryPath;\n  } catch {\n    // No entry file found\n    throw new Error(\n      \"No server entry file found. Did you forget to build your project?\",\n    );\n  }\n};\n\nexport const getServerEntryHandler = async (\n  distDir: string,\n  expectedRelativePaths: string[],\n  exportName?: string,\n) => {\n  const astroServerEntry = await getServerEntry(\n    process.cwd(),\n    distDir,\n    expectedRelativePaths,\n  );\n  const handler = await import(astroServerEntry).then((mod) =>\n    exportName ? mod[exportName] : mod,\n  );\n  if (!handler) {\n    throw new Error(\n      `Export ${exportName} not found in ${path.relative(process.cwd(), astroServerEntry)}`,\n    );\n  }\n\n  return handler;\n};\n\nexport type { SelfSignedCertificate } from \"./mkcert.js\";\nexport { default as express } from \"express\";\n"],"names":["createCommand","createExpressServer","express","getSelfSignedCertificate","getServerEntryHandler","getServerStartedOnString","loadCertificateFiles","parseCLIOptions","startServers","name","description","cli","Command","option","DefaultWebSocketServerPort","parse","cliOptions","opts","app","use","mockyBalboaMiddleware","certificate","cert","key","ca","Promise","all","fs","readFile","rootCA","resolve","undefined","hostname","https","httpsCa","httpsCert","httpsKey","path","createSelfSignedCertificate","getServerUrls","protocol","port","hosts","push","includes","map","host","url","join","startServer","webSocketServerOptions","parseInt","websocketPort","mockServerOptions","timeout","reject","server","createServer","error","http","listen","logger","info","getServerEntry","cwd","distDir","expectedRelativePaths","possibleFiles","flatMap","relativePath","entryPath","serverEntryPath","any","file","fullPath","stats","stat","isFile","Error","exportName","astroServerEntry","process","handler","then","mod","relative"],"mappings":"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"}