# 超级美眉-通用核心 **Repository Path**: qiuwenwu91/mm_corejs ## Basic Information - **Project Name**: 超级美眉-通用核心 - **Description**: 这是一个通用的nodeJS模块,提供给其他模块继承使用的 Base / Lifecycle / Manage / Mod 基类。 - **Primary Language**: NodeJS - **License**: ISC - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-09-26 - **Last Updated**: 2026-10-02 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # mm_corejs 通用 Node.js 模块基类库。提供统一的**实体基类、生命周期状态机、管理器集合、模块执行入口**,并内置日志、参数校验与终端着色工具,供其它业务模块继承使用。 - 入口:`index.js`(CommonJS) - 运行时依赖:无 - 测试覆盖率:100%(见 `jest.config.js` 阈值) ## 特性 - **Lifecycle**:最底层的生命周期机制——状态机 + 模板方法 + 事件。零依赖(不持有 `server` / `config` / `logger`,自身不输出日志),内核与实体都能直接继承。 - **Base**:在 `Lifecycle` 之上提供 `server`、`config`、`logger` 三项实体能力,配置深合并并与静态配置隔离,统一日志前缀与错误出口。 - **Manager**:在实体之上提供「受管集合」(私有字段,经 `set`/`get` 等方法访问)与「全局服务容器」两套容器。 - **Mod**:模块基类,内置声明式参数校验、选项默认值提取、分层选项合并与统一的执行入口。 - **Logger / Validate / colors**:开箱即用的日志、JSON Schema 子集校验、ANSI 着色工具。 ## 安装 ```bash npm install mm_corejs ``` ## 环境要求 - Node.js >= 16(依赖类静态字段与私有字段语法) - 模块规范:CommonJS ## 继承关系 ``` EventEmitter └─ Lifecycle(零依赖:状态机 + 模板方法 + 事件) ├─ Base(server / config / logger) │ ├─ Manager │ └─ Mod └─ 框架内核(如 example/server.js 的 Server,不在 npm 包内) ``` 状态机位于最底层,是因为内核与实体都要用它;`Lifecycle` 不依赖 `Base`,无需注入 `server` 即可继承。 ## 快速开始 `Base` 系列只依赖注入的 `server` 对象,不绑定具体框架。下面是一个可独立运行的最小示例。 ```js 'use strict'; const { Mod } = require('mm_corejs'); /** 最小 Server:满足 Base / Manager 对框架的契约即可 */ class Server { constructor() { this.services = new Map(); this.managers = new Map(); } getService(name) { return this.services.get(name); } hasService(name) { return this.services.has(name); } setService(name, instance) { this.services.set(name, instance); return this; } getManager(name) { return this.managers.get(name); } hasManager(name) { return this.managers.has(name); } } /** 自定义模块:只实现 main,校验与合并交给基类 */ class EchoMod extends Mod { static config = { name: 'echo', title: '回声模块', params: { type: 'object', required: ['text'], properties: { text: { type: 'string', min_length: 1 } } }, options: { type: 'object', properties: { prefix: { type: 'string', default: 'echo: ' } } } }; async main(ctx, params, options) { return `${options.prefix}${params.text}`; } } (async () => { const server = new Server(); const echo = new EchoMod(server, { name: 'echo' }); echo.on('error', (err) => console.error(err.message)); await echo.init(); await echo.start(); // prefix 取自 config.options.properties.prefix.default,输出:echo: 你好 console.log(await echo.run({}, { text: '你好' })); // 单次调用覆盖默认值,输出:你好 console.log(await echo.run({}, { text: '你好' }, { prefix: '' })); await echo.stop(); await echo.dispose(); })(); ``` > 上面的 `Server` 只实现库要求的最小契约。带**生命周期钩子、插件机制与 `init`/`start`/`stop`/`dispose` 编排**的示例容器见 [`example/server.js`](./example/server.js);该编排属框架(内核)层能力,非本库要求。 ## API ### Base `new Base(server, config = {})` 继承 `Lifecycle`,因此天然具备状态机、模板方法与事件;在此之上提供 `server` / `config` / `logger`,并补齐 `Lifecycle` 留空的 `log` / `_injectLogger` / `_label` 三个缝隙。 | 成员 | 说明 | | --- | --- | | `static config` | 静态默认配置:`{ name: '', title: '', description: '' }`。子类声明时只需写差异字段,继承链上各层配置会自动深合并 | | `config` | 实例配置,合并顺序:`静态默认值(含继承链各层)-> 已存在配置 -> 传入 config`。**深合并**(普通对象递归、数组整体覆盖),结果与各层静态配置均不共享引用 | | `is(other)` | 是否为同一实例 | | `toString()` | 返回 `[类名:name]` 或 `[类名]` | | `getServer()` / `setServer(server)` | 读取 / 设置框架引用(`server` 缺失时抛 `TypeError`) | | `getLogger()` / `setLogger(logger)` | 读取 / 设置日志器(未提供时新建 `Logger`) | | `log(type, message, ...args)` | 按类型记录日志,自动追加 `_label()` 前缀;未知级别回落为 `info`(覆盖 `Lifecycle` 的空实现) | | `_label()` | 实体标签 `[类名.name]`,用于日志前缀与错误文案(覆盖 `Lifecycle` 的 `[类名]`;子类不应覆盖) | | `setConfig(config)` | 深合并并更新配置,返回 `this`(子类不应覆盖) | | `_injectLogger()` | 未设置日志器时,尝试从 `server.getService('logger')` 注入;服务为空或返回自身时跳过(覆盖 `Lifecycle` 的空实现;子类不应覆盖) | | `_ensureLogger()` | 确保日志器可用:缺失时回落到默认 `Logger`,返回日志器实例 | | `_emitError(err)` | 统一错误出口(继承自 `Lifecycle`):有 `error` 监听器时触发 `error` 事件,否则降级为 `error` 日志,**绝不抛出** | | `_initOptions()` | 声明式配置:从 `config.options` 提取默认值写入 `this.options`;未配置 `config.options` 时删除 `this.options` 并返回 `null` | | `_pickDefaults(properties)` | 声明式配置:从 JSON Schema 的 `properties` 中收集各属性的 `default`;无 `properties` 时返回空对象 | `log` 的 `type` 取值为 `Logger` 的方法名:`info` / `debug` / `error` / `success` / `warn`。 ### Lifecycle `new Lifecycle()` 最底层的生命周期机制:**零依赖**(仅继承 `EventEmitter`),不持有 `server` / `config` / `logger`,自身不输出任何日志。因此框架内核(如示例 `Server`)与实体(`Base` 及其子类)都能直接继承同一套机制。 状态流转: ``` created -> initing -> inited -> starting -> started -> stopping -> stopped -> disposing -> disposed ``` 任意步骤抛出异常都会进入 `error` 状态。状态流转受合法流转表约束,非法跳转会抛 `Error`。 静态常量 `Lifecycle.STATES`:`created` / `initing` / `inited` / `starting` / `started` / `stopping` / `stopped` / `disposing` / `disposed` / `error`。 模板方法(子类不应覆盖): | 方法 | 前置状态 | 调用钩子 | 说明 | | --- | --- | --- | --- | | `async init()` | 必须为 `created`,否则抛错 | `onInit` | 初始化 | | `async start()` | 必须为 `inited`,否则抛错 | `onStart` | 启动 | | `async stop()` | 仅 `started` 时生效,否则直接返回 | `onStop` | 停止 | | `async dispose()` | 已 `disposed` 时直接返回 | `onDispose` | 释放。处于运行态时**先执行 `stop`**,保证 `onStop` 被调用 | 钩子(子类覆盖):`async onInit()` / `async onStart()` / `async onStop()` / `async onDispose()`。 可覆盖缝隙(默认空实现,供上层补齐): | 成员 | 说明 | | --- | --- | | `log(type, message, ...args)` | 日志输出,默认空实现(自身零输出);`Base` 覆盖为真实日志 | | `_injectLogger()` | 日志注入,默认空实现;`Base` 覆盖为从全局服务注入 | | `_label()` | 错误文案标签,默认返回 `[类名]`;`Base` 覆盖为 `[类名.name]` | > 从运行态直接调用 `dispose()` 的事件序列为 `stop -> stopped -> dispose -> disposed`;`stop` 失败不阻塞释放。 状态查询: | 成员 | 说明 | | --- | --- | | `state`(getter) | 当前状态 | | `getState()` | 当前状态 | | `isState(state)` | 是否处于指定状态 | | `isRunning()` | 是否处于 `started` | | `_setState(state)` | 内部状态设置,校验合法流转,非法时抛 `Error` 并保持原状态;合法时返回 `this` | | `_assertState(expected, action)` | 状态断言,不符时抛 `Error` | 事件: | 事件 | 参数 | 触发时机 | | --- | --- | --- | | `init` / `inited` | 无 | 初始化前后 | | `start` / `started` | 无 | 启动前后 | | `stop` / `stopped` | 无 | 停止前后 | | `dispose` / `disposed` | 无 | 释放前后 | | `error` | `err` | 钩子抛错时 | > `error` 事件统一由 `_emitError` 触发:**已注册监听器**时事件被触发,未注册时降级为 `log('error', ...)`(`Lifecycle` 的 `log` 是空实现,故此时静默;`Base` 则写入日志),因此不会再导致进程中断(钩子抛出的异常本身仍会向调用方抛出)。 子类示例: ```js 'use strict'; const { Lifecycle } = require('mm_corejs'); class Worker extends Lifecycle { async onInit() { this.tasks = []; } async onStart() { this.tasks.push('heartbeat'); } async onStop() { this.tasks = []; } async onDispose() { this.tasks = null; } } ``` > `Lifecycle` 自身不输出日志、也不持有配置。需要日志与配置能力时改继承 `Base`(`Base` 会把 `log` / `_injectLogger` / `_label` 补上,并携带 `server` / `config`)。 ### Manager `new Manager(server, config = {})` 继承 `Base`,扩展静态配置为 `{ name, title, description, version, directories, options }`。 - 构造时要求 `config.name` 非空,否则抛 `TypeError`。 - 新增实例属性:`options`(选项默认值集合,未配置 `config.options` 时该属性不存在)。 - **受管集合为私有字段**(资源表与释放函数表),无法直接读写;写入走 `set` / `del` / `clear`,读取走 `get` / `has` / `keys` / `values` / `entries` / `size`。这样可保证两张表的键始终一致。 选项默认值(继承自 `Base`): | 方法 | 说明 | | --- | --- | | `_initOptions()` | 从 `config.options` 提取默认值写入 `this.options`;未配置 `config.options` 时删除 `this.options` 并返回 `null` | | `_pickDefaults(properties)` | 从 `properties` 中收集各属性的 `default`,无 `properties` 时返回空对象 | 来源优先级:`config.options.defaults` > `config.options.properties[key].default`。取 `defaults` 时会浅拷贝,不会与静态配置共享引用。 受管集合(私有字段,仅可通过下列方法访问): | 方法 | 说明 | | --- | --- | | `set(key, value, opts?)` | 写入资源;键已存在时抛 `Error`;`opts.dispose` 为函数时登记释放逻辑 | | `get(key)` | 读取资源 | | `has(key)` | 是否存在 | | `keys()` / `values()` / `entries()` | 键 / 值 / 键值对数组 | | `size()` | 资源数量 | | `async del(key)` | 释放并删除资源,`dispose` 内抛错时经 `_emitError` 通知,返回删除是否成功 | | `async clear()` | 逆序执行全部释放逻辑并清空集合;`onDispose` 默认调用它 | 全局服务容器与框架交互: | 方法 | 说明 | | --- | --- | | `getService(name)` | 读取全局服务(`server.getService`) | | `hasService(name)` | 全局服务是否存在 | | `regService(name, instance)` | 注册全局服务,返回 `this` | | `getManager(name)` | 读取全局管理器(`server.getManager`) | | `hasManager(name)` | 全局管理器是否存在 | 事件:`set(key, value)`、`del(key)`、`clear`、`error(err)`。 示例: ```js 'use strict'; const { Manager, Logger } = require('mm_corejs'); class LoggerManager extends Manager { constructor(server, config = {}) { super(server, config); // 此刻全局服务表里尚无 logger(本模块自身就是 logger 的来源),无法自动注入; // 而 init() 会先打日志、再调 onInit,故必须在构造阶段手动设置 this.setLogger(new Logger()); } async onInit() { this.regService('logger', this.getLogger()); this.set('level', this.config.level || 'info'); this.set('sink', 'console', { dispose: () => this.log('info', '日志输出端已关闭') }); } } ``` ### Mod `new Mod(server, config = {})` 继承 `Base`,扩展静态配置为 `{ name, title, description, version, directories, options, params }`。 - 构造时要求 `config.name` 非空,否则抛 `TypeError`。 - 新增实例属性:`options`(选项默认值集合,未配置 `config.options` 时该属性不存在)。 | 方法 | 说明 | | --- | --- | | `async run(ctx, params, options)` | 统一执行入口(模板方法,子类不应覆盖),内部调用 `_run` | | `async _run(ctx, params, options)` | 模板方法,子类不应覆盖。未 `started` 时抛错;依次执行 `_mergeOptions` -> `_validateOptions` -> `_validateParams` -> `main` | | `async main(ctx, params, options)` | 业务逻辑,默认返回 `null`,**子类覆盖** | | `async _mergeOptions(options, ctx)` | 合并选项,默认把 `this.options` 与传入 `options` 浅合并(后者覆盖前者);子类覆盖可实现分层优先级 | | `async _validate(params, config)` | 用 `Validate` 校验,`config` 为空时直接放行并返回空字符串 | | `async _validateOptions(options)` | 按 `config.options` 校验合并后的选项,失败抛 `Error` | | `async _validateParams(params)` | 按 `config.params` 校验调用参数,失败抛 `Error` | | `_initOptions()` | 继承自 `Base`。从 `config.options` 提取默认值写入 `this.options`;未配置 `config.options` 时删除 `this.options` 并返回 `null` | | `_pickDefaults(properties)` | 继承自 `Base`。从 `properties` 中收集各属性的 `default`,无 `properties` 时返回空对象 | `config.options` 同时承担两个职责:JSON Schema 子集校验(`type` / `required` / `properties` 等)与默认值声明。校验器会忽略 `defaults` / `default` 等未知关键字,因此两者可以写在同一个对象里。默认值来源优先级:`config.options.defaults` > `config.options.properties[key].default`。 执行顺序:`run` -> `_run` -> 运行状态检查 -> `_mergeOptions` -> `_validateOptions` -> `_validateParams` -> `main`。 分层合并示例: ```js async _mergeOptions(options = {}, ctx = {}) { // 优先级由低到高:模块默认值 < 模块配置 < 上下文用户配置 < 单次调用 return Object.assign({}, DEFAULT_OPTIONS, this.config.options.defaults, ctx.user_options || {}, options); } ``` ### Logger | 方法 | 输出 | 颜色 | | --- | --- | --- | | `info(msg, ...args)` | `console.info` | 蓝 | | `debug(msg, ...args)` | `console.debug` | 白 | | `error(msg, ...args)` | `console.error` | 红 | | `success(msg, ...args)` | `console.log` | 绿 | | `warn(msg, ...args)` | `console.warn` | 黄 | 颜色开关按输出流分别检测:`info` / `debug` / `success` 走 stdout,按 `process.stdout` 判断;`error` / `warn` 走 stderr,按 `process.stderr` 判断。因此当 stderr 被重定向到文件或管道时,错误与警告不会残留 ANSI 转义序列。 ### Validate `new Validate(config = {})`,`check(params)` 通过时返回空字符串,否则返回**首个**错误信息。 支持的关键字: | 关键字 | 适用类型 | 说明 | | --- | --- | --- | | `type` | 任意 | `null` / `array` / `object` / `string` / `number` / `integer` / `boolean` | | `enum` | 任意 | 值必须在给定数组中 | | `const` | 任意 | 值必须严格等于给定值 | | `min_length` / `max_length` | `string` | 字符串长度上下限(注意:非 JSON Schema 标准的 `minLength`) | | `minimum` / `maximum` | `number` | 数值上下限 | | `pattern` | `string` | 正则匹配,字符串形式 | | `required` | `object` | 必填字段列表 | | `properties` | `object` | 逐字段递归校验 | | `items` | `array` | 逐元素递归校验 | 校验按关键字顺序短路返回首个错误;`number` 会排除 `NaN` 与 `Infinity`;错误信息带字段路径(如 `user.name: required missing`)。 ```js 'use strict'; const { Validate } = require('mm_corejs'); const validate = new Validate({ type: 'object', required: ['name', 'age'], properties: { name: { type: 'string', min_length: 2 }, age: { type: 'integer', minimum: 0, maximum: 150 }, role: { type: 'string', enum: ['admin', 'user'] }, email: { type: 'string', pattern: '^[^@]+@[^@]+$' }, tags: { type: 'array', items: { type: 'string' } } } }); validate.check({ name: 'qww', age: 30 }); // '' validate.check({ name: 'q', age: 30 }); // 'name: min_length expected >= 2 but got 1' ``` ### colors | 导出 | 说明 | | --- | --- | | `colors` | 已按 `process.stdout` 检测好可用性的着色器实例 | | `createColors(enabled)` | 按指定开关创建着色器 | | `forStream(stream)` | 针对指定流检测并创建着色器 | | `supportsColor(stream)` | 判断流是否应启用颜色 | | `windowsSupportsVT()` | 判断 Windows 终端是否支持 ANSI/VT | | `STYLES` | ANSI 样式表(名称 -> `[开启码, 关闭码]`) | 颜色开关遵循 `NO_COLOR`(禁用)、`FORCE_COLOR`(强制,`0` / `false` 表示禁用)、`TERM=dumb`、非 TTY 以及 CI 环境等常见约定。 着色器实例成员: | 成员 | 说明 | | --- | --- | | `enabled` | 当前是否启用颜色 | | 样式名 | `reset` / `bold` / `dim` / `italic` / `underline` / `blink` / `inverse` / `hidden` / `strikethrough`,前景色 `black`~`white`、`gray`、`redBright` 等,背景色 `bgBlack`~`bgWhite`、`bgGray` | | `ansi256(code)` | 生成 256 色前景着色函数 | | `rgb(r, g, b)` | 生成真彩色前景着色函数 | | `bgRgb(r, g, b)` | 生成真彩色背景着色函数 | | `style(names)` | 组合样式,如 `c.style('bold underline cyan')('文本')` | ```js 'use strict'; const { colors: lib } = require('mm_corejs'); const c = lib.colors; console.log(c.red('错误')); console.log(c.style('bold underline cyan')('组合样式')); console.log(c.rgb(255, 128, 0)('真彩色')); ``` ## Server 契约 库不绑定具体框架,仅要求注入的 `server` 提供以下方法: | 方法 | 使用方 | | --- | --- | | `getService(name)` | `Base._injectLogger`、`Manager.getService` | | `hasService(name)` | `Manager.hasService` | | `setService(name, instance)` | `Manager.regService` | | `getManager(name)` | `Manager.getManager` | | `hasManager(name)` | `Manager.hasManager` | > 只要满足上表即可承载基类。示例容器 [`example/server.js`](./example/server.js) 直接继承本库的 `Lifecycle`,在此之上额外提供了 `addManager`、插件机制(`use`)与生命周期编排(`hook`)——这些属**框架(内核)层职责**,并非本库对 server 的要求,真实项目可按需取舍或替换为完整内核。 ### 示例容器 `example/server.js` 示例容器**直接 `extends Lifecycle`**——状态机、模板方法与事件复用基类,自身只保留容器与编排。在最小契约之外,它把「像服务端」的部分补齐,便于脱离框架独立演示: | 分类 | 成员 | 说明 | | --- | --- | --- | | 容器 | `getService` / `hasService` / `setService`、`getManager` / `hasManager` / `addManager` | 前五项即上表契约 | | 状态 | `state`、`isState(state)`、`isRunning()` | 继承自 `Lifecycle`,含 `initing` / `starting` / `stopping` / `disposing` / `error` 等中间态 | | 插件 | `use(plugin)`、`unuse(name)`、`getPlugin(name)`、`hasPlugin(name)`、`listPlugins()` | 插件为普通对象:`{ name, apply(server)?, init/start/stop/dispose(server)? }`;回调名即阶段名(刻意避开 `Lifecycle` 的 `onXxx`),`apply` 注册期执行并可直接 `setService` / `addManager` / `hook`。`unuse` 会执行插件 `dispose` 回调、摘除其钩子并移除登记 | | 钩子 | `hook(name, fn)` | `name ∈ init/start/stop/dispose`,`fn(server)` 可异步,按注册序 `await`(停止/释放逆序)。钩子条目记录来源(`source`),插件卸载时可精确摘除自己的钩子 | | 中间件 | `useMiddleware(name, fn)`、`listMiddlewares()` | 调用链中间件,签名 `async (call, next)`;`call` 为 `{ name, mod, ctx, params, options, result }`,`await next()` 向内继续,不调 `next` 或抛错即中断。返回值由内核经 `call.result` 统一持有,中间件无需 `return next()` | | 调用 | `runMod(name, ctx, params, options)` | 按名调用模块的统一入口,**所有调用都经过中间件链**。守卫先于中间件:未 `started`、模块不存在或不具备 `run` 时直接抛错 | | 编排 | `init()` / `start()` / `stop()` / `dispose()` | 继承自 `Lifecycle` 的模板方法;在 `onInit` / `onStart` / `onStop` / `onDispose` 钩子里驱动所有钩子与受管 managers(启动按注册序,停止/释放逆序) | | 事件 | `init` / `inited` / `start` / `started` / `stop` / `stopped` / `dispose` / `disposed` / `error` | 同步观察;`hook` 负责可异步的编排点,事件负责同步通知 | | 日志 | `log(type, message, ...args)` | 内核未配置日志器,仅 `error` 级别写控制台,其余静默 | > 健壮性:`use()` 在 `apply` 或挂钩子抛错时**回滚**登记与已挂钩子,不留半注册状态;`unuse()` 中插件 `dispose` 抛错经 `_emitError` 通知,不阻塞卸载;`onDispose` 结束时清空钩子表、插件表与中间件表。 #### 鉴权与安全的落点 `getService` / `setService` / `addManager` 是**受信组件之间的内部接线**,不是攻击面,因此**不挂拦截**——而且 `Base` 在构造期就同步调用 `server.getService('logger')`,一旦该路径改为异步钩子,同步契约即被破坏。给服务查表加鉴权,等于给自家客厅门上锁而窗户敞开。 真正的攻击面是**外部输入进入系统之处**,即调用模块时的 `ctx` 与 `params`。示例容器把调用收敛到 `runMod`,横切逻辑(鉴权、审计、限流)统一由中间件实施: ```js server.useMiddleware('auth', async (call, next) => { if (!call.ctx.user || !call.ctx.user.token) { throw new Error('unauthorized: invalid token'); } await next(); }); await server.runMod('llm', { user: { token } }, { prompt: '...' }); // 拦截点在此生效 ``` > **代价**:业务代码不得直连 `mod.run()`,否则绕过中间件。拦截点统一的前提,是调用路径统一。 > 编排逻辑集中在示例容器内,`lib/` 的基类不涉及依赖排序、注册表增删、信号处理等内核职责(边界与 [`doc/基类适用性分析.md`](./doc/基类适用性分析.md) 一致)。 ## 示例 源码仓库的 `example/` 目录提供完整可运行示例(随仓库分发,不包含在 npm 包内): ```bash npm run demo ``` | 文件 | 内容 | | --- | --- | | `example/server.js` | 示例框架容器:服务契约 + 生命周期钩子 + 插件机制 + `init`/`start`/`stop`/`dispose` 编排 | | `example/logger_manager.js` | `Manager` 的全局服务注册与私有受管集合 | | `example/llm_mod.js` | `Mod` 的声明式校验与分层配置合并 | | `example/lifecycle_demo.js` | `Lifecycle` 零依赖状态机与钩子 | | `example/demo.js` | 汇总演示的入口 | ## 开发 ```bash npm test # jest --coverage,覆盖率阈值为 100% npx eslint . # 命名规范与 JSDoc 检查 ``` ## 许可 [ISC](./LICENSE)