@vedangiitb/qwintly-core 1.4.7 → 1.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/dist/ai/prompts/codegen.prompt.js +2 -2
  2. package/dist/ai/prompts/codegen.prompt.js.map +1 -1
  3. package/dist/ai/prompts/examples/codegen.examples.d.ts.map +1 -1
  4. package/dist/ai/prompts/examples/codegen.examples.js +40 -22
  5. package/dist/ai/prompts/examples/codegen.examples.js.map +1 -1
  6. package/dist/ai/toolLoop/toolLoopRunner.d.ts.map +1 -1
  7. package/dist/ai/toolLoop/toolLoopRunner.js +1 -4
  8. package/dist/ai/toolLoop/toolLoopRunner.js.map +1 -1
  9. package/dist/ai/tools/implementations/factories.d.ts +39 -2
  10. package/dist/ai/tools/implementations/factories.d.ts.map +1 -1
  11. package/dist/ai/tools/implementations/insertElement.impl.d.ts +39 -3
  12. package/dist/ai/tools/implementations/insertElement.impl.d.ts.map +1 -1
  13. package/dist/ai/tools/implementations/insertElement.impl.js +54 -9
  14. package/dist/ai/tools/implementations/insertElement.impl.js.map +1 -1
  15. package/dist/ai/tools/schemas/insertElement.schema.d.ts +107 -2
  16. package/dist/ai/tools/schemas/insertElement.schema.d.ts.map +1 -1
  17. package/dist/ai/tools/schemas/insertElement.schema.js +35 -42
  18. package/dist/ai/tools/schemas/insertElement.schema.js.map +1 -1
  19. package/dist/ai/tools/validators/builderElement.zod.d.ts +88 -2
  20. package/dist/ai/tools/validators/builderElement.zod.d.ts.map +1 -1
  21. package/dist/ai/tools/validators/builderElement.zod.js +22 -21
  22. package/dist/ai/tools/validators/builderElement.zod.js.map +1 -1
  23. package/dist/tests/insertUpdate.impl.test.js +122 -10
  24. package/dist/tests/insertUpdate.impl.test.js.map +1 -1
  25. package/dist/tests/toolLoopRunner.routes.test.js +3 -1
  26. package/dist/tests/toolLoopRunner.routes.test.js.map +1 -1
  27. package/package.json +1 -1
@@ -40,10 +40,9 @@ test("insert/update tools: inject ids, insert under parent, update props and cla
40
40
  const insert = createInsertElementImpl(deps);
41
41
  const updateProps = createUpdatePropsImpl(deps);
42
42
  const updateClassName = createUpdateClassNameImpl(deps);
43
- const inserted = await insert("/a", "root", {
44
- type: "text",
45
- props: { text: "hello" },
46
- });
43
+ const inserted = await insert("/a", "root", [
44
+ { id: "new_el", parentId: "parent", type: "text", props: { text: "hello" } }
45
+ ]);
47
46
  assert.equal(inserted.success, true, `unexpected response: ${JSON.stringify(inserted)}`);
48
47
  const insertedId = inserted.inserted_id;
49
48
  assert.ok(typeof insertedId === "string" && insertedId.startsWith("el_"));
@@ -51,7 +50,9 @@ test("insert/update tools: inject ids, insert under parent, update props and cla
51
50
  route: "/a",
52
51
  parent_id: "root",
53
52
  before_id: "existing",
54
- element: { type: "text", props: { text: "first" } },
53
+ elements: [
54
+ { id: "first_el", parentId: "parent", type: "text", props: { text: "first" } }
55
+ ],
55
56
  });
56
57
  assert.equal(inserted2.success, true, `unexpected response: ${JSON.stringify(inserted2)}`);
57
58
  const inserted2Id = inserted2.inserted_id;
