mirror of
https://github.com/morluto/rea.git
synced 2026-11-02 21:06:04 +00:00
Reuse successful format validation for exact authenticated immutable native and imported JavaScript results. Preserve schema normalization and alias topology before borrowing source data; owned JavaScript graph wrappers retain their existing metadata-normalization path. Validated with 130 source tests, 36 compiled CLI/MCP boundary tests, 27 default-deadline integration tests, full checks/docs/build, and paired real retained-Evidence tasks. The report preserves cold costs, previous negative results, incomplete resource-stopped runs, and the limits of offline token estimates. Fixes #4810 Co-authored-by: gensokyo-dev <342580529+gensokyo-dev@users.noreply.github.com> Co-authored-by: Kaoru0822 <208281322+kaoru0822-kitauji@users.noreply.github.com>
213 lines
7.6 KiB
TypeScript
213 lines
7.6 KiB
TypeScript
import { isProxy } from "node:util/types";
|
|
|
|
import type { JsonValue } from "./jsonValue.js";
|
|
|
|
const immutableSnapshots = new WeakSet<object>();
|
|
interface SnapshotEntry {
|
|
readonly value: unknown;
|
|
readonly exiting: boolean;
|
|
}
|
|
|
|
/** Recognize an owned JSON snapshot sealed by this module, or an immutable scalar. */
|
|
export const isImmutableJsonSnapshot = (value: unknown): value is JsonValue =>
|
|
value === null ||
|
|
typeof value === "string" ||
|
|
typeof value === "boolean" ||
|
|
(typeof value === "number" && Number.isFinite(value)) ||
|
|
(typeof value === "object" && immutableSnapshots.has(value));
|
|
|
|
/** Borrow one own member of a completed snapshot, carrying its sealing proof. */
|
|
export const immutableJsonSnapshotMember = (
|
|
snapshot: unknown,
|
|
key: string,
|
|
): JsonValue | undefined => {
|
|
if (
|
|
typeof snapshot !== "object" ||
|
|
snapshot === null ||
|
|
!immutableSnapshots.has(snapshot)
|
|
)
|
|
return undefined;
|
|
const descriptor = Object.getOwnPropertyDescriptor(snapshot, key);
|
|
if (descriptor === undefined || !Object.hasOwn(descriptor, "value"))
|
|
return undefined;
|
|
// Completed sealing checked and froze every reachable member. Register only
|
|
// the selected child; do not traverse it again or add per-node weak entries.
|
|
const member = descriptor.value as JsonValue;
|
|
if (typeof member === "object" && member !== null)
|
|
immutableSnapshots.add(member);
|
|
return member;
|
|
};
|
|
|
|
/** Match a completed snapshot to its parsed representation without recursion. */
|
|
export const canBorrowParsedJsonSnapshot = <Parsed extends JsonValue>(
|
|
source: unknown,
|
|
parsed: Parsed,
|
|
): source is Parsed => {
|
|
if (!isImmutableJsonSnapshot(source)) return false;
|
|
if (Object.is(source, parsed)) return true;
|
|
const pending: unknown[] = [source, parsed];
|
|
const compared = new Map<object, object>();
|
|
const sources = new Map<object, object>();
|
|
while (pending.length > 0) {
|
|
const right = pending.pop();
|
|
const left = pending.pop();
|
|
if (
|
|
typeof left !== "object" ||
|
|
left === null ||
|
|
typeof right !== "object" ||
|
|
right === null
|
|
) {
|
|
if (!Object.is(left, right)) return false;
|
|
continue;
|
|
}
|
|
if (
|
|
isProxy(right) ||
|
|
Object.getPrototypeOf(left) !== Object.getPrototypeOf(right)
|
|
)
|
|
return false;
|
|
const previous = compared.get(left);
|
|
if (previous !== undefined) {
|
|
if (previous !== right) return false;
|
|
continue;
|
|
}
|
|
if (sources.has(right)) return false;
|
|
// Preserve sharing in both directions, including descendants of an
|
|
// identical subtree that may also appear in a separately parsed field.
|
|
compared.set(left, right);
|
|
sources.set(right, left);
|
|
const keys = Reflect.ownKeys(left);
|
|
if (keys.length !== Reflect.ownKeys(right).length) return false;
|
|
for (const key of keys) {
|
|
// A parser may drop hidden fields or normalize enumerability. Generic
|
|
// deep equality omits these differences and can borrow unparsed data.
|
|
if (typeof key !== "string") return false;
|
|
const a = Object.getOwnPropertyDescriptor(left, key);
|
|
const b = Object.getOwnPropertyDescriptor(right, key);
|
|
if (
|
|
a === undefined ||
|
|
b === undefined ||
|
|
!Object.hasOwn(a, "value") ||
|
|
!Object.hasOwn(b, "value") ||
|
|
a.enumerable !== b.enumerable
|
|
)
|
|
return false;
|
|
pending.push(a.value as unknown, b.value as unknown);
|
|
}
|
|
}
|
|
return true;
|
|
};
|
|
|
|
/** Seal an already parsed, owned JSON snapshot for immutable zero-copy reuse. */
|
|
export const freezeJsonSnapshot = <T extends JsonValue>(value: T): T => {
|
|
const pending: SnapshotEntry[] = [{ value, exiting: false }];
|
|
// These sets exist only during traversal, while the owned root already
|
|
// retains every child. Strong sets avoid per-node ephemeron GC work.
|
|
const visited = new Set<object>();
|
|
const ancestors = new Set<object>();
|
|
const objects: object[] = [];
|
|
while (pending.length > 0) {
|
|
const entry = pending.pop();
|
|
if (entry === undefined) break;
|
|
const item = entry.value;
|
|
if (entry.exiting && typeof item === "object" && item !== null) {
|
|
ancestors.delete(item);
|
|
visited.add(item);
|
|
objects.push(item);
|
|
continue;
|
|
}
|
|
if (isImmutableJsonSnapshot(item)) continue;
|
|
if (typeof item !== "object" || item === null)
|
|
throw new TypeError("Immutable JSON snapshots require JSON values");
|
|
if (ancestors.has(item))
|
|
throw new TypeError(
|
|
"Immutable JSON snapshots cannot contain circular references",
|
|
);
|
|
if (visited.has(item)) continue;
|
|
ancestors.add(item);
|
|
pending.push({ value: item, exiting: true });
|
|
pushSnapshotChildren(item, pending);
|
|
}
|
|
// Register only after every value is checked and every object is frozen.
|
|
// An arbitrary Object.freeze call cannot forge this snapshot identity.
|
|
for (const item of objects) Object.freeze(item);
|
|
// Register the requested root, rather than retaining weak entries for every
|
|
// nested object. Sealing an Evidence or bundle skips previously sealed roots.
|
|
if (typeof value === "object" && value !== null)
|
|
immutableSnapshots.add(value);
|
|
return value;
|
|
};
|
|
|
|
/**
|
|
* Seal exclusively owned JSON in bounded steps, authenticating only completion.
|
|
* Abandonment or failure can leave the provisional input partly frozen.
|
|
*/
|
|
export function* freezeOwnedJsonSnapshotSteps<T extends JsonValue>(
|
|
value: T,
|
|
): Generator<void, T> {
|
|
const pending: SnapshotEntry[] = [{ value, exiting: false }];
|
|
const visited = new Set<object>();
|
|
const ancestors = new Set<object>();
|
|
let examined = 0;
|
|
try {
|
|
while (pending.length > 0) {
|
|
if (examined++ === 4096) {
|
|
examined = 0;
|
|
yield;
|
|
}
|
|
const entry = pending.pop();
|
|
if (entry === undefined) break;
|
|
const item = entry.value;
|
|
if (entry.exiting && typeof item === "object" && item !== null) {
|
|
ancestors.delete(item);
|
|
visited.add(item);
|
|
continue;
|
|
}
|
|
if (isImmutableJsonSnapshot(item)) continue;
|
|
if (typeof item !== "object" || item === null)
|
|
throw new TypeError("Immutable JSON snapshots require JSON values");
|
|
if (ancestors.has(item))
|
|
throw new TypeError(
|
|
"Immutable JSON snapshots cannot contain circular references",
|
|
);
|
|
if (visited.has(item)) continue;
|
|
ancestors.add(item);
|
|
pending.push({ value: item, exiting: true });
|
|
pushSnapshotChildren(item, pending);
|
|
// Lock every inspected edge before the scheduler can release control.
|
|
Object.freeze(item);
|
|
}
|
|
if (typeof value === "object" && value !== null)
|
|
immutableSnapshots.add(value);
|
|
return value;
|
|
} finally {
|
|
pending.length = 0;
|
|
visited.clear();
|
|
ancestors.clear();
|
|
}
|
|
}
|
|
|
|
const pushSnapshotChildren = (item: object, pending: SnapshotEntry[]): void => {
|
|
if (isProxy(item))
|
|
throw new TypeError("Immutable JSON snapshots cannot contain proxies");
|
|
const prototype: unknown = Object.getPrototypeOf(item);
|
|
if (
|
|
!Array.isArray(item) &&
|
|
prototype !== Object.prototype &&
|
|
prototype !== null
|
|
)
|
|
throw new TypeError("Immutable JSON snapshots require ordinary objects");
|
|
if (Array.isArray(item))
|
|
for (let index = 0; index < item.length; index += 1)
|
|
if (!Object.hasOwn(item, index))
|
|
throw new TypeError(
|
|
"Immutable JSON snapshots cannot contain sparse arrays",
|
|
);
|
|
for (const key of Object.getOwnPropertyNames(item)) {
|
|
const descriptor = Object.getOwnPropertyDescriptor(item, key);
|
|
if (descriptor === undefined || !Object.hasOwn(descriptor, "value"))
|
|
throw new TypeError("Immutable JSON snapshots cannot contain accessors");
|
|
const child: unknown = descriptor.value;
|
|
pending.push({ value: child, exiting: false });
|
|
}
|
|
};
|