/**
 * This file is part of the NocoBase (R) project.
 * Copyright (c) 2020-2024 NocoBase Co., Ltd.
 * Authors: NocoBase Team.
 *
 * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
 * For more information, please refer to: https://www.nocobase.com/agreement.
 */

var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
  for (var name in all)
    __defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
  if (from && typeof from === "object" || typeof from === "function") {
    for (let key of __getOwnPropNames(from))
      if (!__hasOwnProp.call(to, key) && key !== except)
        __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
  }
  return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
  // If the importer is in node compatibility mode or this is not an ESM
  // file that has been converted to a CommonJS file using a Babel-
  // compatible transform (i.e. "__esModule" has not been set), then set
  // "default" to the CommonJS "module.exports" for node compatibility.
  isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
  mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
var ScriptInstruction_exports = {};
__export(ScriptInstruction_exports, {
  default: () => ScriptInstruction
});
module.exports = __toCommonJS(ScriptInstruction_exports);
var import_node_events = require("node:events");
var import_node_path = __toESM(require("node:path"));
var import_node_worker_threads = require("node:worker_threads");
var import_winston = __toESM(require("winston"));
var import_plugin_workflow = require("@nocobase/plugin-workflow");
var import_cache_logger = require("./cache-logger");
class ScriptInstruction extends import_plugin_workflow.Instruction {
  /**
   * Returns the worker script path based on whether WORKFLOW_SCRIPT_MODULES is configured.
   * - WORKFLOW_SCRIPT_MODULES set: uses Node.js vm with require support (module whitelist)
   * - WORKFLOW_SCRIPT_MODULES unset: uses isolated-vm for maximum security (no require, no Node.js APIs)
   */
  static get workerScript() {
    var _a;
    const hasModules = ((_a = process.env.WORKFLOW_SCRIPT_MODULES) == null ? void 0 : _a.split(",").filter(Boolean).length) > 0;
    return import_node_path.default.join(__dirname, hasModules ? "Vm.js" : "IsolatedVm.js");
  }
  static async run(source, args, options) {
    const { logger, timeout } = options;
    let result;
    const worker = new import_node_worker_threads.Worker(this.workerScript, {
      workerData: { source, args, options: timeout ? { timeout } : {} }
    });
    worker.on("message", (message) => {
      if (message.type === "result") {
        result = message.result;
      }
    });
    worker.stdout.on("data", (data) => {
      logger.info(data.toString());
    });
    worker.stderr.on("data", (data) => {
      logger.error(data.toString());
    });
    const excution = new Promise((resolve, reject) => {
      worker.on("error", (error) => {
        reject(error);
      });
      const stdoutPromise = (0, import_node_events.once)(worker.stdout, "close");
      const stderrPromise = (0, import_node_events.once)(worker.stderr, "close");
      worker.on("exit", (code) => {
        Promise.all([stdoutPromise, stderrPromise]).then(() => {
          if (code !== 0) {
            reject(new Error(`Worker stopped with exit code ${code}`));
          }
          resolve(result);
        }).catch(reject);
      });
    });
    try {
      await excution;
    } catch (e) {
      console.log(e);
      return {
        status: import_plugin_workflow.JOB_STATUS.ERROR,
        result: e.message
      };
    }
    return {
      status: import_plugin_workflow.JOB_STATUS.RESOLVED,
      result
    };
  }
  async run(node, prevJob, processor) {
    const { content = "", continue: cont, timeout } = node.config;
    const args = processor.getParsedValue(node.config.arguments ?? [], node.id);
    const _args = args.reduce((pre, item) => ({ ...pre, [item.name]: item.value }), {});
    const { workflow } = processor.execution;
    const sync = this.workflow.isWorkflowSync(workflow);
    processor.logger.info(`run script execution node id: ${node.id}, start in ${(/* @__PURE__ */ new Date()).toLocaleString()}`);
    if (sync) {
      const result = await this.constructor.run(content, _args, {
        timeout,
        logger: processor.logger
      });
      if (result.status === import_plugin_workflow.JOB_STATUS.RESOLVED) {
        processor.logger.info(`run script test success, node id: ${node.id},the result is ${result.result}`);
      } else {
        processor.logger.error(`run script test failed, node id: ${node.id},the reason is ${result.result}`);
      }
      return {
        result: result.result,
        status: cont ? import_plugin_workflow.JOB_STATUS.RESOLVED : result.status === import_plugin_workflow.JOB_STATUS.RESOLVED ? import_plugin_workflow.JOB_STATUS.RESOLVED : import_plugin_workflow.JOB_STATUS.ERROR
      };
    }
    const { id } = processor.saveJob({
      status: import_plugin_workflow.JOB_STATUS.PENDING,
      nodeId: node.id,
      nodeKey: node.key,
      upstreamId: (prevJob == null ? void 0 : prevJob.id) ?? null
    });
    processor.logger.info(`script (#${node.id}) has been started, waiting for response...`);
    await processor.exit();
    const jobResult = {
      status: import_plugin_workflow.JOB_STATUS.PENDING
    };
    this.constructor.run(content, _args, { timeout, logger: processor.logger }).then((res) => {
      if (res.status === import_plugin_workflow.JOB_STATUS.RESOLVED) {
        processor.logger.info(`script (#${node.id}) get result success`);
        jobResult.status = import_plugin_workflow.JOB_STATUS.RESOLVED;
        jobResult.result = res.result;
        processor.logger.info(`run script execution success, node id: ${node.id},the result is ${res.result}`);
        return;
      }
      if (cont) {
        processor.logger.warn(`script (#${node.id}) get result failed, the reason is ${res.result}`);
        jobResult.status = import_plugin_workflow.JOB_STATUS.RESOLVED;
        jobResult.result = res.result;
        return;
      }
      processor.logger.info(`script (#${node.id}) get result failed, the reason is ${res.result}`);
      jobResult.status = import_plugin_workflow.JOB_STATUS.ERROR;
      jobResult.result = res.result;
    }).catch((e) => {
      processor.logger.error(`script (#${node.id}) get result failed, the reason is ${e.message}`);
      jobResult.status = import_plugin_workflow.JOB_STATUS.ERROR;
      jobResult.result = e.message;
    }).finally(() => {
      processor.logger.debug(`script (#${node.id}) ended, resume workflow...`);
      setImmediate(async () => {
        const job = await this.workflow.db.getRepository("jobs").findOne({ filterByTk: id });
        job.set(jobResult);
        this.workflow.resume(job);
      });
    });
  }
  async resume(node, job, processor) {
    return job;
  }
  async test(config = {}) {
    const { content, timeout } = config;
    const args = (config.arguments ?? []).reduce((pre, item) => ({ ...pre, [item.name]: item.value }), {});
    const transport = new import_cache_logger.CacheTransport();
    const logger = import_winston.default.createLogger({
      transports: [transport]
    });
    const result = await this.constructor.run(content, args, { timeout, logger });
    const log = transport.getLogs();
    return {
      ...result,
      log: log == null ? void 0 : log.join("")
    };
  }
}
