Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2f7fd76
wip(rest,spec,client): record page payload route — POST /data/:object…
claude Oct 29, 2026
3900fb0
test(rest,dogfood): record page payload — route pins and non-admin RL…
claude Oct 29, 2026
8b1bd01
feat(console): open the record page from one page payload
claude Oct 29, 2026
6bd9ed9
fix(rest,spec): record page refusals on sendError, Parsed aliases, ge…
claude Oct 29, 2026
ace8a04
Merge origin/main into claude/project-thread-2m7jqd
claude Oct 29, 2026
0f1aa30
test(rest): the record page route joins the registrar list, the write…
claude Oct 29, 2026
a108fda
fix(plugin-detail): sideEffects names the two modules that now subscr…
claude Oct 29, 2026
df99e5d
chore(changeset): record page payload declares Metadata-protocol: add…
claude Oct 29, 2026
330e18b
docs(rest): shorten the record page route's module header; the part t…
claude Oct 29, 2026
5a7107f
fix(rest): a declared related filter the list read refuses is a locat…
claude Oct 29, 2026
35a8720
fix(spec): record page related answer is rows or refusal; explain key…
claude Oct 29, 2026
7d6f3ed
Merge origin/main into claude/project-thread-2m7jqd
claude Oct 29, 2026
367cfd2
fix(app-shell): a seeded part nobody reads never surfaces as an unhan…
claude Oct 29, 2026
0efe2a4
test(app-shell): in a drawer the next record is asked once: as a page…
claude Oct 29, 2026
8ad4407
fix(types): take the record page request shapes from the spec
claude Oct 30, 2026
3926932
fix(rest): the record page passes its context to the API gate and spe…
claude Oct 30, 2026
6f040ab
test(dogfood): read the record page's related answer through its two …
claude Oct 30, 2026
428642e
Merge origin/main into claude/project-thread-2m7jqd
claude Oct 30, 2026
780dd8c
chore(spec): regenerate the surface artifacts on the merged tree
claude Oct 30, 2026
49204ce
Merge origin/main into claude/project-thread-2m7jqd
claude Oct 30, 2026
722a37c
fix(console): warm both neighbours' record pages in the v4 pane
claude Oct 30, 2026
f66a010
Merge origin/main into claude/project-thread-2m7jqd
claude Oct 30, 2026
da3ef50
Merge origin/main into claude/project-thread-2m7jqd
claude Oct 30, 2026
6293d95
chore(spec): regenerate the surface artifacts on the merged tree
claude Oct 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 24 additions & 0 deletions .changeset/27627-console-record-page-payload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
---
'@object-ui/types': minor
'@object-ui/data-objectstack': minor
'@object-ui/components': minor
'@object-ui/plugin-detail': minor
---

feat(console): a record page opens from one request, and the record a drawer step shows next is asked ahead (#27627)

Clause-②: yes (widening: optional `DataSource.getRecordPage` / `prefetchRecordPage`, new exports; no existing contract changes)
Metadata-protocol: added

The record page (full page, split pane and drawer) asks `DataSource.getRecordPage` once when it opens, before any of its readers runs, and hands each part of the answer to the reader that used to ask for it:

- the record, with the lookups the page resolves;
- each related list's total and first rows: the tab badges, collapsed and compact related cards and reference rail cards take them, so no `top=1` count probe and no separate first-rows read goes out;
- the header's `update` / `delete` verdicts and Claim's `transfer` decision, the in-place edit blocks' field projection, and the approval rows and lock.

Readers still read for themselves after the open (a refresh, a write, a change on the data-invalidation bus), and a page on an adapter without `getRecordPage` reads as before. Lists the page cannot name in the request stay their own read: a list declared through a list view, a list whose filter carries a context token, and lists past the fiftieth. In the drawer, the record a ↑ / ↓ step shows next is asked ahead (`DataSource.prefetchRecordPage`), so stepping opens from an answer already on its way; on such an adapter that replaces the drawer's warm-up of the next record's related counts.

- `@object-ui/types`: `DataSource.getRecordPage?` / `prefetchRecordPage?` and the `RecordPageRequest` / `RecordPagePayload` shapes.
- `@object-ui/data-objectstack`: both methods; concurrent identical reads share one request, a prefetched answer is taken once, and a write drops them all.
- `@object-ui/components`: `RelatedCountStore.seed` / `takeRows`.
- `@object-ui/plugin-detail`: `seedRecordVerdict`, `seedRecordFieldAccess`, `seedQueueClaimDecision`, `normalizeRelatedListSort`, `COMPACT_RELATED_ROWS`; a compact related card takes its first rows from the record page's answer. Its `sideEffects` array now names `queueClaim` and `recordFieldAccess` too: each drops a seeded answer on a data change through a bus subscription made at load.
19 changes: 19 additions & 0 deletions .changeset/27627-record-page-payload.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
---
'@objectstack/spec': minor
'@objectstack/rest': minor
'@objectstack/client': minor
---

feat(rest): `POST /api/v1/data/{object}/{id}/page` answers everything a record page asks when it opens, in one request (#27627)

Clause-②: yes (widening: a new route and its request and response shapes; no existing route, key or answer changes)
Metadata-protocol: added

A record page used to open in two or three stages of requests: the record, the approval list, the access verdicts and each related list's definition first, then the approval lock and one count probe per related list. The new route answers all of it from one request:

- **The request names the parts** (`RecordPageRequestSchema`, `@objectstack/spec/api`): `expand` (the record's lookups to resolve), `related` (each related list the page draws: the child object, its relationship field to this record, an optional declared `filter`, `top` first rows, an optional `sort`), `explain` (`update` / `delete` / `transfer`), `fieldAccess` and `approvals`. The body is strict and the query-parameter set is closed and empty.
- **Every part is the read it replaces, as the same caller** (`RecordPageResponseSchema`): the record is `GET /data/{object}/{id}`; a related list is `GET /data/{child}` with the parent condition AND the declared filter, and `$count`; `fieldAccess` is `GET /data/{object}/{id}/field-access`; a verdict is `POST /security/explain` with the record id; `approvals` is `GET /approvals/requests` for the record (the pending row enriched as `GET /approvals/requests/{id}`) and the record lock. Object permissions, row-level security, sharing and field-level security therefore apply exactly as they apply to those reads.
- **A refusal stays where it belongs.** A record the caller cannot read refuses the whole request with the record read's own answer. A part whose read refuses carries that refusal (`{ code, status, message }`) in place of its answer, and the other parts are answered; a related list the caller may not read carries neither a count nor rows.
- **A related entry must be a relationship to this record**: a lookup or master-detail field of the child that references the page's object, or the request is a located `400` before anything is read.
- **Server-Timing**: the route's metadata reads are timed as a `meta` span beside `db` and `total`, under the existing gate (`OS_SERVER_TIMING=true`, or `X-OS-Debug-Timing: 1` from an admin).
- `@objectstack/client` gains `data.recordPage(object, id, request)`.
3 changes: 2 additions & 1 deletion content/docs/references/api/index.mdx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
---
title: API Protocol — complete schema reference
navTitle: API Protocol
description: "The ObjectStack API Protocol in 32 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
description: "The ObjectStack API Protocol in 33 reference pages: every schema in @objectstack/spec with its properties, types, defaults and a TypeScript example."
---

{/* ⚠️ AUTO-GENERATED — DO NOT EDIT. Run build-docs.ts to regenerate. Hand-written docs live in the module folders under content/docs/. */}
Expand Down Expand Up @@ -35,6 +35,7 @@ This section contains all protocol schemas for the api layer of ObjectStack.
<Card href="/docs/references/api/query-adapter" title="Query Adapter" description="Source: packages/spec/src/api/query-adapter.zod.ts" />
<Card href="/docs/references/api/realtime" title="Realtime" description="Source: packages/spec/src/api/realtime.zod.ts" />
<Card href="/docs/references/api/realtime-shared" title="Realtime Shared" description="Source: packages/spec/src/api/realtime-shared.zod.ts" />
<Card href="/docs/references/api/record-page" title="Record Page" description="Source: packages/spec/src/api/record-page.zod.ts" />
<Card href="/docs/references/api/rest-server" title="Rest Server" description="Source: packages/spec/src/api/rest-server.zod.ts" />
<Card href="/docs/references/api/router" title="Router" description="Source: packages/spec/src/api/router.zod.ts" />
<Card href="/docs/references/api/sortability" title="Sortability" description="Source: packages/spec/src/api/sortability.zod.ts" />
Expand Down
1 change: 1 addition & 0 deletions content/docs/references/api/meta.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
"misc",
"package-api-assembled",
"package-lifecycle",
"record-page",
"sortability"
]
}
Loading
Loading