packages/message-loader/SKILL.md
Code generation and contribution rules for @hile/message-loader. Use when editing this package or when the user asks about @hile/message-loader patterns or API.
npx skillsauth add cevio/hile message-loaderInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
本文档是面向 AI 编码模型和人类开发者的 代码生成规范,阅读后应能正确地使用本库编写符合架构规则的代码。
@hile/message-loader 是一个 基于文件系统的消息路由加载器,将目录结构映射为路由表,实现请求/分发模式的消息处理。
它与 @hile/http 的 Loader 类似,但面向消息通信场景(WebSocket、IPC、Worker Threads 等),而非 HTTP 请求。
核心流程:
文件系统 路由表 分发
messages/ ┌──────────────────────┐
├── index.msg.ts ──────► │ / │
├── hello.msg.ts ──────► │ /hello │ ──► dispatch(url, data, extras?)
├── users/ │ │ 找到路由 → 执行 fn
│ ├── index.msg.ts ─────► │ /users │ 未找到 → NotFoundException
│ └── [id].msg.ts ─────► │ /users/:id │
└── ... └──────────────────────┘
+ register('/-/x', fn) ───► │ /-/x │
关键设计:
*.msg.{ts,js,tsx,jsx} 文件自动注册为消息路由users/index.msg.ts 映射为 /users[id].msg.ts 转换为 :id,参数通过处理器入参对象的 params 传递prefix 添加统一前缀(如 /-)register(routePath, fn) 运行时注册单条路由,返回注销函数(与 load 并列使用)dispatch 扩展入参 — dispatch(path, data, extras) 将 extras 展开并入 fn 的参数(如传入 { client } 供 handler 使用)load() 返回注销函数,调用后移除该次加载的所有路由;register() 各自返回单路由注销rou3 作为路由匹配引擎import { MessageLoader, defineMessage, NotFoundException, type MessageLoaderProps, type MessageRegisterProps, type MessageFunction } from '@hile/message-loader';
interface MessageLoaderProps {
suffix?: string; // 文件后缀标记,默认 'msg'
defaultSuffix?: string; // 折叠后缀,默认 '/index'
prefix?: string; // 路径前缀,默认 ''
}
type MessageFunction<T = any, E extends Record<string, any> = {}> = (data: {
params?: Record<string, string>;
data: T;
url: string;
} & E) => any;
interface MessageRegisterProps<T = any> {
id: number;
fn: MessageFunction<T>;
}
class MessageLoader {
constructor(props: MessageLoaderProps);
load(directory: string): Promise<() => void>;
register<T = any, E extends Record<string, any> = {}>(routePath: string, fn: MessageFunction<T, E>): () => void;
dispatch(path: string, data: any, extras?: Record<string, any>): Promise<any>;
}
function defineMessage<T = any>(fn: MessageFunction<T>): MessageRegisterProps<T>;
每个 *.msg.ts 文件需要 export default 一个 MessageRegisterProps 对象,推荐使用 defineMessage 工厂函数:
// messages/hello.msg.ts
import { defineMessage } from '@hile/message-loader';
export default defineMessage(async ({ data, params, url }) => {
return { greeting: `Hello, ${data.name}!` };
});
// messages/users/[id].msg.ts
import { defineMessage } from '@hile/message-loader';
export default defineMessage(async ({ params, data }) => {
const user = await db.findUser(params!.id);
return user;
});
import { MessageLoader } from '@hile/message-loader';
import path from 'node:path';
const loader = new MessageLoader({
suffix: 'msg',
prefix: '/-',
});
await loader.load(path.resolve(__dirname, 'messages'));
const result = await loader.dispatch('/-/hello', { name: 'world' });
register(与 load 并列)const loader = new MessageLoader({ prefix: '/-' });
const off = loader.register('/-/status', async ({ data }) => ({ ok: true, data }));
// await loader.dispatch('/-/status', { ping: 1 });
// off(); // 仅移除该路由
register 与 defineMessage/load 使用不同的内部 id 分配器,id 仅用于内部区别 handler,不影响 URL 匹配。
dispatch 与 extras// 第三参会展开合并到 handler 入参,例如注入连接上下文:
await loader.dispatch('/-/find', payload, { client: wsClient });
import { MessageWs } from '@hile/message-ws';
import { MessageLoader } from '@hile/message-loader';
const loader = new MessageLoader({ suffix: 'msg', prefix: '/-' });
await loader.load(path.resolve(__dirname, 'messages'));
class AppWs extends MessageWs {
protected async exec(data: { url: string; data: any }): Promise<any> {
return loader.dispatch(data.url, data.data);
}
}
| 规则 | 说明 |
|------|------|
| 文件必须 export default | 缺少默认导出的文件会被静默跳过 |
| 默认导出必须是 MessageRegisterProps | 推荐使用 defineMessage() 创建 |
| fn 接收 { params, data, url, ...extras } | params/data/url 为固定键;extras 来自 dispatch 第三参 |
| dispatch 返回 Promise | 即使 fn 是同步函数也会被 Promise.resolve 包装 |
| register 返回单路由注销 | 仅移除该路径注册;与 load() 批处理注销独立 |
| dispatch 可选第三参 extras | 与 { params, data, url } 合并传入 fn,勿与内置键无意冲突 |
| 路径未匹配时 dispatch 抛出错误 | NotFoundException(status: 'NOT_FOUND') |
| load 返回注销函数 | 调用后移除该次 load 注册的全部路由 |
// ❌ 不使用 defineMessage,手动构造对象时遗漏 id
export default { fn: () => 'hello' };
// ✅ 使用 defineMessage 自动分配 id
export default defineMessage(() => 'hello');
// ❌ dispatch 时未 await
const result = loader.dispatch('/hello', data);
console.log(result); // Promise 对象
// ✅ await dispatch 结果
const result = await loader.dispatch('/hello', data);
// ❌ 文件名后缀不匹配 suffix 配置
// suffix 配置为 'msg',但文件命名为 hello.handler.ts
export default defineMessage(() => 'hello');
// ✅ 文件名后缀与 suffix 一致
// hello.msg.ts
export default defineMessage(() => 'hello');
// ❌ 在 dispatch 时使用错误的路径(未加 prefix)
const loader = new MessageLoader({ prefix: '/-' });
loader.dispatch('/hello', data); // 未找到
// ❌ extras 与内置键同名,覆盖语义
await loader.dispatch('/x', {}, { data: 'oops' });
// ✅ extras 使用不与 params/data/url 冲突的键(如 client、auth)
await loader.dispatch('/x', { a: 1 }, { client });
devops
@hile/model: defineModel/loadModel 定义和消费模型;services 依赖注入;pipeline 中间件链;每次 loadModel 重新执行 main
development
Code generation and contribution rules for @hile/micro-dynamic-configs. Use when editing this package or when the user asks about dynamic config patterns or API.
development
Code generation and contribution rules for @hile/cache. Use when editing this package or when the user asks about @hile/cache API, types, patterns, or features.
development
Code generation and contribution rules for @hile/micro. Use when editing this package or when the user asks about @hile/micro API, types, patterns, or features.