@@ -134,11 +135,9 @@ test("dynamic/nested routes resolution in tools", async () => {
134
135
  const updateProps = createUpdatePropsImpl(deps);
135
136
  const updateClassName = createUpdateClassNameImpl(deps);
136
137
  const deleteElement = createDeleteElementImpl(deps);
137
- // Test insert_element on dynamic route '/product/123'
138
- const insRes = await insert("/product/123", "root", {
139
- type: "button",
140
- props: { text: "Buy Now" },
141
- });
138
+ const insRes = await insert("/product/123", "root", [
139
+ { id: "buy_btn", parentId: "parent", type: "button", props: { text: "Buy Now" } }
140
+ ]);
142
141
  assert.equal(insRes.success, true);
143
142
  // Verify it updated '/app/product/[id]/pageConfig.json'
144
143
  const afterProduct = JSON.parse(await fs.readFile(filePath, "utf-8"));
@@ -170,4 +169,117 @@ test("dynamic/nested routes resolution in tools", async () => {
170
169
  await fs.rm(workspaceRoot, { recursive: true, force: true });
171
170
  }
172
171
  });
172
+ test("insert tool: supports flat elements list with parent-child mapping", async () => {
173
+ const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), "qwintly-core-flat-"));
174
+ try {
175
+ const routeDir = path.join(workspaceRoot, "app", "flat-test");
176
+ await fs.mkdir(routeDir, { recursive: true });
177
+ const filePath = path.join(routeDir, "pageConfig.json");
178
+ await fs.writeFile(filePath, JSON.stringify({
179
+ elements: [
180
+ {
181
+ id: "root",
182
+ type: "div",
183
+ children: [],
184
+ },
185
+ ],
186
+ }, null, 2) + "\n", "utf-8");
187
+ const deps = { workspaceRoot, fs: makeRealFs() };
188
+ const insert = createInsertElementImpl(deps);
189
+ // Call insert using the flat elements format
190
+ const inserted = await insert({
191
+ route: "/flat-test",
192
+ parent_id: "root",
193
+ elements: [
194
+ {
195
+ id: "container",
196
+ parentId: "parent",
197
+ type: "div",
198
+ className: "bg-red-500",
199
+ },
200
+ {
201
+ id: "heading",
202
+ parentId: "container",
203
+ type: "text",
204
+ props: { text: "Hello from Flat List" },
205
+ },
206
+ ],
207
+ });
208
+ assert.equal(inserted.success, true, `unexpected response: ${JSON.stringify(inserted)}`);
209
+ const after = JSON.parse(await fs.readFile(filePath, "utf-8"));
210
+ const children = after.elements[0].children;
211
+ assert.equal(children.length, 1);
212
+ const insertedContainer = children[0];
213
+ assert.equal(insertedContainer.className, "bg-red-500");
214
+ assert.equal(insertedContainer.type, "div");
215
+ // Verify children inside the container
216
+ assert.equal(insertedContainer.children.length, 1);
217
+ const insertedHeading = insertedContainer.children[0];
218
+ assert.equal(insertedHeading.type, "text");
219
+ assert.equal(insertedHeading.props.text, "Hello from Flat List");
220
+ // Verify that proper real IDs starting with el_ were generated
221
+ assert.ok(insertedContainer.id.startsWith("el_"));
222
+ assert.ok(insertedHeading.id.startsWith("el_"));
223
+ }
224
+ finally {
225
+ await fs.rm(workspaceRoot, { recursive: true, force: true });
226
+ }
227
+ });
228
+ test("insert tool: supports multiple sibling root elements in flat array", async () => {
229
+ const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), "qwintly-core-multi-flat-"));
230
+ try {
231
+ const routeDir = path.join(workspaceRoot, "app", "multi-flat-test");
232
+ await fs.mkdir(routeDir, { recursive: true });
233
+ const filePath = path.join(routeDir, "pageConfig.json");
234
+ await fs.writeFile(filePath, JSON.stringify({
235
+ elements: [
236
+ {
237
+ id: "root",
238
+ type: "div",
239
+ children: [],
240
+ },
241
+ ],
242
+ }, null, 2) + "\n", "utf-8");
243
+ const deps = { workspaceRoot, fs: makeRealFs() };
244
+ const insert = createInsertElementImpl(deps);
245
+ // Call insert with two sibling root elements
246
+ const inserted = await insert({
247
+ route: "/multi-flat-test",
248
+ parent_id: "root",
249
+ elements: [
250
+ {
251
+ id: "card_1",
252
+ parentId: "parent",
253
+ type: "div",
254
+ className: "card-1",
255
+ },
256
+ {
257
+ id: "card_2",
258
+ parentId: "parent",
259
+ type: "div",
260
+ className: "card-2",
261
+ },
262
+ {
263
+ id: "heading_1",
264
+ parentId: "card_1",
265
+ type: "text",
266
+ props: { text: "Heading 1" },
267
+ },
268
+ ],
269
+ });
270
+ assert.equal(inserted.success, true, `unexpected response: ${JSON.stringify(inserted)}`);
271
+ assert.ok(Array.isArray(inserted.inserted_ids));
272
+ assert.equal(inserted.inserted_ids.length, 2);
273
+ const after = JSON.parse(await fs.readFile(filePath, "utf-8"));
274
+ const children = after.elements[0].children;
275
+ assert.equal(children.length, 2);
276
+ assert.equal(children[0].className, "card-1");
277
+ assert.equal(children[1].className, "card-2");
278
+ assert.equal(children[0].children.length, 1);
279
+ assert.equal(children[0].children[0].props.text, "Heading 1");
280
+ }
281
+ finally {
282
+ await fs.rm(workspaceRoot, { recursive: true, force: true });
283
+ }
284
+ });
173
285
  //# sourceMappingURL=insertUpdate.impl.test.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"insertUpdate.impl.test.js","sourceRoot":"","sources":["../../src/tests/insertUpdate.impl.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs/promises\";\nimport { createInsertElementImpl } from \"../ai/tools/implementations/insertElement.impl.js\";\nimport { createUpdatePropsImpl } from \"../ai/tools/implementations/updateProps.impl.js\";\nimport { createUpdateClassNameImpl } from \"../ai/tools/implementations/updateClassName.impl.js\";\nimport { createDeleteElementImpl } from \"../ai/tools/implementations/deleteElement.impl.js\";\nimport { matchRoute } from \"../ai/tools/helpers/pageConfigJson.helpers.js\";\n\ntype Deps = Parameters<typeof createInsertElementImpl>[0];\ntype CoreFs = Deps[\"fs\"];\n\nconst makeRealFs = (overrides?: Partial<CoreFs>): CoreFs => {\n return {\n readFile: async (absolutePath) => fs.readFile(absolutePath, \"utf-8\"),\n writeFile: async (absolutePath, content) =>\n fs.writeFile(absolutePath, content ?? \"\", \"utf-8\"),\n mkdirp: async (absoluteDir) => {\n await fs.mkdir(absoluteDir, { recursive: true });\n },\n rmFile: async (absolutePath) => fs.rm(absolutePath, { force: true }),\n stat: async (absolutePath) => fs.stat(absolutePath),\n safeReadDir: async (absoluteDir) =>\n fs.readdir(absoluteDir, { withFileTypes: true }),\n ...(overrides ?? {}),\n };\n};\n\ntest(\"insert/update tools: inject ids, insert under parent, update props and classname\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-\"));\n try {\n const routeDir = path.join(workspaceRoot, \"app\", \"a\");\n await fs.mkdir(routeDir, { recursive: true });\n const filePath = path.join(routeDir, \"pageConfig.json\");\n await fs.writeFile(\n filePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"root\",\n type: \"div\",\n children: [{ id: \"existing\", type: \"text\", props: { text: \"x\" } }],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n const deps = { workspaceRoot, fs: makeRealFs() } as any;\n const insert = createInsertElementImpl(deps);\n const updateProps = createUpdatePropsImpl(deps);\n const updateClassName = createUpdateClassNameImpl(deps);\n\n const inserted = await insert(\"/a\", \"root\", {\n type: \"text\",\n props: { text: \"hello\" },\n } as any);\n assert.equal(\n (inserted as any).success,\n true,\n `unexpected response: ${JSON.stringify(inserted)}`,\n );\n const insertedId = (inserted as any).inserted_id as string;\n assert.ok(typeof insertedId === \"string\" && insertedId.startsWith(\"el_\"));\n\n const inserted2 = await insert({\n route: \"/a\",\n parent_id: \"root\",\n before_id: \"existing\",\n element: { type: \"text\", props: { text: \"first\" } },\n } as any);\n assert.equal(\n (inserted2 as any).success,\n true,\n `unexpected response: ${JSON.stringify(inserted2)}`,\n );\n const inserted2Id = (inserted2 as any).inserted_id as string;\n assert.ok(typeof inserted2Id === \"string\" && inserted2Id.startsWith(\"el_\"));\n\n const up1 = await updateProps({\n route: \"/a\",\n element_id: insertedId,\n text: \"updated\",\n href: \"/x\",\n } as any);\n assert.equal((up1 as any).success, true);\n\n const up2 = await updateClassName(\"/a\", insertedId, \"text-red-500\");\n assert.equal((up2 as any).success, true);\n\n const after = JSON.parse(await fs.readFile(filePath, \"utf-8\"));\n const children = after.elements[0].children as any[];\n assert.equal(children[0].id, inserted2Id);\n assert.equal(children[1].id, \"existing\");\n\n const found = children.find((e) => e.id === insertedId);\n assert.ok(found);\n assert.equal(found.props.text, \"updated\");\n assert.equal(found.props.href, \"/x\");\n assert.equal(found.className, \"text-red-500\");\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n\ntest(\"matchRoute helper correctly matches physical paths\", () => {\n // exact match\n assert.equal(matchRoute(\"/\", \"/\"), true);\n assert.equal(matchRoute(\"/dashboard\", \"/dashboard\"), true);\n assert.equal(matchRoute(\"/dashboard\", \"/dashboard/\"), true);\n assert.equal(matchRoute(\"/dashboard/\", \"/dashboard\"), true);\n \n // dynamic segment\n assert.equal(matchRoute(\"/product/[id]\", \"/product/123\"), true);\n assert.equal(matchRoute(\"/product/[id]\", \"/product/hi\"), true);\n assert.equal(matchRoute(\"/product/[id]\", \"/product/\"), false);\n assert.equal(matchRoute(\"/product/[id]\", \"/product/123/reviews\"), false);\n assert.equal(matchRoute(\"/product/[id]/reviews\", \"/product/abc/reviews\"), true);\n\n // catch-all segment\n assert.equal(matchRoute(\"/blog/[...slug]\", \"/blog/react\"), true);\n assert.equal(matchRoute(\"/blog/[...slug]\", \"/blog/react/hooks/state\"), true);\n assert.equal(matchRoute(\"/blog/[...slug]\", \"/blog\"), false); // required catch-all requires at least one segment\n\n // optional catch-all segment\n assert.equal(matchRoute(\"/blog/[[...slug]]\", \"/blog\"), true);\n assert.equal(matchRoute(\"/blog/[[...slug]]\", \"/blog/react\"), true);\n assert.equal(matchRoute(\"/blog/[[...slug]]\", \"/blog/react/hooks\"), true);\n});\n\ntest(\"dynamic/nested routes resolution in tools\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-dynamic-\"));\n try {\n // 1. Create a dynamic route /product/[id]\n const routeDir = path.join(workspaceRoot, \"app\", \"product\", \"[id]\");\n await fs.mkdir(routeDir, { recursive: true });\n const filePath = path.join(routeDir, \"pageConfig.json\");\n await fs.writeFile(\n filePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"root\",\n type: \"div\",\n children: [{ id: \"prod_title\", type: \"text\", props: { text: \"Product Name\" } }],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n // 2. Create a catch-all route /blog/[...slug]\n const blogDir = path.join(workspaceRoot, \"app\", \"blog\", \"[...slug]\");\n await fs.mkdir(blogDir, { recursive: true });\n const blogFilePath = path.join(blogDir, \"pageConfig.json\");\n await fs.writeFile(\n blogFilePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"blog_root\",\n type: \"div\",\n children: [{ id: \"blog_content\", type: \"text\", props: { text: \"Blog Post\" } }],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n const deps = { workspaceRoot, fs: makeRealFs() } as any;\n const insert = createInsertElementImpl(deps);\n const updateProps = createUpdatePropsImpl(deps);\n const updateClassName = createUpdateClassNameImpl(deps);\n const deleteElement = createDeleteElementImpl(deps);\n\n // Test insert_element on dynamic route '/product/123'\n const insRes = await insert(\"/product/123\", \"root\", {\n type: \"button\",\n props: { text: \"Buy Now\" },\n } as any);\n assert.equal(insRes.success, true);\n\n // Verify it updated '/app/product/[id]/pageConfig.json'\n const afterProduct = JSON.parse(await fs.readFile(filePath, \"utf-8\"));\n assert.equal(afterProduct.elements[0].children.length, 2);\n const newBtn = afterProduct.elements[0].children[1];\n assert.equal(newBtn.type, \"button\");\n assert.equal(newBtn.props.text, \"Buy Now\");\n\n // Test update_props on dynamic route '/product/hi-there'\n const upRes = await updateProps({\n route: \"/product/hi-there\",\n element_id: \"prod_title\",\n text: \"Updated Product Title\",\n } as any);\n assert.equal(upRes.success, true);\n\n // Test update_classname on catch-all route '/blog/react/hooks/state'\n const classRes = await updateClassName(\"/blog/react/hooks/state\", \"blog_content\", \"text-blue-500\");\n assert.equal(classRes.success, true);\n\n // Verify blog config updated\n const afterBlog = JSON.parse(await fs.readFile(blogFilePath, \"utf-8\"));\n const updatedTitle = afterBlog.elements[0].children[0];\n assert.equal(updatedTitle.className, \"text-blue-500\");\n\n // Test delete_element on catch-all route\n const delRes = await deleteElement(\"/blog/react/something-else\", \"blog_content\");\n assert.equal(delRes.success, true);\n\n const afterDelBlog = JSON.parse(await fs.readFile(blogFilePath, \"utf-8\"));\n assert.equal(afterDelBlog.elements[0].children.length, 0);\n\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n"]}
