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);

// src/index.ts
var src_exports = {};
__export(src_exports, {
  BaseRpcServer: () => BaseRpcServer,
  Fn: () => Fn,
  LifeCycleConfig: () => LifeCycleConfig,
  Plugable: () => Plugable,
  Proxy: () => Proxy2,
  RpcServer: () => RpcServer,
  combine: () => combine,
  createServer: () => createServer,
  getHttpMethods: () => getHttpMethods,
  isArrowFunction: () => isArrowFunction,
  isObject: () => isObject,
  log: () => log,
  mergeDeep: () => mergeDeep
});
module.exports = __toCommonJS(src_exports);
var import_debug5 = __toESM(require("debug"));
var import_koa_compose3 = __toESM(require("koa-compose"));

// src/fn.ts
var import_debug3 = __toESM(require("debug"));

// src/plugin.ts
var import_debug2 = __toESM(require("debug"));
var import_koa = __toESM(require("koa"));
var import_koa_compose = __toESM(require("koa-compose"));

// src/utils.ts
var import_debug = __toESM(require("debug"));
var debugLog = (0, import_debug.default)("@tomrpc/core");
var isArrowFunction = (func) => {
  if (typeof func === "function") {
    const source = func.toString();
    return /^\([^)]*\)\s*=>/.test(source);
  } else {
    return false;
  }
};
function getHttpMethods() {
  return [
    "get",
    "post",
    "put",
    "head",
    "delete",
    "options",
    "trace",
    "copy",
    "lock",
    "mkcol",
    "move",
    "purge",
    "propfind",
    "proppatch",
    "unlock",
    "report",
    "mkactivity",
    "checkout",
    "merge",
    "m-search",
    "notify",
    "subscribe",
    "unsubscribe",
    "patch",
    "search",
    "connect"
  ];
}
function log(msg, debugFlag) {
  if (debugFlag) {
    console.log(msg);
  } else {
    debugLog(msg);
  }
}
function isObject(item) {
  return item && typeof item === "object" && !Array.isArray(item);
}
function mergeDeep(target, ...sources) {
  if (!sources.length)
    return target;
  const source = sources.shift();
  if (isObject(target) && isObject(source)) {
    for (const key in source) {
      if (isObject(source[key])) {
        if (!target[key])
          Object.assign(target, { [key]: {} });
        mergeDeep(target[key], source[key]);
      } else {
        Object.assign(target, { [key]: source[key] });
      }
    }
  }
  return mergeDeep(target, ...sources);
}

// src/plugin.ts
var log2 = (0, import_debug2.default)("@tomrpc/core/plugin");
var Plugable = class {
  constructor(cfg) {
    this.init = [];
    this.load = [];
    this.config = mergeDeep(
      {
        bind: {}
      },
      cfg
    );
    this.name = "base";
    this.app = new import_koa.default();
    this.init = [];
    this.load = [];
    this.prefix = this.config.prefix ? this.config.prefix : "";
    this.compose = import_koa_compose.default;
    this.load.push(this.getMiddleware());
  }
  /**
   * move middleware to load stage, use template pattern & compose
   */
  getMiddleware() {
    log2("getMiddleware");
    const pre = this.pre();
    const process = this.process();
    const post = this.post();
    return (0, import_koa_compose.default)([pre, process, post]);
  }
  pre() {
    return async (ctx, next) => {
      log2("pre");
      await next();
      log2("pre end");
    };
  }
  post() {
    return async (ctx, next) => {
      log2("after");
      await next();
      log2("after end");
    };
  }
  process() {
    return async (ctx, next) => {
      log2("process default");
      await next();
      log2("process default end");
    };
  }
  use(x) {
    this.app.use(x);
  }
  addInit(i) {
    this.init.push(i);
  }
  addLoad(i) {
    this.load.push(i);
  }
};

