text: "use strict";
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
      function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
      return new (P || (P = Promise))(function (resolve, reject) {
          function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
          function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
          function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
          step((generator = generator.apply(thisArg, _arguments || [])).next());
      });
  };
  var __importDefault = (this && this.__importDefault) || function (mod) {
      return (mod && mod.__esModule) ? mod : { "default": mod };
  };
  Object.defineProperty(exports, "__esModule", { value: true });
  exports.IntrospectionEngine = exports.IntrospectionError = exports.IntrospectionPanic = void 0;
  const chalk_1 = __importDefault(require("chalk"));
  const child_process_1 = require("child_process");
  const debug_1 = __importDefault(require("@prisma/debug"));
  const byline_1 = __importDefault(require("./utils/byline"));
  const debugRpc = debug_1.default('IntrospectionEngine:rpc');
  const debugStderr = debug_1.default('IntrospectionEngine:stderr');
  const debugStdin = debug_1.default('IntrospectionEngine:stdin');
  const fs_1 = __importDefault(require("fs"));
  const now_1 = require("./utils/now");
  const panic_1 = require("./panic");
  const resolveBinary_1 = require("./resolveBinary");
  class IntrospectionPanic extends Error {
      constructor(message, rustStack, request) {
          super(message);
          this.rustStack = rustStack;
          this.request = request;
      }
  }
  exports.IntrospectionPanic = IntrospectionPanic;
  class IntrospectionError extends Error {
      constructor(message, code) {
          super(message);
          this.code = code;
      }
  }
  exports.IntrospectionError = IntrospectionError;
  let messageId = 1;
  /* tslint:disable */
  class IntrospectionEngine {
      constructor({ debug, cwd } = {
          debug: false,
          cwd: process.cwd(),
      }) {
          this.listeners = {};
          this.messages = [];
          this.isRunning = false;
          if (debug) {
              debug_1.default.enable('IntrospectionEngine*');
          }
          this.debug = Boolean(debug);
          this.cwd = cwd || process.cwd();
      }
      stop() {
          if (this.child) {
              this.child.kill();
              this.isRunning = false;
          }
      }
      rejectAll(err) {
          Object.entries(this.listeners).map(([id, listener]) => {
              listener(null, err);
              delete this.listeners[id];
          });
      }
      registerCallback(id, callback) {
          this.listeners[id] = callback;
      }
      getDatabaseDescription(schema) {
          return this.runCommand(this.getRPCPayload('getDatabaseDescription', { schema }));
      }
      introspect(schema) {
          this.lastUrl = schema;
          return this.runCommand(this.getRPCPayload('introspect', { schema }));
      }
      listDatabases(schema) {
          this.lastUrl = schema;
          return this.runCommand(this.getRPCPayload('listDatabases', { schema }));
      }
      getDatabaseMetadata(schema) {
          this.lastUrl = schema;
          return this.runCommand(this.getRPCPayload('getDatabaseMetadata', { schema }));
      }
      handleResponse(response) {
          let result;
          try {
              result = JSON.parse(response);
          }
          catch (e) {
              console.error(`Could not parse introspection engine response: ${response.slice(0, 200)}`);
          }
          if (result) {
              if (result.backtrace) {
                  // if there is a backtrace on the result, it's probably an error
                  console.log(result);
              }
              if (!result.id) {
                  console.error(`Response ${JSON.stringify(result)} doesn't have an id and I can't handle that (yet)`);
              }
              if (!this.listeners[result.id]) {
                  console.error(`Got result for unknown id ${result.id}`);
              }
              if (this.listeners[result.id]) {
                  this.listeners[result.id](result);
                  delete this.listeners[result.id];
              }
          }
      }
      init() {
          if (!this.initPromise) {
              this.initPromise = this.internalInit();
          }
          return this.initPromise;
      }
      internalInit() {
          return new Promise(
          // eslint-disable-next-line no-async-promise-executor, @typescript-eslint/no-misused-promises
          (resolve, reject) => __awaiter(this, void 0, void 0, function* () {
              var _a;
              try {
                  const binaryPath = yield resolveBinary_1.resolveBinary('introspection-engine');
                  debugRpc('starting introspection engine with binary: ' + binaryPath);
                  this.child = child_process_1.spawn(binaryPath, {
                      stdio: ['pipe', 'pipe', 'pipe'],
                      env: process.env,
                      cwd: this.cwd,
                  });
                  this.isRunning = true;
                  this.child.on('error', (err) => {
                      console.error('[introspection-engine] error: %s', err);
                      reject(err);
                      this.rejectAll(err);
                  });
                  (_a = this.child.stdin) === null || _a === void 0 ? void 0 : _a.on('error', (err) => {
                      console.error(err);
                  });
                  // eslint-disable-next-line @typescript-eslint/no-misused-promises
                  this.child.on('exit', (code) => __awaiter(this, void 0, void 0, function* () {
                      // handle panics
                      this.isRunning = false;
                      if (code === 255 && this.lastError && this.lastError.is_panic) {
                          let sqlDump;
                          if (this.lastUrl) {
                              try {
                                  sqlDump = yield this.getDatabaseDescription(this.lastUrl);
                              }
                              catch (e) { } // eslint-disable-line no-empty
                          }
                          const err = new panic_1.RustPanic(this.lastError.message, this.lastError.backtrace, this.lastRequest, panic_1.ErrorArea.INTROSPECTION_CLI, 
                          /* schemaPath */ undefined, 
                          /* schema */ undefined, sqlDump);
                          this.rejectAll(err);
                          reject(err);
                          return;
                      }
                      const messages = this.messages.join('\n');
                      let err;
                      if (code !== 0 || messages.includes('panicked at')) {
                          let errorMessage = chalk_1.default.red.bold('Error in introspection engine: ') + messages;
                          if (messages.includes('\u001b[1;94m-->\u001b[0m')) {
                              errorMessage =
                                  `${chalk_1.default.red.bold('Schema parsing\n')}` + messages;
                          }
                          else if (this.lastError && this.lastError.msg === 'PANIC') {
                              errorMessage = serializePanic(this.lastError);
                              err = new IntrospectionPanic(errorMessage, messages, this.lastRequest);
                          }
                          else if (messages.includes('panicked at')) {
                              err = new IntrospectionPanic(errorMessage, messages, this.lastRequest);
                          }
                          err = err || new Error(errorMessage);
                          this.rejectAll(err);
                          reject(err);
                      }
                  }));
                  this.child.stdin.on('error', (err) => {
                      debugStdin(err);
                  });
                  byline_1.default(this.child.stderr).on('data', (data) => {
                      const msg = String(data);
                      this.messages.push(msg);
                      debugStderr(msg);
                      try {
                          const json = JSON.parse(msg);
                          if (json.backtrace) {
                              this.lastError = json;
                          }
                          if (json.level === 'ERRO') {
                              this.lastError = json;
                          }
                      }
                      catch (e) {
                          //
                      }
                  });
                  byline_1.default(this.child.stdout).on('data', (line) => {
                      this.handleResponse(String(line));
                  });
                  setImmediate(() => {
                      resolve();
                  });
              }
              catch (e) {
                  reject(e);
              }
          }));
      }
      runCommand(request) {
          var _a;
          return __awaiter(this, void 0, void 0, function* () {
              yield this.init();
              if ((_a = this.child) === null || _a === void 0 ? void 0 : _a.killed) {
                  throw new Error(`Can't execute ${JSON.stringify(request)} because introspection engine already exited.`);
              }
              return new Promise((resolve, reject) => {
                  this.registerCallback(request.id, (response, err) => __awaiter(this, void 0, void 0, function* () {
                      var _a, _b, _c, _d, _e, _f;
                      if (err) {
                          return reject(err);
                      }
                      if (typeof response.result !== 'undefined') {
                          resolve(response.result);
                      }
                      else {
                          if (response.error) {
                              debugRpc(response);
                              if ((_a = response.error.data) === null || _a === void 0 ? void 0 : _a.is_panic) {
                                  const message = (_d = (_c = (_b = response.error.data) === null || _b === void 0 ? void 0 : _b.error) === null || _c === void 0 ? void 0 : _c.message) !== null && _d !== void 0 ? _d : response.error.message;
                                  // Handle error and displays the interactive dialog to send panic error
                                  let sqlDump;
                                  if (this.lastUrl) {
                                      try {
                                          sqlDump = yield this.getDatabaseDescription(this.lastUrl);
                                      }
                                      catch (e) { } // eslint-disable-line no-empty
                                  }
                                  reject(new panic_1.RustPanic(message, message, request, panic_1.ErrorArea.INTROSPECTION_CLI, undefined, undefined, sqlDump));
                              }
                              else if ((_e = response.error.data) === null || _e === void 0 ? void 0 : _e.message) {
                                  // Print known error code & message from engine
                                  // See known errors at https://github.com/prisma/specs/tree/master/errors#prisma-sdk
                                  let message = `${chalk_1.default.redBright(response.error.data.message)}\n`;
                                  if ((_f = response.error.data) === null || _f === void 0 ? void 0 : _f.error_code) {
                                      message =
                                          chalk_1.default.redBright(`${response.error.data.error_code}\n\n`) +
                                              message;
                                      reject(new IntrospectionError(message, response.error.data.error_code));
                                  }
                                  else {
                                      reject(new Error(message));
                                  }
                              }
                              else {
                                  const text = this.persistError(request, this.messages.join('\n'));
                                  reject(new Error(`${chalk_1.default.redBright('Error in RPC')}\n Request: ${JSON.stringify(request, null, 2)}\nResponse: ${JSON.stringify(response, null, 2)}\n${response.error.message}\n\n${text}\n`));
                              }
                          }
                          else {
                              reject(new Error(`Got invalid RPC response without .result property: ${JSON.stringify(response)}`));
                          }
                      }
                  }));
                  if (this.child.stdin.destroyed) {
                      throw new Error(`Can't execute ${JSON.stringify(request)} because introspection engine is destroyed.`);
                  }
                  debugRpc('SENDING RPC CALL', JSON.stringify(request));
                  this.child.stdin.write(JSON.stringify(request) + '\n');
                  this.lastRequest = request;
              });
          });
      }
      persistError(request, message) {
          const filename = `failed-${request.method}-${now_1.now()}.md`;
          const file = `# Failed ${request.method} at ${new Date().toISOString()}
  ## RPC One-Liner
  \`\`\`json
  ${JSON.stringify(request)}
  \`\`\`
  
  ## RPC Input Readable
  \`\`\`json
  ${JSON.stringify(request, null, 2)}
  \`\`\`
  
  ## Stack Trace
  \`\`\`bash
  ${message}
  \`\`\`
  `;
          fs_1.default.writeFileSync(filename, file);
          return `Wrote ${chalk_1.default.bold(filename)} with debugging information.
  Please put that file into a gist and post it in Slack.
  1. ${chalk_1.default.greenBright(`cat ${filename} | pbcopy`)}
  2. Create a gist ${chalk_1.default.greenBright.underline(`https://gist.github.com/new`)}`;
      }
      getRPCPayload(method, params) {
          return {
              id: messageId++,
              jsonrpc: '2.0',
              method,
              params: [
                  Object.assign({}, params),
              ],
          };
      }
  }
  exports.IntrospectionEngine = IntrospectionEngine;
  function serializePanic(log) {
      return `${chalk_1.default.red.bold('Error in introspection engine.\nReason: ')}${chalk_1.default.red(`${log.reason} in ${chalk_1.default.underline(`${log.file}:${log.line}:${log.column}`)}`)}
  
  Please create an issue in the ${chalk_1.default.bold('prisma')} repo with the error 🙏:
  ${chalk_1.default.underline('https://github.com/prisma/prisma/issues/new')}\n`;
  }
  //# sourceMappingURL=IntrospectionEngine.js.map