diff --git a/evals/assets/peeler.html b/evals/assets/peeler.html index 9516e387c..3270c9158 100644 --- a/evals/assets/peeler.html +++ b/evals/assets/peeler.html @@ -20,7 +20,12 @@

Knife Set

Peeler

The ultimate tool for peeling fruits and vegetables.

- + + +
+ hi world +
+
diff --git a/evals/peeler_simple.eval.ts b/evals/peeler_simple.eval.ts index 3817a8f87..776528a86 100644 --- a/evals/peeler_simple.eval.ts +++ b/evals/peeler_simple.eval.ts @@ -8,13 +8,13 @@ const exactMatch = (args: { input; output; expected? }) => { }; }; -Eval('Vanta', { +Eval('Peeler Simple', { data: () => { return [ { input: { text: 'add the peeler to cart', - desired: `body > div.page-wrapper > div.nav_component > div.nav_element.w-nav > div.padding-global > div > div > nav > div.nav_cta-wrapper.is-new > a.nav_cta-button-desktop.is-smaller.w-button`, + desired: null, }, }, ]; @@ -30,8 +30,16 @@ Eval('Vanta', { await stageHand.act({ action: input.text }); + const successMessageLocator = stageHand.page.locator( + 'text="Congratulations, you have 1 A in your cart"' + ); + await successMessageLocator.waitFor({ state: 'visible', timeout: 5000 }); + const isVisible = await successMessageLocator.isVisible(); + await stageHand.browser.close(); + return isVisible; + return false; }, scores: [exactMatch], diff --git a/examples/index.ts b/examples/index.ts index 23d3b35d7..eace61d1f 100644 --- a/examples/index.ts +++ b/examples/index.ts @@ -9,7 +9,7 @@ async function example() { await stageHand.waitForSettledDom(); await stageHand.act({ - action: 'click the next date on the calendar that has times available', + action: 'click the next available date', }); } diff --git a/lib/playwright/dom.ts b/lib/playwright/dom.ts new file mode 100644 index 000000000..d2227da16 --- /dev/null +++ b/lib/playwright/dom.ts @@ -0,0 +1,111 @@ +import { Locator, type Page } from '@playwright/test'; +import jsdom from 'jsdom'; +const { JSDOM } = jsdom; + +const interactiveElementTypes = [ + 'A', + 'BUTTON', + 'DETAILS', + 'EMBED', + 'INPUT', + 'LABEL', + 'MENU', + 'MENUITEM', + 'OBJECT', + 'SELECT', + 'TEXTAREA', + 'SUMMARY', +]; + +const interactiveRoles = [ + 'button', + 'menu', + 'menuitem', + 'link', + 'checkbox', + 'radio', + 'slider', + 'tab', + 'tabpanel', + 'textbox', + 'combobox', + 'grid', + 'listbox', + 'option', + 'progressbar', + 'scrollbar', + 'searchbox', + 'switch', + 'tree', + 'treeitem', + 'spinbutton', + 'tooltip', +]; +const interactiveAriaRoles = ['menu', 'menuitem', 'button']; + +const isInteractiveElement = (element: HTMLElement) => { + const elementType = element.tagName; + const elementRole = element.getAttribute('role'); + const elementAriaRole = element.getAttribute('aria-role'); + + if ( + element.getAttribute('disabled') === 'true' || + element.hidden || + element.ariaDisabled + ) { + return false; + } + + return ( + (elementType && interactiveElementTypes.includes(elementType)) || + (elementRole && interactiveRoles.includes(elementRole)) || + (elementAriaRole && interactiveAriaRoles.includes(elementAriaRole)) + ); +}; + +async function cleanDOM(startingLocator: Locator) { + console.log('---DOM CLEANING--- starting cleaning'); + const domString = await startingLocator.evaluate((el) => el.outerHTML); + if (!domString) { + throw new Error("error selecting DOM that doesn't exist"); + } + const { document } = new JSDOM(domString).window; + const candidateElements: Array = []; + const DOMQueue: Array = [document.body]; + while (DOMQueue.length > 0) { + const element = DOMQueue.pop(); + if (element) { + const childrenCount = element.children.length; + // if you have no children you are a leaf node + if (childrenCount === 0) { + candidateElements.push(element); + continue; + } else if (isInteractiveElement(element)) { + candidateElements.push(element); + continue; + } + for (let i = childrenCount - 1; i >= 0; i--) { + const child = element.children[i]; + + DOMQueue.push(child as HTMLElement); + } + } + } + + const cleanedHtml = candidateElements + + .map((r) => + r.outerHTML + .split('\n') + .map((line) => line.trim()) + .join(' ') + ) + .join(',\n'); + + console.log('---DOM CLEANING--- CLEANED HTML STRING'); + console.log(cleanedHtml); + + return cleanedHtml; +} + +export { cleanDOM }; diff --git a/lib/playwright/index.ts b/lib/playwright/index.ts index 91ce705fd..eea5df6ee 100644 --- a/lib/playwright/index.ts +++ b/lib/playwright/index.ts @@ -3,12 +3,12 @@ import { type Browser, type BrowserContext, chromium, - Locator, } from '@playwright/test'; import { expect } from '@playwright/test'; import Cache from '../cache'; import OpenAI from 'openai'; import crypto from 'crypto'; +import { cleanDOM } from './dom'; require('dotenv').config({ path: '.env' }); @@ -35,46 +35,6 @@ async function getBrowser(env: 'LOCAL' | 'BROWSERBASE' = 'BROWSERBASE') { } } -const interactiveElements = [ - 'a', - 'button', - "[role='button']", - "[aria-role='button']", - 'details', - 'embed', - 'input', - 'label', - 'menu', - "[role='menu']", - "[aria-role='menu']", - 'menuitem', - "[role='menuitem']", - "[aria-role='menuitem']", - 'object', - 'select', - 'textarea', - 'summary', - "[role='link']", - "[role='checkbox']", - "[role='radio']", - "[role='slider']", - "[role='tab']", - "[role='tabpanel']", - "[role='textbox']", - "[role='combobox']", - "[role='grid']", - "[role='listbox']", - "[role='option']", - "[role='progressbar']", - "[role='scrollbar']", - "[role='searchbox']", - "[role='switch']", - "[role='tree']", - "[role='treeitem']", - "[role='spinbutton']", - "[role='tooltip']", -]; - export class Stagehand { private openai: OpenAI; public observations: { [key: string]: { result: string; id: string } }; @@ -119,34 +79,6 @@ export class Stagehand { return this.page.evaluate(() => window.waitForDomSettle()); } - async cleanDOM(parent: Locator) { - const elementsSelector = interactiveElements.join(', '); - - console.log('\nCLEAN DOM SELECTOR'); - console.log(elementsSelector); - - const foundElements = await parent.locator(elementsSelector).all(); - console.log(foundElements); - const results = await Promise.allSettled( - foundElements.map((el) => el.evaluate((el) => el.outerHTML)) - ); - - console.log('\nFOUND ELEMENTS STRING'); - console.log(results); - - const cleanedHtml = results - .filter( - (r): r is PromiseFulfilledResult => r.status === 'fulfilled' - ) - .map((r) => r.value) - .join('\n'); - - console.log('\nCLEANED HTML STRING'); - console.log(cleanedHtml); - - return cleanedHtml; - } - getKey(operation) { return crypto.createHash('sha256').update(operation).digest('hex'); } @@ -170,7 +102,7 @@ export class Stagehand { return key; } - const fullBody = await this.cleanDOM(this.page.locator('body')); + const fullBody = await cleanDOM(this.page.locator('body')); const selectorResponse = await this.openai.chat.completions.create({ model: 'gpt-4o', @@ -239,7 +171,6 @@ export class Stagehand { async act({ observation, action, - data, }: { observation?: string; action: string; @@ -274,7 +205,7 @@ export class Stagehand { console.log('observation', this.observations[observation].result); } - const area = await this.cleanDOM( + const area = await cleanDOM( observation ? this.page.locator(this.observations[observation].result) : this.page.locator('body') @@ -286,14 +217,13 @@ export class Stagehand { { role: 'system', content: - 'You are helping the user automate browser by finding one or more actions to take.\n\nyou will be given a DOM element, an overall goal, and data to use when taking actions.\n\nuse selectors that are least likely to change\n\nfor each action required to complete the goal, follow this format in raw JSON, no markdown\n\n[{\n method: string (the required playwright function to call)\n locator: string (the locator to find the element to act on),\nargs: Array (the required arguments)\n}]\n\n\n\n', + 'You are helping the user automate browser by finding one or more actions to take.\n\nyou will be given a list of potential DOM elements and a goal to accompplish.\n\nuse selectors that are least likely to change and directly select the element\n\nfor each action required to complete the goal, follow this format in raw JSON, no markdown\n\n[{\n method: string (the required playwright function to call)\n locator: string (the locator to find the element to act on),\nargs: Array (the required arguments)\n}]\n\n\n\n', }, { role: 'user', content: ` action: ${action}, - DOM: ${area} - data: ${JSON.stringify(data)}`, + DOM: ${area}`, }, ], diff --git a/package.json b/package.json index 659634a78..1c5d356f1 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "license": "ISC", "devDependencies": { "@playwright/test": "^1.42.1", + "@types/jsdom": "^21.1.6", "@types/node": "^20.11.30", "prettier": "^3.2.5", "tsx": "^4.10.5" @@ -23,6 +24,7 @@ "braintrust": "^0.0.127", "dotenv": "^16.4.5", "e": "^0.2.33", + "jsdom": "^24.0.0", "openai": "^4.29.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 16690bbb2..52286b76e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -17,6 +17,9 @@ dependencies: e: specifier: ^0.2.33 version: 0.2.33 + jsdom: + specifier: ^24.0.0 + version: 24.0.0 openai: specifier: ^4.29.2 version: 4.29.2 @@ -25,6 +28,9 @@ devDependencies: '@playwright/test': specifier: ^1.42.1 version: 1.42.1 + '@types/jsdom': + specifier: ^21.1.6 + version: 21.1.6 '@types/node': specifier: ^20.11.30 version: 20.11.30 @@ -490,6 +496,14 @@ packages: playwright: 1.42.1 dev: true + /@types/jsdom@21.1.6: + resolution: {integrity: sha512-/7kkMsC+/kMs7gAYmmBR9P0vGTnOoLhQhyhQJSlXGI5bzTHp6xdo0TtKWQAsz6pmSAeVqKSbqeyP6hytqr9FDw==} + dependencies: + '@types/node': 20.11.30 + '@types/tough-cookie': 4.0.5 + parse5: 7.1.2 + dev: true + /@types/node-fetch@2.6.11: resolution: {integrity: sha512-24xFj9R5+rfQJLRyM56qh+wnVSYhyXC2tkoBndtY0U+vubqNsYXGjufB2nn8Q6gt0LrARwL6UBtMCSVCwl4B1g==} dependencies: @@ -508,6 +522,10 @@ packages: dependencies: undici-types: 5.26.5 + /@types/tough-cookie@4.0.5: + resolution: {integrity: sha512-/Ad8+nIOV7Rl++6f1BdKxFSMgmoqEoYbHRpPcx3JEfv8VRsQe9Z4mCXeJBzxs7mbHY/XOZZuXlRNfhpVPbs6ZA==} + dev: true + /abort-controller@3.0.0: resolution: {integrity: sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==} engines: {node: '>=6.5'} @@ -515,6 +533,15 @@ packages: event-target-shim: 5.0.1 dev: false + /agent-base@7.1.1: + resolution: {integrity: sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==} + engines: {node: '>= 14'} + dependencies: + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /agentkeepalive@4.5.0: resolution: {integrity: sha512-5GG/5IbQQpC9FpkRGsSvZI5QYeSCzlJHdpBQntCsuTOxhKD8lqKhrleg2Yi7yvMIf82Ycmmqln9U8V9qwEiJew==} engines: {node: '>= 8.0.0'} @@ -666,10 +693,25 @@ packages: resolution: {integrity: sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==} dev: false + /cssstyle@4.0.1: + resolution: {integrity: sha512-8ZYiJ3A/3OkDd093CBT/0UKDWry7ak4BdPTFP2+QEP7cmhouyq/Up709ASSj2cK02BbZiMgk7kYjZNS4QP5qrQ==} + engines: {node: '>=18'} + dependencies: + rrweb-cssom: 0.6.0 + dev: false + /d3-array@0.7.1: resolution: {integrity: sha512-Ifi3fH46Bco+Lb1mOlTxbFEuF3NdyElEVVD+EmoK327I0JzKAP4x57cl+HoxHqFcVd8F/uXLC+wtY3n/R1uO2w==} dev: false + /data-urls@5.0.0: + resolution: {integrity: sha512-ZYP5VBHshaDAiVZxjbRVcFJpc+4xGgT0bK3vzy1HLN8jTO975HEbuYzZJcHoQEY5K1a0z8YayJkyVETa08eNTg==} + engines: {node: '>=18'} + dependencies: + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + dev: false + /debug@4.3.4: resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==} engines: {node: '>=6.0'} @@ -682,6 +724,10 @@ packages: ms: 2.1.2 dev: false + /decimal.js@10.4.3: + resolution: {integrity: sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==} + dev: false + /delayed-stream@1.0.0: resolution: {integrity: sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==} engines: {node: '>=0.4.0'} @@ -708,6 +754,10 @@ packages: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} dev: false + /entities@4.5.0: + resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} + engines: {node: '>=0.12'} + /esbuild@0.18.20: resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} engines: {node: '>=12'} @@ -830,12 +880,46 @@ packages: engines: {node: '>=8'} dev: false + /html-encoding-sniffer@4.0.0: + resolution: {integrity: sha512-Y22oTqIU4uuPgEemfz7NDJz6OeKf12Lsu+QC+s3BVpda64lTiMYCyGwg5ki4vFxkMwQdeZDl2adZoqUgdFuTgQ==} + engines: {node: '>=18'} + dependencies: + whatwg-encoding: 3.1.1 + dev: false + + /http-proxy-agent@7.0.2: + resolution: {integrity: sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.1 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + + /https-proxy-agent@7.0.4: + resolution: {integrity: sha512-wlwpilI7YdjSkWaQ/7omYBMTliDcmCN8OLihO6I9B86g06lMyAoqgoDpV0XqoaPOKj+0DIdAvnsWfyAAhmimcg==} + engines: {node: '>= 14'} + dependencies: + agent-base: 7.1.1 + debug: 4.3.4 + transitivePeerDependencies: + - supports-color + dev: false + /humanize-ms@1.2.1: resolution: {integrity: sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==} dependencies: ms: 2.1.3 dev: false + /iconv-lite@0.6.3: + resolution: {integrity: sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==} + engines: {node: '>=0.10.0'} + dependencies: + safer-buffer: 2.1.2 + dev: false + /install@0.13.0: resolution: {integrity: sha512-zDml/jzr2PKU9I8J/xyZBQn8rPCAY//UOYNmR01XwNwyfhEWObo2SWfSl1+0tm1u6PhxLwDnfsT/6jB7OUxqFA==} engines: {node: '>= 0.10'} @@ -854,6 +938,10 @@ packages: engines: {node: '>=8'} dev: false + /is-potential-custom-element-name@1.0.1: + resolution: {integrity: sha512-bCYeRA2rVibKZd+s2625gGnGF/t7DSqDs4dP7CrLA1m7jKWz6pps0LpYLJN8Q64HtmPKJ1hrN3nzPNKFEKOUiQ==} + dev: false + /js-levenshtein@1.1.6: resolution: {integrity: sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g==} engines: {node: '>=0.10.0'} @@ -866,6 +954,42 @@ packages: argparse: 2.0.1 dev: false + /jsdom@24.0.0: + resolution: {integrity: sha512-UDS2NayCvmXSXVP6mpTj+73JnNQadZlr9N68189xib2tx5Mls7swlTNao26IoHv46BZJFvXygyRtyXd1feAk1A==} + engines: {node: '>=18'} + peerDependencies: + canvas: ^2.11.2 + peerDependenciesMeta: + canvas: + optional: true + dependencies: + cssstyle: 4.0.1 + data-urls: 5.0.0 + decimal.js: 10.4.3 + form-data: 4.0.0 + html-encoding-sniffer: 4.0.0 + http-proxy-agent: 7.0.2 + https-proxy-agent: 7.0.4 + is-potential-custom-element-name: 1.0.1 + nwsapi: 2.2.10 + parse5: 7.1.2 + rrweb-cssom: 0.6.0 + saxes: 6.0.0 + symbol-tree: 3.2.4 + tough-cookie: 4.1.4 + w3c-xmlserializer: 5.0.0 + webidl-conversions: 7.0.0 + whatwg-encoding: 3.1.1 + whatwg-mimetype: 4.0.0 + whatwg-url: 14.0.0 + ws: 8.17.0 + xml-name-validator: 5.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /json-schema@0.4.0: resolution: {integrity: sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==} dev: false @@ -979,6 +1103,10 @@ packages: whatwg-url: 5.0.0 dev: false + /nwsapi@2.2.10: + resolution: {integrity: sha512-QK0sRs7MKv0tKe1+5uZIQk/C8XGza4DAnztJG8iD+TpJIORARrCxczA738awHrZoHeTjSSoHqao2teO0dC/gFQ==} + dev: false + /openai@4.23.0: resolution: {integrity: sha512-ey2CXh1OTcTUa0AWZWuTpgA9t5GuAG3DVU1MofCRUI7fQJij8XJ3Sr0VtgxoAE69C9wbHBMCux8Z/IQZfSwHiA==} hasBin: true @@ -1019,6 +1147,11 @@ packages: yaml: 2.4.2 dev: false + /parse5@7.1.2: + resolution: {integrity: sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==} + dependencies: + entities: 4.5.0 + /playwright-core@1.42.1: resolution: {integrity: sha512-mxz6zclokgrke9p1vtdy/COWBH+eOZgYUVVU34C73M+4j4HLlQJHtfcqiqqxpP0o8HhMkflvfbquLX5dg6wlfA==} engines: {node: '>=16'} @@ -1046,10 +1179,42 @@ packages: hasBin: true dev: true + /psl@1.9.0: + resolution: {integrity: sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==} + dev: false + + /punycode@2.3.1: + resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} + engines: {node: '>=6'} + dev: false + + /querystringify@2.2.0: + resolution: {integrity: sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==} + dev: false + + /requires-port@1.0.0: + resolution: {integrity: sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==} + dev: false + /resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} dev: true + /rrweb-cssom@0.6.0: + resolution: {integrity: sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==} + dev: false + + /safer-buffer@2.1.2: + resolution: {integrity: sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==} + dev: false + + /saxes@6.0.0: + resolution: {integrity: sha512-xAg7SOnEhrm5zI3puOOKyy1OMcMlIJZYNJY7xLBwSze0UjhPLnWfj2GF2EpT0jmzaJKIWKHLsaSSajf35bcYnA==} + engines: {node: '>=v12.22.7'} + dependencies: + xmlchars: 2.2.0 + dev: false + /simple-git@3.24.0: resolution: {integrity: sha512-QqAKee9Twv+3k8IFOFfPB2hnk6as6Y6ACUpwCtQvRYBAes23Wv3SZlHVobAzqcE8gfsisCvPw3HGW3HYM+VYYw==} dependencies: @@ -1089,10 +1254,31 @@ packages: has-flag: 4.0.0 dev: false + /symbol-tree@3.2.4: + resolution: {integrity: sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==} + dev: false + + /tough-cookie@4.1.4: + resolution: {integrity: sha512-Loo5UUvLD9ScZ6jh8beX1T6sO1w2/MpCRpEP7V280GKMVUQ0Jzar2U3UJPsrdbziLEMMhu3Ujnq//rhiFuIeag==} + engines: {node: '>=6'} + dependencies: + psl: 1.9.0 + punycode: 2.3.1 + universalify: 0.2.0 + url-parse: 1.5.10 + dev: false + /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} dev: false + /tr46@5.0.0: + resolution: {integrity: sha512-tk2G5R2KRwBd+ZN0zaEXpmzdKyOYksXwywulIX95MBODjSzMIuQnQ3m8JxgbhnL1LeVo7lqQKsYa1O3Htl7K5g==} + engines: {node: '>=18'} + dependencies: + punycode: 2.3.1 + dev: false + /tsx@4.10.5: resolution: {integrity: sha512-twDSbf7Gtea4I2copqovUiNTEDrT8XNFXsuHpfGbdpW/z9ZW4fTghzzhAG0WfrCuJmJiOEY1nLIjq4u3oujRWQ==} engines: {node: '>=18.0.0'} @@ -1107,6 +1293,18 @@ packages: /undici-types@5.26.5: resolution: {integrity: sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==} + /universalify@0.2.0: + resolution: {integrity: sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==} + engines: {node: '>= 4.0.0'} + dev: false + + /url-parse@1.5.10: + resolution: {integrity: sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==} + dependencies: + querystringify: 2.2.0 + requires-port: 1.0.0 + dev: false + /uuid@9.0.1: resolution: {integrity: sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==} hasBin: true @@ -1120,6 +1318,13 @@ packages: resolution: {integrity: sha512-LlFybRJEriSuBnUhQyG5bwglhh50EpTL2ul23MPIuR1odjO7XaMLFV8vHGwp7AZciFxtYOeiSCT5st+XSPONiQ==} dev: false + /w3c-xmlserializer@5.0.0: + resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==} + engines: {node: '>=18'} + dependencies: + xml-name-validator: 5.0.0 + dev: false + /web-streams-polyfill@3.3.3: resolution: {integrity: sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==} engines: {node: '>= 8'} @@ -1134,6 +1339,31 @@ packages: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} dev: false + /webidl-conversions@7.0.0: + resolution: {integrity: sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==} + engines: {node: '>=12'} + dev: false + + /whatwg-encoding@3.1.1: + resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==} + engines: {node: '>=18'} + dependencies: + iconv-lite: 0.6.3 + dev: false + + /whatwg-mimetype@4.0.0: + resolution: {integrity: sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==} + engines: {node: '>=18'} + dev: false + + /whatwg-url@14.0.0: + resolution: {integrity: sha512-1lfMEm2IEr7RIV+f4lUNPOqfFL+pO+Xw3fJSqmjX9AbXcXcYOkCe1P6+9VBZB6n94af16NfZf+sSk0JCBZC9aw==} + engines: {node: '>=18'} + dependencies: + tr46: 5.0.0 + webidl-conversions: 7.0.0 + dev: false + /whatwg-url@5.0.0: resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==} dependencies: @@ -1141,6 +1371,28 @@ packages: webidl-conversions: 3.0.1 dev: false + /ws@8.17.0: + resolution: {integrity: sha512-uJq6108EgZMAl20KagGkzCKfMEjxmKvZHG7Tlq0Z6nOky7YF7aq4mOx6xK8TJ/i1LeK4Qus7INktacctDgY8Ow==} + engines: {node: '>=10.0.0'} + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: '>=5.0.2' + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + dev: false + + /xml-name-validator@5.0.0: + resolution: {integrity: sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==} + engines: {node: '>=18'} + dev: false + + /xmlchars@2.2.0: + resolution: {integrity: sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw==} + dev: false + /yaml@2.4.2: resolution: {integrity: sha512-B3VqDZ+JAg1nZpaEmWtTXUlBneoGx6CPM9b0TENK6aoSu5t73dItudwdgmi6tHlIZZId4dZ9skcAQ2UbcyAeVA==} engines: {node: '>= 14'}