Skip to content

声明式 apis:(ApiEndpoint)入站面全链路零执行:元数据装载成功、路由从未挂载、matchEndpoint 全仓无实现 #4936

Description

@xuyushun441-sys

#4910(入站 rateLimit 接执行)的开工核查中发现,记录备查,未认领

基线:origin/main @ 2e284b2546证据是真实 boot,不是 grep 推断(pnpm dev -- --fresh -p 39147,showcase,47 plugins)。

事实

showcase 声明了两个 ApiEndpoint(examples/app-showcase/src/system/apis/index.ts),经 defineStack({ apis })ARTIFACT_FIELD_TO_TYPE.apis = 'api' 装进元数据。元数据端一切正常:

$ curl -b cookies 'http://localhost:39147/api/v1/meta/api'
{"type":"api","items":[
  {"name":"showcase_task_feed","path":"/api/v1/showcase/tasks","method":"GET",
   "type":"object_operation","objectParams":{"object":"showcase_task","operation":"find"},
   "authRequired":true,"cacheTtl":30, ...},
  {"name":"showcase_inquiry_purge_api","path":"/api/v1/showcase/inquiries/purge","method":"POST",
   "type":"flow","target":"showcase_inquiry_purge","authRequired":true, ...}]}
HTTP 200

执行端零命中 —— 同一个 boot、同一个会话 cookie:

GET  /api/v1/showcase/tasks            → HTTP 404 {"error":"Not found"}
POST /api/v1/showcase/inquiries/purge  → HTTP 404 {"error":"Not found"}
GET  /api/v1/data/showcase_task?top=1  → HTTP 200   ← 阳性对照,同 cookie 同 prefix

两处断链(任一都足以致死,现在两处同时存在)

① 路径从未挂载。 dispatcher-plugin.ts 逐条显式注册路由(/data/meta/ai/* 通配等),元数据声明的任意路径没有任何 catch-all/api/v1/showcase/tasks 根本进不了 dispatcher.dispatch(),直接落到 Hono 的 notFound —— 上面那个裸 {"error":"Not found"} 就是它,不是 dispatcher 的语义 404(routeNotFound)。

② 即便进得去,匹配器不存在。 http-dispatcher.ts:1646handleApiEndpoint 这样开头:

const metaSvc = await this.resolveService('metadata', context.environmentId);
if (!metaSvc || typeof (metaSvc as any).matchEndpoint !== 'function') {
    return { handled: false };
}

全仓 matchEndpoint 只有 3 处命中,全在 http-dispatcher.ts 自己身上(1650 判断、1653 调用、1713 注释)。MetadataManager / NodeMetadataManager 都没有这个方法,任何 plugin 也没有补。所以这个分支恒等于 { handled: false } —— 是 grep 找得到、运行时永不执行的死代码,正是 ADR-0076「一条路由一个属主」小节点名的那类输入。

波及面:不止 rateLimit 一个键

ApiEndpointSchema(packages/spec/src/api/endpoint.zod.ts)的每一个键都因此是 declared ≠ enforced,不只是 #4686 盯的 rateLimit:

声明语义 实际
path / method 路由地址 从未挂载
type / target / objectParams 执行目标 从未执行
authRequired 要求认证 零执行(注意:这条是安全语义)
cacheTtl 响应缓存 零执行
inputMapping / outputMapping 出入参映射 零执行
rateLimit 限流 零执行(#4686 / #4910)
summary / description 文档 未进 /openapi.json

examples/app-showcase/src/coverage.ts:181 目前写着这套「executed by the runtime dispatcher (handleApiEndpoint)」并把它记作已覆盖能力 —— 这是 Prime Directive #10 明令禁止的「advertise a capability the runtime doesn't deliver」,同 PR 要一并修正。

建议(协议级,留给维护者裁决,不要顺手做)

  1. 接上执行:补 catch-all 挂载 + 在元数据服务上实现 matchEndpoint,并把 authRequired / cacheTtl / inputMapping / outputMapping 逐键接到执行器 —— 工作量远大于 「v17」入站 rateLimit 接执行:ApiEndpoint / HttpServer 的 RateLimitConfig 推导为 runtime token bucket 配置,dispatcher 生效(#4686 拆向之一) #4910 的设想,且 authRequired 的默认值(true)一旦真生效就是 breaking 面。
  2. 按 ADR-0049 整面摘除:apis:stack.zod.ts 退役,走完整退休套件,showcase 的两个端点改为已经能跑的 code-mounted 形式(src/system/server/ 里已有一个)。
  3. 先摘执行残骸:保留声明面待日后实现,但删掉 handleApiEndpoint 这段永不执行的死代码 + LEGACY_CHAIN_PREFIXES 里的 /__api-endpoint 条目 + coverage.ts 的失实声明,让「未实现」变成响亮的缺席而不是看起来已实现。

⚠️ 这条阻塞 #4910:#4910 要求「授权 rateLimit 配置经真实 dispatcher 路径产生 429」,而端点本身根本不经过 dispatcher。详见 #4910 的核查评论。

关联:#4686#4910、ADR-0049、ADR-0076、Prime Directive #10

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions