Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
12 changes: 12 additions & 0 deletions .changeset/23050-s13b-meta-item-read-liveness-citations.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
'@objectstack/spec': patch
---

The liveness ledger's `/meta` cache-flag, per-item switch, doc and book rows cite the code that reads their keys where it now lives

Clause-②: no (only the rows' evidence paths and notes move and every status stays `live`: `MetadataEndpointsConfigSchema`, `DocSchema` and `BookSchema` accept exactly what they accepted before, and no state count moves)

- `liveness/metadata_endpoints.json`: the `enableCache` row and the `if (metadata.endpoints.item !== false)` half of the `endpoints.item` row cited `packages/rest/src/rest-server.ts#registerMetadataEndpointsInner`. `@objectstack/rest` moved the per-item reads to `packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes` with no behaviour change, so those references follow them. The `registerPerItemRoute` half of the `endpoints.item` row keeps its citation: that call still lives in the inner method's head.
- `liveness/doc.json`: the `content` row's single-item branch, and the `_note`'s book-tree and single-item branches, cite the new module. The `doc` list branch keeps its citation.
- `liveness/book.json`: the `_note` names the new module for the `book/:name/tree` branch.
- The quoted code, the producers, the verdicts and the row notes are unchanged.
10 changes: 10 additions & 0 deletions .changeset/23050-s13b-meta-item-read-routes-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/rest': patch
---

The `/meta` per-item read routes live in their own module, with no route, handler or answer changed

Clause-②: no (a move: the four routes per base, their place in the `/meta` registration order, every handler, every response and the anonymous-deny gate in front of them are unchanged, and the published declarations are byte-identical)

- `RestServer#registerMetadataEndpointsInner` keeps its name, docblock, place and head. The statement that registers `GET {metaPath}/:type/:name/references`, `GET {metaPath}/:type/:name/layers`, `GET {metaPath}/book/:name/tree` and `GET {metaPath}/:type/:name` moved, re-indented, to `packages/rest/src/meta-item-read-routes.ts`, and the method calls it at its old position, inside the umbrella's guarded registration. The `metadata.endpoints.item` switch gates the same mounts.
- Nothing to do for a consumer: no export of the package entry, no route, no configuration key and no response moved.
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/areas/api-backend.json
Original file line number Diff line number Diff line change
Expand Up @@ -2446,7 +2446,7 @@
"source": [
"packages/spec/liveness/metadata_endpoints.json (the ADR-0049 ledger this item answers: prefix, enableCache, maskObjectFields and three endpoints.* live, cacheTtl and endpoints.schema dead)",
"packages/spec/src/api/rest-server.zod.ts#MetadataEndpointsConfigSchema (the declared contract — four endpoints.* switches whose describe() strings enumerate every mount they gate since #15542/#15854, the ADR-0106 D8 docblock and the two retiredKey tombstones)",
"packages/rest/src/rest-server.ts#registerMetadataEndpointsInner (metaPath, the item endpoint gate and registerPerItemRoute — the per-item switch spelled as a call so the later members of that face carry it), (resolveObjectMasker — the metaType/maskObjectFields fork), (normalizeConfig — isObjectSchemaMaskingEnabled and the OS_ALLOW_UNMASKED_OBJECT_METADATA escape hatch), (getRoutes) + packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes (the types, items and maintenance endpoint gates) + packages/rest/src/discovery-routes.ts#registerDiscoveryEndpoints (discovery.routes.metadata built from prefix)",
"packages/rest/src/rest-server.ts#registerMetadataEndpointsInner (metaPath and registerPerItemRoute — the per-item switch spelled as a call so the later members of that face carry it), (resolveObjectMasker — the metaType/maskObjectFields fork), (normalizeConfig — isObjectSchemaMaskingEnabled and the OS_ALLOW_UNMASKED_OBJECT_METADATA escape hatch), (getRoutes) + packages/rest/src/meta-collection-routes.ts#registerMetaCollectionRoutes (the types, items and maintenance endpoint gates) + packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes (the item endpoint gate on the per-item reads) + packages/rest/src/discovery-routes.ts#registerDiscoveryEndpoints (discovery.routes.metadata built from prefix)",
"ADR-0106 D8 (per-caller field masking of served object schemas) · #14691 (the two retirements) · #7526 (the /meta/types ordering fix the `types` switch takes with it) · ADR-0076 D12 · #15542 / #15854 (the switch-radius ruling that created `maintenance` and gave `item` its own writes) · #15543 (why the measured blast radius of that move is zero: no shipped boot path authors a RestServerConfig)",
"sibling clause: platform-core.metadata-registry-serving (it drives the served REGISTRY on the default config; this item covers the config that decides what is served and to whom) · platform-core.docs-audience-gate (the audience-gated types that are a cache carve-out here)"
],
Expand Down
2 changes: 1 addition & 1 deletion docs/qa/platform-checklist/areas/platform-core.json
Original file line number Diff line number Diff line change
Expand Up @@ -1263,7 +1263,7 @@
"ADR-0090 (audience vocabulary)",
"ADR-0049 (fail closed — unresolvable holdings deny)",
"packages/spec/src/system/book.zod.ts#audienceAllows — audienceAllows / resolveDocAudiences / docAudienceAllows / resolveBookClaimedDocs / deriveImplicitPackageBook / resolveBookTree (UNCATEGORIZED_KEY exclusion)",
"packages/rest/src/rest-server.ts#metaPath — GET <metaPath>/book/:name/tree (audience gate, per-entry filter, resolveDocLocale, refuseRepeatedQueryParams)",
"packages/rest/src/meta-item-read-routes.ts#registerMetaItemReadRoutes — GET <metaPath>/book/:name/tree (audience gate, per-entry filter, resolveDocLocale, refuseRepeatedQueryParams)",
"#2774 (feat(security)!: enforce book audience at the REST read layer)",
"packages/spec/liveness/book.json + doc.json (the ledgers whose coverage.json waivers this item retires)"
],
Expand Down
3 changes: 2 additions & 1 deletion packages/qa/dogfood/test/authz-conformance.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1635,7 +1635,8 @@ describe('#9084 — the rest-server.ts transport tripwire keys off mechanics', (
// [#23050 slice 11] Length 11 → 12 (`form-routes.ts`), still 0 over it.
// [#23050 slice 12] Length 12 → 13 (`crud-routes.ts`), still 0 over it.
// [#23050 slice 13a] Length 13 → 14 (`meta-collection-routes.ts`), still 0 over it.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(14);
// [#23050 slice 13b] Length 14 → 15 (`meta-item-read-routes.ts`), still 0 over it.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(15);
expect(REST_SERVER_REGISTRAR_PATHS[0]).toBe(REST_SERVER);
const listed = REST_SERVER_REGISTRAR_PATHS.map((file) => readFileSync(join(REPO_ROOT, file), 'utf8'));
for (const probe of [MECHANICS, ROUTES]) {
Expand Down
26 changes: 22 additions & 4 deletions packages/qa/dogfood/test/authz-probe-blind-spot.census.ts
Original file line number Diff line number Diff line change
Expand Up @@ -540,16 +540,18 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
'module on the registrar list (`packages/rest/src/rest-server-registrar-files.ts`) — ' +
'`data-action-routes.ts`, `batch-routes.ts`, `approvals-routes.ts`, `sharing-routes.ts`, ' +
'`analytics-routes.ts`, `security-routes.ts`, `discovery-routes.ts`, `email-routes.ts`, ' +
'`search-routes.ts`, `ui-routes.ts`, `form-routes.ts`, `crud-routes.ts` and `meta-collection-routes.ts`, ' +
'`search-routes.ts`, `ui-routes.ts`, `form-routes.ts`, `crud-routes.ts`, `meta-collection-routes.ts` and ' +
'`meta-item-read-routes.ts`, ' +
'the modules the bodies of ' +
'`registerDataActionEndpoints`, `registerBatchEndpoints`, `registerApprovalsEndpoints`, ' +
'`registerSharingEndpoints`, `registerSharingRuleEndpoints`, `registerAnalyticsEndpoints`, ' +
'`registerSecurityExplainEndpoints`, `registerSecurityEndpoints`, `registerDiscoveryEndpoints`, ' +
'`registerOpenApiEndpoints`, `registerEmailEndpoints`, `registerSearchEndpoints`, ' +
'`registerUiEndpoints`, `registerFormEndpoints` and `registerCrudEndpoints`, and the first three `/meta` ' +
'`registerUiEndpoints`, `registerFormEndpoints` and `registerCrudEndpoints`, and the first four `/meta` ' +
'groups of `registerMetadataEndpointsInner`, moved to; ' +
'reachable = those inside registerMetadataEndpoints, PLUS every site in the registrar list\'s named `/meta` ' +
'subset (`REST_SERVER_META_ROUTE_FILES`: `meta-collection-routes.ts`), whose registrations the inner method ' +
'subset (`REST_SERVER_META_ROUTE_FILES`: `meta-collection-routes.ts` and `meta-item-read-routes.ts`), whose ' +
'registrations the inner method ' +
'runs inside the umbrella\'s swap',
// [#15542 / #15854] ⭐ THE POPULATION RULE LEARNED A SECOND SPELLING, and
// the numbers it produces did NOT move: 80 / 19 / 61, exactly as before.
Expand Down Expand Up @@ -849,10 +851,25 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
// `private register*Endpoints(` stays 16, `registerPerItemRoute(` stays 8 and
// `enforceAuth` stays 112: the moved groups spell neither term, and the seam
// spells neither (the umbrella's wrapper is what gates them).
// [#23050 slice 13b] 70 / 19 / 51 UNCHANGED, and `reachable` holds by the rule
// 13a taught, with no rule change: the fourth `/meta` group, the per-item reads
// (four direct registration sites: `/references`, `/layers`, the book tree and
// the item read, all REACHABLE through the same umbrella key), moved to
// `meta-item-read-routes.ts`, which joins the list (length 14 -> 15) and the
// named `/meta` subset (1 -> 2) with its own control. The inner method builds
// its host literal inside the umbrella's swap and calls the module at the
// group's old position, so the subset's length and the module calls inside the
// inner method move together (both 2). Measured at the move commit, before the
// list learned the module: population 66 and `this.routeManager.register(` 59
// over fourteen files, the four sites out of reach. `this.routeManager.register(` stays 63
// over the fifteen files (2 here + 9 + 5 + 7 + 8 + 1 + 6 + 4 + 1 + 1 + 1 + 2 +
// 6 + 6 + 4), `private register*Endpoints(` stays 16, `registerPerItemRoute(`
// stays 8 and `enforceAuth` stays 112: the moved group spells neither term,
// and neither does the seam.
controls: {
'private register*Endpoints(': 16,
'this.routeManager.register(': 63,
'registrar list length': 14,
'registrar list length': 15,
'data-action-routes.ts: this.routeManager.register(': 9,
'batch-routes.ts: this.routeManager.register(': 5,
'approvals-routes.ts: this.routeManager.register(': 7,
Expand All @@ -866,6 +883,7 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
'form-routes.ts: this.routeManager.register(': 2,
'crud-routes.ts: this.routeManager.register(': 6,
'meta-collection-routes.ts: this.routeManager.register(': 6,
'meta-item-read-routes.ts: this.routeManager.register(': 4,
// [#23050 slice 13a] Recorded AS the subset's length, not as a literal: the
// measured side is the module calls inside the inner method, so the two
// can only move together.
Expand Down
28 changes: 23 additions & 5 deletions packages/rest/src/execctx-consumer-census.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,11 @@ const SOURCE = readFileSync(resolve(HERE, 'rest-server.ts'), 'utf8');
* `registerMetadataEndpointsInner`' body to `meta-collection-routes.ts`, with
* their three: the `_drafts` and `_migrate-stored` doors' and the per-type
* list's, each locally caught (the list's on its continuation line), as before.
*
* [#23050 slice 13b] …and the fourth, the per-item reads, to
* `meta-item-read-routes.ts`, with its one: the item read's, locally caught on
* its continuation line, as before — one of §7's and §8's four continuation
* sites, which those sections find there because they read every file here.
*/
const SITE_FILES = REST_SERVER_REGISTRAR_FILES;
const SOURCES = new Map<string, string>(SITE_FILES.map((file) => [
Expand All @@ -139,9 +144,9 @@ const siteKey = (file: string, line: number) => `${file}:${line}`;
* `registerSecurityExplainEndpoints`, `registerSecurityEndpoints`,
* `registerOpenApiEndpoints`, `registerEmailEndpoints`,
* `registerSearchEndpoints`, `registerUiEndpoints`,
* `registerCrudEndpoints`) hands its moved body — and the one
* `registerMetadataEndpointsInner` builds for its moved `/meta` groups (#23050
* slice 13a), counted as the moved module's registrar —
* `registerCrudEndpoints`) hands its moved body — and each one
* `registerMetadataEndpointsInner` builds for a moved `/meta` group's module
* (#23050 slices 13a and 13b), counted as that module's registrar —
* forwards each member to the server with `(...a) => this.<member>(...a)`, so
* a moved site's stack shows
* that forwarder above the site itself. The site is the first censused frame
Expand Down Expand Up @@ -748,9 +753,22 @@ describe('[#13160] §2 the consumer surface, counted from the tree', () => {
// forwarder's two mentions in). Fourteen forwarders now: (14 - 1) + 3 - 2,
// the module carrying one host literal that reads the seam, so neither map
// gains an entry.
expect(SITE_FILES).toHaveLength(14);
//
// [#23050 slice 13b] 62 sites UNCHANGED / 133 → 137 mentions — the same MOVE.
// The fourth `/meta` group, the per-item reads, left for
// `meta-item-read-routes.ts` with its one LOCALLY CAUGHT site (the item read's,
// on its continuation line, as before):
// 12 + 9 + 5 + 7 + 8 + 1 + 5 + 2 + 1 + 1 + 1 + 0 + 6 + 3 + 1 = 62 sites. The
// body also carries one prose mention, which moved with it. The four new
// mentions are this slice's seam, the two spellings once (the second host
// literal the inner method builds, its forwarder, two; the host interface's
// member, two): `rest-server.ts` stays at 55 (the site and the prose mention
// out, one forwarder's two mentions in). Fifteen forwarders now:
// (15 - 1) + 3 - 2, the module carrying one host literal that reads the
// seam, so neither map gains an entry.
expect(SITE_FILES).toHaveLength(15);
expect(SITES.length).toBe(62);
expect([...SOURCES.values()].reduce((n, text) => n + text.split('resolveExecCtx').length - 1, 0)).toBe(133);
expect([...SOURCES.values()].reduce((n, text) => n + text.split('resolveExecCtx').length - 1, 0)).toBe(137);
const extraMovedRegistrars = Object.values(EXTRA_MOVED_REGISTRARS).reduce((n, k) => n + (k ?? 0), 0);
expect(extraMovedRegistrars).toBe(3);
const registrarsReadingNoExecCtx = Object.values(MOVED_REGISTRARS_READING_NO_EXECCTX).reduce((n, k) => n + (k ?? 0), 0);
Expand Down
6 changes: 5 additions & 1 deletion packages/rest/src/meta-draft-read-door-census.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,11 @@ describe('[#20338] census: every draft switch in rest-server.ts is ledgered', ()
// per-type list's `?preview=draft`. A key names its route, never its file,
// so no ledger row changed:
// 8 = 3 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 3, length 13 → 14.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(14);
// [#23050 slice 13b] The per-item reads moved to `meta-item-read-routes.ts`
// with two more: the item read's `?state=draft` and `?preview=draft`. Keys
// name the route, so again no ledger row changed:
// 8 = 1 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 3 + 2, length 14 → 15.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(15);
expect(SOURCE_FILES.map((file) => file.fileName)).toEqual([...REST_SERVER_REGISTRAR_FILES]);
expect(SITES).toHaveLength(8);
});
Expand Down
Loading
Loading