refactor(service-storage): 摘除 Local/S3 适配器的 list(prefix) 实现与测试 (#5541) - #6061
Conversation
The implementation half of the #5540 contract retirement (ADR-0049 enforce-or-remove; analysis #5266). #5983 landed the spec member and the SwappableStorageService passthrough atomically; this removes what it left: - `LocalStorageAdapter.list` (single-level readdir, directories returned as files) and `S3StorageAdapter.list` (recursive ListObjectsV2, silently truncated at 1000 objects, IsTruncated/ContinuationToken never read); - the `'list'` label in each adapter's private `track()` metrics vocabulary, which no site can produce anymore; - the tests that pinned those two dialects, plus the dead `FakeAdapter.list` in the swappable proxy's test. Zero in-repo consumers: after #5983 the only surviving references were the two producers and their own tests. The absence is held by a new runtime pin, `storage-adapter-list-retirement.test.ts`. tsc cannot hold this line -- a class may carry members its interface does not declare, which is exactly what the #5540 changeset promised adapter authors -- verified by restoring both methods: the pin goes red on both adapters while `pnpm --filter @objectstack/service-storage build` (tsup DTS, i.e. tsc) still exits 0. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
|
The latest updates on your projects. Learn more about Vercel for GitHub. 1 Skipped Deployment
|
📓 Docs Drift CheckThis PR changes 1 package(s): 4 hand-written doc(s) reference the affected code and may need an implementation-accuracy re-verification:
|
attempt-3 重投(run 31118691490/31118691377/31118691509)在平台故障期间 发起,恢复后 4 小时仍无 runner,且 API 拒绝取消: 「Cannot cancel a workflow re-run that has not yet queued」——重投请求 卡死在预入队状态,平台侧不可解。同期新建 run 秒级拿 runner,证明队列 本身健康。换头 SHA 让全套 check 全新起跑。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD
|
队列管家:本 PR 是链上连坐**,自身无问题 ⇒ ⛔ 未重投、无需改动** 本 PR 所在的队列世代 该文件不在本 PR 的改动面内。 本轮以 base sha 重建队列链序为 #5999 → #6067 → #6086 → #6061 → #6068; 反证:链上位于 #6067 之前的 #5999 没有这条红(它的红是另一条已知 flaky,在 CI job 而非 Lint job)⇒ 责任边界与链序一致。 处置:⛔ 不重投(重投无效——只要 #6067 仍在本 PR 之前,该红就会重现)、⛔ 不改代码。#6067 转绿或出队后本 PR 自动恢复。完整签名与修法建议已留在 #6067 的拦截评论。 已核让行:本 PR 最近 30 分钟无车道 PM 动作;本 PR 当前仍在队列内(无 Generated by Claude Code |
合并 main 到 dca5bd3 后再全量重测,余量在一小时内被兑付了两笔,记录如下: - `@objectstack/objectql` 实测 339 -> **345**(+6 全是 TS2554,全在 `src/summary-rollup.test.ts`,由飞行途中落地的 #5749 / PR #6013 扩写)。 记档 349 把它静默吸收了 —— 若按精确值 339 记账,这就是同一场赛跑的第 6 次红。 按裁决「实测 +10」把记录抬到 **355**,恢复满额余量。 - `@objectstack/service-storage` 42 -> 41 -> **42**:`IStorageService.list(prefix)` 的退休被拆成两个 PR,spec 半边(#5540 / PR #5983)减 1、适配器半边 (#5541 / PR #6061)删旧测试(-1 TS7006)又新增 `storage-adapter-list-retirement.test.ts`(+2 TS2835),净 +1。上一轮我按实测 下调到 41,一小时后就被咬红 —— 正是派发令说的「非余量条目被基漂移咬住」, 按同一记档规则给这条加 +10,记 **52**,不开精确校准 lap。 一个值得写进文档块的新形状:**拆成两个 PR 的退休会让计数先降后升**,在两半之间 记下的精确值,推上去之前就已经过期。 `rest` / `lint` 两条实测未动(153 / 32),余量原样,note 补记「一小时后在 77c7c88 复测仍是该值」。 重测输出:四条记档余量各打印一行 ℹ(各 -10),无一条上漂,exit 0。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014wsZeReNTqiceBfLb5Pyf5
Fixes #5541
#5540摘掉了IStorageService.list?(prefix)这个契约成员;本 PR 是它的实现半 —— 摘掉两个出厂适配器自己的list实现、它们的测试,以及各自track()指标词表里那个再也产不出来的'list'标签。范围核对(⛔ 不重做 PR #5983 已完成的部分)
开工第一步读了
efedd28(PR #5983)的终态 diff。它已原子落地:spec 契约成员 +SwappableStorageService.list代理透传 + 该文件两处测试 + 文档 + ADR-0087 D3 登记。这些一律没有再动。swappable-storage-service.ts:74-76留的指向本单的注释也保持原样。本 PR 只做剩下的四处(两个生产者 + 它们的测试):
local-storage-adapter.tslist(单层readdir、把子目录当文件返回);track()词表去掉'list's3-storage-adapter.tslist(单发ListObjectsV2,IsTruncated/ContinuationToken都不读,过 1000 个对象静默截断);track()词表去掉'list'local-storage-adapter.test.tslocal-storage-adapter.metrics.test.tslist() does not double-count head per entry整条退役(它钉的是被删实现的内部细节)swappable-storage-service.test.tsFakeAdapter.list—— #5983 之后它已无人调用,是个「真适配器都没有的表面」的假体残留storage-adapter-list-retirement.test.ts(新增)list零消费方证据(全仓引用面)
摘除前后同一条查询,
packages/services/service-storage/内:改前的四处全部是测试,没有一处产品代码调用 —— 这与 #5266 的分析一致:唯一的仓内调用点是 #5983 已删掉的代理透传。跨包声明式 + 带引号精确名两种查法(
storage.list(/storageService.list(/adapter.list(/async list(/list?(prefix)在packages/examples/apps/scripts/全仓扫过,除packages/spec的退役登记文本外,命中的async list(...)全是同名不同物:metadata loader 的list(type)、messaging outbox 的list(filter)、suspended-run-store的list(),与存储无关。存储侧零残余。必答项:
StorageFileInfo是否随之退役?不退役 —— 它仍有消费方,
list从来不是它唯一的生产者。 实测:IStorageService.getInfo(key)的返回类型就是它,而getInfo是契约的必选成员(packages/spec/src/contracts/storage-service.ts:124),spec: 按 ADR-0049 摘除IStorageService.list(prefix)契约成员(零消费方,双适配器语义分叉 —— #5266 方案 2,维护者已批) #5540 没有动它;local-storage-adapter.ts:184、s3-storage-adapter.ts:198、swappable-storage-service.ts:70;getInfo的返回标注。所以 changeset 措辞按「成员摘除、类型保留」写,没有任何类型退役的语句。
反向验证(方向先声明,再跑)
预判写在动手之前:把
list加回两个适配器,新留钉应变红,而包的 build(tsup DTS,即 tsc)应保持绿。后半句才是这条钉子存在的理由 —— class 携带接口未声明的多余成员不是类型错误,这正是 #5540 changeset 对适配器作者的承诺原文(「an implementation left in place still compiles」),所以 tsc 守不住这条线。两半都如预判:
随后已还原,
git diff --stat与还原前逐字节一致。留钉里第三条用例(仍暴露upload/download/delete/exists/getInfo)是防镜像失效的:一个「适配器什么方法都没有」的空壳同样会让前两条绿。验证
门是从
.github/workflows/lint.yml逐条枚举跑的,不是凭记忆挑的。pnpm --filter @objectstack/service-storage test→ 22 files / 282 tests passed(含新留钉 3 条)pnpm --filter @objectstack/service-storage build→ DTS 成功(该包无typecheckscript,tsc 走 tsup DTS 这条道,与 refactor(spec)!: 按 ADR-0049 摘除IStorageService.list(prefix)—— 零消费方、双适配器语义分叉 (#5540) #5983 commit message 记录的一致)turbo run build(service-storage + 全部三个下游cli/plugin-dev/dogfood及其依赖闭包)→ 62 tasks successful,证明删除对下游零类型影响lint(ESLint)、check:slot-lookup、check:query-options-erasure、check:nul-bytes、check:doc-authoring、check:docs-audit-scope、check:role-word、check:adr-anchors、check:org-identifier、check:authz-resolver、check:service-providers、check:route-envelope、check:error-code-casing、check:wildcard-fallthrough、check:init-service-contract、check:durability-log-level、check:startup-registry-verdict、check:objectui-changeset、check:release-notes、check:release-body、check:node-version、check:workflow-status-functions、check:published-files、check:engine-double-contract、check:resume-authority-declared、check:type-check-coverage、check:driver-conformance、check:stall-guard、check:skill-frame-sync、check:i18n、check:i18n-coverage→ 全 PASSpackages/spec,未重生成任何 spec 产物(git status -- packages/spec content/docs docs/为空)changeset
.changeset/storage-adapter-list-implementations-removed.md,@objectstack/service-storage: patch。按约束写得响亮:两个方言各自错在哪、该成员从未有过仓内消费方、以及未来按需回归的形状指引 —— 回来时是带游标的list(prefix, { cursor, limit }),返回一页 + 续传 token,并要求 nested keys / 目录项 / 超 1000 对象三类 adapter-conformance 用例证明两个后端答案一致,不继承坏签名(#5266 维护者裁决原文)。取 patch 而非 major:契约破坏是 #5540 的、已在那里发布;这里同时如实列出直接持有具体类LocalStorageAdapter/S3StorageAdapter调.list()的下游该改成什么,没有藏。Generated by Claude Code