Files
rea/src/domain/analysisView/environmentStateWriteOmitted.ts
T
Edgar Arakelyan 5375ff0f9a feat(environment): extend evidence-gated write coverage
Retain bodiless writes, proven omitted fields, identity-free projections and digit-group sequences. Preserve late projection provenance and current omission facts, and check absence statuses before replay shortcuts.

Assisted-by: Kogasa (gensokyo-skills:kogasa-surprise-testing)
2026-10-22 23:46:52 +08:00

147 lines
4.4 KiB
TypeScript

import {
at,
isObject,
listItems,
projectsFields,
scalarText,
type JsonObject,
type StateItemView,
type StateListView,
type StateRecordFacts,
type StateView,
} from "./environmentStateRules.js";
import {
succeeded,
type CheckOutcome,
type ReadBodies,
type SimulatedState,
} from "./environmentStateWriteChecks.js";
/**
* Fields a type's recordings always carried that a later successful whole read shows the
* application leaves out of an entity the state holds, as APIs that omit empty or false members
* do: no entity of the type must carry them, in any of its views.
*/
export class OmittedFields {
readonly #omitted = new Map<string, Set<string>>();
constructor(private readonly retain: (bytes: number) => void) {}
/** Check an item or list read with the fields reads showed omitted, this one's included, no longer required, naming this one's in the outcome. */
check<View extends StateItemView | StateListView>(
state: SimulatedState,
view: View,
record: StateRecordFacts,
read: ReadBodies & {
readonly idField: string;
readonly collection: string;
readonly itemId?: string;
},
compare: (view: View) => CheckOutcome | undefined,
): CheckOutcome | undefined {
const omitted = lackedFields(state, view, record, read);
const noted = this.#omitted.get(view.type);
const loosened = relaxed(
view,
omitted.length === 0 ? noted : new Set([...(noted ?? []), ...omitted]),
);
const outcome = compare(loosened);
return outcome === undefined ? undefined : { ...outcome, omitted };
}
/** Note the fields a read the gate counts shows left out, so no view of its type requires them. */
keep(view: StateView, outcome: CheckOutcome): void {
if (view.shape !== "item" && view.shape !== "list") return;
for (const field of outcome.omitted ?? []) this.#note(view.type, field);
}
/** The current required-field facts, shared by observations, checks and final output. */
view(view: StateView): StateView {
return view.shape === "item" || view.shape === "list"
? relaxed(view, this.#omitted.get(view.type))
: view;
}
/** The views with the fields reads showed omitted no longer required. */
views(views: readonly StateView[]): StateView[] {
return views.map((view) => this.view(view));
}
#note(type: string, field: string): void {
let fields = this.#omitted.get(type);
if (fields === undefined) {
fields = new Set();
this.#omitted.set(type, fields);
}
if (!fields.has(field)) {
this.retain(64 + field.length * 2);
fields.add(field);
}
}
}
/** The required fields a successful whole read's held entities lack. */
const lackedFields = (
state: SimulatedState,
view: StateItemView | StateListView,
record: StateRecordFacts,
read: Pick<ReadBodies, "body"> & {
readonly idField: string;
readonly collection: string;
readonly itemId?: string;
},
): string[] => {
if (
!succeeded(record) ||
read.body === undefined ||
projectsFields(record.query)
)
return [];
const lacked = new Set<string>();
for (const entity of heldEntities(state, view, read))
for (const field of view.required_fields)
if (!Object.hasOwn(entity, field)) lacked.add(field);
return [...lacked];
};
/** The entities a read shows that the state holds: an item read's object naming the path's id, a list read's items. */
const heldEntities = (
state: SimulatedState,
view: StateItemView | StateListView,
read: Pick<ReadBodies, "body"> & {
readonly idField: string;
readonly collection: string;
readonly itemId?: string;
},
): JsonObject[] => {
const object = view.shape === "item" ? at(read.body!, view.pointer) : null;
const shown =
view.shape === "list"
? (listItems(read.body!, view.pointer) ?? [])
: isObject(object) &&
read.itemId !== undefined &&
scalarText(object[read.idField]) === read.itemId
? [object]
: [];
return shown.filter((entity) => {
const id = scalarText(entity[read.idField]);
return (
id !== undefined &&
state.entity(view.type, read.collection, id) !== undefined
);
});
};
const relaxed = <View extends StateItemView | StateListView>(
view: View,
omitted: ReadonlySet<string> | undefined,
): View =>
omitted === undefined
? view
: {
...view,
required_fields: view.required_fields.filter(
(field) => !omitted.has(field),
),
};