fix: hoist $defs to the schema root for set_tasks
Refs like "#/$defs/AddTask" resolve from the document root, but Type.Cyclic nested the $defs inside tasks.items, so the moonshot-flavored gateway reported "$defs not found for reference". Hoist all $defs entries to the parameter schema root and strip the synthetic $id that would change fragment resolution. Update the regression test to pin the layout. Bump version to 0.2.3.
This commit is contained in:
+49
-12
@@ -38,6 +38,41 @@ function normalizeLocalRefs<T>(schema: T): T {
|
||||
return schema;
|
||||
}
|
||||
|
||||
/**
|
||||
* TypeBox's Type.Cyclic nests $defs inside the referenced item schema, but
|
||||
* refs like "#/$defs/AddTask" resolve from the document root. Strict gateways
|
||||
* (moonshot-flavored) reject a root-style ref when the definition is nested.
|
||||
* Hoist every $defs entry to the schema root and drop the synthetic $id, which
|
||||
* would otherwise change fragment resolution inside the definition.
|
||||
*/
|
||||
function hoistDefsToRoot<T>(schema: T): T {
|
||||
const defs = new Map<string, unknown>();
|
||||
function walk(node: unknown): void {
|
||||
if (Array.isArray(node)) {
|
||||
for (const item of node) walk(item);
|
||||
return;
|
||||
}
|
||||
if (!node || typeof node !== "object") return;
|
||||
const obj = node as Record<string, unknown>;
|
||||
const nested = obj["$defs"];
|
||||
if (nested && typeof nested === "object") {
|
||||
for (const [name, def] of Object.entries(nested)) {
|
||||
if (!defs.has(name)) defs.set(name, def);
|
||||
}
|
||||
delete obj["$defs"];
|
||||
}
|
||||
for (const value of Object.values(obj)) walk(value);
|
||||
}
|
||||
walk(schema);
|
||||
for (const def of defs.values()) {
|
||||
if (def && typeof def === "object") {
|
||||
delete (def as Record<string, unknown>)["$id"];
|
||||
}
|
||||
}
|
||||
if (defs.size > 0) (schema as Record<string, unknown>)["$defs"] = Object.fromEntries(defs);
|
||||
return schema;
|
||||
}
|
||||
|
||||
const AddTaskSchema = Type.Cyclic(
|
||||
{
|
||||
AddTask: Type.Object(
|
||||
@@ -61,18 +96,20 @@ const AddTaskSchema = Type.Cyclic(
|
||||
"AddTask",
|
||||
);
|
||||
|
||||
export const SetTaskParams = normalizeLocalRefs(
|
||||
Type.Object(
|
||||
{
|
||||
mode: SetTaskModeSchema,
|
||||
tasks: Type.Array(AddTaskSchema, {
|
||||
description: "Tasks to append or install. May be empty only in replace mode.",
|
||||
}),
|
||||
force: Type.Optional(
|
||||
Type.Boolean({ description: "Use only when the user explicitly abandons unfinished tasks." }),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
export const SetTaskParams = hoistDefsToRoot(
|
||||
normalizeLocalRefs(
|
||||
Type.Object(
|
||||
{
|
||||
mode: SetTaskModeSchema,
|
||||
tasks: Type.Array(AddTaskSchema, {
|
||||
description: "Tasks to append or install. May be empty only in replace mode.",
|
||||
}),
|
||||
force: Type.Optional(
|
||||
Type.Boolean({ description: "Use only when the user explicitly abandons unfinished tasks." }),
|
||||
),
|
||||
},
|
||||
{ additionalProperties: false },
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user