Skip to content

Commit 78c2e50

Browse files
jkomorosclaudehappy-otter
authored
Fix wish() object syntax not working after compilation (CT-1084) (commontoolsinc#2173)
The argumentSchema in built-in.ts only accepted strings, causing object parameters like { query: "#favorites" } to be coerced to "{}" during the compilation pipeline. Updated argumentSchema to use anyOf with both string and object schemas, matching the TARGET_SCHEMA in wish.ts that the builtin implementation uses. Added two compilation-level tests that verify object syntax works through the full compileRecipe -> loadRecipe -> run pipeline. 🤖 Generated with [Claude Code](https://claude.ai/code) via [Happy](https://happy.engineering) Co-authored-by: Claude <[email protected]> Co-authored-by: Happy <[email protected]>
1 parent 03c6e19 commit 78c2e50

File tree

2 files changed

+122
-2
lines changed

2 files changed

+122
-2
lines changed

packages/runner/src/builder/built-in.ts

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -158,8 +158,19 @@ export function wish<T = unknown>(
158158
type: "ref",
159159
implementation: "wish",
160160
argumentSchema: {
161-
type: "string",
162-
default: "",
161+
anyOf: [{
162+
type: "string",
163+
default: "",
164+
}, {
165+
type: "object",
166+
properties: {
167+
query: { type: "string" },
168+
path: { type: "array", items: { type: "string" } },
169+
schema: { type: "object" },
170+
context: { type: "object", additionalProperties: { asCell: true } },
171+
scope: { type: "array", items: { type: "string" } },
172+
},
173+
}],
163174
} as const satisfies JSONSchema,
164175
resultSchema,
165176
})(param);

packages/runner/test/wish.test.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -761,4 +761,113 @@ describe("wish built-in", () => {
761761
}
762762
});
763763
});
764+
765+
describe("compiled pattern with object-based wish syntax", () => {
766+
it("preserves object syntax through compilation pipeline", async () => {
767+
// This test ensures that wish({ query: "..." }) object syntax works
768+
// when patterns are compiled and deployed (CT-1084)
769+
const spaceCell = runtime.getCell(space, space).withTx(tx);
770+
const spaceData = { testField: "compiled pattern value" };
771+
spaceCell.set(spaceData);
772+
773+
await tx.commit();
774+
await runtime.idle();
775+
tx = runtime.edit();
776+
777+
// Compile a pattern that uses object-based wish syntax
778+
const program = {
779+
main: "/main.tsx",
780+
files: [
781+
{
782+
name: "/main.tsx",
783+
contents: [
784+
"import { recipe, wish } from 'commontools';",
785+
"export default recipe<{}>('Compiled Wish Test', () => {",
786+
" const spaceResult = wish({ query: '/' });",
787+
" return { spaceResult };",
788+
"});",
789+
].join("\n"),
790+
},
791+
],
792+
};
793+
794+
const compiled = await runtime.recipeManager.compileRecipe(program);
795+
const recipeId = runtime.recipeManager.registerRecipe(compiled, program);
796+
const loadedRecipe = await runtime.recipeManager.loadRecipe(
797+
recipeId,
798+
space,
799+
tx,
800+
);
801+
802+
const resultCell = runtime.getCell<{
803+
spaceResult?: { result?: unknown };
804+
}>(
805+
space,
806+
"compiled wish test result",
807+
undefined,
808+
tx,
809+
);
810+
const result = runtime.run(tx, loadedRecipe, {}, resultCell);
811+
await tx.commit();
812+
await runtime.idle();
813+
tx = runtime.edit();
814+
815+
await runtime.idle();
816+
817+
// The wish should resolve to the space cell data, wrapped in { result: ... }
818+
expect(result.key("spaceResult").get()?.result).toEqual(spaceData);
819+
});
820+
821+
it("preserves object syntax with path through compilation", async () => {
822+
const spaceCell = runtime.getCell(space, space).withTx(tx);
823+
spaceCell.set({
824+
nested: { deep: { value: "found it" } },
825+
});
826+
827+
await tx.commit();
828+
await runtime.idle();
829+
tx = runtime.edit();
830+
831+
const program = {
832+
main: "/main.tsx",
833+
files: [
834+
{
835+
name: "/main.tsx",
836+
contents: [
837+
"import { recipe, wish } from 'commontools';",
838+
"export default recipe<{}>('Compiled Wish Path Test', () => {",
839+
" const deepValue = wish({ query: '/', path: ['nested', 'deep', 'value'] });",
840+
" return { deepValue };",
841+
"});",
842+
].join("\n"),
843+
},
844+
],
845+
};
846+
847+
const compiled = await runtime.recipeManager.compileRecipe(program);
848+
const recipeId = runtime.recipeManager.registerRecipe(compiled, program);
849+
const loadedRecipe = await runtime.recipeManager.loadRecipe(
850+
recipeId,
851+
space,
852+
tx,
853+
);
854+
855+
const resultCell = runtime.getCell<{
856+
deepValue?: { result?: string };
857+
}>(
858+
space,
859+
"compiled wish path test result",
860+
undefined,
861+
tx,
862+
);
863+
const result = runtime.run(tx, loadedRecipe, {}, resultCell);
864+
await tx.commit();
865+
await runtime.idle();
866+
tx = runtime.edit();
867+
868+
await runtime.idle();
869+
870+
expect(result.key("deepValue").get()?.result).toEqual("found it");
871+
});
872+
});
764873
});

0 commit comments

Comments
 (0)