Repository navigation
Commit 0fab7ba
fix(rest)!: rollback accepts only a positive integer toVersion (#25532)
<!-- claude-projects-attribution: {"github_login":"os-sales"} -->
_Requested by **Sales** · [project
thread](https://claude.ai/code/project/chan_01D5bWFY3rFzgJcytD5gD4Uu?thread=cmsg_01D5bWFY3rFzgJcytD5gD4UuDWJsBGQpadCH8JR2xZQ5re)_
Fixes #25482
Clause-②: no (narrowing)
Metadata-protocol: none
## What changed
`POST /api/v1/meta/:type/:name/rollback`
(`packages/rest/src/meta-item-history-routes.ts`) read its version with
a bare `Number(...)`, so `{"toVersion": true}` restored v1, `[2]` and
`"2"` restored v2, and `1.5` reached the protocol and answered `404
VERSION_NOT_FOUND`.
- **JSON body** (`toVersion`, then the `version` alias): the value is
judged as it came against `UNDECLARED_WHOLE_NUMBER_PARAM`, the same
whole-number declaration `/diff` reads its versions against. A boolean,
an array, a string, an object or a fraction does not parse. Then the
door's existing `< 1` check applies.
- **Query string** (`?toVersion=`): read through
`readDeclaredQueryNumber` with that declaration, the reader `/diff`
uses. No second number parser. Its thrown `VALIDATION_FAILED` is caught
at the read and answered as this door's own refusal.
- Every refusal is still the door's existing `400 INVALID_REQUEST` with
the message `'toVersion' (positive integer) is required`. The precedence
(`toVersion`, `version`, `?toVersion=`) and the gates in front of the
read are unchanged.
### Why a numeric string in the body is now refused (the narrowing arm)
The door declares no request schema. The only in-repo client,
`@objectstack/client`'s `rollbackItem(type, name, toVersion: number)`,
sends `JSON.stringify({ toVersion })` with a number. The console's
caller, objectui `packages/data-objectstack/src/metadata-client.ts`
`rollback(type, name, toVersion: number)` read at the pinned
`.objectui-sha` `6fb31dcc99`, sends a JSON number too. No real client
sends a string, so `"2"` in a JSON body is refused like the other
non-numbers. A `?toVersion=3` query string is still read. That refusal
of an input that used to be accepted is the `(narrowing)` arm. The
changeset carries the BREAKING banner, the FROM/TO and the ADR-0087
disposition (`not-required (no-migration-prescription)`).
### The census ledger
`rest-server-query-number-census.test.ts` ledgered the rollback's
`Number(toVersionRaw)` as an `exempt` coercion. That site is gone, so
its row is deleted and the pinned site count goes from 9 to 8. The row's
own reason said that moving the door onto `VALIDATION_FAILED` would be a
wire change, so the refusal stays `INVALID_REQUEST`. The exemption's
three query pins (`abc`, blank, `Infinity`) and its lit control are kept
in the new block.
## Tests
Through the real `RestServer` route (`registerRoutes()` then the
registered handler), in
`packages/rest/src/rest-server-query-number-census.test.ts`, describe
`#25482 rollback toVersion`:
- Body `toVersion` set to `true`, `[2]`, `"2"`, `1.5`, `0`, `-1`,
`"abc"` or `{}` answers `400`, `code: INVALID_REQUEST`, and
`rollbackMetaItem` is never called.
- The `version` alias set to `true`, `[2]`, `1.5` or `0` gets the same
answer.
- `?toVersion=` set to `abc`, blank, `Infinity`, `1.5`, `0` or `-1` gets
the same answer, and so does an absent version.
- Lit controls: body `{toVersion: 2}` reaches `rollbackMetaItem` as `2`
with `200`, the alias `{version: 4}` reaches it as `4`, `?toVersion=3`
reaches it as `3`, and a body version wins over `?toVersion=`.
All run at head `e03bd96c3a`:
- `pnpm --filter @objectstack/rest exec vitest run --maxWorkers=2
src/rest-server-query-number-census.test.ts
src/rest-server-query-multiplicity.test.ts` passed 2/2 files and 67/67
tests (lock `VERDICT command-exit 0`).
- `pnpm --filter @objectstack/rest typecheck && pnpm --filter
@objectstack/rest test`: typecheck clean, then 316 files passed, 6347
tests passed and 326 skipped (lock `VERDICT command-exit 0`).
- `pnpm lint` exited 0, and `pnpm check:type-check-debt` exited 0
(re-measured, none above its recorded number). Both ran under the lock.
- Ablation, run once with the fix committed.
`scripts/ablation-replace.mjs` swapped the body judgement for
`Number.isFinite(Number(bodyVersion)) ? Number(bodyVersion) :
undefined`. The anchor count went 1 to 0, the replacement count 0 to 1,
and the blob `cd8508485ed8` became `a1c834b191a0`. The `#25482` block
then failed 7 tests and passed 16: body `true`, `[2]`, `"2"` and `1.5`,
and the alias `true`, `[2]` and `1.5`. That is the expected red. A trap
restored the file: its blob equals HEAD (`cd8508485ed8`) and `git diff
HEAD` is empty. The test imports `./rest-server.js` from `src`, so no
`dist` was involved.
- Derived gates: `node scripts/pm/dispatch-gates.mjs --commands` named
67, and `--ran` reconciled them as 67 accounted for, 66 run and 1 NOT
MEASURED. The one is `pnpm check:dual-build-cjs-loads` (exit 3,
PREREQUISITE NOT MET: it reads built `dist/` for every package and this
worktree built only the `@objectstack/rest` closure). CI's `Lint & Repo
Gates` runs it. All other 66 exited 0.
- Dogfood battery (the `(narrowing)` arm requires it): `pnpm --filter
'@objectstack/dogfood^...' build && pnpm --filter @objectstack/dogfood
test`, run under the lock at `e03bd96c3a` with no shard and no path.
Lock `VERDICT command-exit 0`. 290 test files passed and 3 skipped
(293). 2494 tests passed and 18 skipped (2512). No failures.
## Acceptance notes
- `rest-server-query-number-census.test.ts`'s module header still says
"ten sites in the whole file today", which was already stale before this
change. Not touched here.
---
🤖 Generated with [Claude Code](https://claude.com/claude-code)
https://claude.ai/code/session_01CxBKycaiUpA1yGx1XyoGqX
Co-authored-by: Claude <noreply@anthropic.com>1 parent c15fd9a commit 0fab7ba
3 files changed
Lines changed: 115 additions & 22 deletions
File tree
- .changeset
- packages/rest/src
| 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 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
797 | 797 | | |
798 | 798 | | |
799 | 799 | | |
800 | | - | |
| 800 | + | |
801 | 801 | | |
802 | 802 | | |
803 | 803 | | |
| |||
806 | 806 | | |
807 | 807 | | |
808 | 808 | | |
809 | | - | |
810 | | - | |
811 | | - | |
| 809 | + | |
| 810 | + | |
| 811 | + | |
| 812 | + | |
| 813 | + | |
| 814 | + | |
| 815 | + | |
| 816 | + | |
| 817 | + | |
| 818 | + | |
| 819 | + | |
| 820 | + | |
| 821 | + | |
| 822 | + | |
| 823 | + | |
| 824 | + | |
| 825 | + | |
| 826 | + | |
| 827 | + | |
| 828 | + | |
| 829 | + | |
| 830 | + | |
| 831 | + | |
| 832 | + | |
| 833 | + | |
| 834 | + | |
| 835 | + | |
812 | 836 | | |
813 | 837 | | |
814 | 838 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
183 | 183 | | |
184 | 184 | | |
185 | 185 | | |
186 | | - | |
187 | | - | |
188 | | - | |
189 | | - | |
190 | | - | |
191 | | - | |
192 | | - | |
193 | | - | |
194 | | - | |
195 | | - | |
196 | 186 | | |
197 | 187 | | |
198 | 188 | | |
| |||
306 | 296 | | |
307 | 297 | | |
308 | 298 | | |
309 | | - | |
| 299 | + | |
310 | 300 | | |
311 | 301 | | |
312 | 302 | | |
| |||
361 | 351 | | |
362 | 352 | | |
363 | 353 | | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
364 | 358 | | |
365 | 359 | | |
366 | | - | |
| 360 | + | |
367 | 361 | | |
368 | 362 | | |
369 | 363 | | |
| |||
493 | 487 | | |
494 | 488 | | |
495 | 489 | | |
496 | | - | |
497 | | - | |
| 490 | + | |
| 491 | + | |
498 | 492 | | |
499 | 493 | | |
500 | 494 | | |
501 | | - | |
| 495 | + | |
| 496 | + | |
502 | 497 | | |
503 | 498 | | |
504 | 499 | | |
505 | 500 | | |
| 501 | + | |
| 502 | + | |
| 503 | + | |
| 504 | + | |
| 505 | + | |
| 506 | + | |
| 507 | + | |
| 508 | + | |
| 509 | + | |
| 510 | + | |
| 511 | + | |
| 512 | + | |
| 513 | + | |
| 514 | + | |
| 515 | + | |
| 516 | + | |
| 517 | + | |
| 518 | + | |
| 519 | + | |
| 520 | + | |
| 521 | + | |
| 522 | + | |
| 523 | + | |
| 524 | + | |
| 525 | + | |
| 526 | + | |
506 | 527 | | |
507 | 528 | | |
508 | | - | |
| 529 | + | |
509 | 530 | | |
| 531 | + | |
| 532 | + | |
| 533 | + | |
510 | 534 | | |
511 | | - | |
| 535 | + | |
| 536 | + | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
512 | 543 | | |
513 | 544 | | |
514 | 545 | | |
515 | 546 | | |
516 | 547 | | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
517 | 561 | | |
518 | | - | |
| 562 | + | |
519 | 563 | | |
520 | 564 | | |
521 | 565 | | |
522 | 566 | | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
523 | 573 | | |
0 commit comments