From fa7a2e0b455c4595461a18e6922ed389fae169b7 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 7 Aug 2026 12:31:51 +0000 Subject: [PATCH] =?UTF-8?q?docs(analytics):=20`err.code`=20=E7=9A=84?= =?UTF-8?q?=E7=BA=BF=E4=B8=8A=E8=90=BD=E7=82=B9=E6=98=AF=20`error.code`,?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=20`error.details.code`=20(#6123)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit `errorResponseBase` 只把 `err.code` **暂存**进 `details`;共享构造器 `buildApiError`(`packages/runtime/src/error-envelope.ts:117`)随后经 `splitSemanticCode` 把它**提升**进 `ApiErrorSchema` 声明的 `error.code`, 并把已经清空的 `details` 作为 `undefined` 返回 —— 于是整个 `details` 键 被省略,`error.details.code` 在线上从不存在。 两处已合入的说明文字指向了这个不存在的键,一并更正并各补一句提升机制: - `.changeset/analytics-read-scope-compile-failed-500.md`(#5367 的 changeset, 已合入未发布,会随 `CHANGELOG.md` 发到 npm); - `packages/services/service-analytics/src/read-scope-sql.ts` 文件头。 纯文字/注释修正,零行为变化:两文件均未改动任何可执行代码行。 Co-Authored-By: Claude Fable 5 Claude-Session: https://claude.ai/code/session_015a5qkLzpGXhLL2F5gvJ7dD --- ...analytics-read-scope-compile-failed-500.md | 7 +++++- .../service-analytics/src/read-scope-sql.ts | 23 ++++++++++++++++--- 2 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.changeset/analytics-read-scope-compile-failed-500.md b/.changeset/analytics-read-scope-compile-failed-500.md index 628777f3e6..7b07946d73 100644 --- a/.changeset/analytics-read-scope-compile-failed-500.md +++ b/.changeset/analytics-read-scope-compile-failed-500.md @@ -67,7 +67,12 @@ the disclosure question to be re-decided message by message). `@objectstack/service-analytics` (ADR-0112 D3) and typed as `RegisteredErrorCode` at the constructor, so an unregistered code is a compile error. It is legible on the wire through the sibling `/analytics/query` exit, - which puts a thrown `err.code` in `error.details.code` (#3842). + which puts a thrown `err.code` at **`error.code`** (#3842) — read it there. + `errorResponseBase` only stages the code inside a `details` object; + `buildApiError` then runs `splitSemanticCode`, which promotes it into the + declared `error.code` field and drops the now-empty `details`, so the key is + omitted from the body and `error.details.code` is never present: + `{"success":false,"error":{"code":"READ_SCOPE_COMPILE_FAILED","message":"Internal server error","httpStatus":500}}`. **Which inputs are refused did not change.** No refusal condition moved: nothing that used to lower now throws, and nothing that used to throw now lowers. That is diff --git a/packages/services/service-analytics/src/read-scope-sql.ts b/packages/services/service-analytics/src/read-scope-sql.ts index ada0a1df5e..fc07c4bd74 100644 --- a/packages/services/service-analytics/src/read-scope-sql.ts +++ b/packages/services/service-analytics/src/read-scope-sql.ts @@ -123,9 +123,26 @@ import { likePattern, LIKE_ESCAPE_CHAR } from './like-pattern.js'; * remove; the route keys on the DECLARATION instead. * * The code is what a machine reads: `dispatcher-plugin.errorResponseBase`, the - * sibling `/analytics/query` exit, puts a thrown `err.code` in - * `error.details.code` (#3842), so `READ_SCOPE_COMPILE_FAILED` is legible there - * without anyone parsing prose. + * sibling `/analytics/query` exit, puts a thrown `err.code` on the wire at + * `error.code` (#3842), so `READ_SCOPE_COMPILE_FAILED` is legible there without + * anyone parsing prose. + * + * ⚠️ At `error.code` — NOT `error.details.code`, which is where this note + * pointed until #6123 corrected it. `errorResponseBase` only STAGES the code in + * a `details` object; `buildApiError` then runs `splitSemanticCode` + * (`@objectstack/runtime`, `src/error-envelope.ts:117`), which PROMOTES it into + * the declared `ApiErrorSchema` field and returns the now-empty `details` as + * `undefined` — so the key is omitted from the body and `error.details.code` is + * never present to read. The measured 500 body is exactly: + * + * ```json + * {"success":false,"error":{"code":"READ_SCOPE_COMPILE_FAILED", + * "message":"Internal server error","httpStatus":500}} + * ``` + * + * Pinned end-to-end in `@objectstack/runtime`'s + * `analytics-query-read-scope-withhold.test.ts`, which asserts the code at + * `error.code` against a real `AnalyticsService` on a real mounted route. * * ⚠️ Deliberately NOT a 4xx of any flavour, including a 422. Option A on the * decision card was `READ_SCOPE_INVALID` / 422 ("not your fault, not a crash");