@kotori-bot/loader 1.5.1 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,267 +1,680 @@
1
+
2
+ /**
3
+ * @Package @kotori-bot/loader
4
+ * @Version 1.6.0-beta.1
5
+ * @Author Hotaru <biyuehuya@gmail.com>
6
+ * @Copyright 2024 Hotaru. All rights reserved.
7
+ * @License GPL-3.0
8
+ * @Link https://github.com/kotorijs/kotori
9
+ * @Date 2024/6/6 21:03:54
10
+ */
11
+
1
12
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || function (mod) {
19
- if (mod && mod.__esModule) return mod;
20
- var result = {};
21
- if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
- __setModuleDefault(result, mod);
23
- return result;
13
+ var __create = Object.create;
14
+ var __defProp = Object.defineProperty;
15
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
16
+ var __getOwnPropNames = Object.getOwnPropertyNames;
17
+ var __getProtoOf = Object.getPrototypeOf;
18
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
19
+ var __export = (target, all) => {
20
+ for (var name in all)
21
+ __defProp(target, name, { get: all[name], enumerable: true });
24
22
  };
25
- var __importDefault = (this && this.__importDefault) || function (mod) {
26
- return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ var __copyProps = (to, from, except, desc) => {
24
+ if (from && typeof from === "object" || typeof from === "function") {
25
+ for (let key of __getOwnPropNames(from))
26
+ if (!__hasOwnProp.call(to, key) && key !== except)
27
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
28
+ }
29
+ return to;
27
30
  };
28
- Object.defineProperty(exports, "__esModule", { value: true });
29
- exports.Loader = void 0;
30
- /*
31
- * @Author: hotaru biyuehuya@gmail.com
32
- * @Blog: https://hotaru.icu
33
- * @Date: 2023-06-24 15:12:55
34
- * @LastEditors: Hotaru biyuehuya@gmail.com
35
- * @LastEditTime: 2024-05-12 16:24:15
36
- */
37
- const core_1 = require("@kotori-bot/core");
38
- const node_path_1 = __importDefault(require("node:path"));
39
- const node_fs_1 = __importDefault(require("node:fs"));
40
- const logger_1 = __importStar(require("@kotori-bot/logger"));
41
- const runner_1 = __importStar(require("./runner"));
42
- const log_1 = __importDefault(require("../utils/log"));
43
- const constants_1 = require("../constants");
44
- const server_1 = __importDefault(require("../service/server"));
45
- const file_1 = __importDefault(require("../service/file"));
46
- function getBaseDir(file, dir) {
47
- const handle = (root) => {
48
- const baseDir = {
49
- root,
50
- modules: node_path_1.default.join(root, 'modules'),
51
- data: node_path_1.default.join(root, 'data'),
52
- logs: node_path_1.default.join(root, 'logs')
53
- };
54
- Object.values(baseDir).forEach((val) => {
55
- if (!node_fs_1.default.existsSync(val))
56
- node_fs_1.default.mkdirSync(val);
57
- });
58
- return baseDir;
31
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
32
+ // If the importer is in node compatibility mode or this is not an ESM
33
+ // file that has been converted to a CommonJS file using a Babel-
34
+ // compatible transform (i.e. "__esModule" has not been set), then set
35
+ // "default" to the CommonJS "module.exports" for node compatibility.
36
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
37
+ mod
38
+ ));
39
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
40
+
41
+ // src/class/loader.ts
42
+ var loader_exports = {};
43
+ __export(loader_exports, {
44
+ Loader: () => Loader,
45
+ default: () => loader_default
46
+ });
47
+ module.exports = __toCommonJS(loader_exports);
48
+ var import_core4 = require("@kotori-bot/core");
49
+ var import_node_path3 = __toESM(require("path"));
50
+ var import_node_fs2 = __toESM(require("fs"));
51
+ var import_logger4 = __toESM(require("@kotori-bot/logger"));
52
+
53
+ // src/class/runner.ts
54
+ var import_node_fs = __toESM(require("fs"));
55
+ var import_node_path = __toESM(require("path"));
56
+ var import_core = require("@kotori-bot/core");
57
+ var import_logger2 = require("@kotori-bot/logger");
58
+
59
+ // src/constants.ts
60
+ var DEV_FILE = ".ts";
61
+ var BUILD_FILE = ".js";
62
+ var DEV_CODE_DIRS = "./src/";
63
+ var DEV_IMPORT = `${DEV_CODE_DIRS}index.ts`;
64
+ var CONFIG_EXT = [".toml", ".yml", ".yaml", ".json"];
65
+ var BUILD_CONFIG_NAME = "kotori";
66
+ var DEV_CONFIG_NAME = "kotori.dev";
67
+ var SUPPORTS_VERSION = /(1\.1\.0)|(1\.2\.0)|(1\.3\.(.*))|(1\.4\.(.*))/;
68
+ var SUPPORTS_HALF_VERSION = /(x\.x\.(.*?))/;
69
+ var BUILD_MODE = "build";
70
+ var DEV_MODE = "dev";
71
+ var DEV_SOURCE_MODE = "dev-source";
72
+ var CORE_MODULES = [
73
+ "@kotori-bot/kotori-plugin-core",
74
+ "@kotori-bot/kotori-plugin-i18n-command",
75
+ "@kotori-bot/kotori-plugin-filter"
76
+ // '@kotori-bot/kotori-plugin-webui'
77
+ ];
78
+
79
+ // src/utils/logger.ts
80
+ var import_logger = require("@kotori-bot/logger");
81
+ var KotoriLogger = class extends import_logger.Logger {
82
+ constructor(optionsSelf, ctx) {
83
+ super(optionsSelf);
84
+ this.optionsSelf = optionsSelf;
85
+ this.ctx = ctx;
86
+ }
87
+ setLabel() {
88
+ const origin = Object.create(this.optionsSelf.label);
89
+ const label = this.ctx.identity ? [this.ctx.identity, ...this.optionsSelf.label] : this.optionsSelf.label;
90
+ this[/* @__PURE__ */ (() => "options")()].label = label;
91
+ return () => {
92
+ this[/* @__PURE__ */ (() => "options")()].label = origin;
59
93
  };
60
- if (dir)
61
- return handle(node_path_1.default.resolve(dir));
62
- let root = node_path_1.default.resolve(__dirname, '..').replace('loader', 'kotori');
63
- let count = 0;
64
- while (!node_fs_1.default.existsSync(node_path_1.default.join(root, file))) {
65
- if (count > 5) {
66
- logger_1.default.fatal(`cannot find file ${file} `);
67
- process.exit();
68
- }
69
- root = node_path_1.default.join(root, '..');
70
- count += 1;
71
- }
72
- return handle(root);
94
+ }
95
+ fatal(...args) {
96
+ const dispose = this.setLabel();
97
+ super.fatal(...args);
98
+ dispose();
99
+ }
100
+ error(...args) {
101
+ const dispose = this.setLabel();
102
+ super.error(...args);
103
+ dispose();
104
+ }
105
+ warn(...args) {
106
+ const dispose = this.setLabel();
107
+ super.warn(...args);
108
+ dispose();
109
+ }
110
+ info(...args) {
111
+ const dispose = this.setLabel();
112
+ super.info(...args);
113
+ dispose();
114
+ }
115
+ record(...args) {
116
+ const dispose = this.setLabel();
117
+ super.record(...args);
118
+ dispose();
119
+ }
120
+ debug(...args) {
121
+ const dispose = this.setLabel();
122
+ super.debug(...args);
123
+ dispose();
124
+ }
125
+ trace(...args) {
126
+ const dispose = this.setLabel();
127
+ super.trace(...args);
128
+ dispose();
129
+ }
130
+ };
131
+ var logger_default = KotoriLogger;
132
+
133
+ // src/class/runner.ts
134
+ var localeTypeSchema = import_core.Tsu.Union([
135
+ import_core.Tsu.Union([import_core.Tsu.Literal("en_US"), import_core.Tsu.Literal("ja_JP")]),
136
+ import_core.Tsu.Union([import_core.Tsu.Literal("zh_TW"), import_core.Tsu.Any()])
137
+ ]);
138
+ var modulePackageSchema = import_core.Tsu.Object({
139
+ name: import_core.Tsu.String().regexp(/kotori-plugin-[a-z]([a-z,0-9]{2,13})\b/),
140
+ version: import_core.Tsu.String(),
141
+ description: import_core.Tsu.String(),
142
+ main: import_core.Tsu.String(),
143
+ license: import_core.Tsu.Literal("GPL-3.0"),
144
+ keywords: import_core.Tsu.Custom(
145
+ (val) => Array.isArray(val) && val.includes("kotori") && val.includes("chatbot") && val.includes("kotori-plugin")
146
+ ),
147
+ author: import_core.Tsu.Union([import_core.Tsu.String(), import_core.Tsu.Array(import_core.Tsu.String())]),
148
+ peerDependencies: import_core.Tsu.Object({
149
+ "kotori-bot": import_core.Tsu.String()
150
+ }),
151
+ kotori: import_core.Tsu.Object({
152
+ enforce: import_core.Tsu.Union([import_core.Tsu.Literal("pre"), import_core.Tsu.Literal("post")]).optional(),
153
+ meta: import_core.Tsu.Object({
154
+ language: import_core.Tsu.Array(localeTypeSchema).default([])
155
+ }).default({ language: [] })
156
+ }).default({
157
+ enforce: void 0,
158
+ meta: { language: [] }
159
+ })
160
+ });
161
+ function moduleLoadOrder(pkg) {
162
+ if (CORE_MODULES.includes(pkg.name)) return 1;
163
+ if (pkg.name.includes(import_core.DATABASE_PREFIX)) return 2;
164
+ if (pkg.name.includes(import_core.ADAPTER_PREFIX)) return 3;
165
+ if (pkg.kotori.enforce === "pre") return 4;
166
+ if (!pkg.kotori.enforce) return 5;
167
+ return 6;
73
168
  }
74
- /* eslint consistent-return: 0 */
75
- function getCoreConfig(file, baseDir) {
169
+ var Runner = class {
170
+ baseDir;
171
+ options;
172
+ ctx;
173
+ isDev;
174
+ isSourceDev;
175
+ [import_core.Symbols.modules] = /* @__PURE__ */ new Map();
176
+ constructor(ctx, config) {
177
+ this.ctx = ctx;
178
+ this.baseDir = config.baseDir;
179
+ this.options = config.options;
180
+ this.isDev = this.options.mode.startsWith(DEV_MODE);
181
+ this.isSourceDev = this.options.mode === DEV_SOURCE_MODE;
182
+ const loggerOptions = {
183
+ level: this.ctx.config.global.level ?? config.level,
184
+ label: [],
185
+ transports: [
186
+ new import_logger2.ConsoleTransport({
187
+ template: "<blue>%time%</blue> %level% (<bold>%pid%</bold>) %labels%: %msg%",
188
+ time: "M/D H:m:s"
189
+ }),
190
+ new import_logger2.FileTransport({ dir: this.baseDir.logs, filter: (data) => data.level >= import_logger2.LoggerLevel.WARN })
191
+ ]
192
+ };
193
+ ctx.provide("logger", new logger_default(loggerOptions, this.ctx));
194
+ ctx.inject("logger");
195
+ }
196
+ getDirFiles(rootDir) {
197
+ const files = import_node_fs.default.readdirSync(rootDir);
198
+ const list = [];
199
+ files.forEach((fileName) => {
200
+ const file = import_node_path.default.join(rootDir, fileName);
201
+ if (import_node_fs.default.statSync(file).isDirectory()) {
202
+ list.push(...this.getDirFiles(file));
203
+ }
204
+ if (import_node_path.default.parse(file).ext !== (this.isSourceDev ? DEV_FILE : BUILD_FILE)) return;
205
+ list.push(import_node_path.default.resolve(file));
206
+ });
207
+ return list;
208
+ }
209
+ getModuleRootDir() {
210
+ const moduleRootDir = [];
211
+ [
212
+ ...this.ctx.config.global.dirs.map((dir) => import_node_path.default.resolve(this.ctx.baseDir.root, dir)),
213
+ this.ctx.baseDir.modules
214
+ ].forEach((dir) => {
215
+ if (import_node_fs.default.existsSync(dir) && import_node_fs.default.statSync(dir).isDirectory()) moduleRootDir.push(dir);
216
+ });
217
+ return moduleRootDir;
218
+ }
219
+ async checkModuleFiles(rootDir, filename) {
220
+ const dir = import_node_path.default.join(rootDir, filename);
221
+ if (!import_node_fs.default.statSync(dir).isDirectory()) return;
222
+ if (rootDir !== this.ctx.baseDir.modules && !filename.startsWith(import_core.PLUGIN_PREFIX)) return;
223
+ const packagePath = import_node_path.default.join(dir, "package.json");
224
+ let pkg;
225
+ if (!import_node_fs.default.existsSync(packagePath)) return;
76
226
  try {
77
- const result1 = core_1.Tsu.Object({
78
- global: core_1.Tsu.Object({
79
- dirs: core_1.Tsu.Array(core_1.Tsu.String()).default([]),
80
- port: core_1.Tsu.Number().default(core_1.DEFAULT_PORT),
81
- lang: runner_1.localeTypeSchema.default(core_1.DEFAULT_CORE_CONFIG.global.lang),
82
- 'command-prefix': core_1.Tsu.String().default(core_1.DEFAULT_CORE_CONFIG.global['command-prefix'])
83
- }),
84
- plugin: core_1.Tsu.Object({})
85
- .index(core_1.Tsu.Object({
86
- filter: core_1.Tsu.Object({}).default({})
87
- }).default({ filter: {} }))
88
- .default(core_1.DEFAULT_CORE_CONFIG.plugin)
89
- })
90
- .default({ global: Object.assign(core_1.DEFAULT_CORE_CONFIG.global), plugin: core_1.DEFAULT_CORE_CONFIG.plugin })
91
- .parse((0, core_1.loadConfig)(node_path_1.default.join(baseDir.root, file), 'yaml'));
92
- return core_1.Tsu.Object({
93
- adapter: core_1.Tsu.Object({})
94
- .index(core_1.Tsu.Object({
95
- extends: core_1.Tsu.String(),
96
- master: core_1.Tsu.Union([core_1.Tsu.Number(), core_1.Tsu.String()]),
97
- lang: runner_1.localeTypeSchema.default(result1.global.lang),
98
- 'command-prefix': core_1.Tsu.String().default(result1.global['command-prefix'])
99
- }))
100
- .default(core_1.DEFAULT_CORE_CONFIG.adapter)
101
- }).parse(result1);
227
+ pkg = JSON.parse(import_node_fs.default.readFileSync(packagePath).toString());
228
+ } catch {
229
+ throw new import_core.DevError(this.ctx.format("error.dev.package.illegal", [packagePath]));
102
230
  }
103
- catch (err) {
104
- if (!(err instanceof core_1.TsuError))
105
- throw err;
106
- logger_1.default.fatal(`file ${file} format error: ${err.message}`);
107
- process.exit();
231
+ const result = modulePackageSchema.parseSafe(pkg);
232
+ if (!result.value) {
233
+ if (rootDir !== this.ctx.baseDir.modules) return;
234
+ throw new import_core.DevError(this.ctx.format("error.dev.package.missing", [packagePath, result.error.message]));
108
235
  }
109
- }
110
- class Loader extends core_1.Container {
111
- ctx;
112
- loadCount = 0;
113
- constructor(options) {
114
- super();
115
- const file = options && options.mode?.startsWith(constants_1.DEV_MODE) ? constants_1.DEV_CONFIG_NAME : constants_1.BUILD_CONFIG_NAME;
116
- const runnerConfig = {
117
- baseDir: getBaseDir(file, options?.dir),
118
- options: { mode: (options?.mode || constants_1.BUILD_MODE) },
119
- level: options?.level || options?.mode?.startsWith(constants_1.DEV_MODE) ? logger_1.LoggerLevel.DEBUG : logger_1.LoggerLevel.INFO
120
- };
121
- const ctx = new core_1.Core(getCoreConfig(file, runnerConfig.baseDir));
122
- ctx.provide('runner', new runner_1.default(ctx, runnerConfig));
123
- ctx.mixin('runner', ['baseDir', 'options']);
124
- core_1.Container.setInstance(ctx);
125
- ctx.provide('loader-tools', { format: (0, core_1.formatFactory)(ctx.i18n), locale: ctx.i18n.locale.bind(ctx.i18n) });
126
- ctx.mixin('loader-tools', ['locale', 'format']);
127
- ctx.i18n.use(node_path_1.default.resolve(__dirname, '../../locales'));
128
- this.ctx = core_1.Container.getInstance();
129
- this.ctx.logger.trace(`options:`, options);
130
- this.ctx.logger.trace(`runnerConfig:`, runnerConfig);
131
- this.ctx.logger.trace(`baseDir:`, this.ctx.baseDir);
132
- this.ctx.logger.trace(`options:`, this.ctx.options);
133
- this.ctx.logger.trace(`config:`, this.ctx.config);
134
- this.ctx.logger.trace(`where:`, __dirname, __filename);
135
- this.ctx.logger.trace(`running:`, process.cwd());
136
- }
137
- run() {
138
- (0, log_1.default)(this.ctx.pkg, this.ctx);
139
- this.catchError();
140
- this.listenMessage();
141
- this.setPreService();
142
- this.loadAllModules();
143
- this.checkUpdate();
236
+ pkg = result.data;
237
+ const devMode = this.isSourceDev && (0, import_node_fs.existsSync)(import_node_path.default.resolve(dir, DEV_IMPORT));
238
+ const main = import_node_path.default.resolve(dir, devMode ? DEV_IMPORT : pkg.main);
239
+ if (!import_node_fs.default.existsSync(main)) throw new import_core.DevError(this.ctx.format("error.dev.main_file", [main]));
240
+ const dirs = import_node_path.default.join(dir, devMode ? DEV_CODE_DIRS : import_node_path.default.dirname(pkg.main));
241
+ const files = import_node_fs.default.statSync(dirs).isDirectory() ? this.getDirFiles(dirs) : [];
242
+ this[import_core.Symbols.modules].set(pkg.name, [
243
+ { pkg, files, main },
244
+ this.ctx.config.plugin[(0, import_core.stringRightSplit)(pkg.name, import_core.PLUGIN_PREFIX)] || {}
245
+ ]);
246
+ }
247
+ getModuleList(rootDir) {
248
+ this.ctx.logger.trace("load dirs:", rootDir);
249
+ import_node_fs.default.readdirSync(rootDir).forEach(async (filename) => {
250
+ await this.checkModuleFiles(rootDir, filename);
251
+ });
252
+ }
253
+ loadLang(lang) {
254
+ if (lang) this.ctx.i18n.use((0, import_node_path.resolve)(...Array.isArray(lang) ? lang : [lang]));
255
+ }
256
+ loadEx(instance, origin) {
257
+ this.ctx.logger.trace("module:", instance, origin);
258
+ if (!instance.main) return;
259
+ const parsed = (schema) => {
260
+ const result = schema.parseSafe(config);
261
+ if (!result.value)
262
+ throw new import_core.ModuleError(this.ctx.format("error.module.config", [pkg.name, result.error.message]));
263
+ return result.data;
264
+ };
265
+ const { main, pkg } = instance;
266
+ let obj = require(main);
267
+ let config = origin;
268
+ const adapterName = pkg.name.split(import_core.ADAPTER_PREFIX)[1];
269
+ if (this.ctx.get("decorators")?.registers.includes(pkg.name)) {
270
+ this.ctx.emit("ready_module_decorators", pkg.name);
271
+ return;
144
272
  }
145
- handleError(err, prefix) {
146
- if (!(err instanceof core_1.KotoriError)) {
147
- if (err instanceof Error) {
148
- this.ctx.logger.label(prefix).error(err.message, err.stack);
149
- }
150
- else {
151
- this.ctx.logger.label(prefix).error(err);
152
- }
153
- return;
154
- }
155
- const list = {
156
- ServiceError: () => this.ctx.logger.label('service').warn,
157
- ModuleError: () => this.ctx.logger.label('module').error,
158
- UnknownError: () => this.ctx.logger.error,
159
- DevError: () => this.ctx.logger.label('error').debug
160
- };
161
- list[err.name]().bind(this.ctx.logger)(err.message, err.stack);
273
+ if (import_core.Adapter.isPrototypeOf.call(import_core.Adapter, obj.default) && adapterName && (!obj.config || obj.config instanceof import_core.Parser)) {
274
+ this.ctx[import_core.Symbols.adapter].set(adapterName, [obj.default, obj.config]);
275
+ obj = {};
276
+ } else if (import_core.Service.isPrototypeOf.call(import_core.Service, obj.default)) {
277
+ obj = {};
278
+ } else if (obj.config instanceof import_core.Parser) {
279
+ config = parsed(obj.config);
162
280
  }
163
- catchError() {
164
- process.on('uncaughtExceptionMonitor', (err) => this.handleError(err, 'sync'));
165
- process.on('unhandledRejection', (err) => this.handleError(err, 'async'));
166
- process.on('SIGINT', () => process.exit());
167
- this.ctx.logger.debug(this.ctx.locale('loader.debug.info'));
281
+ if (obj.lang) this.loadLang(obj.lang);
282
+ if (obj.default) {
283
+ if (obj.default.lang) this.loadLang(obj.default.lang);
284
+ if (obj.default.config instanceof import_core.Parser) config = parsed(obj.default.config);
285
+ } else if (obj.Main) {
286
+ if (obj.Main.lang) this.loadLang(obj.Main.lang);
287
+ if (obj.Main.config instanceof import_core.Parser) config = parsed(obj.Main.config);
168
288
  }
169
- listenMessage() {
170
- this.ctx.on('connect', (data) => {
171
- const { type, mode, normal, address: addr, adapter } = data;
172
- let msg;
173
- if (type === 'connect') {
174
- switch (mode) {
175
- case 'ws':
176
- msg = this.ctx.format(`loader.bots.${normal ? 'connect' : 'reconnect'}`, [addr]);
177
- break;
178
- case 'ws-reverse':
179
- msg = this.ctx.format(`loader.bots.${normal ? 'start' : 'restart'}`, [addr]);
180
- break;
181
- default:
182
- msg = this.ctx.format('loader.bots.ready', [addr]);
183
- }
184
- }
185
- else {
186
- switch (mode) {
187
- case 'ws':
188
- msg = this.ctx.format(`loader.bots.disconnect${normal ? '' : '.error'}`, [addr]);
189
- break;
190
- case 'ws-reverse':
191
- msg = this.ctx.format(`loader.bots.stop${normal ? '' : '.error'}`, [addr]);
192
- break;
193
- default:
194
- msg = this.ctx.format('loader.bots.dispose', [addr]);
195
- }
196
- }
197
- adapter.ctx.logger[normal ? 'info' : 'warn'](msg);
198
- });
199
- this.ctx.on('status', ({ status, adapter }) => adapter.ctx.logger.info(status));
200
- this.ctx.on('ready_module', (data) => {
201
- if (typeof data.instance !== 'object')
202
- return;
203
- const pkg = data.instance.name
204
- ? this.ctx.get('runner')[core_1.Symbols.modules].get(data.instance.name)
205
- : undefined;
206
- if (!pkg)
207
- return;
208
- this.loadCount += 1;
209
- const { name, version, author, peerDependencies } = pkg[0].pkg;
210
- this.ctx.logger.info(this.ctx.format('loader.modules.load', [name, version, Array.isArray(author) ? author.join(',') : author]));
211
- const requiredVersion = peerDependencies['kotori-bot'];
212
- if (requiredVersion.includes('workspace') ||
213
- constants_1.SUPPORTS_VERSION.exec(requiredVersion) ||
214
- requiredVersion.includes(this.ctx.pkg.version))
215
- return;
216
- if (constants_1.SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
217
- this.ctx.logger.warn(this.ctx.format('loader.modules.incomplete', [requiredVersion]));
218
- }
219
- else {
220
- this.ctx.logger.error(this.ctx.format('loader.modules.unsupported', [requiredVersion]));
221
- }
289
+ this.ctx.load({ name: pkg.name, ...obj, config });
290
+ }
291
+ unloadEx(instance) {
292
+ instance.files.forEach((file) => delete require.cache[require.resolve(file)]);
293
+ this.ctx.load({ name: instance.pkg.name });
294
+ }
295
+ loadAll() {
296
+ this.getModuleRootDir().forEach((dir) => this.getModuleList(dir));
297
+ const modules = [];
298
+ this[import_core.Symbols.modules].forEach((val) => modules.push(val));
299
+ modules.sort(([{ pkg: pkg1 }], [{ pkg: pkg2 }]) => moduleLoadOrder(pkg1) - moduleLoadOrder(pkg2)).forEach((el) => this.loadEx(...el));
300
+ if (this.isDev) this.watcher();
301
+ }
302
+ watcher() {
303
+ this[import_core.Symbols.modules].forEach(
304
+ (data) => data[0].files.forEach(
305
+ (file) => import_node_fs.default.watchFile(file, async () => {
306
+ this.ctx.logger.debug(this.ctx.format("loader.debug.reload", [data[0].pkg.name]));
307
+ this.unloadEx(data[0]);
308
+ this.loadEx(...data);
309
+ })
310
+ )
311
+ );
312
+ }
313
+ };
314
+ var runner_default = Runner;
315
+
316
+ // src/utils/log.ts
317
+ function loadInfo(info, ctx) {
318
+ process.stdout.write("Kotori Bot is loading...");
319
+ process.stdout.write(`
320
+ \u2588\u2588\u2557 \u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2557
321
+ \u2588\u2588\u2551 \u2588\u2588\u2554\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u255A\u2550\u2550\u2588\u2588\u2554\u2550\u2550\u255D\u2588\u2588\u2554\u2550\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551
322
+ \u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551
323
+ \u2588\u2588\u2554\u2550\u2588\u2588\u2557 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557\u2588\u2588\u2551
324
+ \u2588\u2588\u2551 \u2588\u2588\u2557\u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D \u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D\u2588\u2588\u2551 \u2588\u2588\u2551\u2588\u2588\u2551
325
+ \u255A\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u2550\u2550\u2550\u2550\u255D \u255A\u2550\u255D \u255A\u2550\u255D\u255A\u2550\u255D
326
+ `);
327
+ ctx.logger.record("loader base dir:", ctx.baseDir.root);
328
+ ctx.logger.info(`Kotori Bot Version: ${info.version} License: ${info.license}`);
329
+ ctx.logger.info(`Kotori Bot By ${info.author}`);
330
+ ctx.logger.info(`Copyright \xA9 2023 - 2024 ${info.author} All rights reserved`);
331
+ }
332
+ var log_default = loadInfo;
333
+
334
+ // src/service/server.ts
335
+ var import_core2 = require("@kotori-bot/core");
336
+ var import_node_http = require("http");
337
+ var import_path_to_regexp = require("path-to-regexp");
338
+ var import_express = __toESM(require("express"));
339
+ var import_ws = __toESM(require("ws"));
340
+ var Server = class extends import_core2.Service {
341
+ app;
342
+ server;
343
+ wsServer;
344
+ wsRoutes = /* @__PURE__ */ new Map();
345
+ constructor(ctx, config) {
346
+ super(ctx, config, "server");
347
+ this.app = (0, import_express.default)();
348
+ this.app.use(import_express.default.json());
349
+ this.app.use("/", (req, res, next) => {
350
+ let isWebui = false;
351
+ ctx[import_core2.Symbols.modules].forEach((module2) => {
352
+ if (isWebui) return;
353
+ if (module2[0].pkg.name === "@kotori-bot/kotori-plugin-webui") isWebui = true;
354
+ });
355
+ if (isWebui || req.url !== "/") {
356
+ next();
357
+ return;
358
+ }
359
+ res.setHeader("Content-type", "text/html");
360
+ res.send(
361
+ /* html */
362
+ `<h1>Welcome to kotori!</h1>`
363
+ );
364
+ });
365
+ this.server = (0, import_node_http.createServer)(this.app);
366
+ this.wsServer = new import_ws.default.Server({ noServer: true });
367
+ this.server.on("upgrade", (req, socket, head) => {
368
+ this.wsServer.handleUpgrade(req, socket, head, (ws) => {
369
+ this.wsServer.emit("connection", ws, req);
370
+ });
371
+ });
372
+ this.wsServer.on("connection", (ws, req) => {
373
+ let triggered = false;
374
+ for (const [template, list] of this.wsRoutes.entries()) {
375
+ if (!req.url) continue;
376
+ const result = (0, import_path_to_regexp.match)(template, { decode: decodeURIComponent })(req.url);
377
+ if (!result) continue;
378
+ if (!triggered) triggered = true;
379
+ list.forEach((callback) => {
380
+ callback(ws, Object.assign(req, { params: result.params }));
222
381
  });
382
+ }
383
+ if (!triggered) ws.close(1002);
384
+ });
385
+ }
386
+ start() {
387
+ this.server.listen(this.config.port, () => {
388
+ this.ctx.logger.label("server").info(`http server start at http://127.0.0.1:${this.config.port}`);
389
+ this.ctx.logger.label("server").info(`websocket server start at ws://127.0.0.1:${this.config.port}`);
390
+ });
391
+ }
392
+ stop() {
393
+ this.wsServer.close();
394
+ this.server.close();
395
+ }
396
+ get(path3, ...callback) {
397
+ this.app.get(path3, ...callback);
398
+ }
399
+ post(path3, ...callback) {
400
+ this.app.post(path3, ...callback);
401
+ }
402
+ patch(path3, ...callback) {
403
+ this.app.patch(path3, ...callback);
404
+ }
405
+ put(path3, ...callback) {
406
+ this.app.put(path3, ...callback);
407
+ }
408
+ delete(path3, ...callback) {
409
+ this.app.delete(path3, ...callback);
410
+ }
411
+ all(path3, ...callback) {
412
+ this.app.all(path3, ...callback);
413
+ }
414
+ use(path3, ...callback) {
415
+ if (typeof path3 === "string") this.app.use(path3, ...callback);
416
+ else this.app.use("/", path3, ...callback);
417
+ }
418
+ router = import_express.default.Router;
419
+ json = import_express.default.json;
420
+ static = import_express.default.static;
421
+ urlencoded = import_express.default.urlencoded;
422
+ wss(path3, callback) {
423
+ const list = this.wsRoutes.get(path3) || /* @__PURE__ */ new Set();
424
+ list.add(callback);
425
+ this.wsRoutes.set(path3, list);
426
+ return () => list.delete(callback);
427
+ }
428
+ };
429
+ var server_default = Server;
430
+
431
+ // src/service/file.ts
432
+ var import_core3 = require("@kotori-bot/core");
433
+ var import_node_path2 = require("path");
434
+ var File = class extends import_core3.Service {
435
+ constructor(ctx) {
436
+ super(ctx, {}, "file");
437
+ }
438
+ getDir() {
439
+ return (0, import_node_path2.join)(this.ctx.baseDir.data, ...this.ctx.identity ? this.ctx.identity.split("/") : []);
440
+ }
441
+ getFile(filename) {
442
+ return (0, import_node_path2.join)(this.getDir(), filename);
443
+ }
444
+ /* eslint-disable @typescript-eslint/no-explicit-any */
445
+ load(filename, type, init) {
446
+ return (0, import_core3.loadConfig)(this.getFile(filename), type, init);
447
+ }
448
+ /* eslint-enable @typescript-eslint/no-explicit-any */
449
+ save(filename, data, type) {
450
+ (0, import_core3.saveConfig)(this.getFile(filename), data, type);
451
+ }
452
+ create(filename, data, type) {
453
+ (0, import_core3.createConfig)(this.getFile(filename), data, type);
454
+ }
455
+ };
456
+ var file_default = File;
457
+
458
+ // src/class/loader.ts
459
+ function getBaseDir(filename, dir) {
460
+ let root = dir ? import_node_path3.default.resolve(dir) : import_node_path3.default.resolve(__dirname, "..").replace("loader", "kotori");
461
+ let filenameFull;
462
+ let count = 0;
463
+ let index = 0;
464
+ while (!filenameFull) {
465
+ if (count > 5) {
466
+ import_logger4.default.fatal(`cannot find file ${filename} `);
467
+ process.exit();
223
468
  }
224
- setPreService() {
225
- this.ctx.service('server', new server_1.default(this.ctx.extends(), { port: this.ctx.config.global.port }));
226
- this.ctx.service('file', new file_1.default(this.ctx.extends()));
469
+ const fullName = `${filename}${CONFIG_EXT[index]}`;
470
+ if (import_node_fs2.default.existsSync(import_node_path3.default.join(root, fullName))) {
471
+ filenameFull = fullName;
472
+ break;
227
473
  }
228
- loadAllModules() {
229
- this.ctx.get('runner').loadAll();
230
- const failLoadCount = this.ctx.get('runner')[core_1.Symbols.modules].size - this.loadCount;
231
- this.ctx.logger.info(this.ctx.format(`loader.modules.all${failLoadCount > 0 ? '.fail' : ''}`, [this.loadCount, failLoadCount]));
232
- this.loadAllAdapter();
233
- this.ctx.emit('ready');
474
+ if (index === CONFIG_EXT.length - 1) {
475
+ root = import_node_path3.default.join(root, "..");
476
+ index = 0;
477
+ count += 1;
478
+ } else {
479
+ index += 1;
234
480
  }
235
- loadAllAdapter() {
236
- const adapters = this.ctx[core_1.Symbols.adapter];
237
- Object.keys(this.ctx.config.adapter).forEach((botName) => {
238
- const botConfig = this.ctx.config.adapter[botName];
239
- const array = adapters.get(botConfig.extends);
240
- if (!array)
241
- return this.ctx.logger.warn(this.ctx.format('loader.adapter.notfound', [botConfig.extends, botName]));
242
- const result = array[1]?.parseSafe(botConfig);
243
- if (result && !result.value)
244
- throw new core_1.ModuleError(this.ctx.format('error.module.config_bot', [botName, result.error.message]));
245
- const bot = new array[0](this.ctx.extends({}, `${botConfig.extends}/${botName}`), result ? result.data : botConfig, botName);
246
- this.ctx.on('ready', () => bot.start());
247
- this.ctx.on('dispose', () => bot.stop());
248
- });
481
+ }
482
+ const baseDir = {
483
+ root,
484
+ modules: import_node_path3.default.join(root, "modules"),
485
+ data: import_node_path3.default.join(root, "data"),
486
+ logs: import_node_path3.default.join(root, "logs"),
487
+ config: filenameFull
488
+ };
489
+ Object.entries(baseDir).filter(([key]) => !["modules", "config"].includes(key)).forEach(([, val]) => {
490
+ if (!import_node_fs2.default.existsSync(val)) import_node_fs2.default.mkdirSync(val);
491
+ });
492
+ return baseDir;
493
+ }
494
+ function getCoreConfig(baseDir) {
495
+ try {
496
+ const result1 = import_core4.Tsu.Object({
497
+ global: import_core4.Tsu.Object({
498
+ dirs: import_core4.Tsu.Array(import_core4.Tsu.String()).default([]),
499
+ port: import_core4.Tsu.Number().default(import_core4.DEFAULT_PORT),
500
+ lang: localeTypeSchema.default(import_core4.DEFAULT_CORE_CONFIG.global.lang),
501
+ "command-prefix": import_core4.Tsu.String().default(import_core4.DEFAULT_CORE_CONFIG.global["command-prefix"])
502
+ }),
503
+ plugin: import_core4.Tsu.Object({}).index(
504
+ import_core4.Tsu.Object({
505
+ filter: import_core4.Tsu.Object({}).default({})
506
+ }).default({ filter: {} })
507
+ ).default(import_core4.DEFAULT_CORE_CONFIG.plugin)
508
+ }).default({ global: Object.assign(import_core4.DEFAULT_CORE_CONFIG.global), plugin: import_core4.DEFAULT_CORE_CONFIG.plugin }).parse((0, import_core4.loadConfig)(import_node_path3.default.join(baseDir.root, baseDir.config), baseDir.config.split(".").pop()));
509
+ return import_core4.Tsu.Object({
510
+ adapter: import_core4.Tsu.Object({}).index(
511
+ import_core4.Tsu.Object({
512
+ extends: import_core4.Tsu.String(),
513
+ master: import_core4.Tsu.Union([import_core4.Tsu.Number(), import_core4.Tsu.String()]),
514
+ lang: localeTypeSchema.default(result1.global.lang),
515
+ "command-prefix": import_core4.Tsu.String().default(result1.global["command-prefix"])
516
+ })
517
+ ).default(import_core4.DEFAULT_CORE_CONFIG.adapter)
518
+ }).parse(result1);
519
+ } catch (err) {
520
+ if (!(err instanceof import_core4.TsuError)) throw err;
521
+ import_logger4.default.fatal(`file ${baseDir.config} format error: ${err.message}`);
522
+ process.exit();
523
+ }
524
+ }
525
+ var Loader = class extends import_core4.Container {
526
+ ctx;
527
+ loadCount = 0;
528
+ constructor(options) {
529
+ super();
530
+ const filename = options && options.mode?.startsWith(DEV_MODE) ? DEV_CONFIG_NAME : BUILD_CONFIG_NAME;
531
+ const runnerConfig = {
532
+ baseDir: getBaseDir(filename, options?.dir),
533
+ options: { mode: options?.mode || BUILD_MODE },
534
+ level: options?.level || options?.mode?.startsWith(DEV_MODE) ? import_logger4.LoggerLevel.DEBUG : import_logger4.LoggerLevel.INFO
535
+ };
536
+ const ctx = new import_core4.Core(getCoreConfig(runnerConfig.baseDir));
537
+ ctx.provide("runner", new runner_default(ctx, runnerConfig));
538
+ ctx.mixin("runner", ["baseDir", "options"]);
539
+ import_core4.Container.setInstance(ctx);
540
+ ctx.provide("loader-tools", { format: (0, import_core4.formatFactory)(ctx.i18n), locale: ctx.i18n.locale.bind(ctx.i18n) });
541
+ ctx.mixin("loader-tools", ["locale", "format"]);
542
+ ctx.i18n.use(import_node_path3.default.resolve(__dirname, "../../locales"));
543
+ this.ctx = import_core4.Container.getInstance();
544
+ this.ctx.logger.trace(`options:`, options);
545
+ this.ctx.logger.trace(`runnerConfig:`, runnerConfig);
546
+ this.ctx.logger.trace(`baseDir:`, this.ctx.baseDir);
547
+ this.ctx.logger.trace(`options:`, this.ctx.options);
548
+ this.ctx.logger.trace(`config:`, this.ctx.config);
549
+ this.ctx.logger.trace(`where:`, __dirname, __filename);
550
+ this.ctx.logger.trace(`running:`, process.cwd());
551
+ }
552
+ run() {
553
+ log_default(this.ctx.pkg, this.ctx);
554
+ this.catchError();
555
+ this.listenMessage();
556
+ this.setPreService();
557
+ this.loadAllModules();
558
+ this.checkUpdate();
559
+ }
560
+ handleError(err, prefix) {
561
+ if (!(err instanceof import_core4.KotoriError)) {
562
+ if (err instanceof Error) {
563
+ this.ctx.logger.label(prefix).error(err.message, err.stack);
564
+ } else {
565
+ this.ctx.logger.label(prefix).error(err);
566
+ }
567
+ return;
249
568
  }
250
- async checkUpdate() {
251
- const { version } = this.ctx.pkg;
252
- const res = await this.ctx.http
253
- .get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/core/package.json" /* GLOBAL.UPDATE */)
254
- .catch(() => this.ctx.logger.error(this.ctx.locale('loader.tips.update.failed')));
255
- if (!res || !core_1.Tsu.Object({ version: core_1.Tsu.String() }).check(res)) {
256
- this.ctx.logger.warn(this.ctx.locale('loader.tips.update.failed'));
257
- }
258
- else if (version === res.version) {
259
- this.ctx.logger.info(this.ctx.locale('loader.tips.update.latest'));
569
+ const list = {
570
+ ServiceError: () => this.ctx.logger.label("service").warn,
571
+ ModuleError: () => this.ctx.logger.label("module").error,
572
+ UnknownError: () => this.ctx.logger.error,
573
+ DevError: () => this.ctx.logger.label("error").debug
574
+ };
575
+ list[err.name]().bind(this.ctx.logger)(err.message, err.stack);
576
+ }
577
+ catchError() {
578
+ process.on("uncaughtExceptionMonitor", (err) => this.handleError(err, "sync"));
579
+ process.on("unhandledRejection", (err) => this.handleError(err, "async"));
580
+ process.on("SIGINT", () => process.exit());
581
+ this.ctx.logger.debug(this.ctx.locale("loader.debug.info"));
582
+ }
583
+ listenMessage() {
584
+ this.ctx.on("connect", (data) => {
585
+ const { type, mode, normal, address: addr, adapter } = data;
586
+ let msg;
587
+ if (type === "connect") {
588
+ switch (mode) {
589
+ case "ws":
590
+ msg = this.ctx.format(`loader.bots.${normal ? "connect" : "reconnect"}`, [addr]);
591
+ break;
592
+ case "ws-reverse":
593
+ msg = this.ctx.format(`loader.bots.${normal ? "start" : "restart"}`, [addr]);
594
+ break;
595
+ default:
596
+ msg = this.ctx.format("loader.bots.ready", [addr]);
260
597
  }
261
- else {
262
- this.ctx.logger.warn(this.ctx.format('loader.tips.update.available', [version, res.version, "https://github.com/kotorijs/kotori" /* GLOBAL.REPO */]));
598
+ } else {
599
+ switch (mode) {
600
+ case "ws":
601
+ msg = this.ctx.format(`loader.bots.disconnect${normal ? "" : ".error"}`, [addr]);
602
+ break;
603
+ case "ws-reverse":
604
+ msg = this.ctx.format(`loader.bots.stop${normal ? "" : ".error"}`, [addr]);
605
+ break;
606
+ default:
607
+ msg = this.ctx.format("loader.bots.dispose", [addr]);
263
608
  }
609
+ }
610
+ adapter.ctx.logger[normal ? "info" : "warn"](msg);
611
+ });
612
+ this.ctx.on("status", ({ status, adapter }) => adapter.ctx.logger.info(status));
613
+ this.ctx.on("ready_module", (data) => {
614
+ if (typeof data.instance !== "object") return;
615
+ const pkg = data.instance.name ? this.ctx.get("runner")[import_core4.Symbols.modules].get(data.instance.name) : void 0;
616
+ if (!pkg) return;
617
+ this.loadCount += 1;
618
+ const { name, version, author, peerDependencies } = pkg[0].pkg;
619
+ this.ctx.logger.info(
620
+ this.ctx.format("loader.modules.load", [name, version, Array.isArray(author) ? author.join(",") : author])
621
+ );
622
+ const requiredVersion = peerDependencies["kotori-bot"];
623
+ if (requiredVersion.includes("workspace") || SUPPORTS_VERSION.exec(requiredVersion) || requiredVersion.includes(this.ctx.pkg.version))
624
+ return;
625
+ if (SUPPORTS_HALF_VERSION.exec(requiredVersion)) {
626
+ this.ctx.logger.warn(this.ctx.format("loader.modules.incomplete", [requiredVersion]));
627
+ } else {
628
+ this.ctx.logger.error(this.ctx.format("loader.modules.unsupported", [requiredVersion]));
629
+ }
630
+ });
631
+ }
632
+ setPreService() {
633
+ this.ctx.service("server", new server_default(this.ctx.extends(), { port: this.ctx.config.global.port }));
634
+ this.ctx.service("file", new file_default(this.ctx.extends()));
635
+ }
636
+ loadAllModules() {
637
+ this.ctx.get("runner").loadAll();
638
+ const failLoadCount = this.ctx.get("runner")[import_core4.Symbols.modules].size - this.loadCount;
639
+ this.ctx.logger.info(
640
+ this.ctx.format(`loader.modules.all${failLoadCount > 0 ? ".failed" : ""}`, [this.loadCount, failLoadCount])
641
+ );
642
+ this.loadAllAdapter();
643
+ this.ctx.emit("ready");
644
+ }
645
+ loadAllAdapter() {
646
+ const adapters = this.ctx[import_core4.Symbols.adapter];
647
+ Object.keys(this.ctx.config.adapter).forEach((botName) => {
648
+ const botConfig = this.ctx.config.adapter[botName];
649
+ const array = adapters.get(botConfig.extends);
650
+ if (!array)
651
+ return this.ctx.logger.warn(this.ctx.format("loader.adapters.notfound", [botConfig.extends, botName]));
652
+ const result = array[1]?.parseSafe(botConfig);
653
+ if (result && !result.value)
654
+ throw new import_core4.ModuleError(this.ctx.format("error.module.config_bot", [botName, result.error.message]));
655
+ const bot = new array[0](
656
+ this.ctx.extends({}, `${botConfig.extends}/${botName}`),
657
+ result ? result.data : botConfig,
658
+ botName
659
+ );
660
+ this.ctx.on("ready", () => bot.start());
661
+ this.ctx.on("dispose", () => bot.stop());
662
+ });
663
+ }
664
+ async checkUpdate() {
665
+ const { version } = this.ctx.pkg;
666
+ const res = await this.ctx.http.get("https://hotaru.icu/api/agent/?url=https://raw.githubusercontent.com/kotorijs/kotori/master/packages/core/package.json" /* UPDATE */).catch(() => this.ctx.logger.error(this.ctx.locale("loader.tips.update.failed")));
667
+ if (!res || !import_core4.Tsu.Object({ version: import_core4.Tsu.String() }).check(res)) {
668
+ this.ctx.logger.warn(this.ctx.locale("loader.tips.update.failed"));
669
+ } else if (version === res.version) {
670
+ this.ctx.logger.info(this.ctx.locale("loader.tips.update.latest"));
671
+ } else {
672
+ this.ctx.logger.warn(this.ctx.format("loader.tips.update.available", [version, res.version, "https://github.com/kotorijs/kotori" /* REPO */]));
264
673
  }
265
- }
266
- exports.Loader = Loader;
267
- exports.default = Loader;
674
+ }
675
+ };
676
+ var loader_default = Loader;
677
+ // Annotate the CommonJS export names for ESM import in node:
678
+ 0 && (module.exports = {
679
+ Loader
680
+ });