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
10 changes: 10 additions & 0 deletions .changeset/23050-s13e-meta-object-state-published-move.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
'@objectstack/rest': patch
---

The `/meta` object state-machine read and the published read live in their own modules, with no route, handler or answer changed

Clause-②: no (a move: the two 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, its per-item registrar included. The statement that registers `GET {metaPath}/object/:name/state/:field` (the legal next states of an object field's state machine) moved, re-indented, to `packages/rest/src/meta-object-state-routes.ts`, and the block that registers `GET {metaPath}/:type/:name/published` (an item's published version) moved to `packages/rest/src/meta-item-history-routes.ts`, beside the history family. The method calls each at its old position, inside the umbrella's guarded registration. The state read still answers to `api.enableMetadata` alone, and the published read still registers through the per-item registrar, so 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.
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 @@ -1638,7 +1638,8 @@ describe('#9084 — the rest-server.ts transport tripwire keys off mechanics', (
// [#23050 slice 13b] Length 14 → 15 (`meta-item-read-routes.ts`), still 0 over it.
// [#23050 slice 13c] Length 15 → 16 (`meta-item-write-routes.ts`), still 0 over it.
// [#23050 slice 13d] Length 16 → 17 (`meta-item-history-routes.ts`), still 0 over it.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(17);
// [#23050 slice 13e] Length 17 → 18 (`meta-object-state-routes.ts`), still 0 over it.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
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
41 changes: 34 additions & 7 deletions packages/qa/dogfood/test/authz-probe-blind-spot.census.ts
Original file line number Diff line number Diff line change
Expand Up @@ -541,17 +541,17 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
'`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`, `meta-collection-routes.ts`, ' +
'`meta-item-read-routes.ts`, `meta-item-write-routes.ts` and `meta-item-history-routes.ts`, ' +
'the modules the bodies of ' +
'`meta-item-read-routes.ts`, `meta-item-write-routes.ts`, `meta-item-history-routes.ts` and ' +
'`meta-object-state-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 six `/meta` ' +
'`registerUiEndpoints`, `registerFormEndpoints` and `registerCrudEndpoints`, and all eight `/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`, `meta-item-read-routes.ts`, ' +
'`meta-item-write-routes.ts` and `meta-item-history-routes.ts`), whose ' +
'`meta-item-write-routes.ts`, `meta-item-history-routes.ts` and `meta-object-state-routes.ts`), whose ' +
'registrations the inner method ' +
'runs inside the umbrella\'s swap',
// [#15542 / #15854] ⭐ THE POPULATION RULE LEARNED A SECOND SPELLING, and
Expand Down Expand Up @@ -910,10 +910,31 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
// `const registerPerItemRoute =` stays 1, `forwarder slice:` stays 1 and
// `enforceAuth` stays 112: the moved group spells none of them, and neither
// does the seam.
// [#23050 slice 13e] 70 / 19 / 51 UNCHANGED, `reachable` held by 13a's rule
// with no rule change, and ONE control re-keyed. The seventh `/meta` group,
// the object state-machine read (one DIRECT registration site, in no
// per-family switch), moved to `meta-object-state-routes.ts`, which joins the
// list (length 17 -> 18) and the named `/meta` subset (4 -> 5) with its own
// control on `this.routeManager.register(` (1). The eighth and last, the
// published read (one helper-routed site), joined `meta-item-history-routes.ts`,
// whose own control goes 5 -> 6, and the whole-population
// `registerPerItemRoute(` control stays 8 over this file plus every module on
// the list (0 here + 2 + 6). Measured at the move commit, before the list
// learned the new module: population 69, the state read's site out of reach.
// RE-KEYED, not relaxed: the inner method now calls the history module TWICE
// (the history family, and the published read at its own position after the
// state read), so `.call(` inside the inner method reads 6 against a subset of
// 5 files, and is recorded as the subset's length PLUS that one second call;
// a `/meta` module called without joining the subset, or a subset entry no
// longer called, still moves it. `this.routeManager.register(` stays 63 over
// the eighteen files (one site moved from here to the new module),
// `private register*Endpoints(` stays 16, `const registerPerItemRoute =`
// stays 1, `forwarder slice:` stays 1 and `enforceAuth` stays 112: the moved
// groups spell none of the last four, and neither does the seam.
controls: {
'private register*Endpoints(': 16,
'this.routeManager.register(': 63,
'registrar list length': 17,
'registrar list length': 18,
'data-action-routes.ts: this.routeManager.register(': 9,
'batch-routes.ts: this.routeManager.register(': 5,
'approvals-routes.ts: this.routeManager.register(': 7,
Expand All @@ -932,11 +953,16 @@ export const PROBE_FILE_CENSUS: readonly ProbeFileReading[] = [
// has no direct site (see above).
'meta-item-write-routes.ts: registerPerItemRoute(': 2,
// [#23050 slice 13d] The same: the history family's module has no direct site.
'meta-item-history-routes.ts: registerPerItemRoute(': 5,
// [#23050 slice 13e] 5 -> 6: the published read joined it, helper-routed too.
'meta-item-history-routes.ts: registerPerItemRoute(': 6,
// [#23050 slice 13e] The object state-machine read registers directly.
'meta-object-state-routes.ts: this.routeManager.register(': 1,
// [#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.
'/meta subset ↔ .call( inside registerMetadataEndpointsInner': REST_SERVER_META_ROUTE_FILES.length,
// [#23050 slice 13e] …PLUS ONE: `meta-item-history-routes.ts` is the one
// module the inner method calls at two positions (see above).
'/meta subset ↔ .call( inside registerMetadataEndpointsInner': REST_SERVER_META_ROUTE_FILES.length + 1,
// Both halves of the new rule carry their own control, so neither can go
// silently to zero: a helper deleted and its routes inlined back would
// still read population 80, and only these two controls would notice the
Expand Down Expand Up @@ -1478,6 +1504,7 @@ export function deriveProbeFileCensus(): {
'forwarder slice: this.routeManager.register(': occurrences(forwarderSlice(src), mountRe),
// [#23050 slice 13a] The module calls inside the inner method — one per
// moved `/meta` group's module — recorded against the subset's length.
// [#23050 slice 13e] Two for `meta-item-history-routes.ts`, recorded as such.
'/meta subset ↔ .call( inside registerMetadataEndpointsInner': occurrences(inner, /\.call\(/g),
enforceAuth: occurrences(all, /enforceAuth/g),
},
Expand Down
38 changes: 34 additions & 4 deletions packages/rest/src/execctx-consumer-census.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ const SOURCE = readFileSync(resolve(HERE, 'rest-server.ts'), 'utf8');
* lines, the change log's and the comparison's on their continuation lines —
* two of §7's and §8's four continuation sites, which those sections find there
* because they read every file here.
*
* [#23050 slice 13e] …and the seventh and eighth, the object state-machine read
* to `meta-object-state-routes.ts` and the published read to
* `meta-item-history-routes.ts`, with none: neither door resolves a caller
* itself (the umbrella's wrapper does, before either handler runs).
*/
const SITE_FILES = REST_SERVER_REGISTRAR_FILES;
const SOURCES = new Map<string, string>(SITE_FILES.map((file) => [
Expand All @@ -157,7 +162,7 @@ const siteKey = (file: string, line: number) => `${file}:${line}`;
* `registerSearchEndpoints`, `registerUiEndpoints`,
* `registerCrudEndpoints`) hands its moved body — and each one
* `registerMetadataEndpointsInner` builds for a moved `/meta` group's module
* (#23050 slices 13a, 13b, 13c and 13d), counted as that module's registrar —
* (#23050 slices 13a to 13e), 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 All @@ -182,11 +187,17 @@ const FORWARDERS = new Set(FORWARDER_LINES.map((line) => siteKey('rest-server.ts
* bodies), each handed over by its own host literal — but see
* {@link MOVED_REGISTRARS_READING_NO_EXECCTX}: only one of its two literals
* carries the forwarder.
*
* [#23050 slice 13e] So does `meta-item-history-routes.ts` (the history family
* and the published read), each handed over by its own host literal the inner
* method builds at its own position — and, as for the discovery module, only
* the first of the two carries the forwarder.
*/
const EXTRA_MOVED_REGISTRARS: Readonly<Partial<Record<(typeof SITE_FILES)[number], number>>> = {
'sharing-routes.ts': 1,
'security-routes.ts': 1,
'discovery-routes.ts': 1,
'meta-item-history-routes.ts': 1,
};
/**
* [#23050 slice 7] The moved registrars whose bodies read no `resolveExecCtx`,
Expand All @@ -201,10 +212,18 @@ const EXTRA_MOVED_REGISTRARS: Readonly<Partial<Record<(typeof SITE_FILES)[number
* either (the AST walk of the body finds no read): the public form doors run
* as the form grant and resolve no caller, so their host literal names no
* `resolveExecCtx` member.
*
* [#23050 slice 13e] Neither the object state-machine read's body
* (`meta-object-state-routes.ts`) nor the published read's
* (`meta-item-history-routes.ts`' second registrar) reads it either (the AST
* walk of each body finds no read): both doors leave the caller to the
* umbrella's wrapper, so neither host literal names a `resolveExecCtx` member.
*/
const MOVED_REGISTRARS_READING_NO_EXECCTX: Readonly<Partial<Record<(typeof SITE_FILES)[number], number>>> = {
'discovery-routes.ts': 1,
'form-routes.ts': 1,
'meta-item-history-routes.ts': 1,
'meta-object-state-routes.ts': 1,
};

/** A stack frame in any censused file, as `<file>:<line>:<column>`. */
Expand Down Expand Up @@ -802,13 +821,24 @@ describe('[#13160] §2 the consumer surface, counted from the tree', () => {
// out, one forwarder's two mentions in). Seventeen forwarders now:
// (17 - 1) + 3 - 2, the module carrying one host literal that reads the seam,
// so neither map gains an entry.
expect(SITE_FILES).toHaveLength(17);
//
// [#23050 slice 13e] 62 sites / 145 mentions UNCHANGED — the same MOVE. The
// seventh and eighth `/meta` groups, the object state-machine read and the
// published read, left for `meta-object-state-routes.ts` (a new module on the
// list) and `meta-item-history-routes.ts` (already on it) with NO site, and
// neither host literal the inner method builds for them names a
// `resolveExecCtx` member, so no mention moved either:
// 5 + 9 + 5 + 7 + 8 + 1 + 5 + 2 + 1 + 1 + 1 + 0 + 6 + 3 + 1 + 2 + 5 + 0 = 62
// sites. Still seventeen forwarders: (18 - 1) + 4 - 4, the published read
// counted as the history module's second registrar in BOTH maps and the new
// module in `MOVED_REGISTRARS_READING_NO_EXECCTX`.
expect(SITE_FILES).toHaveLength(18);
expect(SITES.length).toBe(62);
expect([...SOURCES.values()].reduce((n, text) => n + text.split('resolveExecCtx').length - 1, 0)).toBe(145);
const extraMovedRegistrars = Object.values(EXTRA_MOVED_REGISTRARS).reduce((n, k) => n + (k ?? 0), 0);
expect(extraMovedRegistrars).toBe(3);
expect(extraMovedRegistrars).toBe(4);
const registrarsReadingNoExecCtx = Object.values(MOVED_REGISTRARS_READING_NO_EXECCTX).reduce((n, k) => n + (k ?? 0), 0);
expect(registrarsReadingNoExecCtx).toBe(2);
expect(registrarsReadingNoExecCtx).toBe(4);
expect(FORWARDER_LINES, 'one host-literal resolveExecCtx forwarder per moved registrar whose body reads it: one was respelt or removed, or a module moved without joining SITE_FILES')
.toHaveLength(SITE_FILES.length - 1 + extraMovedRegistrars - registrarsReadingNoExecCtx);
});
Expand Down
7 changes: 6 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 @@ -280,7 +280,12 @@ describe('[#20338] census: every draft switch in rest-server.ts is ledgered', ()
// carrying no draft switch: none of its five doors reads one, so no ledger row
// changed:
// 8 = 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 3 + 2 + 1 + 0, length 16 → 17.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(17);
// [#23050 slice 13e] The object state-machine read moved to
// `meta-object-state-routes.ts`, and the published read to
// `meta-item-history-routes.ts`, carrying no draft switch: neither door reads
// one, so no ledger row changed:
// 8 = 0 + 0 + 0 + 0 + 0 + 2 + 0 + 0 + 0 + 0 + 0 + 0 + 0 + 3 + 2 + 1 + 0 + 0, length 17 → 18.
expect(REST_SERVER_REGISTRAR_FILES).toHaveLength(18);
expect(SOURCE_FILES.map((file) => file.fileName)).toEqual([...REST_SERVER_REGISTRAR_FILES]);
expect(SITES).toHaveLength(8);
});
Expand Down
Loading
Loading