fix(contracts): honor options outside the schema projection cache

Bypass target-only memoization when Standard JSON Schema library options are supplied. Verify shared and inline projections remain independent in either call order.
This commit is contained in:
morluto committed 2026-10-15 01:41:31 +08:00
1 parent 204b9f3ac1
commit 0df0e4f53e
2 files changed
+38 -3

No files matched your search

+33 -1
View File
@@ -10,9 +10,41 @@ import {
presentInputJsonSchema,
} from "./inputSchemaPresentation.js";
import { toolContract } from "./toolContracts.js";
import { toolInputSchemaWithMetadata } from "./toolSchemaMetadata.js";
import {
toolInputSchemaWithMetadata,
transformAdvertisedInputJsonSchema,
} from "./toolSchemaMetadata.js";
import { JAVASCRIPT_APPLICATION_EVIDENCE_EXAMPLE } from "./javascript/javascriptRuntimeReconciliationExample.js";
it.each(["draft-2020-12", "draft-07"] as const)(
"honors projection options independently of cached wire transformations in %s",
(target) => {
const range = z.strictObject({ offset: z.number(), length: z.number() });
const canonical = toolInputSchemaWithMetadata({
...toolContract("open_binary"),
inputSchema: z.strictObject({ before: range, after: range }),
});
const transform = (schema: Record<string, unknown>) => ({
...schema,
title: "Transformed range inputs",
});
const options = { target, libraryOptions: { reused: "inline" as const } };
for (const inlineFirst of [false, true]) {
const wrapped = transformAdvertisedInputJsonSchema(canonical, transform);
const project = wrapped["~standard"].jsonSchema.input;
if (inlineFirst) project(options);
const shared = project({ target });
expect(JSON.stringify(shared)).toContain('"$ref":');
const inline = project(options);
expect(inline).toEqual(
transform(canonical["~standard"].jsonSchema.input(options)),
);
expect(JSON.stringify(inline)).not.toContain('"$ref":');
expect(project({ target })).toBe(shared);
}
},
);
it.each(["draft-2020-12", "draft-07"] as const)(
"preserves the complete inline Evidence contract before deferred authentication in %s",
(target) => {
+5 -2
View File
@@ -146,7 +146,10 @@ export const transformAdvertisedInputJsonSchema = <Schema>(
const byTarget = new Map<string, Record<string, unknown>>();
const transformedInput = (options: unknown): unknown => {
const target = Reflect.get(options as object, "target");
if (typeof target === "string") {
const cacheable =
typeof target === "string" &&
Reflect.get(options as object, "libraryOptions") === undefined;
if (cacheable) {
const cached = byTarget.get(target);
if (cached !== undefined) return cached;
}
@@ -154,7 +157,7 @@ export const transformAdvertisedInputJsonSchema = <Schema>(
const transformed = isSchemaRecord(projected)
? transform(projected)
: projected;
if (typeof target === "string" && isSchemaRecord(transformed))
if (cacheable && isSchemaRecord(transformed))
byTarget.set(target, transformed);
return transformed;
};