1
+ {"version":3,"file":"insertUpdate.impl.test.js","sourceRoot":"","sources":["../../src/tests/insertUpdate.impl.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs/promises\";\nimport { createInsertElementImpl } from \"../ai/tools/implementations/insertElement.impl.js\";\nimport { createUpdatePropsImpl } from \"../ai/tools/implementations/updateProps.impl.js\";\nimport { createUpdateClassNameImpl } from \"../ai/tools/implementations/updateClassName.impl.js\";\nimport { createDeleteElementImpl } from \"../ai/tools/implementations/deleteElement.impl.js\";\nimport { matchRoute } from \"../ai/tools/helpers/pageConfigJson.helpers.js\";\n\ntype Deps = Parameters<typeof createInsertElementImpl>[0];\ntype CoreFs = Deps[\"fs\"];\n\nconst makeRealFs = (overrides?: Partial<CoreFs>): CoreFs => {\n return {\n readFile: async (absolutePath) => fs.readFile(absolutePath, \"utf-8\"),\n writeFile: async (absolutePath, content) =>\n fs.writeFile(absolutePath, content ?? \"\", \"utf-8\"),\n mkdirp: async (absoluteDir) => {\n await fs.mkdir(absoluteDir, { recursive: true });\n },\n rmFile: async (absolutePath) => fs.rm(absolutePath, { force: true }),\n stat: async (absolutePath) => fs.stat(absolutePath),\n safeReadDir: async (absoluteDir) =>\n fs.readdir(absoluteDir, { withFileTypes: true }),\n ...(overrides ?? {}),\n };\n};\n\ntest(\"insert/update tools: inject ids, insert under parent, update props and classname\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-\"));\n try {\n const routeDir = path.join(workspaceRoot, \"app\", \"a\");\n await fs.mkdir(routeDir, { recursive: true });\n const filePath = path.join(routeDir, \"pageConfig.json\");\n await fs.writeFile(\n filePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"root\",\n type: \"div\",\n children: [{ id: \"existing\", type: \"text\", props: { text: \"x\" } }],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n const deps = { workspaceRoot, fs: makeRealFs() } as any;\n const insert = createInsertElementImpl(deps);\n const updateProps = createUpdatePropsImpl(deps);\n const updateClassName = createUpdateClassNameImpl(deps);\n\n const inserted = await insert(\"/a\", \"root\", [\n { id: \"new_el\", parentId: \"parent\", type: \"text\", props: { text: \"hello\" } }\n ] as any);\n assert.equal(\n (inserted as any).success,\n true,\n `unexpected response: ${JSON.stringify(inserted)}`,\n );\n const insertedId = (inserted as any).inserted_id as string;\n assert.ok(typeof insertedId === \"string\" && insertedId.startsWith(\"el_\"));\n\n const inserted2 = await insert({\n route: \"/a\",\n parent_id: \"root\",\n before_id: \"existing\",\n elements: [\n { id: \"first_el\", parentId: \"parent\", type: \"text\", props: { text: \"first\" } }\n ],\n } as any);\n assert.equal(\n (inserted2 as any).success,\n true,\n `unexpected response: ${JSON.stringify(inserted2)}`,\n );\n const inserted2Id = (inserted2 as any).inserted_id as string;\n assert.ok(typeof inserted2Id === \"string\" && inserted2Id.startsWith(\"el_\"));\n\n const up1 = await updateProps({\n route: \"/a\",\n element_id: insertedId,\n text: \"updated\",\n href: \"/x\",\n } as any);\n assert.equal((up1 as any).success, true);\n\n const up2 = await updateClassName(\"/a\", insertedId, \"text-red-500\");\n assert.equal((up2 as any).success, true);\n\n const after = JSON.parse(await fs.readFile(filePath, \"utf-8\"));\n const children = after.elements[0].children as any[];\n assert.equal(children[0].id, inserted2Id);\n assert.equal(children[1].id, \"existing\");\n\n const found = children.find((e) => e.id === insertedId);\n assert.ok(found);\n assert.equal(found.props.text, \"updated\");\n assert.equal(found.props.href, \"/x\");\n assert.equal(found.className, \"text-red-500\");\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n\ntest(\"matchRoute helper correctly matches physical paths\", () => {\n // exact match\n assert.equal(matchRoute(\"/\", \"/\"), true);\n assert.equal(matchRoute(\"/dashboard\", \"/dashboard\"), true);\n assert.equal(matchRoute(\"/dashboard\", \"/dashboard/\"), true);\n assert.equal(matchRoute(\"/dashboard/\", \"/dashboard\"), true);\n \n // dynamic segment\n assert.equal(matchRoute(\"/product/[id]\", \"/product/123\"), true);\n assert.equal(matchRoute(\"/product/[id]\", \"/product/hi\"), true);\n assert.equal(matchRoute(\"/product/[id]\", \"/product/\"), false);\n assert.equal(matchRoute(\"/product/[id]\", \"/product/123/reviews\"), false);\n assert.equal(matchRoute(\"/product/[id]/reviews\", \"/product/abc/reviews\"), true);\n\n // catch-all segment\n assert.equal(matchRoute(\"/blog/[...slug]\", \"/blog/react\"), true);\n assert.equal(matchRoute(\"/blog/[...slug]\", \"/blog/react/hooks/state\"), true);\n assert.equal(matchRoute(\"/blog/[...slug]\", \"/blog\"), false); // required catch-all requires at least one segment\n\n // optional catch-all segment\n assert.equal(matchRoute(\"/blog/[[...slug]]\", \"/blog\"), true);\n assert.equal(matchRoute(\"/blog/[[...slug]]\", \"/blog/react\"), true);\n assert.equal(matchRoute(\"/blog/[[...slug]]\", \"/blog/react/hooks\"), true);\n});\n\ntest(\"dynamic/nested routes resolution in tools\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-dynamic-\"));\n try {\n // 1. Create a dynamic route /product/[id]\n const routeDir = path.join(workspaceRoot, \"app\", \"product\", \"[id]\");\n await fs.mkdir(routeDir, { recursive: true });\n const filePath = path.join(routeDir, \"pageConfig.json\");\n await fs.writeFile(\n filePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"root\",\n type: \"div\",\n children: [{ id: \"prod_title\", type: \"text\", props: { text: \"Product Name\" } }],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n // 2. Create a catch-all route /blog/[...slug]\n const blogDir = path.join(workspaceRoot, \"app\", \"blog\", \"[...slug]\");\n await fs.mkdir(blogDir, { recursive: true });\n const blogFilePath = path.join(blogDir, \"pageConfig.json\");\n await fs.writeFile(\n blogFilePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"blog_root\",\n type: \"div\",\n children: [{ id: \"blog_content\", type: \"text\", props: { text: \"Blog Post\" } }],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n const deps = { workspaceRoot, fs: makeRealFs() } as any;\n const insert = createInsertElementImpl(deps);\n const updateProps = createUpdatePropsImpl(deps);\n const updateClassName = createUpdateClassNameImpl(deps);\n const deleteElement = createDeleteElementImpl(deps);\n\n const insRes = await insert(\"/product/123\", \"root\", [\n { id: \"buy_btn\", parentId: \"parent\", type: \"button\", props: { text: \"Buy Now\" } }\n ] as any);\n assert.equal(insRes.success, true);\n\n // Verify it updated '/app/product/[id]/pageConfig.json'\n const afterProduct = JSON.parse(await fs.readFile(filePath, \"utf-8\"));\n assert.equal(afterProduct.elements[0].children.length, 2);\n const newBtn = afterProduct.elements[0].children[1];\n assert.equal(newBtn.type, \"button\");\n assert.equal(newBtn.props.text, \"Buy Now\");\n\n // Test update_props on dynamic route '/product/hi-there'\n const upRes = await updateProps({\n route: \"/product/hi-there\",\n element_id: \"prod_title\",\n text: \"Updated Product Title\",\n } as any);\n assert.equal(upRes.success, true);\n\n // Test update_classname on catch-all route '/blog/react/hooks/state'\n const classRes = await updateClassName(\"/blog/react/hooks/state\", \"blog_content\", \"text-blue-500\");\n assert.equal(classRes.success, true);\n\n // Verify blog config updated\n const afterBlog = JSON.parse(await fs.readFile(blogFilePath, \"utf-8\"));\n const updatedTitle = afterBlog.elements[0].children[0];\n assert.equal(updatedTitle.className, \"text-blue-500\");\n\n // Test delete_element on catch-all route\n const delRes = await deleteElement(\"/blog/react/something-else\", \"blog_content\");\n assert.equal(delRes.success, true);\n\n const afterDelBlog = JSON.parse(await fs.readFile(blogFilePath, \"utf-8\"));\n assert.equal(afterDelBlog.elements[0].children.length, 0);\n\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n\ntest(\"insert tool: supports flat elements list with parent-child mapping\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-flat-\"));\n try {\n const routeDir = path.join(workspaceRoot, \"app\", \"flat-test\");\n await fs.mkdir(routeDir, { recursive: true });\n const filePath = path.join(routeDir, \"pageConfig.json\");\n await fs.writeFile(\n filePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"root\",\n type: \"div\",\n children: [],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n const deps = { workspaceRoot, fs: makeRealFs() } as any;\n const insert = createInsertElementImpl(deps);\n\n // Call insert using the flat elements format\n const inserted = await insert({\n route: \"/flat-test\",\n parent_id: \"root\",\n elements: [\n {\n id: \"container\",\n parentId: \"parent\",\n type: \"div\",\n className: \"bg-red-500\",\n },\n {\n id: \"heading\",\n parentId: \"container\",\n type: \"text\",\n props: { text: \"Hello from Flat List\" },\n },\n ],\n } as any);\n\n assert.equal(\n (inserted as any).success,\n true,\n `unexpected response: ${JSON.stringify(inserted)}`,\n );\n\n const after = JSON.parse(await fs.readFile(filePath, \"utf-8\"));\n const children = after.elements[0].children as any[];\n assert.equal(children.length, 1);\n \n const insertedContainer = children[0];\n assert.equal(insertedContainer.className, \"bg-red-500\");\n assert.equal(insertedContainer.type, \"div\");\n \n // Verify children inside the container\n assert.equal(insertedContainer.children.length, 1);\n const insertedHeading = insertedContainer.children[0];\n assert.equal(insertedHeading.type, \"text\");\n assert.equal(insertedHeading.props.text, \"Hello from Flat List\");\n \n // Verify that proper real IDs starting with el_ were generated\n assert.ok(insertedContainer.id.startsWith(\"el_\"));\n assert.ok(insertedHeading.id.startsWith(\"el_\"));\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n\ntest(\"insert tool: supports multiple sibling root elements in flat array\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-multi-flat-\"));\n try {\n const routeDir = path.join(workspaceRoot, \"app\", \"multi-flat-test\");\n await fs.mkdir(routeDir, { recursive: true });\n const filePath = path.join(routeDir, \"pageConfig.json\");\n await fs.writeFile(\n filePath,\n JSON.stringify(\n {\n elements: [\n {\n id: \"root\",\n type: \"div\",\n children: [],\n },\n ],\n },\n null,\n 2,\n ) + \"\\n\",\n \"utf-8\",\n );\n\n const deps = { workspaceRoot, fs: makeRealFs() } as any;\n const insert = createInsertElementImpl(deps);\n\n // Call insert with two sibling root elements\n const inserted = await insert({\n route: \"/multi-flat-test\",\n parent_id: \"root\",\n elements: [\n {\n id: \"card_1\",\n parentId: \"parent\",\n type: \"div\",\n className: \"card-1\",\n },\n {\n id: \"card_2\",\n parentId: \"parent\",\n type: \"div\",\n className: \"card-2\",\n },\n {\n id: \"heading_1\",\n parentId: \"card_1\",\n type: \"text\",\n props: { text: \"Heading 1\" },\n },\n ],\n } as any);\n\n assert.equal(\n (inserted as any).success,\n true,\n `unexpected response: ${JSON.stringify(inserted)}`,\n );\n\n assert.ok(Array.isArray((inserted as any).inserted_ids));\n assert.equal((inserted as any).inserted_ids.length, 2);\n\n const after = JSON.parse(await fs.readFile(filePath, \"utf-8\"));\n const children = after.elements[0].children as any[];\n assert.equal(children.length, 2);\n\n assert.equal(children[0].className, \"card-1\");\n assert.equal(children[1].className, \"card-2\");\n\n assert.equal(children[0].children.length, 1);\n assert.equal(children[0].children[0].props.text, \"Heading 1\");\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n"]}
@@ -23,7 +23,9 @@ test("tool loop: insert_element failure includes available routes and hint", asy
23
23
  args: {
24
24
  route: "/invalid-route",
25
25
  parent_id: "root",
26
- element: { type: "text", props: { text: "hi" } },
26
+ elements: [
27
+ { id: "text_el", parentId: "parent", type: "text", props: { text: "hi" } }
28
+ ],
27
29
  },
28
30
  },
