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
@@ -0,0 +1,44 @@
|
||||
import type { HookPoint } from "@umt/contracts";
|
||||
import type { ServerContext } from "../modules/types.ts";
|
||||
import type { HookContext, HookValue, InterceptPoint, RewritePoint } from "./points.ts";
|
||||
import type { HookRejection } from "./registry.ts";
|
||||
|
||||
export type * from "./points.ts";
|
||||
export {
|
||||
DEFAULT_HOOK_TIMEOUT_MS,
|
||||
type HookOptions,
|
||||
type HookRegistrar,
|
||||
HookRegistry,
|
||||
type HookRejection,
|
||||
} from "./registry.ts";
|
||||
|
||||
// 业务节点调用钩子的入口。ctx.hooks 由 bootstrap 装配;测试里手搭的上下文没有它,等同于没有注册。
|
||||
|
||||
/** 这个挂点有没有钩子(上下文构造代价大时先问一下) */
|
||||
export function hasHook(ctx: Pick<ServerContext, "hooks">, point: HookPoint): boolean {
|
||||
return ctx.hooks?.has(point) ?? false;
|
||||
}
|
||||
|
||||
/** 改写:没有钩子时原样返回同一个值;accept 判定单个钩子的结果能不能用(不能用的跳过) */
|
||||
export async function rewriteHook<P extends RewritePoint>(
|
||||
ctx: Pick<ServerContext, "hooks">,
|
||||
point: P,
|
||||
value: HookValue<P>,
|
||||
context: HookContext<P> | (() => HookContext<P>),
|
||||
accept?: (v: HookValue<P>) => boolean,
|
||||
): Promise<HookValue<P>> {
|
||||
const h = ctx.hooks;
|
||||
if (!h?.has(point)) return value;
|
||||
return h.runRewrite(point, value, typeof context === "function" ? context() : context, accept);
|
||||
}
|
||||
|
||||
/** 拦截:返回第一个拒绝,没有钩子或都放行时返回 null */
|
||||
export async function interceptHook<P extends InterceptPoint>(
|
||||
ctx: Pick<ServerContext, "hooks">,
|
||||
point: P,
|
||||
context: HookContext<P> | (() => HookContext<P>),
|
||||
): Promise<HookRejection | null> {
|
||||
const h = ctx.hooks;
|
||||
if (!h?.has(point)) return null;
|
||||
return h.runIntercept(point, typeof context === "function" ? context() : context);
|
||||
}
|
||||
Reference in new issue
Block a user