import type { HOOK_POINTS, HookPoint } from "@umt/contracts"; import type { ParsedRelease } from "@umt/domain"; // 链式钩子挂点的入参 / 返回值类型(挂点清单与语义在 contracts/hooks.ts,ADR-006)。 // rewrite 挂点:value 是要改写的值,context 只读;intercept 挂点只有 context。 /** 资源选择的候选:只能重排或过滤,不能加入新候选(按 sourceReleaseId 映射回原资源) */ export interface SelectionCandidate { sourceReleaseId: string; title: string; parsed: ParsedRelease; } export interface HookPointTypes { "name.recognize": { value: ParsedRelease; context: { title: string; subtitle: string | null; /** 来源资源;按名称识别整理文件时为 null */ sourceReleaseId: string | null; trigger: string; }; }; "organize.rename": { /** 目标文件名(不含目录);返回值不能含路径分隔符 */ value: string; context: { targetPath: string; sourcePath: string; mediaEntityId: string; mediaType: "movie" | "tv"; season: number | null; episodes: { start: number; end: number } | null; }; }; "organize.intercept": { value: never; context: { mediaEntityId: string; mediaType: "movie" | "tv"; action: string; trigger: string; downloadTaskId: string | null; files: { src: string; dst: string; kind: string; role: "main" | "sidecar" }[]; }; }; "resource.selection": { value: SelectionCandidate[]; context: { mediaEntityId: string; subscriptionId: string | null; slotId: string | null }; }; "subscription.completion.check": { value: never; context: { subscriptionId: string; mediaEntityId: string | null; mediaKind: string; /** 本次判定完成的原因(slot_satisfied / download_accepted) */ reason: string; progress: Record; }; }; } type KindOf

= (typeof HOOK_POINTS)[P]["kind"]; export type RewritePoint = { [P in HookPoint]: KindOf

extends "rewrite" ? P : never }[HookPoint]; export type InterceptPoint = { [P in HookPoint]: KindOf

extends "intercept" ? P : never }[HookPoint]; export type HookValue

= HookPointTypes[P]["value"]; export type HookContext

= Readonly; type MaybePromise = T | Promise; /** 返回新值;返回 undefined 表示不改 */ export type RewriteHook

= ( value: HookValue

, context: HookContext

, ) => MaybePromise | undefined>; /** 返回拒绝原因;返回 null / undefined 表示放行 */ export type InterceptHook

= ( context: HookContext

, ) => MaybePromise;