{
  "name": "Self-healing client-side test creator",
  "nodes": [
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "81a7e0a8-b6ed-413d-a5a9-c724e0f0909c",
              "name": "url",
              "value": "https://iqbalacomics.com/",
              "type": "string"
            },
            {
              "id": "0b8e932d-fc75-417b-8027-f8c2dc2a4bf9",
              "name": "change_request",
              "value": "Move the panel for Mr Jones's Smoking Bones to be the first in the list",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -800,
        -48
      ],
      "id": "e25b5670-7b9d-4223-8b4b-62eb85525921",
      "name": "Edit Fields"
    },
    {
      "parameters": {
        "model": "qwen/qwen3-coder-next",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        -336,
        144
      ],
      "id": "6d4d0ce2-2025-4d22-8fdf-63c1437a1fb5",
      "name": "OpenRouter Chat Model3",
      "credentials": {
        "openRouterApi": {
          "id": "baaHEuVk7u6Z0Liu",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $executionId }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        -192,
        160
      ],
      "id": "8a6ff278-97c6-4e1d-9a1d-3a3958a5e50b",
      "name": "Simple Memory2"
    },
    {
      "parameters": {
        "model": "qwen/qwen3-coder-next",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        1072,
        128
      ],
      "id": "f033bed2-f0ed-403e-964c-2d6235f1b7ae",
      "name": "OpenRouter Chat Model",
      "credentials": {
        "openRouterApi": {
          "id": "baaHEuVk7u6Z0Liu",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "{{ $executionId }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        1200,
        144
      ],
      "id": "9d551e74-04e0-4ed4-8615-dc21b8ae068d",
      "name": "Simple Memory3"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=%CONTEXT%\nYou are a QA-bot. Your job is to write effective javascript along with tests to validate the javascript in Browserless.\n\n\n%TASK%\n# 1. Write only the javascript-based tests to validate the above javascript to check the following:\n{{ $('Write QA Rules').item.json.output }}\n\n\n%OUTPUT CONTRACT%\n\nYour entire response is exactly ONE code block containing a BrowserQL mutation { ... }, built from the TEMPLATE below with only the marked sections filled in.\nDo NOT output JavaScript by itself. Do NOT output the tests as a bare array, function, or snippet outside the template. The JavaScript tests are only valid as the filled-in content of the tests: evaluate(content: \"\"\" ... \"\"\") field inside the mutation.\nDo NOT add commentary, headers, or explanation before or after the code block.\nBefore you finish, silently check: does my response start with mutation { and end with the final closing }? If not, discard it and rewrite using the template.\n\nUse below as the template, only filling in the test part. \n\n\n%OUTPUT EXAMPLE/TEMPLATE%\n```\nmutation {\n  goto(url: \"{{ $json.url }}\") {\n    status\n  }\n  setup: evaluate(content: \"\"\"\n    {{ $json.javascript }}\n  \"\"\") {\n    value\n  }\ntests: evaluate(content: \"\"\"\n  (function(){\n    function runTest(name, fn) {\n      try {\n        fn();\n        return { name, passed: true };\n      } catch (e) {\n        return { name, passed: false, error: e.message };\n      }\n    }\n\n    // --- FILL IN: re-derive any variables the tests need (TARGET_SRC, container, etc.) ---\n\n    return [\n      // --- FILL IN: one runTest(...) entry per checklist above ---\n    ];\n  })()\n\"\"\") {\n  value\n}\nsource: html {html}\n}\n```\n\n%FULL BROWSERQL WITH JAVASCRIPT%\n```\n",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        1088,
        -48
      ],
      "id": "767c5832-323b-482c-ba1f-0379a8aded32",
      "name": "Javascript for tests"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=%CHANGE_REQUEST%\n{{ $('Edit Fields').item.json.change_request }}\n\n%GENERATED DOM (HTML)%\n{{ $json.content }}\n\n%TASK%\nClient side experimentation tool uses JavaScript to manipulate DOM. Write technical specification document with following sections:\n\n1. Goal of the technical specification\n\n2. Observations made from the DOM and description of how it would be rendered in the UI\n\n3. Strategy for DOM manipulation (vanilla javascript). The steps and rules for javascript creation\n\n4. Expected DOM changes. Be very specific, ensure UI accurately reflects changes",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        -320,
        -48
      ],
      "id": "ac3e8df4-6c7e-4db4-a46c-7567716035e6",
      "name": "Write specification document"
    },
    {
      "parameters": {
        "content": "## 5) Write automated tests",
        "height": 432,
        "width": 1120
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        880,
        -144
      ],
      "typeVersion": 1,
      "id": "0add4a98-f04b-4353-9639-635c2aecb559",
      "name": "Sticky Note1"
    },
    {
      "parameters": {
        "content": "## 3) Write test rules",
        "height": 432,
        "width": 320
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -16,
        -144
      ],
      "typeVersion": 1,
      "id": "b946d181-ea2b-4fb7-8ad8-0872b292d027",
      "name": "Sticky Note3"
    },
    {
      "parameters": {
        "model": "qwen/qwen3-coder-next",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        384,
        144
      ],
      "id": "ac82f56f-b6c9-4096-a27e-c8dc72e5c2fd",
      "name": "OpenRouter Chat Model4",
      "credentials": {
        "openRouterApi": {
          "id": "baaHEuVk7u6Z0Liu",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "1234"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        528,
        144
      ],
      "id": "50438e48-c337-4178-894f-5bed38da6d01",
      "name": "Simple Memory4"
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "{{ $executionId }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        144,
        144
      ],
      "id": "33b0a0fe-3a6e-4d83-8aa5-7e34db415aa8",
      "name": "Simple Memory1"
    },
    {
      "parameters": {
        "model": "google/gemini-3.1-flash-lite",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        32,
        144
      ],
      "id": "ebb9dd6e-1d2a-4f0e-ae5b-4c6220d54a48",
      "name": "OpenRouter Chat Model2",
      "credentials": {
        "openRouterApi": {
          "id": "baaHEuVk7u6Z0Liu",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "content": "## 4) Write main JS",
        "height": 432,
        "width": 480
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        352,
        -144
      ],
      "typeVersion": 1,
      "id": "8cf4b5e4-e612-4db1-8911-cdba9d103906",
      "name": "Sticky Note4"
    },
    {
      "parameters": {
        "jsCode": "return items.map(item => {\n  let output = item.json.output;\n\n  // Strip markdown code fences if the LLM wrapped the mutation in ```graphql ... ``` or ``` ... ```\n  output = output.trim().replace(/^```(?:graphql|browserql)?\\n?/, '').replace(/```$/, '').trim();\n\n  // Checks to confirm this actually looks like a BrowserQL mutation\n  const checks = [\n    { name: 'contains mutation keyword', pass: /\\bmutation\\b/.test(output) },\n    { name: 'starts with mutation block', pass: /^mutation\\s*\\{/.test(output) },\n    { name: 'has goto field', pass: /\\bgoto\\s*\\(/.test(output) },\n    { name: 'has evaluate field', pass: /\\bevaluate\\s*\\(/.test(output) },\n    { name: 'balanced braces', pass: output.split('{').length === output.split('}').length },\n  ];\n\n  const failed = checks.filter(c => !c.pass).map(c => c.name);\n\n  if (failed.length > 0) {\n    return {\n      json: {\n        error: `Output is not valid BrowserQL — failed checks: ${failed.join(', ')}`,\n        query: output\n      }\n    };\n  }\n\n  return {\n    json: {\n      query: output\n    }\n  };\n});"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        1360,
        -48
      ],
      "id": "82d0cc68-c507-4f93-9d45-5b4361cf8664",
      "name": "Extract Javascript for string2"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=%CHANGE REQUEST DOCUMENT%\n{{ $('Write specification document').item.json.output }}\n\n%QA RULES%\n{{ $json.output }}\n\n%RENDERED DOM (HTML)%\n{{ $('Page HTML').item.json.content }}\n\n\n%TASK%\nReturn only javascript, no other text. No DOMContentLoaded. Give code in sandbox JS wrapper, like (() => {\n//The JS\nreturn true;\n})()\n\n\n%JAVASCRIPT ONLY%",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        384,
        -48
      ],
      "id": "0f70d394-33ea-4762-9503-d91c6972be7d",
      "name": "Write Main Javascript"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=%PREVIOUS OUTPUT%\n{{ $json.query }}\n\n%TASK%\n\nEnsure the output is in format:\n\n```\nmutation {\n  goto(url: \"{{ $('Set 2').item.json.url }}\") {\n    status\n  }\n  setup: evaluate(content: \"\"\"\n    {{ $('Set 2').item.json.javascript }}\n  \"\"\") {\n    value\n  }\ntests: evaluate(content: \"\"\"\n  (function(){\n    function runTest(name, fn) {\n      try {\n        fn();\n        return { name, passed: true };\n      } catch (e) {\n        return { name, passed: false, error: e.message };\n      }\n    }\n\n    // --- FILL IN: re-derive any variables the tests need (TARGET_SRC, container, etc.) ---\n\n    return [\n      // --- FILL IN: one runTest(...) entry per checklist above ---\n    ];\n  })()\n\"\"\") {\n  value\n}\nsource: html {html}\n}\n```\n\nOutput only browserql, no fluff. Tests should go in the relevant place in the template. Return entire BrowserQL\n\n```browserql\n",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        1696,
        -64
      ],
      "id": "51a0afbf-cfb7-4dab-8257-7c49cfec7fc6",
      "name": "AI Agent"
    },
    {
      "parameters": {
        "model": "qwen/qwen3-coder-next",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        1712,
        128
      ],
      "id": "7d88ee16-7128-4858-913b-d35d2772e6b9",
      "name": "OpenRouter Chat Model8",
      "credentials": {
        "openRouterApi": {
          "id": "baaHEuVk7u6Z0Liu",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "{{ $executionId }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        1808,
        144
      ],
      "id": "cf606b63-8727-4a63-8bd8-3283eebaf33e",
      "name": "Simple Memory7"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "e51cb3fa-01da-426e-bfbc-65b81faacc29",
              "name": "url",
              "value": "={{ $('Edit Fields').item.json.url }}",
              "type": "string"
            },
            {
              "id": "ac3f74c1-ff65-48c0-960c-33958a1188e8",
              "name": "javascript",
              "value": "={{ $json.output }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        960,
        -48
      ],
      "id": "6b63aa9a-b520-4939-bb2b-22e89f6e4fe2",
      "name": "Set 2"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://production-lon.browserless.io/chrome/bql?token=YOUR_TOKEN_HERE",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "={{ $json.query }}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        2064,
        -48
      ],
      "id": "1d842d64-5e29-4f92-b6e7-c61f98850937",
      "name": "Run tests"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "18baabb3-a5e5-4e7c-b6fc-c2167585acaf",
              "name": "tests_log",
              "value": "={{ $json.rawMessage }}",
              "type": "string"
            },
            {
              "id": "8baf9811-6e82-443a-9381-1b06471b12f7",
              "name": "javascript",
              "value": "={{ $('Set 2').item.json.javascript }}",
              "type": "string"
            },
            {
              "id": "c88a01e8-652a-48a9-9c77-6b29b01927e3",
              "name": "specification_doc",
              "value": "={{ $('Write specification document').item.json.output }}",
              "type": "string"
            },
            {
              "id": "361c3423-73ea-4fc4-9b59-6ecbae3db42a",
              "name": "qa_rules",
              "value": "={{ $('Write QA Rules').item.json.output }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        2480,
        512
      ],
      "id": "6bb2f253-3c67-4a01-816d-44d00d94570e",
      "name": "Final data"
    },
    {
      "parameters": {
        "jsCode": "function parseLooseArray(str) {\n  const jsonReady = str.replace(\n    /([{,]\\s*)([A-Za-z_$][A-Za-z0-9_$]*)(\\s*:)/g,\n    '$1\"$2\"$3'\n  );\n  return JSON.parse(jsonReady);\n}\n\nconst output = [];\nfor (const item of $input.all()) {\n  const json = item.json;\n  const errorList = json.errors || json.data?.errors || [];\n\n  if (errorList.length === 0) {\n    output.push({ json: { hasErrors: false, message: 'No errors found' } });\n    continue;\n  }\n\n  for (const err of errorList) {\n    const rawMessage = err.message || '';\n    const arrayMatch = rawMessage.match(/\\[[\\s\\S]*\\]/);\n    const arrayString = arrayMatch ? arrayMatch[0] : null;\n\n    if (!arrayString) {\n      // Not a \"tests\" payload error — a genuine top-level error (GraphQL\n      // syntax error, etc). Don't silently treat it as zero failures.\n      output.push({\n        json: {\n          rawMessage,\n          hasErrors: true,\n          path: err.path || null,\n          totalCount: 0,\n          passedCount: 0,\n          failedCount: 0,\n          allTests: [],\n          failedTests: []\n        }\n      });\n      continue;\n    }\n\n    let tests;\n    try {\n      tests = parseLooseArray(arrayString);\n    } catch (e) {\n      output.push({ json: { rawMessage, hasErrors: true} });\n      continue;\n    }\n\n    const failedTests = tests.filter(t => t.passed === false);\n    output.push({\n      json: {\n        rawMessage,\n        hasErrors: failedTests.length > 0,\n        totalCount: tests.length,\n        passedCount: tests.length - failedTests.length,\n        failedCount: failedTests.length,\n        allTests: tests,\n        failedTests\n      }\n    });\n  }\n}\nreturn output;"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        2224,
        -48
      ],
      "id": "52c3eaa0-f133-44a5-92de-ef0a626bd1aa",
      "name": "Evaluate tests"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=%CURRENT JAVASCRIPT%\n```\n{{ $('Set 2').item.json.javascript }}\n```\n\n%TASK%\nUpdate the javascript to fix the errors:\n{{ $json.output }}\n\n%OUTPUT%\nOutput fixed javascript only. No DOMContentLoaded.\n\n%NEW JAVASCRIPT%\n```javascript",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        2992,
        -64
      ],
      "id": "7a10d1d0-02e5-4616-87a5-6c6070a67711",
      "name": "Javascript fixer"
    },
    {
      "parameters": {
        "content": "## 7) Self-heal",
        "height": 432,
        "width": 1120
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        -144
      ],
      "typeVersion": 1,
      "id": "d6e75036-f37b-4d41-80d8-08c9e477f02f",
      "name": "Sticky Note5"
    },
    {
      "parameters": {
        "content": "## 6) Run tests",
        "height": 432,
        "width": 336
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2016,
        -144
      ],
      "typeVersion": 1,
      "id": "c789d047-9d89-428b-bf2a-716db7e19d29",
      "name": "Sticky Note6"
    },
    {
      "parameters": {
        "model": "qwen/qwen3-coder-next",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        2976,
        128
      ],
      "id": "96316515-4af1-44e0-95c4-1573b12a854a",
      "name": "OpenRouter Chat Model7",
      "credentials": {
        "openRouterApi": {
          "id": "baaHEuVk7u6Z0Liu",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $executionId }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        3184,
        144
      ],
      "id": "d2cf6911-b437-4baf-84fc-aebf33ad9508",
      "name": "Simple Memory8"
    },
    {
      "parameters": {},
      "type": "n8n-nodes-base.manualTrigger",
      "typeVersion": 1,
      "position": [
        -960,
        -48
      ],
      "id": "7a058cd7-9ea0-4a8f-a774-3adda9c69035",
      "name": "When clicking ‘Execute workflow’"
    },
    {
      "parameters": {
        "model": "qwen/qwen3-coder-next",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.lmChatOpenRouter",
      "typeVersion": 1,
      "position": [
        2672,
        144
      ],
      "id": "ac3b2dd5-acbf-4a72-8b03-76f9da4b72d6",
      "name": "OpenRouter Chat Model9",
      "credentials": {
        "openRouterApi": {
          "id": "baaHEuVk7u6Z0Liu",
          "name": "OpenRouter account"
        }
      }
    },
    {
      "parameters": {
        "sessionIdType": "customKey",
        "sessionKey": "={{ $executionId }}"
      },
      "type": "@n8n/n8n-nodes-langchain.memoryBufferWindow",
      "typeVersion": 1.3,
      "position": [
        2784,
        144
      ],
      "id": "ae355f53-0723-440f-bcc3-50256daca708",
      "name": "Simple Memory"
    },
    {
      "parameters": {
        "content": "## 2) Write specification document",
        "height": 432,
        "width": 592
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -624,
        -144
      ],
      "typeVersion": 1,
      "id": "4d332b05-203f-47ad-aed1-24009ee65255",
      "name": "Sticky Note"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=Review the Specification document and the HTML carefully. List rules for a QA to write javascript tests.\n\nBe very specific with the test rules, mentioning relevant DOM elements.\n\n%REQUIREMENT%\n{{ $json.output }}\n\n%HTML%\n{{ $('Page HTML').item.json.content }}\n\n%LIST OF THINGS TO CHECK%",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        32,
        -48
      ],
      "id": "cfc7c4fe-b91d-442b-bc29-c947837db3b2",
      "name": "Write QA Rules"
    },
    {
      "parameters": {
        "promptType": "define",
        "text": "=%ORIGINAL HTML%\n{{ $('Page HTML').item.json.content }}\n\n%CHANGED HTML%:\n{{ $('Run tests').item.json.data.source.html }}\n\n%CURRENT JAVASCRIPT%\n```\n{{ $('Set 2').item.json.javascript }}\n```\n\n%ERRORS%\n{{ $json.rawMessage }}\n\n%FIX%\nDescribe the problem and potential fix. Be precise and concise.",
        "options": {}
      },
      "type": "@n8n/n8n-nodes-langchain.agent",
      "typeVersion": 2.2,
      "position": [
        2672,
        -64
      ],
      "id": "bbcae078-8d47-401e-8763-075c60be9353",
      "name": "What went wrong?"
    },
    {
      "parameters": {
        "jsCode": "// Get the JavaScript from the previous node's output\n// Adjust the field name based on what your previous node outputs\nconst jsFromPreviousStep = $input.item.json.javascript ||  // if field is 'javascript'\n                            $input.item.json.code ||        // if field is 'code'\n                            $input.item.json.data ||        // if field is 'data'\n                            $input.item.json.output ||      // if field is 'output'\n                            $input.item.json.result ||      // if field is 'result'\n                            JSON.stringify($input.item.json);; // fallback\n\n// Optional: Clean up if it still has markdown code block markers\nlet cleanJS = jsFromPreviousStep;\nconst markdownMatch = jsFromPreviousStep.match(/```javascript\\n([\\s\\S]*?)\\n```/);\nif (markdownMatch) {\n  cleanJS = markdownMatch[1].trim();\n}\n\n// Return just the JavaScript\nreturn {\n  output: cleanJS,\n  html:$input.first().json.html,\n  specification_doc:$input.first().json.specification_doc,\n  url:$input.first().json.url\n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        656,
        -48
      ],
      "id": "27881ded-dea9-4120-a786-7a929a2cff8a",
      "name": "Extract Javascript (1)"
    },
    {
      "parameters": {
        "jsCode": "// Get the JavaScript from the previous node's output\n// Adjust the field name based on what your previous node outputs\nconst jsFromPreviousStep = $input.item.json.javascript ||  // if field is 'javascript'\n                            $input.item.json.code ||        // if field is 'code'\n                            $input.item.json.data ||        // if field is 'data'\n                            $input.item.json.output ||      // if field is 'output'\n                            $input.item.json.result ||      // if field is 'result'\n                            JSON.stringify($input.item.json);; // fallback\n\n// Optional: Clean up if it still has markdown code block markers\nlet cleanJS = jsFromPreviousStep;\nconst markdownMatch = jsFromPreviousStep.match(/```javascript\\n([\\s\\S]*?)\\n```/);\nif (markdownMatch) {\n  cleanJS = markdownMatch[1].trim();\n}\n\n// Return just the JavaScript\nreturn {\n  output: cleanJS,\n  counter: $('Extract Javascript (1)').first().json.counter +1\n  \n};"
      },
      "type": "n8n-nodes-base.code",
      "typeVersion": 2,
      "position": [
        3280,
        -64
      ],
      "id": "51d54153-45b3-4ce4-9972-6e6ead073501",
      "name": "Extract Javascript (2)"
    },
    {
      "parameters": {
        "method": "POST",
        "url": "https://production-lon.browserless.io/chrome/bql?token=YOUR_TOKEN_HERE",
        "sendBody": true,
        "bodyParameters": {
          "parameters": [
            {
              "name": "query",
              "value": "=mutation ModifyPage {\n  goto(url: \"{{ $json.url }}\", waitUntil: networkIdle) {\n    status\n  }\n  \n  evaluate(content: \"\") {\n    value\n  }\n  \n  html {\n    html\n  }\n}"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.httpRequest",
      "typeVersion": 4.2,
      "position": [
        -592,
        -48
      ],
      "id": "988c4d82-2276-4b7a-bda4-fafb5caed4b7",
      "name": "Get HTML"
    },
    {
      "parameters": {
        "assignments": {
          "assignments": [
            {
              "id": "361d042e-61fa-4cd6-ba97-1b73e17fdddb",
              "name": "content",
              "value": "={{ $json.data.html.html }}",
              "type": "string"
            }
          ]
        },
        "options": {}
      },
      "type": "n8n-nodes-base.set",
      "typeVersion": 3.4,
      "position": [
        -464,
        -48
      ],
      "id": "2bd6409f-5801-4a59-a531-c442128e8dc9",
      "name": "Page HTML"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "80a59f13-59b7-4b29-8bb3-183c504a63e7",
              "leftValue": "={{ $json.hasErrors }}",
              "rightValue": 0,
              "operator": {
                "type": "boolean",
                "operation": "true",
                "singleValue": true
              }
            },
            {
              "id": "1dc36111-bce4-4d57-8509-ba4f94b4da7e",
              "leftValue": "={{ $runIndex }}",
              "rightValue": 3,
              "operator": {
                "type": "number",
                "operation": "lt"
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        2448,
        -48
      ],
      "id": "910c0c1d-21f9-4805-8953-c9ae5b3202cc",
      "name": "Errors?"
    },
    {
      "parameters": {
        "conditions": {
          "options": {
            "caseSensitive": true,
            "leftValue": "",
            "typeValidation": "strict",
            "version": 2
          },
          "conditions": [
            {
              "id": "83d8eaab-3747-4de7-b5fe-ba00065df8fe",
              "leftValue": "={{ $json.error }}",
              "rightValue": "",
              "operator": {
                "type": "string",
                "operation": "exists",
                "singleValue": true
              }
            }
          ],
          "combinator": "and"
        },
        "options": {}
      },
      "type": "n8n-nodes-base.if",
      "typeVersion": 2.2,
      "position": [
        1520,
        -48
      ],
      "id": "505a0251-eff7-4c3d-bc82-4687e130e5ea",
      "name": "Browserless Format error?",
      "retryOnFail": false
    },
    {
      "parameters": {
        "content": "## 8) The output",
        "height": 432,
        "width": 352
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        2400,
        320
      ],
      "typeVersion": 1,
      "id": "6afdf556-ac96-43bf-9294-b74cd99be1a8",
      "name": "Sticky Note7"
    },
    {
      "parameters": {
        "content": "## 1) Provide the information",
        "height": 432,
        "width": 320
      },
      "type": "n8n-nodes-base.stickyNote",
      "position": [
        -976,
        -144
      ],
      "typeVersion": 1,
      "id": "31604f68-ebe0-434a-9420-fa9965a08c85",
      "name": "Sticky Note8"
    }
  ],
  "pinData": {
    "Edit Fields": [
      {
        "json": {
          "url": "https://iqbalacomics.com/",
          "change_request": "Move the panel for Mr Jones's Smoking Bones to be the first in the list"
        }
      }
    ]
  },
  "connections": {
    "Edit Fields": {
      "main": [
        [
          {
            "node": "Get HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model3": {
      "ai_languageModel": [
        [
          {
            "node": "Write specification document",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory2": {
      "ai_memory": [
        [
          {
            "node": "Write specification document",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model": {
      "ai_languageModel": [
        [
          {
            "node": "Javascript for tests",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory3": {
      "ai_memory": [
        [
          {
            "node": "Javascript for tests",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Javascript for tests": {
      "main": [
        [
          {
            "node": "Extract Javascript for string2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Write specification document": {
      "main": [
        [
          {
            "node": "Write QA Rules",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model4": {
      "ai_languageModel": [
        [
          {
            "node": "Write Main Javascript",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory4": {
      "ai_memory": [
        [
          {
            "node": "Write Main Javascript",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory1": {
      "ai_memory": [
        [
          {
            "node": "Write QA Rules",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model2": {
      "ai_languageModel": [
        [
          {
            "node": "Write QA Rules",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Write Main Javascript": {
      "main": [
        [
          {
            "node": "Extract Javascript (1)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Javascript for string2": {
      "main": [
        [
          {
            "node": "Browserless Format error?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "AI Agent": {
      "main": [
        [
          {
            "node": "Extract Javascript for string2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model8": {
      "ai_languageModel": [
        [
          {
            "node": "AI Agent",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory7": {
      "ai_memory": [
        [
          {
            "node": "AI Agent",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Set 2": {
      "main": [
        [
          {
            "node": "Javascript for tests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Run tests": {
      "main": [
        [
          {
            "node": "Evaluate tests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Evaluate tests": {
      "main": [
        [
          {
            "node": "Errors?",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model7": {
      "ai_languageModel": [
        [
          {
            "node": "Javascript fixer",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory8": {
      "ai_memory": [
        [
          {
            "node": "Javascript fixer",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Javascript fixer": {
      "main": [
        [
          {
            "node": "Extract Javascript (2)",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "When clicking ‘Execute workflow’": {
      "main": [
        [
          {
            "node": "Edit Fields",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "OpenRouter Chat Model9": {
      "ai_languageModel": [
        [
          {
            "node": "What went wrong?",
            "type": "ai_languageModel",
            "index": 0
          }
        ]
      ]
    },
    "Simple Memory": {
      "ai_memory": [
        [
          {
            "node": "What went wrong?",
            "type": "ai_memory",
            "index": 0
          }
        ]
      ]
    },
    "Write QA Rules": {
      "main": [
        [
          {
            "node": "Write Main Javascript",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "What went wrong?": {
      "main": [
        [
          {
            "node": "Javascript fixer",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Javascript (1)": {
      "main": [
        [
          {
            "node": "Set 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Extract Javascript (2)": {
      "main": [
        [
          {
            "node": "Set 2",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Get HTML": {
      "main": [
        [
          {
            "node": "Page HTML",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Page HTML": {
      "main": [
        [
          {
            "node": "Write specification document",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Errors?": {
      "main": [
        [
          {
            "node": "What went wrong?",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Final data",
            "type": "main",
            "index": 0
          }
        ]
      ]
    },
    "Browserless Format error?": {
      "main": [
        [
          {
            "node": "AI Agent",
            "type": "main",
            "index": 0
          }
        ],
        [
          {
            "node": "Run tests",
            "type": "main",
            "index": 0
          }
        ]
      ]
    }
  },
  "active": false,
  "settings": {
    "executionOrder": "v1"
  },
  "versionId": "904e9476-a365-4165-b354-52c84af71ffb",
  "meta": {
    "templateCredsSetupCompleted": true,
    "instanceId": "a7d3c8fee44ed6dfa5d3bf2eca37e2240651cc218f5d2ad518615bd97f803884"
  },
  "id": "OqQyHkZqYDOgu2iY",
  "tags": []
}