Skip to content

Commit f081d77

Browse files
committed
refactor(metadata-protocol): move the registry materialize/strip pair out of protocol.ts (#25467 cut 11)
Move-only. The bodies of materializeFromRegistry, governServedObject, stripMaterializedFromRegistry and stripServedObjectColumns move into the new protocol-registry-materialize.ts (host shape B: an `engine` getter and callbacks for materializeFromRegistry / stripMaterializedFromRegistry). Every member keeps its name, docblock and signature in the class as a stub that delegates. governServedItem and stripServedSystemColumns gain an `export` in place so the new module can import them; index.ts names its exports one by one, so the published surface does not move. Claude-Session: https://claude.ai/code/session_011WCU5ZbGMk9LUhayKvYPsx Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0790d0a commit f081d77

2 files changed

Lines changed: 165 additions & 53 deletions

File tree

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
// Copyright (c) 2026 ObjectStack. Licensed under the Apache-2.0 license.
2+
3+
/**
4+
* [#25467 cut 11] The registry materialize/strip pair of
5+
* `ObjectStackProtocolImplementation` — the read exits' registry-side
6+
* convergence of a served object document (`materializeFromRegistry`), the
7+
* whole of what a `/meta` read exit owes an object document
8+
* (`governServedObject`), the write path's inverse of the registry seam
9+
* (`stripMaterializedFromRegistry`) and the whole of what the write path owes
10+
* the read (`stripServedObjectColumns`) — moved out of `protocol.ts` whole.
11+
*
12+
* A MOVE, not a redesign. Each function below is the body of the
13+
* `ObjectStackProtocolImplementation` method of the same name as it stood,
14+
* re-indented one level and otherwise unedited: the same reads, in the same
15+
* order, with the same `try`/`catch` fallbacks for partial registry doubles.
16+
*
17+
* ## How the moved bodies reach the class
18+
*
19+
* The shape #23050 set for `rest-server.ts` (its `data-action-routes.ts`
20+
* header carries the full reasoning), as `protocol-batch-data.ts` applies it.
21+
* The class keeps every method — the published `dist/*.d.ts` lists each one's
22+
* name, docblock and declared signature — and each body is one literal of
23+
* exactly the {@link RegistryMaterializeHost} members that method reads, plus
24+
* a `.call`. A method is handed over as a closure that calls it on the
25+
* protocol at call time, so a test double or spy set on the instance is still
26+
* the one the body reaches, and the moved bodies that call one another do so
27+
* through the protocol, exactly as before. Every other reader of these four
28+
* methods keeps calling the class method.
29+
*
30+
* The field the bodies read, `engine`, goes as a GETTER, so each read happens
31+
* when the body makes it: tests replace it on a constructed protocol.
32+
*
33+
* The module-level declarations of `protocol.ts` the bodies read stay there
34+
* and are imported from there: `canonicalMetaType`, which code that did not
35+
* move reads too, and `governServedItem` / `stripServedSystemColumns`, whose
36+
* docblocks the class's read and write paths cite by name. The module cycle is
37+
* evaluation-safe because every one of them is read only inside a function
38+
* body, never while the two modules are first evaluated. `index.ts` names what
39+
* it publishes from `protocol.ts` one by one, so the `export` the last two
40+
* gained for this keeps them off the published surface, and nothing
41+
* re-exports this module, so the published surface does not move.
42+
*/
43+
44+
import type { ObjectStackProtocolImplementation } from './protocol.js';
45+
import { canonicalMetaType, governServedItem, stripServedSystemColumns } from './protocol.js';
46+
47+
/**
48+
* The `ObjectStackProtocolImplementation` members the functions below read —
49+
* every one of them, and no other; each function's `this` is the `Pick` of the
50+
* members IT reads. Each is typed AS the class member (an indexed access on
51+
* the class), so a change to a member's signature on the class is a change
52+
* here too, and naming a member the class does not have is a compile error.
53+
* See the module header for which members are getters.
54+
*/
55+
export interface RegistryMaterializeHost {
56+
readonly engine: ObjectStackProtocolImplementation['engine'];
57+
materializeFromRegistry: ObjectStackProtocolImplementation['materializeFromRegistry'];
58+
stripMaterializedFromRegistry: ObjectStackProtocolImplementation['stripMaterializedFromRegistry'];
59+
}
60+
61+
/** The body of `ObjectStackProtocolImplementation#materializeFromRegistry`; the method's docblock there is its contract. */
62+
export function materializeFromRegistry<T>(
63+
this: Pick<RegistryMaterializeHost, 'engine'>,
64+
type: string, body: T): T {
65+
if (canonicalMetaType(type) !== 'object') return body;
66+
if (body === null || typeof body !== 'object') return body;
67+
const registry = (this.engine as any)?.registry;
68+
// Partial registry doubles in tests predate this method; a host that
69+
// cannot answer the gate answers exactly as it did before. The
70+
// `provisionSearchCompanionOnto` fallback keeps a double that was
71+
// written against #8038's narrower seam serving what it served then,
72+
// rather than silently losing the companion convergence to a rename.
73+
if (registry && typeof registry.materializeServedObjectOnto === 'function') {
74+
try {
75+
return registry.materializeServedObjectOnto(body) as T;
76+
} catch {
77+
// A read over an in-memory flag, the registry's own resolved
78+
// answer and the body's own fields; a failure here must not turn
79+
// a served schema into a 5xx.
80+
return body;
81+
}
82+
}
83+
if (registry && typeof registry.provisionSearchCompanionOnto === 'function') {
84+
try {
85+
return registry.provisionSearchCompanionOnto(body) as T;
86+
} catch {
87+
return body;
88+
}
89+
}
90+
return body;
91+
}
92+
93+
/** The body of `ObjectStackProtocolImplementation#governServedObject`; the method's docblock there is its contract. */
94+
export function governServedObject<T>(
95+
this: Pick<RegistryMaterializeHost, 'materializeFromRegistry'>,
96+
type: string, item: T): T {
97+
return this.materializeFromRegistry(type, governServedItem(type, item));
98+
}
99+
100+
/** The body of `ObjectStackProtocolImplementation#stripMaterializedFromRegistry`; the method's docblock there is its contract. */
101+
export function stripMaterializedFromRegistry<T>(
102+
this: Pick<RegistryMaterializeHost, 'engine'>,
103+
type: string, item: T): T {
104+
if (canonicalMetaType(type) !== 'object') return item;
105+
if (item === null || typeof item !== 'object') return item;
106+
const registry = (this.engine as any)?.registry;
107+
// Partial registry doubles in tests predate this method; the narrower
108+
// #8038 spelling keeps such a host stripping what it stripped before
109+
// rather than silently losing the companion strip to a rename.
110+
if (registry && typeof registry.stripMaterializedStampsFrom === 'function') {
111+
try {
112+
return registry.stripMaterializedStampsFrom(item) as T;
113+
} catch {
114+
// A read over the body's own fields; a failure here must not
115+
// turn a save into a 5xx.
116+
return item;
117+
}
118+
}
119+
if (registry && typeof registry.stripProvisionedSearchCompanionFrom === 'function') {
120+
try {
121+
return registry.stripProvisionedSearchCompanionFrom(item) as T;
122+
} catch {
123+
return item;
124+
}
125+
}
126+
return item;
127+
}
128+
129+
/** The body of `ObjectStackProtocolImplementation#stripServedObjectColumns`; the method's docblock there is its contract. */
130+
export function stripServedObjectColumns<T>(
131+
this: Pick<RegistryMaterializeHost, 'stripMaterializedFromRegistry'>,
132+
type: string, item: T): T {
133+
return this.stripMaterializedFromRegistry(type, stripServedSystemColumns(type, item));
134+
}

‎packages/metadata-protocol/src/protocol.ts‎

Lines changed: 31 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -386,6 +386,13 @@ import {
386386
isUnservedSealedOverlay,
387387
servedLockState,
388388
} from './protocol-lock-state.js';
389+
// [#25467 cut 11] The registry materialize/strip pair of the served object document.
390+
import {
391+
governServedObject,
392+
materializeFromRegistry,
393+
stripMaterializedFromRegistry,
394+
stripServedObjectColumns,
395+
} from './protocol-registry-materialize.js';
389396

390397
/**
391398
* Canonical Zod schema per metadata type lives in
@@ -560,7 +567,7 @@ function canonicalizeMetaRequestType<T extends { type: string }>(request: T): T
560567
* PUT round-trip would persist the injected columns into `sys_metadata`, and the
561568
* #4326 byte-identical invariant would break the day this shipped.
562569
*/
563-
function governServedItem<T>(type: string, item: T): T {
570+
export function governServedItem<T>(type: string, item: T): T {
564571
if (canonicalMetaType(type) !== 'object') return item;
565572
return applyInjectedSystemColumns(applyAuditFieldGovernance(item));
566573
}
@@ -583,7 +590,7 @@ function governServedItem<T>(type: string, item: T): T {
583590
* the platform's own definition, and a declared `owner_id` carrying the author's
584591
* own label survives untouched.
585592
*/
586-
function stripServedSystemColumns<T>(type: string, item: T): T {
593+
export function stripServedSystemColumns<T>(type: string, item: T): T {
587594
return canonicalMetaType(type) === 'object' ? stripInjectedSystemColumns(item) : item;
588595
}
589596

@@ -9069,32 +9076,12 @@ export class ObjectStackProtocolImplementation implements
90699076
* which is the majority path this converges the minority onto).
90709077
*/
90719078
private materializeFromRegistry<T>(type: string, body: T): T {
9072-
if (canonicalMetaType(type) !== 'object') return body;
9073-
if (body === null || typeof body !== 'object') return body;
9074-
const registry = (this.engine as any)?.registry;
9075-
// Partial registry doubles in tests predate this method; a host that
9076-
// cannot answer the gate answers exactly as it did before. The
9077-
// `provisionSearchCompanionOnto` fallback keeps a double that was
9078-
// written against #8038's narrower seam serving what it served then,
9079-
// rather than silently losing the companion convergence to a rename.
9080-
if (registry && typeof registry.materializeServedObjectOnto === 'function') {
9081-
try {
9082-
return registry.materializeServedObjectOnto(body) as T;
9083-
} catch {
9084-
// A read over an in-memory flag, the registry's own resolved
9085-
// answer and the body's own fields; a failure here must not turn
9086-
// a served schema into a 5xx.
9087-
return body;
9088-
}
9089-
}
9090-
if (registry && typeof registry.provisionSearchCompanionOnto === 'function') {
9091-
try {
9092-
return registry.provisionSearchCompanionOnto(body) as T;
9093-
} catch {
9094-
return body;
9095-
}
9096-
}
9097-
return body;
9079+
// [#25467 cut 11] The body moved to `protocol-registry-materialize.ts` (see `RegistryMaterializeHost`).
9080+
const readEngine = () => this.engine;
9081+
const host: ThisParameterType<typeof materializeFromRegistry> = {
9082+
get engine() { return readEngine(); },
9083+
};
9084+
return (materializeFromRegistry<T>).call(host, type, body);
90989085
}
90999086

91009087
/**
@@ -9107,7 +9094,11 @@ export class ObjectStackProtocolImplementation implements
91079094
* #7556's boundary).
91089095
*/
91099096
private governServedObject<T>(type: string, item: T): T {
9110-
return this.materializeFromRegistry(type, governServedItem(type, item));
9097+
// [#25467 cut 11] The body moved to `protocol-registry-materialize.ts` (see `RegistryMaterializeHost`).
9098+
const host: ThisParameterType<typeof governServedObject> = {
9099+
materializeFromRegistry: (...a) => this.materializeFromRegistry(...a),
9100+
};
9101+
return (governServedObject<T>).call(host, type, item);
91119102
}
91129103

91139104
/**
@@ -9132,29 +9123,12 @@ export class ObjectStackProtocolImplementation implements
91329123
* named stamp — the same reason {@link materializeFromRegistry} does.
91339124
*/
91349125
private stripMaterializedFromRegistry<T>(type: string, item: T): T {
9135-
if (canonicalMetaType(type) !== 'object') return item;
9136-
if (item === null || typeof item !== 'object') return item;
9137-
const registry = (this.engine as any)?.registry;
9138-
// Partial registry doubles in tests predate this method; the narrower
9139-
// #8038 spelling keeps such a host stripping what it stripped before
9140-
// rather than silently losing the companion strip to a rename.
9141-
if (registry && typeof registry.stripMaterializedStampsFrom === 'function') {
9142-
try {
9143-
return registry.stripMaterializedStampsFrom(item) as T;
9144-
} catch {
9145-
// A read over the body's own fields; a failure here must not
9146-
// turn a save into a 5xx.
9147-
return item;
9148-
}
9149-
}
9150-
if (registry && typeof registry.stripProvisionedSearchCompanionFrom === 'function') {
9151-
try {
9152-
return registry.stripProvisionedSearchCompanionFrom(item) as T;
9153-
} catch {
9154-
return item;
9155-
}
9156-
}
9157-
return item;
9126+
// [#25467 cut 11] The body moved to `protocol-registry-materialize.ts` (see `RegistryMaterializeHost`).
9127+
const readEngine = () => this.engine;
9128+
const host: ThisParameterType<typeof stripMaterializedFromRegistry> = {
9129+
get engine() { return readEngine(); },
9130+
};
9131+
return (stripMaterializedFromRegistry<T>).call(host, type, item);
91589132
}
91599133

91609134
/**
@@ -9163,7 +9137,11 @@ export class ObjectStackProtocolImplementation implements
91639137
* path owes {@link governServedObject}.
91649138
*/
91659139
private stripServedObjectColumns<T>(type: string, item: T): T {
9166-
return this.stripMaterializedFromRegistry(type, stripServedSystemColumns(type, item));
9140+
// [#25467 cut 11] The body moved to `protocol-registry-materialize.ts` (see `RegistryMaterializeHost`).
9141+
const host: ThisParameterType<typeof stripServedObjectColumns> = {
9142+
stripMaterializedFromRegistry: (...a) => this.stripMaterializedFromRegistry(...a),
9143+
};
9144+
return (stripServedObjectColumns<T>).call(host, type, item);
91679145
}
91689146

91699147
/**

0 commit comments

Comments
 (0)