- v2 foundation: ActionResult/DevError/ActorContext/ResourceRef/Execution types - ActionRegistry + ResourceRegistry (namespaced types, FAIL FAST on duplicates) - ActionRuntime pipeline: validate -> authorize -> middleware -> handler -> output validation -> audit -> event -> execution persisted (devos_action_executions) - Legacy bridge: v1 services registered as devops.*/core.* resources & actions (47 actions, 9 resources) - Unified Action API: POST /api/v2/actions/:actionId, GET discovery (actions/resources) - 10 v2 unit tests (registry fail-fast, validation, authz, failure audit, events)
74 lines
2.3 KiB
TypeScript
74 lines
2.3 KiB
TypeScript
/**
|
||
* # Resource Registry(docs 03 §11/§12)
|
||
* 平台级资源类型注册表:命名空间类型(core.user / devops.server / seo.website),
|
||
* 每个 Resource 声明 schema、actions、UI metadata。启动阶段 Core → Apps → Plugins 依次注册。
|
||
*/
|
||
import type { z } from "zod";
|
||
import type { ResourceRef } from "../foundation/types";
|
||
|
||
/** # 资源事件定义 **/
|
||
export interface ResourceEventDefinition {
|
||
/** # 形如 resource.created **/
|
||
type: string;
|
||
description?: string;
|
||
}
|
||
|
||
/** # 资源 UI 元数据(通用 UI 自动生成用) **/
|
||
export interface ResourceUI {
|
||
icon?: string;
|
||
listColumns?: string[];
|
||
}
|
||
|
||
/** # Resource 定义(docs 03 §5) **/
|
||
export interface ResourceDefinition<T = unknown> {
|
||
/** # 命名空间类型 <namespace>.<resource> **/
|
||
type: string;
|
||
label: string;
|
||
description?: string;
|
||
schema?: z.ZodType<T>;
|
||
/** # 该资源拥有的 Action id 列表(注册到 ActionRegistry) **/
|
||
actionIds: string[];
|
||
events?: ResourceEventDefinition[];
|
||
ui?: ResourceUI;
|
||
/** # 所有权:organization → 授权自动带组织 scope **/
|
||
ownership?: { organization: boolean };
|
||
/** # 父资源(Project → Website → Page) **/
|
||
parent?: { type: string };
|
||
}
|
||
|
||
export class ResourceRegistry {
|
||
private resources = new Map<string, ResourceDefinition<never>>();
|
||
|
||
/** # 注册:重复 type FAIL FAST(docs 03 §87) */
|
||
register(resource: ResourceDefinition<never>, registeredBy = "core"): void {
|
||
if (!resource.type.includes(".")) {
|
||
throw new Error(`[v2] resource type must be namespaced: ${resource.type}`);
|
||
}
|
||
if (this.resources.has(resource.type)) {
|
||
throw new Error(`[v2] duplicate resource type: ${resource.type}`);
|
||
}
|
||
this.resources.set(resource.type, resource);
|
||
}
|
||
|
||
get(type: string): ResourceDefinition<never> | undefined {
|
||
return this.resources.get(type);
|
||
}
|
||
|
||
has(type: string): boolean {
|
||
return this.resources.has(type);
|
||
}
|
||
|
||
list(): ResourceDefinition<never>[] {
|
||
return [...this.resources.values()].map((r) => r);
|
||
}
|
||
|
||
/** # ResourceRef → 定义(校验类型存在) */
|
||
resolveRef(ref: ResourceRef): ResourceDefinition<never> {
|
||
const def = this.resources.get(ref.type);
|
||
if (!def) throw new Error(`[v2] unknown resource type: ${ref.type}`);
|
||
return def;
|
||
}
|
||
}
|
||
|
||
export const resourceRegistry = new ResourceRegistry();
|