@kotori-bot/core
Version:
Kotori Core
192 lines (191 loc) • 8.27 kB
JavaScript
/**
* @Package @kotori-bot/core
* @Version 1.7.2
* @Author Arimura Sena <me@hotaru.icu>
* @Copyright 2024-2025 Hotaru. All rights reserved.
* @License GPL-3.0
* @Link https://github.com/kotorijs/kotori
* @Date 2026/2/14 15:50:13
*/
;
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 utils_exports = {};
__export(utils_exports, {
Decorators: () => Decorators,
default: () => utils_default
});
module.exports = __toCommonJS(utils_exports);
var import_reflect_metadata = require("reflect-metadata");
var import_node_path = require("node:path");
var import_fast_safe_stringify = __toESM(require("fast-safe-stringify"));
var import_fluoro = require("fluoro");
var import_tsukiko = __toESM(require("tsukiko"));
var import_global = require("../global");
var import_error = require("../utils/error");
class Decorators {
static [import_global.Symbols.decorator] = /* @__PURE__ */ new Map();
static getMeta(target) {
return Reflect.getMetadata(
import_global.Symbols.modules,
typeof target === "string" ? Decorators[import_global.Symbols.decorator].get(target) ?? {} : target
);
}
static setup(ctx) {
ctx.on("literal_ready_module_decorator", (name, config) => Decorators.load(ctx, name, config));
}
static load(ctx, target, config) {
const Constructor = typeof target === "string" ? Decorators[import_global.Symbols.decorator].get(target) : target;
if (!Constructor) return false;
const meta = Decorators.getMeta(Constructor);
if (!meta) return false;
const { name } = meta;
if (meta.lang) ctx.i18n.use(meta.lang);
let configHandle = config;
if (meta.schema) {
const result = meta.schema.parseSafe(config);
if (!result.value) throw new import_error.ModuleError(`config format of module ${name} is error: ${result.error.message}`);
configHandle = result.data;
}
const childCtx = ctx.extends(name);
for (const identity of meta.inject) {
const serviceData = Array.from(ctx[import_fluoro.Tokens.container]).find(
([, service]) => service instanceof import_fluoro.Service && service.identity === identity
);
if (serviceData) childCtx.inject(serviceData[0]);
}
const plugin = new Constructor(childCtx, configHandle);
const bound = (fn, isStatic) => fn.bind(isStatic ? Constructor : plugin);
for (const [fn, options, isStatic] of meta.events) {
childCtx[options.isOnce ? "once" : "on"](options.type, bound(fn, isStatic));
}
for (const [fn, options, isStatic] of meta.midwares) {
childCtx.midware(bound(fn, isStatic), options.priority);
}
for (const [fn, options, isStatic] of meta.commands) {
const oldCmd = childCtx.command(options.template, { ...options, action: bound(fn, isStatic) });
let cmd = oldCmd;
for (const option of JSON.parse((0, import_fast_safe_stringify.default)(options.options ?? []))) cmd = cmd.option(option[0], option[1]);
childCtx[import_global.Symbols.command].delete(oldCmd);
childCtx[import_global.Symbols.command].add(cmd);
}
for (const [fn, options, isStatic] of meta.regexps) {
childCtx.regexp(options.match, bound(fn, isStatic));
}
for (const [fn, options, isStatic] of meta.tasks) {
childCtx.task(options, bound(fn, isStatic));
}
const params = { instance: { inject: meta.inject, name, config: configHandle, default: Constructor } };
ctx.emit("ready_module", params);
return true;
}
pkgName;
error(message = "plugin not decorated") {
return new import_error.DevError(`${message} at module ${this.pkgName}`);
}
getMeta(target) {
const value = "prototype" in target ? target : target.constructor;
let meta = Decorators.getMeta(value);
if (meta) return meta;
meta = { commands: [], events: [], midwares: [], inject: [], regexps: [], name: "unknown", tasks: [] };
Reflect.defineMetadata(import_global.Symbols.modules, meta, value);
return meta;
}
constructor(pkgName) {
this.pkgName = pkgName;
}
import = (target) => {
if (Decorators[import_global.Symbols.decorator].has(this.pkgName)) throw this.error("plugin already decorated");
const meta = this.getMeta(target);
meta.name = this.pkgName;
Decorators[import_global.Symbols.decorator].set(this.pkgName, target);
};
lang = (target, name) => {
const meta = this.getMeta(target);
const result = import_tsukiko.default.Union(import_tsukiko.default.String(), import_tsukiko.default.Array(import_tsukiko.default.String())).parseSafe(target[name]);
if (!result.value) throw this.error("lang must be string or string[]");
meta.lang = (0, import_node_path.resolve)(...typeof result.data === "string" ? [result.data] : result.data);
};
inject = (target, name) => {
const meta = this.getMeta(target);
const result = import_tsukiko.default.Array(import_tsukiko.default.String()).parseSafe(target[name]);
if (!result.value) throw this.error(`inject must be string[] at ${this.pkgName}`);
meta.inject = result.data;
};
schema = (target, name) => {
const meta = this.getMeta(target);
const value = target[name];
if (!(value instanceof import_tsukiko.Parser)) throw this.error("schema must be Parser");
meta.schema = value;
};
on(options) {
return (target, key) => {
const meta = this.getMeta(target);
const fn = target[key];
if (typeof fn !== "function") throw this.error("event callback must be function");
meta.events = [...meta.events ?? [], [fn, options, "prototype" in target]];
};
}
midware(options = {}) {
return (target, key) => {
const meta = this.getMeta(target);
const fn = target[key];
if (typeof fn !== "function") throw this.error("middlewares callback must be function");
meta.midwares = [...meta.midwares ?? [], [fn, options, "prototype" in target]];
};
}
command(options) {
return (target, key) => {
const meta = this.getMeta(target);
const fn = target[key];
if (typeof fn !== "function") throw this.error("command callback must be function");
meta.commands = [...meta.commands ?? [], [fn, options, "prototype" in target]];
};
}
regexp(options) {
return (target, key) => {
const meta = this.getMeta(target);
const fn = target[key];
if (typeof fn !== "function") throw this.error("regexp callback must be function");
meta.regexps = [...meta.regexps ?? [], [fn, options, "prototype" in target]];
};
}
task(options) {
return (target, key) => {
const meta = this.getMeta(target);
const fn = target[key];
if (typeof fn !== "function") throw this.error("task callback must be function");
meta.tasks = [...meta.tasks ?? [], [fn, options, "prototype" in target]];
};
}
}
var utils_default = Decorators;
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
Decorators
});