{"version":3,"file":"cli.mjs","names":["stackList: string[] | undefined","cdkOutput: Record<string, Record<string, string>>","options: any","connection: device | undefined","wsUrl: string | undefined","filePath: string","mappings: Record<string, string>","error: any","err: any"],"sources":["../../cli/cli.ts"],"sourcesContent":["#!/usr/bin/env node\nimport * as fs from 'fs';\nimport * as http from 'http';\nimport * as path from 'path';\nimport { promisify } from 'util';\nimport { device } from 'aws-iot-device-sdk';\nimport * as progam from 'caporal';\nimport * as open from 'open';\nimport { WebSocketServer } from 'ws';\n// @ts-ignore\nimport { getConnection } from '../listener/iot-connection';\nimport { getTopic } from '../listener/topic';\n\nconst readFileAsync = promisify(fs.readFile);\n\n//resolve issue with module import\nlet opener = open;\nif ((open as any).default) {\n  opener = (open as any).default;\n}\n\nasync function run() {\n  let stackList: string[] | undefined;\n  let cdkOutput: Record<string, Record<string, string>>;\n\n  let options: any;\n\n  progam\n    .description('ServerlessSpy web console')\n    .option('--ws <ws>', 'Websocket link')\n    .option(\n      '--cdkoutput <cdkoutput>',\n      'CDK output file that contains IoT Endpoint link in a property ServerlessSpyWsUrl'\n    )\n    .option(\n      '--cdkstack <cdkstack>',\n      'CDK stack in cdk output file. If not specified the first one is picked.'\n    )\n    .option('--open <open>', 'Open browser', progam.BOOL, true)\n    .option(\n      '--port <p>',\n      `A port on localhost where ServerlessSpy web console is accessible.`,\n      progam.INT,\n      '3456'\n    )\n    .option(\n      '--wsport <wsp>',\n      `A port on localhost where ServerlessSpy websocket is accessible.`,\n      progam.INT,\n      '3457'\n    )\n    .action((_args, opt, _logger) => {\n      options = opt;\n    });\n\n  progam.parse(process.argv);\n\n  if (!options.ws && !options.cdkoutput) {\n    throw new Error('--ws or --cdkoutput parameter not specified');\n  }\n\n  if (options.cdkoutput) {\n    const rawdata = fs.readFileSync(options.cdkoutput);\n    cdkOutput = JSON.parse(rawdata.toString());\n    stackList = Object.keys(cdkOutput);\n  }\n\n  const wss = new WebSocketServer({ port: options.wsport });\n  let connection: device | undefined = undefined;\n\n  wss.on('close', async () => {\n    if (connection) connection.end(true);\n  });\n\n  wss.on('connection', async function connect(ws) {\n    console.log('Connection');\n    ws.on('message', function message(data) {\n      console.log('received: %s', data);\n    });\n\n    let wsUrl: string | undefined;\n    if (options.ws) {\n      wsUrl = options.ws;\n    } else if (cdkOutput) {\n      if (cdkOutput[options.cdkstack]) {\n        wsUrl = cdkOutput[options.cdkstack].ServerlessSpyWsUrl;\n      } else if (cdkOutput[Object.keys(cdkOutput)[0]]) {\n        wsUrl = cdkOutput[Object.keys(cdkOutput)[0]].ServerlessSpyWsUrl;\n      }\n    }\n\n    if (!wsUrl) {\n      throw new Error('Missing IoT endpoint url');\n    }\n\n    const wsUrlWithoutScope = wsUrl.split('/')[0];\n\n    connection = await getConnection(true, wsUrlWithoutScope);\n\n    const topic = getTopic('#');\n    console.log(`Subscribing to ${topic}`);\n\n    connection.on('connect', () => {\n      console.log('Connection opened');\n      if (connection) {\n        connection.subscribe(topic);\n      }\n    });\n\n    connection.on('message', (topic: string, data: Buffer) => {\n      ws.send(\n        JSON.stringify({\n          ...JSON.parse(JSON.parse(data.toString()).data),\n          topic,\n        })\n      );\n    });\n  });\n\n  http\n    .createServer((request, response) => {\n      void (async () => {\n        try {\n          //console.log('request ', request.url);\n          let filePath: string = `.${request.url}`;\n          //remove query parameters\n          filePath = filePath.split('?')[0];\n          let rootFolder = __dirname;\n\n          if (request.url?.startsWith('/webServerlessSpy.js')) {\n            //get transpiled TS to JS files\n            rootFolder = getCompiledJsPath();\n          } else if (request.url?.startsWith('/bootstrap/')) {\n            filePath = filePath.substring('/bootstrap/'.length);\n            const bootstrapFolder = await getNpmModuleInstalledPath(\n              'bootstrap'\n            );\n\n            rootFolder = bootstrapFolder;\n          } else if (request.url?.startsWith('/bootstrap-icons/')) {\n            filePath = filePath.substring('/bootstrap-icons/'.length);\n            const bootstrapFolder = await getNpmModuleInstalledPath(\n              'bootstrap-icons'\n            );\n\n            rootFolder = bootstrapFolder;\n          } else {\n            if (filePath === './') {\n              filePath = './index.html';\n            }\n          }\n\n          filePath = path.join(rootFolder, filePath);\n          //console.log(`${request.url} --> ${filePath}`);\n\n          const extname = String(path.extname(filePath)).toLowerCase();\n          const mimeTypes: any = {\n            '.html': 'text/html',\n            '.js': 'text/javascript',\n            '.css': 'text/css',\n            '.json': 'application/json',\n            '.png': 'image/png',\n            '.jpg': 'image/jpg',\n            '.gif': 'image/gif',\n            '.svg': 'image/svg+xml',\n            '.wav': 'audio/wav',\n            '.mp4': 'video/mp4',\n            '.woff': 'application/font-woff',\n            '.ttf': 'application/font-ttf',\n            '.eot': 'application/vnd.ms-fontobject',\n            '.otf': 'application/font-otf',\n            '.wasm': 'application/wasm',\n          };\n\n          const contentType = mimeTypes[extname] || 'application/octet-stream';\n\n          if (request.url === '/stackList') {\n            response.writeHead(200, { 'Content-Type': 'application/json' });\n            response.end(JSON.stringify(stackList), 'utf-8');\n          } else if (request.url === '/stackTopicMappings') {\n            response.writeHead(200, { 'Content-Type': 'application/json' });\n            const mappings: Record<string, string> = {};\n            if (cdkOutput) {\n              for (const [stackName, stack] of Object.entries(cdkOutput)) {\n                if (stack.ServerlessSpyWsUrl) {\n                  const [_, scope] = stack.ServerlessSpyWsUrl.split('/');\n                  if (scope) {\n                    mappings[stackName] = scope;\n                  }\n                }\n              }\n            }\n            response.end(JSON.stringify(mappings), 'utf-8');\n          } else if (request.url?.match('^/wsUrl')) {\n            response.writeHead(200, { 'Content-Type': 'text/html' });\n            response.end(`ws:localhost:${options.wsport}`, 'utf-8');\n          } else {\n            try {\n              const content = await readFileAsync(filePath);\n\n              response.writeHead(200, { 'Content-Type': contentType });\n              response.end(content, 'utf-8');\n            } catch (error: any) {\n              if (error.code === 'ENOENT') {\n                response.writeHead(404, { 'Content-Type': 'text/html' });\n                response.end(\n                  `No such file or directory ${request.url}`,\n                  'utf-8'\n                );\n              } else {\n                response.writeHead(500);\n                response.end(`Error: ${error.code} ..\\n`);\n              }\n            }\n          }\n        } catch (err: any) {\n          response.writeHead(500, { 'Content-Type': 'text/html' });\n          response.end(err.message, 'utf-8');\n        }\n      })();\n    })\n    .listen(options.port);\n\n  console.log(\n    `ServerlessSpy console runing at http://localhost:${options.port}`\n  );\n  if (options.open) {\n    await opener(`http://localhost:${options.port}`);\n  }\n}\n\nrun().catch(console.error);\n\nfunction getNpmModuleInstalledPath(npm: string) {\n  let folder = path.join(__dirname, '../', 'node_modules', npm);\n  if (fs.existsSync(folder)) {\n    return folder;\n  }\n\n  let folderAsPackage = path.join(__dirname, '../../', 'node_modules', npm);\n\n  if (fs.existsSync(folderAsPackage)) {\n    return folderAsPackage;\n  }\n\n  // When boostrap ends up in importing projects root node_modules and not in serverless-spys node_modules\n  folderAsPackage = path.join(__dirname, '../../../../', 'node_modules', npm);\n\n  if (fs.existsSync(folderAsPackage)) {\n    return folderAsPackage;\n  }\n\n  throw new Error(\n    `Can not find package in folder ${folder} and ${folderAsPackage}`\n  );\n}\n\nfunction getCompiledJsPath() {\n  let folder = path.join(__dirname, '../', 'lib/cli');\n  if (fs.existsSync(folder)) {\n    return folder;\n  }\n\n  let folderAsPackage = path.join(__dirname, '../../', 'lib/cli');\n\n  if (fs.existsSync(folderAsPackage)) {\n    return folderAsPackage;\n  }\n\n  throw new Error(\n    `Can not find compiled files in folder ${folder} and ${folderAsPackage}`\n  );\n}\n"],"mappings":"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"}