fix: enforce console schema contract safeguards
This commit is contained in:
@@ -262,18 +262,22 @@ const TraceFrameSchema = Schema.Struct({
|
||||
state_changes: JsonObjectSchema,
|
||||
});
|
||||
|
||||
const StructuralPathPartsSchema = Schema.Array(PathSegmentSchema).pipe(
|
||||
Schema.filter((parts) => parts.length > 0),
|
||||
);
|
||||
|
||||
const InputPathBindingSchema = Schema.Struct({
|
||||
path: Schema.Union(
|
||||
Schema.String,
|
||||
Schema.Struct({
|
||||
parts: Schema.Array(PathSegmentSchema),
|
||||
parts: StructuralPathPartsSchema,
|
||||
root: Schema.Literal("input", "state", "context"),
|
||||
}),
|
||||
),
|
||||
target: Schema.Union(
|
||||
Schema.String,
|
||||
Schema.Struct({
|
||||
parts: Schema.Array(PathSegmentSchema),
|
||||
parts: StructuralPathPartsSchema,
|
||||
root: Schema.Literal("local"),
|
||||
}),
|
||||
),
|
||||
@@ -283,7 +287,7 @@ const InputValueBindingSchema = Schema.Struct({
|
||||
target: Schema.Union(
|
||||
Schema.String,
|
||||
Schema.Struct({
|
||||
parts: Schema.Array(PathSegmentSchema),
|
||||
parts: StructuralPathPartsSchema,
|
||||
root: Schema.Literal("local"),
|
||||
}),
|
||||
),
|
||||
@@ -295,22 +299,18 @@ const InputBindingSchema = Schema.Union(
|
||||
InputValueBindingSchema,
|
||||
);
|
||||
|
||||
const StatePathPartsSchema = Schema.Array(PathSegmentSchema).pipe(
|
||||
Schema.filter((parts) => parts.length > 0),
|
||||
);
|
||||
|
||||
const OutputBindingSchema = Schema.Struct({
|
||||
source: Schema.Union(
|
||||
Schema.String,
|
||||
Schema.Struct({
|
||||
parts: Schema.Array(PathSegmentSchema),
|
||||
parts: StructuralPathPartsSchema,
|
||||
root: Schema.Literal("local"),
|
||||
}),
|
||||
),
|
||||
target: Schema.Union(
|
||||
Schema.String,
|
||||
Schema.Struct({
|
||||
parts: StatePathPartsSchema,
|
||||
parts: StructuralPathPartsSchema,
|
||||
root: Schema.Literal("state"),
|
||||
}),
|
||||
),
|
||||
|
||||
@@ -1100,6 +1100,49 @@ describe("authored RPC and manifest schema parity", () => {
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects structural binding paths without any segments", () => {
|
||||
const inputPayload = {
|
||||
workspace_id: "console.demo",
|
||||
revision: 3,
|
||||
step_id: "render",
|
||||
};
|
||||
const outputPayload = { ...inputPayload, revision: 4 };
|
||||
const cases = [
|
||||
{
|
||||
schema: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
|
||||
payload: {
|
||||
...inputPayload,
|
||||
bindings: [{ path: { root: "input", parts: [] }, target: "title" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
schema: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
|
||||
payload: {
|
||||
...inputPayload,
|
||||
bindings: [{ path: "input.title", target: { root: "local", parts: [] } }],
|
||||
},
|
||||
},
|
||||
{
|
||||
schema: WorkflowDraftWorkspacesSetStepInputBindingsPayloadSchema,
|
||||
payload: {
|
||||
...inputPayload,
|
||||
bindings: [{ target: { root: "local", parts: [] }, value: "title" }],
|
||||
},
|
||||
},
|
||||
{
|
||||
schema: WorkflowDraftWorkspacesSetStepOutputBindingsPayloadSchema,
|
||||
payload: {
|
||||
...outputPayload,
|
||||
bindings: [{ source: { root: "local", parts: [] }, target: "state.title" }],
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
for (const testCase of cases) {
|
||||
expect(accepts(testCase.schema, testCase.payload)).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it("catalogs every authored RPC exactly once", () => {
|
||||
const expectedMethods = [
|
||||
"workflow.health",
|
||||
|
||||
Reference in New Issue
Block a user