Repository navigation
Commit 81b4599
fix(spec)!: transport select / $select and GetDataRequestSchema.select declare the projection the doors accept (#26949)
<!-- claude-projects-attribution: {"github_login":"os-support-ai"} -->
_Requested by **Yibo Wang** · [project
thread](https://claude.ai/code/project/chan_01BVUyyPie8gHwoPM18iABwB?thread=cmsg_01BVUyyPie8gHwoPM18iABwB85i2if6JKQ1KFLarHMRiWX)_
Fixes #26909
Clause-②: no (narrowing)
Metadata-protocol: none
Skills-docs: none — `skills/**` teaches only a named projection
(`skills/objectui/guides/data-integration.md:82`, `$select: ['name',
'email']`) and never an empty one; the generated reference pages
`content/docs/references/{api/protocol,data/data-engine}.mdx` are
regenerated in this diff.
ADR-0087: not-required (no-migration-prescription) — in the changeset.
**BREAKING** (accept-set narrowing of published transport declarations,
`patch`): `QueryTransportParamsSchema.select` / `$select` (so
`QueryWithTransportSchema` and `FindDataRequestSchema.query`) and
`GetDataRequestSchema.select` refuse a projection naming no field, which
they admitted. The doors already refused it with a 400.
## 维护者速读
- **改了什么**:数据接口里「选哪些字段」(`select` / `$select`)
的三处公开声明收窄到接口真正接受的范围:空串、纯空格、只有逗号、空数组、含空白字段名的数组都被拒;三处声明共用同一条规则。
- **为什么改**:服务端读列表、读单条时早已对这些值回 400,但请求体侧和按 id 读取的声明还写着「都收」,按声明生成的客户端或 AI
作者会写出运行时被拒的请求;上一张卡 #26817 只修了 GET 查询串那一处。
- **风险与回滚**:唯一的运行时变化在 `POST /data/:object/query`:空投影仍是 400 且不读数据,但错误码从
`VALIDATION_ERROR` 变成请求体 schema 的 `VALIDATION_FAILED`(与该接口对
`limit`、`count` 的既有做法一致,先例 #25269)。回滚 = revert 本 PR。
- **先看**:`packages/spec/src/data/data-engine.zod.ts`(新增的投影规则与 `select` /
`$select`
两行)、`packages/spec/src/api/protocol.zod.ts`(`HttpFindQueryParamsSchema.select`
与 `GetDataRequestSchema.select`
改为引用同一规则)、`.changeset/26909-transport-select-empty-projection.md`。
- **两分钟验证**:`pnpm --filter @objectstack/spec exec vitest run
src/data/query-transport.test.ts src/api/protocol.test.ts`,预期全绿,其中 `§6
the projection spellings admit exactly what the doors accept` 一组都在。
- **需要你做的**:无,席位落地。
## What changes
- **One rule, three declarations.** `data-engine.zod.ts` gains the
projection's accept set beside `QueryTransportParamsSchema`: a string
arm `z.string().regex(/[^\s,]/)` (byte-identical to the rule #26817 gave
`HttpFindQueryParamsSchema.select`, which the #26901 review proved equal
to the door's split / trim / drop-empty) and an array arm
`z.array(member.regex(/\S/)).min(1)`. `select` and `$select` use both
arms (`$select` keeps `FieldNodeSchema` as its member, so the retired
object-form message is unchanged). `HttpFindQueryParamsSchema.select`
now reads the string arm off `QueryTransportParamsSchema.shape.select`
and `GetDataRequestSchema.select` reads the array arm, as #26901 already
read `count`: no new export, no copy. Both are patterns and a length
check, not refinements, so they project into JSON Schema (`pattern`,
`minItems`) and add no `dropped-refinements` site.
- **Array rule, measured.** `projectionFieldsValue`
(`metadata-protocol/src/protocol.ts:4648`) keeps every array member and
refuses only `[]`; `assertProjectionFieldsExist` then checks every
member against the object's fields, where a blank name is never one
(`400 INVALID_FIELD`). The declaration refuses `[]` and any blank
member, which covers the all-blank list the card names and `['name',
'']`, both refused by the door today.
- **`.describe()` texts** of the three members now state the accept set;
the two reference pages are regenerated.
`HttpFindQueryParamsSchema.select`'s row is unchanged.
- **Door pins moved (claim amendment).** `POST /data/:object/query`
parses its body with `FindDataRequestSchema`
(`rest/src/crud-routes.ts:363`, `runtime/src/domains/data.ts:242`), so
an empty `select` / `$select` is now refused at the schema: `400
VALIDATION_FAILED`, `fields[0].field` = `query.select` /
`query.$select`, still before any read. This follows the #25269
precedent (`rest/src/data-page-window-door.test.ts` header: "the code is
the schema door's rather than the normalizer's, because the schema now
says what the normalizer enforces"), the same way that route already
answers `{"count":"maybe"}`. The two POST rows in
`rest/src/data-empty-projection-count-door.test.ts` and
`runtime/src/domains/data-empty-projection-count.test.ts` move to new
`VALIDATION_FAILED` tests, which also cover `[]` and `['name', '']`.
`{"fields": []}` (canonical `QuerySchema.fields`, not in this card) and
every GET row stay `VALIDATION_ERROR`, unchanged.
## Why
After #26901, two declarations of the one `findData` door disagreed on
`select` (the GET querystring strict, the body side loose), and the
by-id request admitted `[]` that `getData` refuses. A generated client,
a docs reader or an AI author trusting the published schema learned the
rule only from a runtime 400.
## Readers measured
- `QueryWithTransportSchema` / `FindDataRequestSchema.query`: the `POST
/data/:object/query` body gate in rest and the runtime dispatcher. The
one runtime behaviour change, above.
- RPC alias table / `foldRpcQueryOptions`, `QuerySchema.fields`:
untouched; `QuerySchema` does not read these members.
- `GetDataRequestSchema`: no runtime parse (the by-id routes hand
`select` to `getData`, which refuses through `projectionFieldsValue`);
published declaration only.
- `HttpFindQueryParamsSchema`: no runtime reader; same regex and
message, so #26901's pins stay green unchanged.
- Codegen / reference: `content/docs/references/**` regenerated;
`check:generated` green on 14 artifacts (key-level ledgers unmoved).
- Client SDK: `data.find` sending `?select=` for `select: []` is #26910
(client lane), not touched.
- Stored rows: none. No metadata schema embeds
`QueryWithTransportSchema`, `QueryTransportParamsSchema` or
`GetDataRequestSchema` (only `FindDataRequestSchema.query`), so
`Metadata-protocol: none`.
- In-repo callers sending an empty projection: none outside the door
tests above (examples, ui, client grepped).
## Tests
Locally, under `os-verify-lock`:
- `@objectstack/spec` test (96f9915; the later commit touches only
rest/runtime tests): Test Files 738 passed, Tests 22136 passed, 1 todo.
`typecheck`: exit 0.
- `@objectstack/metadata-protocol` test (96f9915): Test Files 273
passed, 4 skipped; Tests 21693 passed, 26 skipped. Unchanged, including
`protocol.empty-projection-unknown-count.test.ts` (in-process
`findData`, no schema).
- `@objectstack/rest` (1821443): `data-empty-projection-count-door`,
`data-page-window-door`, `zod-union-fields`,
`rest-server-canonical-query-ast`: 4 files, 127 passed, 18 skipped.
- `@objectstack/runtime` (1821443): `data-empty-projection-count`,
`data-write-request-gate`, `data-record-read-status-parity`: 3 files, 96
passed.
- New pins: `query-transport.test.ts` §6 (`''`, `' '`, `' , '`, `','`,
`[]`, `['', ' ']`, `['name', '']` refused on `select` and `` by
`QueryTransportParamsSchema` and `QueryWithTransportSchema`, the issue
at the member's own path; string and array lists kept unchanged; `null`
still a withdrawal); `protocol.test.ts` (`GetDataRequestSchema.select`
refuses `[]`, `['', ' ']`, `['name', '']` and keeps lists;
`FindDataRequestSchema.query` refuses the blank forms on both
spellings). #26901's `HttpFindQueryParamsSchema` pins are untouched and
green.
- A first run of the moved door pins found a blank member is refused at
its own index (`query.select.1`), not `query.select`; the pins name the
index (1821443).
- Full dogfood battery (96f9915, `pnpm --filter @objectstack/dogfood
test`, no shard, lock VERDICT command-exit 0): Test Files 340 passed, 3
skipped; Tests 2754 passed, 20 skipped; no failures. 1821443 changes
only two rest/runtime test files, which dogfood does not import.
## Acceptance notes
- The order asked that a runtime refusal stay the door's
`VALIDATION_ERROR`; that cannot hold for the POST body without changing
the rest and runtime gates, and seat 3 accepted the #25269 route (claim
amendment) before the pins moved.
## Verification
`node scripts/pm/dispatch-gates.mjs --commands --repo
objectstack-ai/objectstack` on 1821443: 105 derived families, run
serially with no build in flight, each exit captured before any pipe:
**105 exit 0**. `--ran` reconciliation: "105 derived famil(ies)
accounted for — 105 run, 0 NOT-MEASURED". (The same list on 96f9915 had
two built-output gates, `check:skill-examples` and
`check:dual-build-cjs-loads`, exit 3 before the dogfood closure build;
both exit 0 on the rerun.) `pnpm --filter @objectstack/spec
check:generated --fix`: 14 artifacts, only `content/docs/references/**`
regenerated. CI families: read from this PR's check-runs.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01WGufzgm45oXexLiY5mMigQ
---------
Co-authored-by: Claude <noreply@anthropic.com>1 parent 7bb8084 commit 81b4599
9 files changed
Lines changed: 189 additions & 17 deletions
File tree
- .changeset
- content/docs/references
- api
- data
- packages
- rest/src
- runtime/src/domains
- spec/src
- api
- data
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
| 16 | + | |
| 17 | + | |
| 18 | + | |
| 19 | + | |
| 20 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
920 | 920 | | |
921 | 921 | | |
922 | 922 | | |
923 | | - | |
| 923 | + | |
924 | 924 | | |
925 | 925 | | |
926 | 926 | | |
927 | 927 | | |
928 | 928 | | |
929 | 929 | | |
930 | | - | |
| 930 | + | |
931 | 931 | | |
932 | 932 | | |
933 | 933 | | |
| |||
1038 | 1038 | | |
1039 | 1039 | | |
1040 | 1040 | | |
1041 | | - | |
| 1041 | + | |
1042 | 1042 | | |
1043 | 1043 | | |
1044 | 1044 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1307 | 1307 | | |
1308 | 1308 | | |
1309 | 1309 | | |
1310 | | - | |
| 1310 | + | |
1311 | 1311 | | |
1312 | 1312 | | |
1313 | 1313 | | |
1314 | 1314 | | |
1315 | 1315 | | |
1316 | 1316 | | |
1317 | | - | |
| 1317 | + | |
1318 | 1318 | | |
1319 | 1319 | | |
1320 | 1320 | | |
| |||
1385 | 1385 | | |
1386 | 1386 | | |
1387 | 1387 | | |
1388 | | - | |
| 1388 | + | |
1389 | 1389 | | |
1390 | 1390 | | |
1391 | 1391 | | |
1392 | 1392 | | |
1393 | 1393 | | |
1394 | 1394 | | |
1395 | | - | |
| 1395 | + | |
1396 | 1396 | | |
1397 | 1397 | | |
1398 | 1398 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
13 | 13 | | |
14 | 14 | | |
15 | 15 | | |
16 | | - | |
| 16 | + | |
17 | 17 | | |
18 | 18 | | |
19 | 19 | | |
| |||
59 | 59 | | |
60 | 60 | | |
61 | 61 | | |
62 | | - | |
63 | | - | |
64 | 62 | | |
65 | 63 | | |
66 | 64 | | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| 69 | + | |
| 70 | + | |
| 71 | + | |
| 72 | + | |
| 73 | + | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
67 | 79 | | |
68 | 80 | | |
69 | 81 | | |
| |||
161 | 173 | | |
162 | 174 | | |
163 | 175 | | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
164 | 187 | | |
165 | 188 | | |
166 | 189 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
112 | 112 | | |
113 | 113 | | |
114 | 114 | | |
115 | | - | |
116 | 115 | | |
117 | 116 | | |
118 | 117 | | |
| |||
127 | 126 | | |
128 | 127 | | |
129 | 128 | | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
130 | 152 | | |
131 | 153 | | |
132 | 154 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
612 | 612 | | |
613 | 613 | | |
614 | 614 | | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
| 618 | + | |
| 619 | + | |
| 620 | + | |
| 621 | + | |
| 622 | + | |
| 623 | + | |
| 624 | + | |
| 625 | + | |
| 626 | + | |
| 627 | + | |
615 | 628 | | |
616 | 629 | | |
617 | 630 | | |
| |||
718 | 731 | | |
719 | 732 | | |
720 | 733 | | |
| 734 | + | |
| 735 | + | |
| 736 | + | |
| 737 | + | |
| 738 | + | |
| 739 | + | |
| 740 | + | |
| 741 | + | |
| 742 | + | |
721 | 743 | | |
722 | 744 | | |
723 | 745 | | |
| |||
0 commit comments