// src/fn.ts
var log3 = (0, import_debug3.default)("@tomrpc/core/fn");
var Fn = class extends Plugable {
  constructor(cfg) {
    super(
      mergeDeep(
        {
          prefix: "/api",
          functions: []
        },
        cfg
      )
    );
    this.name = "Fn";
    this.prefix = this.config.prefix;
    this.app.use(this.compose([this.before(), this.mount(), this.default()]));
  }
  fn(key, fn) {
    if (!this.config["functions"])
      this.config["functions"] = {};
    const pre = key.split("")[0] == "/" ? "" : "/";
    this.config["functions"][pre + key] = fn;
  }
  add(items) {
    for (const [name, fn] of Object.entries(items)) {
      if (isArrowFunction(fn)) {
        console.log(
          `this.rpcFunctions[${name}] is arrow function, please use ctx as param, not this`
        );
      }
      if (this.config["functions"][name]) {
        log3(`add ${name}: ${fn}`);
        console.log(`this.rpcFunctions[${name}] exisit`);
      }
      this.fn(name, fn);
    }
  }
  default() {
    return async (ctx, next) => {
      log3("default");
      ctx.body = "[fn plugin] no fn repsonse, please check your fn if exist";
      log3("default end");
    };
  }
  mount() {
    return async (ctx, next) => {
      log3(this);
      const routers = this.config["functions"];
      log3(routers);
      const path = ctx.path;
      log3("mountMiddleware" + ctx.path);
      const key = "/" + path.replace("/", "").split("/").join(".");
      if (!["POST", "PUT", "PATCH"].includes(ctx.method) && !ctx.query.$p) {
        console.log("not match $p param, no process");
        ctx.body = "not match $p param, no process";
      } else {
        const param = ctx.method === "POST" ? ctx.request.body : JSON.parse(ctx.query.$p);
        log3(key);
        log3(param);
        log3(routers[key]);
        if (routers[key]) {
          const args = [...param, ctx];
          console.dir(`call fn ${ctx.path}, with [${param}]`);
          const result = routers[key].apply(ctx, args);
          ctx.body = result;
        } else {
          const msg = JSON.stringify(ctx, null, 4);
          ctx.body = ` not match path ${ctx.path}  
 ctx = ${msg}`;
          await next();
        }
      }
    };
  }
  before() {
    return async (ctx, next) => {
      log3("before");
      const key = ctx.path.replace("/", "").split("/").join(".");
      const lastKey = key.split(".").pop();
      const httpMethods = getHttpMethods();
      const supportMethods = [];
      httpMethods.forEach(function(m) {
        if (lastKey.indexOf(m) != -1) {
          log3(m);
          supportMethods.push(m);
          return m;
        }
      });
      if (supportMethods.length === 0) {
        log3(ctx.path + ",\u6CA1\u6709\u5339\u914D\u5230\u5305\u542Bget/post\u7B49\u5F00\u5934\u7684\u51FD\u6570");
        await next();
      } else if (ctx.method.toLowerCase() === supportMethods[0]) {
        console.log("\u5339\u914D\u5230\u5305\u542Bget/post\u7B49\u65B9\u6CD5\u7684\u51FD\u6570");
        await next();
      } else {
        log3("\u5339\u914D\u5230\u5305\u542Bget/post\u7B49\u65B9\u6CD5\u7684\u51FD\u6570\uFF0C\u4F46method\u4E0D\u5BF9");
        ctx.body = "process fn:" + lastKey + " , you need send " + supportMethods[0] + " request from client";
      }
      log3("beforeOne end");
    };
  }
};

