feat: 生命周期广播事件与链式钩子设施(ADR-006)
- contracts:补齐生命周期广播事件(订阅、下载、整理结束、刮削、字幕、作品关注 / 元数据 / 季集结构、站点可达性) 的类型与 payload schema,LIFECYCLE_EVENTS 清单与 lifecycleKey 幂等键;HOOK_POINTS 链式钩子挂点; FEATURES 功能清单与 SLOT_POSITIONS 前端插槽位置 - outbox 中继支持一条事件路由给多个订阅方(其余订阅方用 <键>#<任务名>);生命周期事件允许多模块订阅, 没有订阅方时也被中继认领并镜像到 SSE - 服务端 HookRegistry:按注册顺序执行、rewrite / intercept 两类、单钩子超时与异常隔离、无注册零开销; ServerModule 增加 manifest 与 registerHooks,bootstrap 装配 ctx.hooks - ADR-006 与 server README「如何写一个功能模块」 Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Cxwgnn2pbzQ7rXv3rq7fei
This commit is contained in:
1 parent
0f656f0f58
commit
1db689d9a7
16 files changed
+1121
-22
No files matched your search
@@ -21,8 +21,11 @@ export interface OutboxRoute {
|
||||
priority?: number;
|
||||
}
|
||||
|
||||
/** 返回 null 表示该事件无需建任务,直接标记已投递 */
|
||||
export type OutboxRouteFn = (event: OutboxEvent) => OutboxRoute | null;
|
||||
/**
|
||||
* 返回 null 表示该事件无需建任务,直接标记已投递。
|
||||
* 返回数组 = 广播给多个订阅方(ADR-006):第一个任务用事件的幂等键,其余用 `<键>#<任务名>`,重投时由引擎幂等去重。
|
||||
*/
|
||||
export type OutboxRouteFn = (event: OutboxEvent) => OutboxRoute | readonly OutboxRoute[] | null;
|
||||
|
||||
export interface OutboxRelayOptions {
|
||||
db: AnyDb;
|
||||
@@ -82,11 +85,13 @@ export class OutboxRelay {
|
||||
for (const ev of rows.rows) {
|
||||
try {
|
||||
const routeFn = this.opts.routes[ev.type];
|
||||
const route = routeFn ? routeFn(ev) : null;
|
||||
if (route) {
|
||||
const payloadTrace = (ev.payload as { traceId?: unknown } | null)?.traceId;
|
||||
const routed = routeFn ? routeFn(ev) : null;
|
||||
const routes = (routed === null ? [] : Array.isArray(routed) ? routed : [routed]) as readonly OutboxRoute[];
|
||||
const baseKey = ev.idempotency_key ?? OutboxRelay.idempotencyKey(ev.id);
|
||||
const payloadTrace = (ev.payload as { traceId?: unknown } | null)?.traceId;
|
||||
for (const [i, route] of routes.entries()) {
|
||||
await this.opts.engine.enqueue(route.task, route.input, {
|
||||
idempotencyKey: ev.idempotency_key ?? OutboxRelay.idempotencyKey(ev.id),
|
||||
idempotencyKey: i === 0 ? baseKey : `${baseKey}#${route.task}`,
|
||||
traceId: route.traceId ?? (typeof payloadTrace === "string" ? payloadTrace : randomUUID()),
|
||||
...(route.priority === undefined ? {} : { priority: route.priority }),
|
||||
});
|
||||
|
||||
Reference in new issue
Block a user