fix(rest): 清扫 REST 组合根的槽查找 —— 16 处类型化,契约由 implements 背书 (#4251 B4) - #5953
Merged
Conversation
#4251 的 B4 批次:REST 组合根的全部槽查找擦除。`rest-api-plugin.ts`(15)与 `external-datasource-routes.ts`(1)改为传入槽的契约类型,棘轮基线 159 → 143 站点 / 34 → 32 文件,两个文件退出祖父名单。无行为变更。 `email` / `sharing` / `sharingRules` / `reports` / `approvals` / `external-datasource` 六个槽在 packages/spec 里本来就有契约,而各自 provider 注册进槽的那个类都写了 implements(`EmailService implements IEmailService` 等), 所以生产侧的形状由编译器每次构建校验,本文件只需具名 —— 即 #4404 用一份受检 claim 取代七个未受检 stand-in 的纪律。`auth` / `objectql` / `i18n` / `analytics` / `security` / `metadata` 取自 ServiceSlotContracts 台账;`objectql` 是 IObjectQLEngine 而非 IDataEngine,因为消费方够到的是完整引擎。 十处查找位于 `Promise<any | undefined>` 的 provider 内,只改查找会在下一行把 契约重新擦掉(规则看不见的包装返回注解残留),故 provider 返回类型一并收窄。 三个无契约的槽分别用三种方式如实表达:`env-registry` 用 RestServer 构造函数 自己声明的 RestEnvRegistry(实参因此受检);`settings` 按 B2 决策给具名本地 surface(service-settings 可选,REST 层不能对它产生依赖),返回类型复用公开的 ResolvedSettingValue;`default-project` 只声明本文件读的那一个字段。服务存在性 探测的槽名是运行期参数,用 unknown —— 它只问槽里有没有东西,从不碰形状。 本批未发现死探测,如实记录而非暗示:逐一核对了 emailService.send、 authService.getApi/isAuthGateActive、svc.queryDataset、ql.transaction、六个 approval 动词、五个 security 方法与五个 federation 方法,全部命中真实成员与 真实 arity。 新增的钉子是运行期测试,这是刻意选择:packages/rest 的 tsconfig 排除 test 文件 且该包无 typecheck script,没有任何 tsc program 编译它们,写类型级断言会是 #5286 / #5449 付过代价的 phantom check。真正的风险是接线 —— provider 是二十参 构造函数的第 6..19 个位置参数、形状完全相同,接错槽处处可赋值且编译器看不见, 所以测试驱动每个 provider 并断言它取回自己那个槽注册的实例,钉住 boot 解析的 槽名集合,以及全部可选槽为空时的降级路径。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GX3sL71LFq8m2usg6VqTSE
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
Contributor
📓 Docs Drift CheckThis PR changes 1 package(s): 11 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
os-zhuang
marked this pull request as ready for review
August 6, 2026 13:02
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Part B4 of #4251 —— REST 组合根批次。本 PR 不 Close 本单,B5–B11 批次留后续。
棘轮基线 159 → 143 站点 / 34 → 32 文件,两个文件全清并退出祖父名单,无新增 key(
baseline key set verified against 9ce0ca9: no files added)。无行为变更。站点分布(实测 16 处,比派发单的 15 处多 1)
派发范围写的是
rest-api-plugin.ts14 +external-datasource-routes.ts1;基线文件记的是 15 + 1,实测也是 16。多出来的那一处是env-registry:同一行同时命中第四形态(let envRegistry: any;分体声明)与类型实参形态,两条规则各报一次。auth/objectql/i18n/analytics/security/metadataServiceSlotContractsemail/sharing/sharingRules/reports/approvals/external-datasourceimplementsenv-registryRestEnvRegistrysettingsdefault-projectunknownobjectql按 #4404 落地的IObjectQLEngine(而非IDataEngine):消费方够到的是完整引擎——batch 路由背后探测的是ql.transaction。六个"无契约"的槽其实都有契约,而且是受检的
这批最大的发现不是缺陷而是盘点结果:
email/sharing/sharingRules/reports/approvals/external-datasource在packages/spec/src/contracts/里本来就有契约,并且各自 provider 注册进槽的那个类都声明了implements(EmailService implements IEmailService、ExternalDatasourceService implements IExternalDatasourceService…)。所以生产侧形状由编译器每次构建校验,本文件只需具名——即 #4404 用一份受检 claim 取代七个未受检 stand-in 的纪律。没有为它们新增 ledger 条目(不动packages/spec),入账与否留给后续决策。包装函数返回注解一并收窄
十处查找位于
async (environmentId?) => Promise< any | undefined >的 provider 内。只改查找会在下一行把契约重新擦掉——正是规则文档里记的 KNOWN RESIDUAL(注解在外层函数上,AST 规则原理上看不见)。每个 provider 的返回类型因此一并收窄为自己那个槽的契约。本批未发现死探测 —— 如实记录,而不是凑一个产出
这一族历史上每批都挖出真缺陷(#4361 的
getMetaItem指着一个从来没有该方法的服务、#4321 的registerInMemory),所以我逐一核对了类型化后消费方做的每一处探测:emailService.send、authService.getApi/isAuthGateActive、svc.queryDataset、ql.transaction、六个 approval 动词、五个 security 方法、五个 federation 方法——全部命中真实成员与真实 arity。一个值得记的次级结论:
external-datasource-routes.ts的五处svc?.method探测在类型化后显出是冗余但正确的——契约的方法都是必需成员,所以只要服务解析成功探测必为真,503 分支只由"服务不存在"到达,而那正是它的用途。不是恒假分支,不改行为。钉子为什么是运行期测试,而不是类型级断言
packages/rest/tsconfig.json排除自己的 test 文件,且该包没有typecheckscript(它是 DEBT / TEST_DEBT 台账条目)——所以没有任何 tsc program 编译这些文件。在这里写@ts-expect-error或Assert< Equal< … > >会永不求值,删掉它每道闸门照样绿:正是 AGENTS.md 禁止、#5286 / #5449 付过代价的 phantom check。而这次改动真正携带的风险恰好是运行期可测的:provider 是二十参构造函数的第 6..19 个位置参数,形状全都是
(environmentId?) => Promise< unknown >,所以接错槽处处可赋值、编译器一声不响。新测试(rest-api-plugin-slot-lookups.test.ts,5 例)因此钉住:'sharingRules'手滑成'sharing-rules'在这里红,而不是在生产上让一条路由永久 501);env-registry/default-project两个 seam 按 RestServer 声明的形态传入;undefined且不抛。反向验证(方向先定后验)
预期方向是红——因为这正是类型抓不到的那一类。把
getService< IReportService >('reports')改成'report'一个字符,5 例中 2 例失败,报文直接点名:已还原。
验证
pnpm --filter @objectstack/rest testtsc --noEmit -p packages/rest/tsconfig.jsonerrors: 2精确相等,且都在我没动的package-routes.ts;两个改动文件 0 errorpnpm check:slot-lookup(强制模式)pnpm eslint(三个改动文件)node scripts/check-nul-bytes.mjs合并 main(2 个 commit)后复跑:
rest-meta-save-receipt-envelope.test.ts(merge 带进来的 #5265 新测试)先报 2 例失败,根因是本 worktree 的metadata-protocoldist 过期(该测试经其 dist 走 save-receipt 路径),重建依赖后 838/838 全绿——与本单勘误记录的同源陷阱,不是 main 红也不是本改动所致。范围外发现
default-project槽有 3 个生产消费方、0 个生产注册方:rest-api-plugin.ts、runtime/http-dispatcher.ts:618、cloud 侧objectos-runtime/kernel-resolver.ts:193;而 6 处注释(含rest-server.ts两处、http-dispatcher.ts两处、domain-handler-registry.ts)都把注册方写成createSingleEnvironmentPlugin,该函数在 objectstack 与 cloud 两个仓库里都不存在,cloud 仅在一处测试里 stub 了这个槽。三个消费方都有后续 fallback、降级路径完整,所以今天没有用户可感的故障——但"单环境默认"这一级解析永远不会触发。本 PR 只按本文件实际读到的字段具名类型化,不改行为;已另行开单,不在本批扩面。