// src/server.ts
var import_bodyparser = require("@koa/bodyparser");
var import_koa2 = __toESM(require("koa"));
var import_koa_compose2 = __toESM(require("koa-compose"));
var import_koa_mount = __toESM(require("koa-mount"));
var LifeCycleConfig = {
  hooks: {
    init: [],
    // 后面需要改成Set，否则会出现重复问题
    before: [],
    load: [],
    after: [],
    default: async (ctx, next) => {
      log("default");
      ctx.body = "[core server] no middleware repsonse, please check your route";
      log("default end");
    }
  },
  before: async (server) => {
    const app = server.app;
    const loadMiddlewares = server.config.hooks.before;
    loadMiddlewares.forEach((mw) => {
      app.use(mw);
    });
  },
  init: async (server) => {
    log("init");
    const app = server.app;
    const loadMiddlewares = server.config.hooks.init;
    loadMiddlewares.forEach((mw) => {
      app.use(mw);
    });
  },
  load: async (server) => {
    const app = server.app;
    const loadMiddlewares = server.config.hooks.load;
    loadMiddlewares.forEach((mw) => {
      app.use(mw);
    });
  },
  after: async (server) => {
    const app = server.app;
    const loadMiddlewares = server.config.hooks.after;
    loadMiddlewares.forEach((mw) => {
      app.use(mw);
    });
  }
};
var BaseRpcServer = class {
  /**
   * Usually, the Server accepts a strategy through the constructor, but also
   * provides a setter to change it at runtime.
   */
  constructor(cfg) {
    this.plugins = [];
    this.proxy = {
      init: [],
      load: [],
      before: {}
    };
    this.config = mergeDeep(LifeCycleConfig, cfg);
    this.app = new import_koa2.default();
    this.app.use((0, import_bodyparser.bodyParser)());
    this.use = this.app.use;
  }
  /**
   * Usually, the Server allows replacing a Strategy object at runtime.
   */
  plugin(strategy) {
    this.plugins.push(strategy);
  }
  mount() {
    log("mount");
    const cfg = {};
    for (const plugin of this.plugins) {
      plugin.server = this;
      plugin.serverConfig = this.config;
      if (plugin.name === "base") {
        log("plugin name \u6CA1\u6709\u4FEE\u6539\uFF0C\u53EF\u80FD\u4F1A\u51FA\u73B0serverConfig\u83B7\u53D6\u95EE\u9898\uFF0C\u8BF7\u5173\u6CE8");
      }
      this.config[plugin.name] = plugin.config;
      cfg[plugin.name] = plugin;
    }
    for (const plugin of this.plugins) {
      if (plugin.config.proxy) {
        if (plugin.config.proxy.inject === "init") {
          log("plugin.config.proxy.inject init");
          this.proxy.init.push(plugin.proxy());
        }
        if (plugin.config.proxy.inject === "load") {
          log("plugin.config.proxy.inject load");
          this.proxy.load.push(plugin.proxy());
        }
        if (plugin.config.proxy.inject === "before") {
          log("plugin.config.proxy.inject before");
          for (const i in plugin.config.proxy.before) {
            const name = plugin.config.proxy.before[i];
            if (!this.proxy.before[name.toLowerCase()])
              this.proxy.before[name.toLowerCase()] = [];
            this.proxy.before[name.toLowerCase()].push(plugin.proxy());
          }
        }
      }
    }
    this.config.hooks.init = this.proxy.init;
    for (const plugin of this.plugins) {
      log("init stage");
      if (plugin.init.length > 0)
        this.config.hooks.init.push(...plugin.init);
    }
    this.config.init(this);
    this.config.hooks.load = this.proxy.load;
    for (const plugin of this.plugins) {
      log("load stage");
      if (plugin.load.length > 0)
        this.config.hooks.load.push(...plugin.load);
    }
    this.config.load(this);
    for (const plugin of this.plugins) {
      log("mount plugin " + plugin.prefix);
      const mw = this.proxy.before[plugin.name.toLowerCase()] || [];
      const app = plugin.prefix === "" ? (0, import_koa_mount.default)(plugin.app) : (0, import_koa_mount.default)(plugin.prefix, plugin.app);
      this.app.use((0, import_koa_compose2.default)([...mw, app]));
    }
    log("\u515C\u5E95\u7684else");
    this.app.use(this.config.hooks.default);
  }
  /**
   * make app ready, add before hook & mount & after hook
   */
  prepare() {
    log("prepare");
    this.config.before(this);
    this.mount();
    this.config.after(this);
    this.app.use(function(ctx) {
      ctx.body = "default";
    });
  }
  callback() {
    log("callback");
    this.prepare();
    return this.app.callback();
  }
  /**
   * Start @tomrpc/core server with port
   */
  /* v8 ignore next 3 */
  start(port) {
    log("start");
    this.prepare();
    const _port = port || 3e3;
    return this.app.listen(_port, () => {
      console.log("@tomrpc/core listening on " + _port);
    });
  }
};

// src/proxy.ts
var import_debug4 = __toESM(require("debug"));
var log4 = (0, import_debug4.default)("@tomrpc/core/fn");
var ProxyDefaultConfig = {
  proxy: {
    inject: "before",
    //init | load | before | after
    before: []
  }
};
var Proxy2 = class extends Plugable {
  constructor(cfg) {
    super(mergeDeep(ProxyDefaultConfig, cfg));
  }
  proxy() {
    return async (ctx, next) => {
      log4("proxy default");
      await next();
      log4("proxy default end");
    };
  }
};

// src/index.ts
var log5 = (0, import_debug5.default)("@tomrpc/core/index");
var combine = import_koa_compose3.default;
var RpcServer = class extends BaseRpcServer {
  constructor(cfg) {
    super(cfg);
    this.fnPlugin = new Fn(mergeDeep({}, cfg.fn));
    this.plugin(this.fnPlugin);
    this.base = ".";
    this.init = this["config"]["hooks"]["init"];
    this.before = this["config"]["hooks"]["before"];
    this.load = this["config"]["hooks"]["load"];
    this.after = this["config"]["hooks"]["after"];
  }
  fn(key, fun) {
    log5("use rpc.fn add fn =" + key);
    this.fnPlugin.fn(key, fun);
  }
  add(items) {
    log5("use rpc.add add fn =" + items);
    this.fnPlugin.add(items);
  }
};
function createServer(cfg) {
  return new RpcServer(mergeDeep({ fn: {} }, cfg));
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
  BaseRpcServer,
  Fn,
  LifeCycleConfig,
  Plugable,
  Proxy,
  RpcServer,
  combine,
  createServer,
  getHttpMethods,
  isArrowFunction,
  isObject,
  log,
  mergeDeep
});