29
31
  ],
@@ -1 +1 @@
1
- {"version":3,"file":"toolLoopRunner.routes.test.js","sourceRoot":"","sources":["../../src/tests/toolLoopRunner.routes.test.ts"],"names":[],"mappings":"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","sourcesContent":["import assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs/promises\";\nimport { FunctionCallingConfigMode } from \"@google/genai\";\nimport { runToolLoop } from \"../ai/toolLoop/toolLoopRunner.js\";\n\ntest(\"tool loop: insert_element failure includes available routes and hint\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-runner-\"));\n try {\n await fs.mkdir(path.join(workspaceRoot, \"app\"), { recursive: true });\n // Let's create an existing route\n await fs.mkdir(path.join(workspaceRoot, \"app\", \"dashboard\"), { recursive: true });\n await fs.writeFile(path.join(workspaceRoot, \"app\", \"dashboard\", \"pageConfig.json\"), \"{}\");\n\n let aiCalls = 0;\n const aiCall = async () => {\n aiCalls += 1;\n if (aiCalls === 1) {\n return {\n functionCalls: [\n {\n name: \"insert_element\",\n args: {\n route: \"/invalid-route\",\n parent_id: \"root\",\n element: { type: \"text\", props: { text: \"hi\" } },\n },\n },\n ],\n };\n }\n return { functionCalls: [], text: \"ok\" };\n };\n\n const res = await runToolLoop({\n initialContents: [],\n tools: [],\n workspaceRoot,\n aiCall,\n logger: async () => {},\n toolCallingMode: FunctionCallingConfigMode.ANY,\n maxSteps: 5,\n keepFullTrace: false,\n });\n\n assert.equal(res.finalText, \"ok\");\n\n const toolResponses = res.modelContents.filter(\n (c: any) =>\n c?.role === \"user\" &&\n Array.isArray(c?.parts) &&\n c.parts.some((p: any) => p?.functionResponse?.name === \"insert_element\"),\n );\n assert.equal(toolResponses.length, 1);\n\n const response = toolResponses[0].parts.find(\n (p: any) => p?.functionResponse?.name === \"insert_element\",\n )?.functionResponse?.response;\n\n assert.equal(response?.success, false);\n assert.match(String(response?.error ?? \"\"), /insert_element failed/i);\n assert.match(String(response?.error ?? \"\"), /create_new_route/i);\n assert.deepEqual(response?.available_routes, [\"/dashboard\"]);\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n\ntest(\"tool loop: get_available_routes retrieves routes\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-runner-\"));\n try {\n await fs.mkdir(path.join(workspaceRoot, \"app\"), { recursive: true });\n await fs.writeFile(path.join(workspaceRoot, \"app\", \"pageConfig.json\"), \"{}\");\n await fs.mkdir(path.join(workspaceRoot, \"app\", \"dashboard\"), { recursive: true });\n await fs.writeFile(path.join(workspaceRoot, \"app\", \"dashboard\", \"pageConfig.json\"), \"{}\");\n\n let aiCalls = 0;\n const aiCall = async () => {\n aiCalls += 1;\n if (aiCalls === 1) {\n return {\n functionCalls: [{ name: \"get_available_routes\", args: {} }],\n };\n }\n return { functionCalls: [], text: \"ok\" };\n };\n\n const res = await runToolLoop({\n initialContents: [],\n tools: [],\n workspaceRoot,\n aiCall,\n logger: async () => {},\n toolCallingMode: FunctionCallingConfigMode.ANY,\n maxSteps: 5,\n keepFullTrace: false,\n });\n\n assert.equal(res.finalText, \"ok\");\n\n const toolResponses = res.modelContents.filter(\n (c: any) =>\n c?.role === \"user\" &&\n Array.isArray(c?.parts) &&\n c.parts.some((p: any) => p?.functionResponse?.name === \"get_available_routes\"),\n );\n assert.equal(toolResponses.length, 1);\n\n const response = toolResponses[0].parts.find(\n (p: any) => p?.functionResponse?.name === \"get_available_routes\",\n )?.functionResponse?.response;\n\n assert.equal(response?.success, true);\n assert.deepEqual(response?.routes, [\"/\", \"/dashboard\"]);\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n"]}
1
+ {"version":3,"file":"toolLoopRunner.routes.test.js","sourceRoot":"","sources":["../../src/tests/toolLoopRunner.routes.test.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,oBAAoB,CAAC;AACxC,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAClC,OAAO,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAC1D,OAAO,EAAE,WAAW,EAAE,MAAM,kCAAkC,CAAC;AAE/D,IAAI,CAAC,sEAAsE,EAAE,KAAK,IAAI,EAAE;IACtF,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACvF,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,iCAAiC;QACjC,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClF,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;QAE1F,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE;YACxB,OAAO,IAAI,CAAC,CAAC;YACb,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBAClB,OAAO;oBACL,aAAa,EAAE;wBACb;4BACE,IAAI,EAAE,gBAAgB;4BACtB,IAAI,EAAE;gCACJ,KAAK,EAAE,gBAAgB;gCACvB,SAAS,EAAE,MAAM;gCACjB,QAAQ,EAAE;oCACR,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE;iCAC3E;6BACF;yBACF;qBACF;iBACF,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC3C,CAAC,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC;YAC5B,eAAe,EAAE,EAAE;YACnB,KAAK,EAAE,EAAE;YACT,aAAa;YACb,MAAM;YACN,MAAM,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;YACtB,eAAe,EAAE,yBAAyB,CAAC,GAAG;YAC9C,QAAQ,EAAE,CAAC;YACX,aAAa,EAAE,KAAK;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAElC,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAC5C,CAAC,CAAM,EAAE,EAAE,CACT,CAAC,EAAE,IAAI,KAAK,MAAM;YAClB,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC;YACvB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,KAAK,gBAAgB,CAAC,CAC3E,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEtC,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAC1C,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,KAAK,gBAAgB,CAC3D,EAAE,gBAAgB,EAAE,QAAQ,CAAC;QAE9B,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;QACvC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,wBAAwB,CAAC,CAAC;QACtE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC,EAAE,mBAAmB,CAAC,CAAC;QACjE,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,gBAAgB,EAAE,CAAC,YAAY,CAAC,CAAC,CAAC;IAC/D,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC,CAAC,CAAC;AAEH,IAAI,CAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;IAClE,MAAM,aAAa,GAAG,MAAM,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,sBAAsB,CAAC,CAAC,CAAC;IACvF,IAAI,CAAC;QACH,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QACrE,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;QAC7E,MAAM,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,WAAW,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;QAClF,MAAM,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,iBAAiB,CAAC,EAAE,IAAI,CAAC,CAAC;QAE1F,IAAI,OAAO,GAAG,CAAC,CAAC;QAChB,MAAM,MAAM,GAAG,KAAK,IAAI,EAAE;YACxB,OAAO,IAAI,CAAC,CAAC;YACb,IAAI,OAAO,KAAK,CAAC,EAAE,CAAC;gBAClB,OAAO;oBACL,aAAa,EAAE,CAAC,EAAE,IAAI,EAAE,sBAAsB,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;iBAC5D,CAAC;YACJ,CAAC;YACD,OAAO,EAAE,aAAa,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC;QAC3C,CAAC,CAAC;QAEF,MAAM,GAAG,GAAG,MAAM,WAAW,CAAC;YAC5B,eAAe,EAAE,EAAE;YACnB,KAAK,EAAE,EAAE;YACT,aAAa;YACb,MAAM;YACN,MAAM,EAAE,KAAK,IAAI,EAAE,GAAE,CAAC;YACtB,eAAe,EAAE,yBAAyB,CAAC,GAAG;YAC9C,QAAQ,EAAE,CAAC;YACX,aAAa,EAAE,KAAK;SACrB,CAAC,CAAC;QAEH,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;QAElC,MAAM,aAAa,GAAG,GAAG,CAAC,aAAa,CAAC,MAAM,CAC5C,CAAC,CAAM,EAAE,EAAE,CACT,CAAC,EAAE,IAAI,KAAK,MAAM;YAClB,KAAK,CAAC,OAAO,CAAC,CAAC,EAAE,KAAK,CAAC;YACvB,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,KAAK,sBAAsB,CAAC,CACjF,CAAC;QACF,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;QAEtC,MAAM,QAAQ,GAAG,aAAa,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,IAAI,CAC1C,CAAC,CAAM,EAAE,EAAE,CAAC,CAAC,EAAE,gBAAgB,EAAE,IAAI,KAAK,sBAAsB,CACjE,EAAE,gBAAgB,EAAE,QAAQ,CAAC;QAE9B,MAAM,CAAC,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC;QACtC,MAAM,CAAC,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC,CAAC;IAC1D,CAAC;YAAS,CAAC;QACT,MAAM,EAAE,CAAC,EAAE,CAAC,aAAa,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;IAC/D,CAAC;AACH,CAAC,CAAC,CAAC","sourcesContent":["import assert from \"node:assert/strict\";\nimport test from \"node:test\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs/promises\";\nimport { FunctionCallingConfigMode } from \"@google/genai\";\nimport { runToolLoop } from \"../ai/toolLoop/toolLoopRunner.js\";\n\ntest(\"tool loop: insert_element failure includes available routes and hint\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-runner-\"));\n try {\n await fs.mkdir(path.join(workspaceRoot, \"app\"), { recursive: true });\n // Let's create an existing route\n await fs.mkdir(path.join(workspaceRoot, \"app\", \"dashboard\"), { recursive: true });\n await fs.writeFile(path.join(workspaceRoot, \"app\", \"dashboard\", \"pageConfig.json\"), \"{}\");\n\n let aiCalls = 0;\n const aiCall = async () => {\n aiCalls += 1;\n if (aiCalls === 1) {\n return {\n functionCalls: [\n {\n name: \"insert_element\",\n args: {\n route: \"/invalid-route\",\n parent_id: \"root\",\n elements: [\n { id: \"text_el\", parentId: \"parent\", type: \"text\", props: { text: \"hi\" } }\n ],\n },\n },\n ],\n };\n }\n return { functionCalls: [], text: \"ok\" };\n };\n\n const res = await runToolLoop({\n initialContents: [],\n tools: [],\n workspaceRoot,\n aiCall,\n logger: async () => {},\n toolCallingMode: FunctionCallingConfigMode.ANY,\n maxSteps: 5,\n keepFullTrace: false,\n });\n\n assert.equal(res.finalText, \"ok\");\n\n const toolResponses = res.modelContents.filter(\n (c: any) =>\n c?.role === \"user\" &&\n Array.isArray(c?.parts) &&\n c.parts.some((p: any) => p?.functionResponse?.name === \"insert_element\"),\n );\n assert.equal(toolResponses.length, 1);\n\n const response = toolResponses[0].parts.find(\n (p: any) => p?.functionResponse?.name === \"insert_element\",\n )?.functionResponse?.response;\n\n assert.equal(response?.success, false);\n assert.match(String(response?.error ?? \"\"), /insert_element failed/i);\n assert.match(String(response?.error ?? \"\"), /create_new_route/i);\n assert.deepEqual(response?.available_routes, [\"/dashboard\"]);\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n\ntest(\"tool loop: get_available_routes retrieves routes\", async () => {\n const workspaceRoot = await fs.mkdtemp(path.join(os.tmpdir(), \"qwintly-core-runner-\"));\n try {\n await fs.mkdir(path.join(workspaceRoot, \"app\"), { recursive: true });\n await fs.writeFile(path.join(workspaceRoot, \"app\", \"pageConfig.json\"), \"{}\");\n await fs.mkdir(path.join(workspaceRoot, \"app\", \"dashboard\"), { recursive: true });\n await fs.writeFile(path.join(workspaceRoot, \"app\", \"dashboard\", \"pageConfig.json\"), \"{}\");\n\n let aiCalls = 0;\n const aiCall = async () => {\n aiCalls += 1;\n if (aiCalls === 1) {\n return {\n functionCalls: [{ name: \"get_available_routes\", args: {} }],\n };\n }\n return { functionCalls: [], text: \"ok\" };\n };\n\n const res = await runToolLoop({\n initialContents: [],\n tools: [],\n workspaceRoot,\n aiCall,\n logger: async () => {},\n toolCallingMode: FunctionCallingConfigMode.ANY,\n maxSteps: 5,\n keepFullTrace: false,\n });\n\n assert.equal(res.finalText, \"ok\");\n\n const toolResponses = res.modelContents.filter(\n (c: any) =>\n c?.role === \"user\" &&\n Array.isArray(c?.parts) &&\n c.parts.some((p: any) => p?.functionResponse?.name === \"get_available_routes\"),\n );\n assert.equal(toolResponses.length, 1);\n\n const response = toolResponses[0].parts.find(\n (p: any) => p?.functionResponse?.name === \"get_available_routes\",\n )?.functionResponse?.response;\n\n assert.equal(response?.success, true);\n assert.deepEqual(response?.routes, [\"/\", \"/dashboard\"]);\n } finally {\n await fs.rm(workspaceRoot, { recursive: true, force: true });\n }\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vedangiitb/qwintly-core",
3
- "version": "1.4.7",
3
+ "version": "1.4.9",
4
4
  "description": "Qwintly Core",
5
5
  "homepage": "https://github.com/vedangiitb/qwintly-core#readme",
6
6
  "bugs": {