From 8ddef0188522d963ee7211844863dca5e61b2315 Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Tue, 6 Jan 2026 15:23:25 -0800 Subject: [PATCH 1/2] Implement code changes to improve functionality and maintainability - Refactor existing code for better readability - Optimize performance in critical sections - Update documentation to reflect recent changes - Remove deprecated methods and clean up unused imports --- dev/dev.spec.json | 116 +- dev/index.js | 22 +- package-lock.json | 808 ++-------- package.json | 5 +- src/config.js | 2 +- src/scopes/cleanup.js | 91 ++ src/scopes/code.js | 18 + src/scopes/index.js | 32 + src/scopes/registry.js | 116 ++ src/scopes/terminal.js | 173 +++ src/scopes/waitUntil.js | 141 ++ src/tests.js | 24 +- src/tests/runCode.js | 13 +- src/tests/runShell.js | 215 ++- src/tests/terminateScope.js | 73 + src/tests/typeKeys.js | 126 +- test/artifacts/scope_basic.spec.json | 134 ++ test/artifacts/scope_errors.spec.json | 94 ++ test/artifacts/scope_lifecycle.spec.json | 140 ++ test/artifacts/scope_typeKeys.spec.json | 165 ++ test/artifacts/scope_waitUntil.spec.json | 136 ++ test/scopes.test.js | 1756 ++++++++++++++++++++++ 22 files changed, 3696 insertions(+), 704 deletions(-) create mode 100644 src/scopes/cleanup.js create mode 100644 src/scopes/code.js create mode 100644 src/scopes/index.js create mode 100644 src/scopes/registry.js create mode 100644 src/scopes/terminal.js create mode 100644 src/scopes/waitUntil.js create mode 100644 src/tests/terminateScope.js create mode 100644 test/artifacts/scope_basic.spec.json create mode 100644 test/artifacts/scope_errors.spec.json create mode 100644 test/artifacts/scope_lifecycle.spec.json create mode 100644 test/artifacts/scope_typeKeys.spec.json create mode 100644 test/artifacts/scope_waitUntil.spec.json create mode 100644 test/scopes.test.js diff --git a/dev/dev.spec.json b/dev/dev.spec.json index 34832626..5484b13d 100644 --- a/dev/dev.spec.json +++ b/dev/dev.spec.json @@ -3,74 +3,108 @@ { "steps": [ { - "goTo": "http://localhost:8092", - "waitUntil": { - "find": { - "selector": "button", - "elementText": "Standard Button", - "elementTestId": "standard-btn", - "elementAria": "Sample Standard Button", - "elementId": "standard-btn", - "elementClass": ["btn"], - "elementAttribute": { - "type": "button", - "value": "Standard Button" + "runShell": { + "command": "sh", + "args": ["-c", "echo 'READY' && sleep 5"], + "scope": "basic-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "READY" } } } }, { - "find": "Text Elements" - }, + "terminateScope": { + "scope": "basic-scope" + } + } + ] + }, + { + "steps": [ { - "find": "text-elements" + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SERVER_STARTED' && while true; do sleep 1; done"], + "scope": "args-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SERVER_STARTED" + } + } + } }, { - "find": "Sample Input Button" + "runShell": "echo 'Other work while scope runs'" }, { - "find": "submit-btn" + "terminateScope": { + "scope": "args-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sleep", + "args": ["10"], + "scope": "no-wait-scope" + } }, { - "find": "#tables" + "runShell": "echo 'Background process started'" }, { - "find": { - "elementText": "Text Elements" + "terminateScope": { + "scope": "no-wait-scope" } - }, + } + ] + }, + { + "steps": [ { - "find": { - "elementId": "text-elements" + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SCOPE_A_READY' && sleep 10"], + "scope": "multi-scope-a", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SCOPE_A_READY" + } + } } }, { - "find": { - "elementAria": "Sample Input Button" + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SCOPE_B_READY' && sleep 10"], + "scope": "multi-scope-b", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SCOPE_B_READY" + } + } } }, { - "find": { - "elementTestId": "submit-btn" - } + "runShell": "echo 'Both scopes are running'" }, { - "find": { - "selector": "#tables" + "terminateScope": { + "scope": "multi-scope-a" } }, { - "find": { - "selector": "button", - "elementText": "Standard Button", - "elementTestId": "standard-btn", - "elementAria": "Sample Standard Button", - "elementId": "standard-btn", - "elementClass": ["btn"], - "elementAttribute": { - "type": "button", - "value": "Standard Button" - } + "terminateScope": { + "scope": "multi-scope-b" } } ] diff --git a/dev/index.js b/dev/index.js index 35cf3702..af62972b 100644 --- a/dev/index.js +++ b/dev/index.js @@ -7,7 +7,9 @@ require("dotenv").config({ path: path.join(__dirname, ".env") }); */ async function main() { const json = { - input: ["heretto:example"], + input: [ + "dev/dev.spec.json", + ], logLevel: "debug", runOn: [ { @@ -24,15 +26,15 @@ async function main() { docDetectiveApi: { apiKey: process.env.KEY, }, - heretto: [ - { - name: "example", - organizationId: process.env.HERETTO_ORG_ID, // Organization ID is the subdomain of your Heretto instance (e.g., "silva" for "silva.heretto.com") - username: process.env.HERETTO_USERNAME, // Your Heretto username/email - apiToken: process.env.HERETTO_TOKEN, // Your Heretto API token (https://help.heretto.com/en/heretto-ccms/api/ccms-api-authentication/basic-authentication) - uploadOnChange: true, // Upload changed screenshots back to Heretto - }, - ], + // heretto: [ + // { + // name: "example", + // organizationId: process.env.HERETTO_ORG_ID, // Organization ID is the subdomain of your Heretto instance (e.g., "silva" for "silva.heretto.com") + // username: process.env.HERETTO_USERNAME, // Your Heretto username/email + // apiToken: process.env.HERETTO_TOKEN, // Your Heretto API token (https://help.heretto.com/en/heretto-ccms/api/ccms-api-authentication/basic-authentication) + // uploadOnChange: true, // Upload changed screenshots back to Heretto + // }, + // ], }, }; // console.log(json); diff --git a/package-lock.json b/package-lock.json index 4a8e139b..cfbe843b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -19,12 +19,13 @@ "appium-geckodriver": "^2.1.6", "appium-safari-driver": "^4.1.6", "axios": "^1.13.2", - "doc-detective-common": "^3.6.1", - "doc-detective-resolver": "^3.6.2", + "doc-detective-common": "file:../common", + "doc-detective-resolver": "file:../resolver", "dotenv": "^17.2.3", "geckodriver": "^6.1.0", "jq-web": "^0.6.2", "json-schema-faker": "^0.5.9", + "node-pty": "^1.1.0", "pixelmatch": "^7.1.0", "pngjs": "^7.0.0", "posthog-node": "^5.18.1", @@ -72,6 +73,51 @@ "@img/sharp-win32-x64": "^0.34.5" } }, + "../common": { + "name": "doc-detective-common", + "version": "3.6.1", + "license": "AGPL-3.0-only", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^15.1.3", + "ajv": "^8.17.1", + "ajv-errors": "^3.0.0", + "ajv-formats": "^3.0.1", + "ajv-keywords": "^5.1.0", + "axios": "^1.13.2", + "yaml": "^2.8.2" + }, + "devDependencies": { + "chai": "^6.2.2", + "mocha": "^11.7.5", + "sinon": "^21.0.1" + } + }, + "../resolver": { + "name": "doc-detective-resolver", + "version": "3.6.2", + "license": "AGPL-3.0-only", + "dependencies": { + "@apidevtools/json-schema-ref-parser": "^15.1.3", + "adm-zip": "^0.5.16", + "ajv": "^8.17.1", + "axios": "^1.13.2", + "doc-detective-common": "file:../common", + "dotenv": "^17.2.3", + "fast-xml-parser": "^5.3.3", + "json-schema-faker": "^0.5.9", + "posthog-node": "^5.18.1" + }, + "devDependencies": { + "body-parser": "^2.2.1", + "chai": "^6.2.2", + "express": "^5.2.1", + "mocha": "^11.7.5", + "proxyquire": "^2.1.3", + "semver": "^7.7.3", + "sinon": "^21.0.1", + "yaml": "^2.8.2" + } + }, "node_modules/@apidevtools/json-schema-ref-parser": { "version": "15.1.3", "license": "MIT", @@ -1592,15 +1638,6 @@ "node": ">= 0.6" } }, - "node_modules/adm-zip": { - "version": "0.5.16", - "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.5.16.tgz", - "integrity": "sha512-TGw5yVi4saajsSEgz25grObGHEUaDrniwvA2qwSC060KfqGPdglhvPMA2lPIoxs3PQIItj2iag35fONcQqgUaQ==", - "license": "MIT", - "engines": { - "node": ">=12.0" - } - }, "node_modules/agent-base": { "version": "7.1.4", "license": "MIT", @@ -1611,6 +1648,7 @@ "node_modules/ajv": { "version": "8.17.1", "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.3", "fast-uri": "^3.0.1", @@ -1622,15 +1660,6 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ajv-errors": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ajv-errors/-/ajv-errors-3.0.0.tgz", - "integrity": "sha512-V3wD15YHfHz6y0KdhYFjyy9vWtEVALT9UrxfN3zqlI6dMioHnJrqOYfyPKol3oqrnCM9uwkcdCwkJ0WUcbLMTQ==", - "license": "MIT", - "peerDependencies": { - "ajv": "^8.0.1" - } - }, "node_modules/ajv-formats": { "version": "3.0.1", "license": "MIT", @@ -1646,18 +1675,6 @@ } } }, - "node_modules/ajv-keywords": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", - "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.3" - }, - "peerDependencies": { - "ajv": "^8.8.2" - } - }, "node_modules/ansi-regex": { "version": "6.2.2", "license": "MIT", @@ -1687,6 +1704,7 @@ "integrity": "sha512-MIifVZdSLdFBRY084PaaQyU8ilG+fgGoVdzGkRLj9Q8hZ2x/ENwjiOveyWLxcjsWMYYwUtm3TTKRvWGrJvX3Iw==", "hasInstallScript": true, "license": "Apache-2.0", + "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/base-plugin": "^3.0.5", @@ -6142,7 +6160,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-driver/-/base-driver-10.1.2.tgz", "integrity": "sha512-AxCYznylRw4xpOx30dAIQ8m5GsVM9ob/MPwc/KyiKhtofLNxQQlFLvLs7SeX7s8iwvwr0puMasOqlWrWy3gVbA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "@appium/types": "^1.1.2", @@ -6177,7 +6194,6 @@ "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-3.0.0.tgz", "integrity": "sha512-X7U0nedUMKV3nn9c4R0Zgvdvv6cw97tbDlHSZicq1snGPi/oX9DgGmFSURWtxDdnBWd3V0YviKhqAYAVvoWQ/A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.5.1", "lodash": "^4.17.4", @@ -6192,7 +6208,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-plugin/-/base-plugin-3.0.5.tgz", "integrity": "sha512-w27EqXQ3zY7xKp61IizbzhZGlxQvFr8uKxeELP5TU/+YxnjU8MyVYmWCG7RXp0dh9n7mZen/WPjZtvqdiBOsMQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/support": "^7.0.4" @@ -6207,7 +6222,6 @@ "resolved": "https://registry.npmjs.org/@appium/docutils/-/docutils-2.2.0.tgz", "integrity": "sha512-bwZIShYLRnWCRdGEgjBX+POXyI0X8fbZ3FNHWDlNzYjCBodYckiMw8X48WDwMRd0AeP5mzjBbHSpH7DKfebuFg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "chalk": "4.1.2", @@ -6237,7 +6251,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -6411,7 +6424,6 @@ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", - "peer": true, "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", @@ -6613,7 +6625,6 @@ "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", "license": "Apache-2.0", - "peer": true, "dependencies": { "kleur": "^4.1.0" }, @@ -6629,7 +6640,6 @@ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", "license": "MIT", - "peer": true, "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" @@ -6651,8 +6661,7 @@ "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/@xmldom/xmldom": { "version": "0.8.11", @@ -6680,7 +6689,6 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", - "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -6694,7 +6702,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -6740,7 +6747,6 @@ "integrity": "sha512-MIifVZdSLdFBRY084PaaQyU8ilG+fgGoVdzGkRLj9Q8hZ2x/ENwjiOveyWLxcjsWMYYwUtm3TTKRvWGrJvX3Iw==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/base-plugin": "^3.0.5", @@ -6821,7 +6827,6 @@ "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-3.0.0.tgz", "integrity": "sha512-X7U0nedUMKV3nn9c4R0Zgvdvv6cw97tbDlHSZicq1snGPi/oX9DgGmFSURWtxDdnBWd3V0YviKhqAYAVvoWQ/A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.5.1", "lodash": "^4.17.4", @@ -6836,7 +6841,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -6954,8 +6958,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0", - "peer": true + "license": "Python-2.0" }, "node_modules/appium-geckodriver/node_modules/async": { "version": "3.2.6", @@ -7065,7 +7068,6 @@ "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "5.1.2" }, @@ -7087,7 +7089,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", - "peer": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -7099,7 +7100,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -7120,7 +7120,6 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -7180,7 +7179,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -7206,7 +7204,6 @@ "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -7229,7 +7226,6 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -7274,7 +7270,6 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "license": "MIT", - "peer": true, "dependencies": { "restore-cursor": "^3.1.0" }, @@ -7287,7 +7282,6 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -7300,7 +7294,6 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "license": "ISC", - "peer": true, "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", @@ -7315,7 +7308,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -7327,15 +7319,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/cliui/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -7353,7 +7343,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -7369,7 +7358,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -7387,7 +7375,6 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8" } @@ -7397,7 +7384,6 @@ "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" @@ -7429,7 +7415,6 @@ "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -7442,7 +7427,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -7452,7 +7436,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -7465,7 +7448,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -7487,7 +7469,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6" } @@ -7513,7 +7494,6 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "license": "MIT", - "peer": true, "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -7529,7 +7509,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -7543,7 +7522,6 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -7553,7 +7531,6 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -7563,7 +7540,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.6.0" } @@ -7639,7 +7615,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -7657,7 +7632,6 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "license": "MIT", - "peer": true, "dependencies": { "clone": "^1.0.2" }, @@ -7679,7 +7653,6 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -7699,15 +7672,13 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/diff": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.3.1" } @@ -7742,8 +7713,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/emoji-regex": { "version": "8.0.0", @@ -7755,15 +7725,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -7827,7 +7795,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -7836,15 +7803,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -7881,7 +7846,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -7955,7 +7919,6 @@ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 4.9.1" } @@ -7964,15 +7927,13 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -8009,8 +7970,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/follow-redirects": { "version": "1.15.11", @@ -8102,7 +8062,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8112,7 +8071,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -8166,7 +8124,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", - "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -8176,7 +8133,6 @@ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -8288,8 +8244,7 @@ "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/has-flag": { "version": "4.0.0", @@ -8351,7 +8306,6 @@ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -8364,8 +8318,7 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", @@ -8373,7 +8326,6 @@ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -8390,7 +8342,6 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -8400,15 +8351,13 @@ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", - "peer": true, "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", @@ -8428,15 +8377,13 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/iconv-lite": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "license": "MIT", - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -8488,7 +8435,6 @@ "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.10" } @@ -8528,7 +8474,6 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -8546,8 +8491,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/is-stream": { "version": "2.0.1", @@ -8662,7 +8606,6 @@ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -8671,8 +8614,7 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/lazystream": { "version": "1.0.1", @@ -8721,7 +8663,6 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", - "peer": true, "engines": { "node": ">=14" }, @@ -8792,7 +8733,6 @@ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -8828,7 +8768,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -8838,7 +8777,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -8851,7 +8789,6 @@ "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "3.1.0", "methods": "~1.1.2", @@ -8867,7 +8804,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -8876,15 +8812,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8894,7 +8828,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -8904,7 +8837,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -8921,7 +8853,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -8931,8 +8862,7 @@ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "license": "ISC", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/minipass": { "version": "7.1.2", @@ -8957,7 +8887,6 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", - "peer": true, "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", @@ -8974,7 +8903,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -8983,15 +8911,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/morgan/node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -9019,7 +8945,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -9059,7 +8984,6 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -9072,15 +8996,13 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -9093,7 +9015,6 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -9112,7 +9033,6 @@ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "license": "MIT", - "peer": true, "dependencies": { "fn.name": "1.x.x" } @@ -9122,7 +9042,6 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "license": "MIT", - "peer": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -9138,7 +9057,6 @@ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "license": "MIT", - "peer": true, "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -9192,7 +9110,6 @@ "resolved": "https://registry.npmjs.org/package-changed/-/package-changed-3.0.0.tgz", "integrity": "sha512-HSRbrO+Ab5AuqqYGSevtKJ1Yt96jW1VKV7wrp8K4SKj5tyDp/7D96uPCQyCPiNtWTEH/7nA3hZ4z2slbc9yFxg==", "license": "ISC", - "peer": true, "dependencies": { "commander": "^6.2.0" }, @@ -9237,7 +9154,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -9287,7 +9203,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -9383,7 +9298,6 @@ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", - "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -9403,7 +9317,6 @@ "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -9419,7 +9332,6 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -9429,7 +9341,6 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -9578,7 +9489,6 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "license": "MIT", - "peer": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -9592,7 +9502,6 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -9615,7 +9524,6 @@ "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -9624,8 +9532,7 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/sanitize-filename": { "version": "1.6.3", @@ -9641,8 +9548,7 @@ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-geckodriver/node_modules/semver": { "version": "7.7.3", @@ -9661,7 +9567,6 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", @@ -9688,7 +9593,6 @@ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", "license": "MIT", - "peer": true, "dependencies": { "etag": "~1.8.1", "fresh": "~0.5.2", @@ -9705,7 +9609,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -9728,15 +9631,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", - "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -9761,8 +9662,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-geckodriver/node_modules/sharp": { "version": "0.34.5", @@ -9847,7 +9747,6 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -9867,7 +9766,6 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -9884,7 +9782,6 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9903,7 +9800,6 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -9981,7 +9877,6 @@ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -9999,7 +9894,6 @@ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -10015,7 +9909,6 @@ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10030,7 +9923,6 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -10040,7 +9932,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -10254,8 +10145,7 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/through": { "version": "2.3.8", @@ -10268,7 +10158,6 @@ "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.6" } @@ -10278,7 +10167,6 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 14.0.0" } @@ -10319,7 +10207,6 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", - "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -10343,7 +10230,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -10388,7 +10274,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -10399,7 +10284,6 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -10409,7 +10293,6 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "license": "MIT", - "peer": true, "dependencies": { "defaults": "^1.0.3" } @@ -10434,7 +10317,6 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", @@ -10457,7 +10339,6 @@ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "license": "MIT", - "peer": true, "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", @@ -10472,7 +10353,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10487,7 +10367,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -10502,7 +10381,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -10544,7 +10422,6 @@ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -10575,7 +10452,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -10585,7 +10461,6 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -10601,7 +10476,6 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "license": "MIT", - "peer": true, "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", @@ -10619,7 +10493,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "license": "ISC", - "peer": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" } @@ -10628,15 +10501,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-geckodriver/node_modules/yargs/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -10654,7 +10525,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -10740,7 +10610,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-driver/-/base-driver-10.1.2.tgz", "integrity": "sha512-AxCYznylRw4xpOx30dAIQ8m5GsVM9ob/MPwc/KyiKhtofLNxQQlFLvLs7SeX7s8iwvwr0puMasOqlWrWy3gVbA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "@appium/types": "^1.1.2", @@ -10775,7 +10644,6 @@ "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-3.0.0.tgz", "integrity": "sha512-X7U0nedUMKV3nn9c4R0Zgvdvv6cw97tbDlHSZicq1snGPi/oX9DgGmFSURWtxDdnBWd3V0YviKhqAYAVvoWQ/A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.5.1", "lodash": "^4.17.4", @@ -10790,7 +10658,6 @@ "resolved": "https://registry.npmjs.org/@appium/base-plugin/-/base-plugin-3.0.5.tgz", "integrity": "sha512-w27EqXQ3zY7xKp61IizbzhZGlxQvFr8uKxeELP5TU/+YxnjU8MyVYmWCG7RXp0dh9n7mZen/WPjZtvqdiBOsMQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/support": "^7.0.4" @@ -10805,7 +10672,6 @@ "resolved": "https://registry.npmjs.org/@appium/docutils/-/docutils-2.2.0.tgz", "integrity": "sha512-bwZIShYLRnWCRdGEgjBX+POXyI0X8fbZ3FNHWDlNzYjCBodYckiMw8X48WDwMRd0AeP5mzjBbHSpH7DKfebuFg==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/support": "^7.0.4", "chalk": "4.1.2", @@ -10835,7 +10701,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -10868,7 +10733,6 @@ "resolved": "https://registry.npmjs.org/@appium/schema/-/schema-1.0.0.tgz", "integrity": "sha512-eowz+sV6YxuHdsuRizEhG+AGeeMsUiS9ryWLkdFSqQlsNB+uDudehpYQiRkrrtaGOBzq7a3GDUB7zjc6eR80QA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "json-schema": "0.4.0", "source-map-support": "0.5.21" @@ -10883,7 +10747,6 @@ "resolved": "https://registry.npmjs.org/@appium/support/-/support-7.0.4.tgz", "integrity": "sha512-xIK7cFzouZJLIG6RMmg50M+EsILSeO9e9sSkR4xNSaJajaop7LtpkbMfO5JpTgdNiIUYyIsr/3TusQhxnpPNvw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/logger": "^2.0.3", "@appium/tsconfig": "^1.1.1", @@ -10934,7 +10797,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -10951,7 +10813,6 @@ "resolved": "https://registry.npmjs.org/@appium/tsconfig/-/tsconfig-1.1.1.tgz", "integrity": "sha512-ikjo037sWgY2Oy0HRPGnrKHnOdUh9JyzstD7E6HlFqcZu8hvOP1hDQmKdoBTz8gkmSbZWcMRZmWaL3Yqaz2pLw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@tsconfig/node20": "20.1.8" }, @@ -10965,7 +10826,6 @@ "resolved": "https://registry.npmjs.org/@appium/types/-/types-1.1.2.tgz", "integrity": "sha512-BDyX99GCXWqsfeDxsqsvb6EIfQD7SLTXcCbmcI1PKDTK2wg9znKOtE0YLzXgI6TFQV3+40Xs6za6La/Mv8/rVQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/logger": "^2.0.3", "@appium/schema": "^1.0.0", @@ -10982,7 +10842,6 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", @@ -10997,7 +10856,6 @@ "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.9.0" } @@ -11007,7 +10865,6 @@ "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.1.90" } @@ -11017,7 +10874,6 @@ "resolved": "https://registry.npmjs.org/@dabh/diagnostics/-/diagnostics-2.0.8.tgz", "integrity": "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q==", "license": "MIT", - "peer": true, "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", @@ -11040,7 +10896,6 @@ "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=18" } @@ -11057,7 +10912,6 @@ "os": [ "darwin" ], - "peer": true, "engines": { "node": "^18.17.0 || ^20.3.0 || >=21.0.0" }, @@ -11080,7 +10934,6 @@ "os": [ "darwin" ], - "peer": true, "funding": { "url": "https://opencollective.com/libvips" } @@ -11111,7 +10964,6 @@ "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", "license": "ISC", - "peer": true, "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -11129,7 +10981,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11141,15 +10992,13 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@isaacs/cliui/node_modules/string-width": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", "license": "MIT", - "peer": true, "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -11167,7 +11016,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -11183,7 +11031,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -11202,7 +11049,6 @@ "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "license": "MIT", "optional": true, - "peer": true, "engines": { "node": ">=14" } @@ -11212,7 +11058,6 @@ "resolved": "https://registry.npmjs.org/@sidvind/better-ajv-errors/-/better-ajv-errors-4.0.1.tgz", "integrity": "sha512-6arF1ssKxItxgitPYXafUoLmsVBA6K7m9+ZGj6hLDoBl7nWpJ33EInwQUdHTle2METeWGxgQiqSex20KZRykew==", "license": "Apache-2.0", - "peer": true, "dependencies": { "kleur": "^4.1.0" }, @@ -11228,7 +11073,6 @@ "resolved": "https://registry.npmjs.org/@so-ric/colorspace/-/colorspace-1.1.6.tgz", "integrity": "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw==", "license": "MIT", - "peer": true, "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" @@ -11238,29 +11082,25 @@ "version": "20.1.8", "resolved": "https://registry.npmjs.org/@tsconfig/node20/-/node20-20.1.8.tgz", "integrity": "sha512-Em+IdPfByIzWRRpqWL4Z7ArLHZGxmc36BxE3jCz9nBFSm+5aLaPMZyjwu4yetvyKXeogWcxik4L1jB5JTWfw7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@types/normalize-package-data": { "version": "2.4.4", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@types/triple-beam": { "version": "1.3.5", "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/@xmldom/xmldom": { "version": "0.8.11", "resolved": "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.11.tgz", "integrity": "sha512-cQzWCtO6C8TQiYl1ruKNn2U6Ao4o4WBBcbL61yJl84x+j5sOWWFU9X7DpND8XZG3daDppSsigMdfAIl2upQBRw==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" } @@ -11270,7 +11110,6 @@ "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", "license": "MIT", - "peer": true, "dependencies": { "event-target-shim": "^5.0.0" }, @@ -11283,7 +11122,6 @@ "resolved": "https://registry.npmjs.org/accepts/-/accepts-2.0.0.tgz", "integrity": "sha512-5cvg6CtKwfgdmVqY1WIiXKc3Q1bkRqGLi+2W/6ao+6Y7gu/RCwRuAhGEzh5B4KlszSuTLgZYuqFqo5bImjNKng==", "license": "MIT", - "peer": true, "dependencies": { "mime-types": "^3.0.0", "negotiator": "^1.0.0" @@ -11297,7 +11135,6 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-3.0.1.tgz", "integrity": "sha512-8iUql50EUR+uUcdRQ3HDqa6EVyo3docL8g5WJ3FNcWmu62IbkGUue/pEyLBW8VGKKucTPgqeks4fIU1DA4yowQ==", "license": "MIT", - "peer": true, "dependencies": { "ajv": "^8.0.0" }, @@ -11315,7 +11152,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11328,7 +11164,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^2.0.1" }, @@ -11345,7 +11180,6 @@ "integrity": "sha512-MIifVZdSLdFBRY084PaaQyU8ilG+fgGoVdzGkRLj9Q8hZ2x/ENwjiOveyWLxcjsWMYYwUtm3TTKRvWGrJvX3Iw==", "hasInstallScript": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "@appium/base-driver": "^10.1.2", "@appium/base-plugin": "^3.0.5", @@ -11390,7 +11224,6 @@ "resolved": "https://registry.npmjs.org/asyncbox/-/asyncbox-3.0.0.tgz", "integrity": "sha512-X7U0nedUMKV3nn9c4R0Zgvdvv6cw97tbDlHSZicq1snGPi/oX9DgGmFSURWtxDdnBWd3V0YviKhqAYAVvoWQ/A==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.5.1", "lodash": "^4.17.4", @@ -11405,7 +11238,6 @@ "resolved": "https://registry.npmjs.org/teen_process/-/teen_process-3.0.4.tgz", "integrity": "sha512-NQA/5PdJWEAxuYoBRW7RajnAyWmfh5iGMY1kEXiAzobtv0ztERcfDnfszpUTuvhFXrvh3E4GvHt9yWbS/EndzQ==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bluebird": "^3.7.2", "lodash": "^4.17.21", @@ -11422,7 +11254,6 @@ "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", "license": "MIT", - "peer": true, "dependencies": { "archiver-utils": "^5.0.2", "async": "^3.2.4", @@ -11441,7 +11272,6 @@ "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", "license": "MIT", - "peer": true, "dependencies": { "glob": "^10.0.0", "graceful-fs": "^4.2.0", @@ -11460,7 +11290,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -11470,7 +11299,6 @@ "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "license": "ISC", - "peer": true, "dependencies": { "foreground-child": "^3.1.0", "jackspeak": "^3.1.2", @@ -11490,15 +11318,13 @@ "version": "10.4.3", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/archiver-utils/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -11514,7 +11340,6 @@ "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "lru-cache": "^10.2.0", "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" @@ -11530,22 +11355,19 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0", - "peer": true + "license": "Python-2.0" }, "node_modules/appium-safari-driver/node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/async-lock": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/asyncbox": { "version": "4.0.1", @@ -11565,15 +11387,13 @@ "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/axios": { "version": "1.13.2", "resolved": "https://registry.npmjs.org/axios/-/axios-1.13.2.tgz", "integrity": "sha512-VPk9ebNqPcy5lRGuSlKx752IlDatOjT9paPlm8A7yOuW2Fbvp4X3JznJtT4f0GzGLLiWE9W8onz51SqLYwzGaA==", "license": "MIT", - "peer": true, "dependencies": { "follow-redirects": "^1.15.6", "form-data": "^4.0.4", @@ -11585,7 +11405,6 @@ "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", "license": "Apache-2.0", - "peer": true, "peerDependencies": { "react-native-b4a": "*" }, @@ -11599,15 +11418,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/bare-events": { "version": "2.8.2", "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", "license": "Apache-2.0", - "peer": true, "peerDependencies": { "bare-abort-controller": "*" }, @@ -11635,22 +11452,19 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/base64-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/base64-stream/-/base64-stream-1.0.0.tgz", "integrity": "sha512-BQQZftaO48FcE1Kof9CmXMFaAdqkcNorgc8CxesZv9nMbbTF1EFyQe89UOuh//QMmdtfUDXyO8rgUalemL5ODA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/basic-auth": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/basic-auth/-/basic-auth-2.0.1.tgz", "integrity": "sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "5.1.2" }, @@ -11663,7 +11477,6 @@ "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.52.tgz", "integrity": "sha512-QxD8cf2eVqJOOz63z6JIN9BzvVs/dlySa5HGSBH5xtR8dPteIRQnBxxKqkNTiT6jbDTF6jAfrd4oMcND9RGbQg==", "license": "Unlicense", - "peer": true, "engines": { "node": ">=0.6" } @@ -11673,7 +11486,6 @@ "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", "license": "MIT", - "peer": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -11685,7 +11497,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -11706,7 +11517,6 @@ "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-2.2.1.tgz", "integrity": "sha512-nfDwkulwiZYQIGwxdy0RUmowMhKcFVcYXUU7m4QlKYim1rUtg83xm2yjZ40QjDuc291AJjjeSc9b++AWHSgSHw==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "^3.1.2", "content-type": "^1.0.5", @@ -11731,7 +11541,6 @@ "resolved": "https://registry.npmjs.org/bplist-creator/-/bplist-creator-0.1.1.tgz", "integrity": "sha512-Ese7052fdWrxp/vqSJkydgx/1MdBnNOCV2XVfbmdGWD2H6EYza+Q4pyYSuVSnCUD22hfI/BFI4jHaC3NLXLlJQ==", "license": "MIT", - "peer": true, "dependencies": { "stream-buffers": "2.2.x" } @@ -11741,7 +11550,6 @@ "resolved": "https://registry.npmjs.org/bplist-parser/-/bplist-parser-0.3.2.tgz", "integrity": "sha512-apC2+fspHGI3mMKj+dGevkGo/tCqVB8jMb6i+OX+E29p0Iposz07fABkRIfVUPNd5A5VbuOz1bZbnmkKLYF+wQ==", "license": "MIT", - "peer": true, "dependencies": { "big-integer": "1.6.x" }, @@ -11768,7 +11576,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -11779,7 +11586,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.0.0" } @@ -11788,15 +11594,13 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -11806,7 +11610,6 @@ "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "function-bind": "^1.1.2" @@ -11820,7 +11623,6 @@ "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "get-intrinsic": "^1.3.0" @@ -11837,7 +11639,6 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -11854,7 +11655,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -11867,7 +11667,6 @@ "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "integrity": "sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==", "license": "MIT", - "peer": true, "dependencies": { "restore-cursor": "^3.1.0" }, @@ -11880,7 +11679,6 @@ "resolved": "https://registry.npmjs.org/cli-spinners/-/cli-spinners-2.9.2.tgz", "integrity": "sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" }, @@ -11893,7 +11691,6 @@ "resolved": "https://registry.npmjs.org/cliui/-/cliui-9.0.1.tgz", "integrity": "sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==", "license": "ISC", - "peer": true, "dependencies": { "string-width": "^7.2.0", "strip-ansi": "^7.1.0", @@ -11908,7 +11705,6 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12" }, @@ -11920,15 +11716,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/cliui/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -11946,7 +11740,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -11962,7 +11755,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-9.0.2.tgz", "integrity": "sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^6.2.1", "string-width": "^7.0.0", @@ -11980,7 +11772,6 @@ "resolved": "https://registry.npmjs.org/clone/-/clone-1.0.4.tgz", "integrity": "sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8" } @@ -11990,7 +11781,6 @@ "resolved": "https://registry.npmjs.org/color/-/color-5.0.3.tgz", "integrity": "sha512-ezmVcLR3xAVp8kYOm4GS45ZLLgIE6SPAFoduLr6hTDajwb3KZ2F46gulK3XpcwRFb5KKGCSezCBAY4Dw4HsyXA==", "license": "MIT", - "peer": true, "dependencies": { "color-convert": "^3.1.3", "color-string": "^2.1.3" @@ -12004,7 +11794,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "~1.1.4" }, @@ -12016,15 +11805,13 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/color-string": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/color-string/-/color-string-2.1.4.tgz", "integrity": "sha512-Bb6Cq8oq0IjDOe8wJmi4JeNn763Xs9cfrBcaylK1tPypWzyoy2G3l90v9k64kjphl/ZJjPIShFztenRomi8WTg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -12037,7 +11824,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -12047,7 +11833,6 @@ "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-3.1.3.tgz", "integrity": "sha512-fasDH2ont2GqF5HpyO4w0+BcewlhHEZOFn9c1ckZdHpJ56Qb7MHhH/IcJZbBGgvdtwdwNbLvxiBEdg336iA9Sg==", "license": "MIT", - "peer": true, "dependencies": { "color-name": "^2.0.0" }, @@ -12060,7 +11845,6 @@ "resolved": "https://registry.npmjs.org/color-name/-/color-name-2.1.0.tgz", "integrity": "sha512-1bPaDNFm0axzE4MEAzKPuqKWeRaT43U/hyxKPBdqTfmPF+d6n7FSoTFxLVULUJOmiLp01KjhIPPH+HrXZJN4Rg==", "license": "MIT", - "peer": true, "engines": { "node": ">=12.20" } @@ -12070,7 +11854,6 @@ "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "license": "MIT", - "peer": true, "dependencies": { "delayed-stream": "~1.0.0" }, @@ -12083,7 +11866,6 @@ "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 6" } @@ -12093,7 +11875,6 @@ "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", "license": "MIT", - "peer": true, "dependencies": { "crc-32": "^1.2.0", "crc32-stream": "^6.0.0", @@ -12110,7 +11891,6 @@ "resolved": "https://registry.npmjs.org/consola/-/consola-3.4.2.tgz", "integrity": "sha512-5IKcdX0nnYavi6G7TtOhwkYzyjfJlatbjMjuLSfE2kYT5pMDOilZ4OvMhi637CcDICTmz3wARPoyhqyX1Y+XvA==", "license": "MIT", - "peer": true, "engines": { "node": "^14.18.0 || >=16.10.0" } @@ -12126,7 +11906,6 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-1.0.1.tgz", "integrity": "sha512-oIXISMynqSqm241k6kcQ5UwttDILMK4BiurCfGEREw6+X9jkkpEe5T9FZaApyLGGOnFuyMWZpdolTXMtvEJ08Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12140,7 +11919,6 @@ "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12150,7 +11928,6 @@ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12160,7 +11937,6 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.2.2.tgz", "integrity": "sha512-D76uU73ulSXrD1UXF4KE2TMxVVwhsnCgfAyTg9k8P6KGZjlXKrOLe4dJQKI3Bxi5wjesZoFXJWElNWBjPZMbhg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6.6.0" } @@ -12169,15 +11945,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/crc-32": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", "license": "Apache-2.0", - "peer": true, "bin": { "crc32": "bin/crc32.njs" }, @@ -12190,7 +11964,6 @@ "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", "license": "MIT", - "peer": true, "dependencies": { "crc-32": "^1.2.0", "readable-stream": "^4.0.0" @@ -12204,7 +11977,6 @@ "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "license": "MIT", - "peer": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -12218,15 +11990,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/cross-spawn/node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "license": "ISC", - "peer": true, "dependencies": { "isexe": "^2.0.0" }, @@ -12242,7 +12012,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.3" }, @@ -12260,7 +12029,6 @@ "resolved": "https://registry.npmjs.org/defaults/-/defaults-1.0.4.tgz", "integrity": "sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A==", "license": "MIT", - "peer": true, "dependencies": { "clone": "^1.0.2" }, @@ -12273,7 +12041,6 @@ "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.4.0" } @@ -12283,7 +12050,6 @@ "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -12294,7 +12060,6 @@ "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "license": "Apache-2.0", "optional": true, - "peer": true, "engines": { "node": ">=8" } @@ -12304,15 +12069,13 @@ "resolved": "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz", "integrity": "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/diff": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/diff/-/diff-8.0.2.tgz", "integrity": "sha512-sSuxWU5j5SR9QQji/o2qMvqRNYRDOcBTgsJ/DeCf4iSN4gW+gNMXM7wFIP+fdXZxoNiAnHUTGjCr+TSWXdRDKg==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.3.1" } @@ -12322,7 +12085,6 @@ "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.1", "es-errors": "^1.3.0", @@ -12336,43 +12098,37 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/ee-first": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/enabled": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -12382,7 +12138,6 @@ "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", "license": "MIT", - "peer": true, "dependencies": { "is-arrayish": "^0.2.1" } @@ -12392,7 +12147,6 @@ "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" } @@ -12402,7 +12156,6 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" } @@ -12412,7 +12165,6 @@ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0" }, @@ -12425,7 +12177,6 @@ "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "get-intrinsic": "^1.2.6", @@ -12441,7 +12192,6 @@ "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -12450,15 +12200,13 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/etag": { "version": "1.8.1", "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12468,7 +12216,6 @@ "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -12478,7 +12225,6 @@ "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.8.x" } @@ -12488,7 +12234,6 @@ "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", "license": "Apache-2.0", - "peer": true, "dependencies": { "bare-events": "^2.7.0" } @@ -12498,7 +12243,6 @@ "resolved": "https://registry.npmjs.org/express/-/express-5.2.1.tgz", "integrity": "sha512-hIS4idWWai69NezIdRt2xFVofaF4j+6INOpJlVOLDO8zXGpUVEVzIYk12UUi2JzjEzWL3IOAxcTubgz9Po0yXw==", "license": "MIT", - "peer": true, "dependencies": { "accepts": "^2.0.0", "body-parser": "^2.2.1", @@ -12548,8 +12292,7 @@ "version": "1.3.2", "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/fast-uri": { "version": "3.1.0", @@ -12573,7 +12316,6 @@ "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.16.tgz", "integrity": "sha512-eRnCtTTtGZFpQCwhJiUOuxPQWRXVKYDn0b2PeHfXL6/Zi53SLAzAHfVhVWK2AryC/WH05kGfxhFIPvTF0SXQzg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 4.9.1" } @@ -12582,15 +12324,13 @@ "version": "4.2.3", "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/finalhandler": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-2.1.1.tgz", "integrity": "sha512-S8KoZgRZN+a5rNwqTxlZZePjT/4cnm0ROV70LedRHZ0p8u9fRID0hJUZQpkKLzro8LfmC8sx23bY6tVNxv8pQA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "encodeurl": "^2.0.0", @@ -12612,7 +12352,6 @@ "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "license": "MIT", - "peer": true, "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -12628,8 +12367,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fn.name/-/fn.name-1.1.0.tgz", "integrity": "sha512-GRnmB5gPyJpAhTQdSZTSp9uaPSvl09KoYcMQtsB9rQoOmzs9dH6ffeccH+Z+cv6P68Hu5bC6JjRh4Ah/mHSNRw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/follow-redirects": { "version": "1.15.11", @@ -12642,7 +12380,6 @@ } ], "license": "MIT", - "peer": true, "engines": { "node": ">=4.0" }, @@ -12657,7 +12394,6 @@ "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "license": "ISC", - "peer": true, "dependencies": { "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" @@ -12674,7 +12410,6 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "license": "ISC", - "peer": true, "engines": { "node": ">=14" }, @@ -12687,7 +12422,6 @@ "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "license": "MIT", - "peer": true, "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", @@ -12704,7 +12438,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12714,7 +12447,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "1.52.0" }, @@ -12727,7 +12459,6 @@ "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -12737,7 +12468,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-2.0.0.tgz", "integrity": "sha512-Rx/WycZ60HOaqLKAi6cHRKKI7zxWbJ31MhntmtwMoaTeF7XFH9hhBp8vITaMidfljRQ6eYWCKkaTK+ykVJHP2A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -12746,7 +12476,6 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/ftp-response-parser/-/ftp-response-parser-1.0.1.tgz", "integrity": "sha512-++Ahlo2hs/IC7UVQzjcSAfeUpCwTTzs4uvG5XfGnsinIFkWUYF4xWwPd5qZuK8MJrmUIxFMuHcfqaosCDjvIWw==", - "peer": true, "dependencies": { "readable-stream": "^1.0.31" }, @@ -12758,15 +12487,13 @@ "version": "0.0.1", "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/ftp-response-parser/node_modules/readable-stream": { "version": "1.1.14", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", "integrity": "sha512-+MeVjFf4L44XUkhM1eYbD8fyEsxcV81pqMSR5gblfcLCHfZvbrqy4/qYHE+/R5HoBUT11WV5O08Cr1n3YXkWVQ==", "license": "MIT", - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.1", @@ -12778,15 +12505,13 @@ "version": "0.10.31", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", "integrity": "sha512-ev2QzSzWPYmy9GuqfIVildA4OdcGLeFZQrq5ys6RtiuF+RQQiZWr8TZNyAcuVXyQRYfEO+MsoB/1BuQVhOJuoQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "license": "MIT", - "peer": true, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -12796,7 +12521,6 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "license": "ISC", - "peer": true, "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -12806,7 +12530,6 @@ "resolved": "https://registry.npmjs.org/get-east-asian-width/-/get-east-asian-width-1.4.0.tgz", "integrity": "sha512-QZjmEOC+IT1uk6Rx0sX22V6uHWVwbdbxf1faPqJ1QhLdGgsRGCZoyaQBm/piRdJy/D2um6hM1UP7ZEeQ4EkP+Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -12819,7 +12542,6 @@ "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "license": "MIT", - "peer": true, "dependencies": { "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", @@ -12844,7 +12566,6 @@ "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", "license": "MIT", - "peer": true, "dependencies": { "dunder-proto": "^1.0.1", "es-object-atoms": "^1.0.0" @@ -12858,7 +12579,6 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -12903,7 +12623,6 @@ "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -12915,23 +12634,20 @@ "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/handle-thing": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/handle-thing/-/handle-thing-2.0.1.tgz", "integrity": "sha512-9Qn4yBxelxoh2Ow62nP+Ka/kMnOXRi8BXnRaUwezLNhqelnN49xKz4F/dPP8OYLxLxq6JDtZb2i9XznUQbNPTg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -12941,7 +12657,6 @@ "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -12954,7 +12669,6 @@ "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "license": "MIT", - "peer": true, "dependencies": { "has-symbols": "^1.0.3" }, @@ -12970,7 +12684,6 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "license": "MIT", - "peer": true, "dependencies": { "function-bind": "^1.1.2" }, @@ -12982,8 +12695,7 @@ "version": "2.8.9", "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/hpack.js": { "version": "2.1.6", @@ -12991,7 +12703,6 @@ "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.1", "obuf": "^1.0.0", @@ -13004,8 +12715,7 @@ "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/hpack.js/node_modules/readable-stream": { "version": "2.3.8", @@ -13013,7 +12723,6 @@ "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -13030,7 +12739,6 @@ "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -13040,15 +12748,13 @@ "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/http-errors": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "license": "MIT", - "peer": true, "dependencies": { "depd": "~2.0.0", "inherits": "~2.0.4", @@ -13068,15 +12774,13 @@ "version": "2.3.0", "resolved": "https://registry.npmjs.org/http-status-codes/-/http-status-codes-2.3.0.tgz", "integrity": "sha512-RJ8XvFvpPM/Dmc5SV+dC4y5PCeOhT3x1Hq0NU3rjGeg5a/CqlhZ7uudknPwZFz4aeAXDcbAyaeP7GAo9lvngtA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/iconv-lite": { "version": "0.7.1", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.7.1.tgz", "integrity": "sha512-2Tth85cXwGFHfvRgZWszZSvdo+0Xsqmw8k8ZwxScfcBneNUraK+dxRxRm24nszx80Y0TVio8kKLt5sLE7ZCLlw==", "license": "MIT", - "peer": true, "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" }, @@ -13106,22 +12810,19 @@ "url": "https://feross.org/support" } ], - "license": "BSD-3-Clause", - "peer": true + "license": "BSD-3-Clause" }, "node_modules/appium-safari-driver/node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/ipaddr.js": { "version": "1.9.1", "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.10" } @@ -13130,15 +12831,13 @@ "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "license": "MIT", - "peer": true, "dependencies": { "hasown": "^2.0.2" }, @@ -13154,7 +12853,6 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -13164,7 +12862,6 @@ "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-1.0.0.tgz", "integrity": "sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -13182,15 +12879,13 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-4.0.0.tgz", "integrity": "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/is-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" }, @@ -13203,7 +12898,6 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -13225,7 +12919,6 @@ "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "license": "BlueOak-1.0.0", - "peer": true, "dependencies": { "@isaacs/cliui": "^8.0.2" }, @@ -13240,15 +12933,13 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/jsftp": { "version": "2.1.3", "resolved": "https://registry.npmjs.org/jsftp/-/jsftp-2.1.3.tgz", "integrity": "sha512-r79EVB8jaNAZbq8hvanL8e8JGu2ZNr2bXdHC4ZdQhRImpSPpnWwm5DYVzQ5QxJmtGtKhNNuvqGgbNaFl604fEQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^3.1.0", "ftp-response-parser": "^1.0.1", @@ -13266,7 +12957,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==", "license": "MIT", - "peer": true, "dependencies": { "ms": "^2.1.1" } @@ -13275,15 +12965,13 @@ "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/json-schema": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "integrity": "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA==", - "license": "(AFL-2.1 OR BSD-3-Clause)", - "peer": true + "license": "(AFL-2.1 OR BSD-3-Clause)" }, "node_modules/appium-safari-driver/node_modules/json-schema-traverse": { "version": "1.0.0", @@ -13297,7 +12985,6 @@ "resolved": "https://registry.npmjs.org/klaw/-/klaw-4.1.0.tgz", "integrity": "sha512-1zGZ9MF9H22UnkpVeuaGKOjfA2t6WrfdrJmGjy16ykcjnKQDmHVX+KI477rpbGevz/5FD4MC3xf1oxylBgcaQw==", "license": "MIT", - "peer": true, "engines": { "node": ">=14.14.0" } @@ -13307,7 +12994,6 @@ "resolved": "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz", "integrity": "sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -13316,15 +13002,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/kuler/-/kuler-2.0.0.tgz", "integrity": "sha512-Xq9nH7KlWZmXAtodXDDRE7vs6DU1gTU8zYDHDiWLSip45Egwq3plLHzPn27NgvzL2r1LMPC1vdqh98sQxtqj4A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/lazystream": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", "license": "MIT", - "peer": true, "dependencies": { "readable-stream": "^2.0.5" }, @@ -13336,15 +13020,13 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/lazystream/node_modules/readable-stream": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", "license": "MIT", - "peer": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -13360,7 +13042,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "~5.1.0" } @@ -13370,7 +13051,6 @@ "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-3.1.3.tgz", "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", "license": "MIT", - "peer": true, "engines": { "node": ">=14" }, @@ -13382,15 +13062,13 @@ "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "license": "MIT", - "peer": true, "dependencies": { "p-locate": "^5.0.0" }, @@ -13406,7 +13084,6 @@ "resolved": "https://registry.npmjs.org/lockfile/-/lockfile-1.0.4.tgz", "integrity": "sha512-cvbTwETRfsFh4nHsL1eGWapU1XFi5Ot9E85sWAwia7Y7EgB7vfqcZhTKZ+l7hCGxSPoushMv5GKhT5PdLv03WA==", "license": "ISC", - "peer": true, "dependencies": { "signal-exit": "^3.0.2" } @@ -13428,7 +13105,6 @@ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "license": "MIT", - "peer": true, "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -13445,7 +13121,6 @@ "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "1.6.0", "@types/triple-beam": "^1.3.2", @@ -13472,7 +13147,6 @@ "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" } @@ -13482,7 +13156,6 @@ "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -13492,7 +13165,6 @@ "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-2.0.0.tgz", "integrity": "sha512-Snk314V5ayFLhp3fkUREub6WtjBfPdCPY1Ln8/8munuLuiYhsABgBVWsozAG+MWMbVEvcdcpbi9R7ww22l9Q3g==", "license": "MIT", - "peer": true, "engines": { "node": ">=18" }, @@ -13505,7 +13177,6 @@ "resolved": "https://registry.npmjs.org/method-override/-/method-override-3.0.0.tgz", "integrity": "sha512-IJ2NNN/mSl9w3kzWB92rcdHpz+HjkxhDJWNDBqSlas+zQdP8wBiJzITPg08M/k2uVvMow7Sk41atndNtt/PHSA==", "license": "MIT", - "peer": true, "dependencies": { "debug": "3.1.0", "methods": "~1.1.2", @@ -13521,7 +13192,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", "integrity": "sha512-OX8XqP7/1a9cqkxYw2yXss15f26NKWBpDXQd0/uK/KPqdQhxbPa994hnzjcE2VqQpDslf55723cKPUOGSmMY3g==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -13530,15 +13200,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/methods": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -13548,7 +13216,6 @@ "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -13558,7 +13225,6 @@ "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "license": "MIT", - "peer": true, "dependencies": { "mime-db": "^1.54.0" }, @@ -13575,7 +13241,6 @@ "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "license": "MIT", - "peer": true, "engines": { "node": ">=6" } @@ -13585,8 +13250,7 @@ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==", "license": "ISC", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/minipass": { "version": "7.1.2", @@ -13602,7 +13266,6 @@ "resolved": "https://registry.npmjs.org/moment/-/moment-2.30.1.tgz", "integrity": "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -13612,7 +13275,6 @@ "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "license": "MIT", - "peer": true, "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", @@ -13629,7 +13291,6 @@ "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", "license": "MIT", - "peer": true, "dependencies": { "ms": "2.0.0" } @@ -13638,15 +13299,13 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/morgan/node_modules/on-finished": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", "integrity": "sha512-ikqdkGAAyf/X/gPhXGvfgAytDZtDbr+bkNUJ0N9h5MI/dmdgCs3l6hoHrcUv41sRKew3jIwrp4qQDXiK99Utww==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -13658,15 +13317,13 @@ "version": "2.1.3", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", "integrity": "sha512-zIdGUrPRFTUELUvr3Gmc7KZ2Sw/h1PiVM0Af/oHB6zgnV1ikqSfRk+TOufi79aHYCW3NiOXmr1BP5nWbzojLaA==", "license": "MIT", - "peer": true, "bin": { "ncp": "bin/ncp" } @@ -13676,7 +13333,6 @@ "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -13707,7 +13363,6 @@ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", "license": "BSD-2-Clause", - "peer": true, "dependencies": { "hosted-git-info": "^2.1.4", "resolve": "^1.10.0", @@ -13720,7 +13375,6 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", "license": "ISC", - "peer": true, "bin": { "semver": "bin/semver" } @@ -13730,7 +13384,6 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -13740,7 +13393,6 @@ "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -13753,15 +13405,13 @@ "resolved": "https://registry.npmjs.org/obuf/-/obuf-1.1.2.tgz", "integrity": "sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "license": "MIT", - "peer": true, "dependencies": { "ee-first": "1.1.1" }, @@ -13774,7 +13424,6 @@ "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -13784,7 +13433,6 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "license": "ISC", - "peer": true, "dependencies": { "wrappy": "1" } @@ -13794,7 +13442,6 @@ "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", "integrity": "sha512-5DXOiRKwuSEcQ/l0kGCF6Q3jcADFv5tSmRaJck/OqkVFcOzutB134KRSfF0xDrL39MNnqxbHBbUUcjZIhTgb2g==", "license": "MIT", - "peer": true, "dependencies": { "fn.name": "1.x.x" } @@ -13804,7 +13451,6 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "license": "MIT", - "peer": true, "dependencies": { "mimic-fn": "^2.1.0" }, @@ -13820,7 +13466,6 @@ "resolved": "https://registry.npmjs.org/ora/-/ora-5.4.1.tgz", "integrity": "sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ==", "license": "MIT", - "peer": true, "dependencies": { "bl": "^4.1.0", "chalk": "^4.1.0", @@ -13844,7 +13489,6 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "license": "MIT", - "peer": true, "dependencies": { "yocto-queue": "^0.1.0" }, @@ -13860,7 +13504,6 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "license": "MIT", - "peer": true, "dependencies": { "p-limit": "^3.0.2" }, @@ -13876,7 +13519,6 @@ "resolved": "https://registry.npmjs.org/package-changed/-/package-changed-3.0.0.tgz", "integrity": "sha512-HSRbrO+Ab5AuqqYGSevtKJ1Yt96jW1VKV7wrp8K4SKj5tyDp/7D96uPCQyCPiNtWTEH/7nA3hZ4z2slbc9yFxg==", "license": "ISC", - "peer": true, "dependencies": { "commander": "^6.2.0" }, @@ -13895,7 +13537,6 @@ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "license": "MIT", - "peer": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -13913,7 +13554,6 @@ "version": "1.1.3", "resolved": "https://registry.npmjs.org/parse-listing/-/parse-listing-1.1.3.tgz", "integrity": "sha512-a1p1i+9Qyc8pJNwdrSvW1g5TPxRH0sywVi6OzVvYHRo6xwF9bDWBxtH0KkxeOOvhUE8vAMtiSfsYQFOuK901eA==", - "peer": true, "engines": { "node": ">=0.6.21" } @@ -13923,7 +13563,6 @@ "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -13933,7 +13572,6 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -13943,7 +13581,6 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -13952,8 +13589,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/path-scurry": { "version": "2.0.1", @@ -13976,7 +13612,6 @@ "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "license": "MIT", - "peer": true, "funding": { "type": "opencollective", "url": "https://opencollective.com/express" @@ -13986,22 +13621,19 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/pkg-dir": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-5.0.0.tgz", "integrity": "sha512-NPE8TDbzl/3YQYY7CSS228s3g2ollTFnc+Qi3tqmqJp9Vg2ovUpixcJEo2HJScN2Ez+kEaal6y70c0ehqJBJeA==", "license": "MIT", - "peer": true, "dependencies": { "find-up": "^5.0.0" }, @@ -14014,7 +13646,6 @@ "resolved": "https://registry.npmjs.org/plist/-/plist-3.1.0.tgz", "integrity": "sha512-uysumyrvkUX0rX/dEVqt8gC3sTBzd4zoWfLeS29nb53imdaXVvLINYXTI2GNqzaMuvacNx4uJQ8+b3zXR0pkgQ==", "license": "MIT", - "peer": true, "dependencies": { "@xmldom/xmldom": "^0.8.8", "base64-js": "^1.5.1", @@ -14029,7 +13660,6 @@ "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", "license": "MIT", - "peer": true, "engines": { "node": ">=4" } @@ -14062,7 +13692,6 @@ "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6.0" } @@ -14071,15 +13700,13 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/proxy-addr": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", "license": "MIT", - "peer": true, "dependencies": { "forwarded": "0.2.0", "ipaddr.js": "1.9.1" @@ -14092,15 +13719,13 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/qs": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.0.tgz", "integrity": "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w==", "license": "BSD-3-Clause", - "peer": true, "dependencies": { "side-channel": "^1.1.0" }, @@ -14116,7 +13741,6 @@ "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -14126,7 +13750,6 @@ "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-3.0.2.tgz", "integrity": "sha512-K5zQjDllxWkf7Z5xJdV0/B0WTNqx6vxG70zJE4N0kBs4LovmEYWJzQGxC9bS9RAKu3bgM40lrd5zoLJ12MQ5BA==", "license": "MIT", - "peer": true, "dependencies": { "bytes": "~3.1.2", "http-errors": "~2.0.1", @@ -14142,7 +13765,6 @@ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", "license": "MIT", - "peer": true, "dependencies": { "@types/normalize-package-data": "^2.4.0", "normalize-package-data": "^2.5.0", @@ -14158,7 +13780,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", "license": "(MIT OR CC0-1.0)", - "peer": true, "engines": { "node": ">=8" } @@ -14168,7 +13789,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", "license": "MIT", - "peer": true, "dependencies": { "abort-controller": "^3.0.0", "buffer": "^6.0.3", @@ -14199,7 +13819,6 @@ } ], "license": "MIT", - "peer": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.2.1" @@ -14210,7 +13829,6 @@ "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "minimatch": "^5.1.0" } @@ -14220,7 +13838,6 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", - "peer": true, "dependencies": { "balanced-match": "^1.0.0" } @@ -14230,7 +13847,6 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^2.0.1" }, @@ -14253,7 +13869,6 @@ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", "license": "MIT", - "peer": true, "dependencies": { "is-core-module": "^2.16.1", "path-parse": "^1.0.7", @@ -14274,7 +13889,6 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14284,7 +13898,6 @@ "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "integrity": "sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==", "license": "MIT", - "peer": true, "dependencies": { "onetime": "^5.1.0", "signal-exit": "^3.0.2" @@ -14317,7 +13930,6 @@ "resolved": "https://registry.npmjs.org/router/-/router-2.2.0.tgz", "integrity": "sha512-nLTrUKm2UyiL7rlhapu/Zl45FwNgkZGaCpZbIHajDYgwlJCOzLSk+cIPAnsEqV955GjILJnKbdQC1nVPz+gAYQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.0", "depd": "^2.0.0", @@ -14333,15 +13945,13 @@ "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/safe-stable-stringify": { "version": "2.5.0", "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" } @@ -14350,15 +13960,13 @@ "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/sanitize-filename": { "version": "1.6.3", "resolved": "https://registry.npmjs.org/sanitize-filename/-/sanitize-filename-1.6.3.tgz", "integrity": "sha512-y/52Mcy7aw3gRm7IrcGDFx/bCk4AhRh2eI9luHOQM86nZsqwiRkkq2GekHXBBD+SmPidc8i2PqtYZl+pWJ8Oeg==", "license": "WTFPL OR ISC", - "peer": true, "dependencies": { "truncate-utf8-bytes": "^1.0.0" } @@ -14368,8 +13976,7 @@ "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==", "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/appium-safari-driver/node_modules/semver": { "version": "7.7.3", @@ -14388,7 +13995,6 @@ "resolved": "https://registry.npmjs.org/send/-/send-1.2.1.tgz", "integrity": "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ==", "license": "MIT", - "peer": true, "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", @@ -14415,7 +14021,6 @@ "resolved": "https://registry.npmjs.org/serve-favicon/-/serve-favicon-2.5.1.tgz", "integrity": "sha512-JndLBslCLA/ebr7rS3d+/EKkzTsTi1jI2T9l+vHfAaGJ7A7NhtDpSZ0lx81HCNWnnE0yHncG+SSnVf9IMxOwXQ==", "license": "MIT", - "peer": true, "dependencies": { "etag": "~1.8.1", "fresh": "~0.5.2", @@ -14432,7 +14037,6 @@ "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.6" } @@ -14455,15 +14059,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/serve-static": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-2.2.1.tgz", "integrity": "sha512-xRXBn0pPqQTVQiC8wyQrKs2MOlX24zQ0POGaj0kultvoOCstBQM5yvOhAVSUwOMjQtTvsPWoNCHfPGwaaQJhTw==", "license": "MIT", - "peer": true, "dependencies": { "encodeurl": "^2.0.0", "escape-html": "^1.0.3", @@ -14488,8 +14090,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/sharp": { "version": "0.34.5", @@ -14498,7 +14099,6 @@ "hasInstallScript": true, "license": "Apache-2.0", "optional": true, - "peer": true, "dependencies": { "@img/colour": "^1.0.0", "detect-libc": "^2.1.2", @@ -14542,7 +14142,6 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "license": "MIT", - "peer": true, "dependencies": { "shebang-regex": "^3.0.0" }, @@ -14555,7 +14154,6 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14577,7 +14175,6 @@ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", @@ -14597,7 +14194,6 @@ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "license": "MIT", - "peer": true, "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" @@ -14614,7 +14210,6 @@ "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -14633,7 +14228,6 @@ "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "license": "MIT", - "peer": true, "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", @@ -14652,15 +14246,13 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "license": "BSD-3-Clause", - "peer": true, "engines": { "node": ">=0.10.0" } @@ -14670,7 +14262,6 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", "license": "MIT", - "peer": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -14681,7 +14272,6 @@ "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "spdx-expression-parse": "^3.0.0", "spdx-license-ids": "^3.0.0" @@ -14691,15 +14281,13 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", - "license": "CC-BY-3.0", - "peer": true + "license": "CC-BY-3.0" }, "node_modules/appium-safari-driver/node_modules/spdx-expression-parse": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", "license": "MIT", - "peer": true, "dependencies": { "spdx-exceptions": "^2.1.0", "spdx-license-ids": "^3.0.0" @@ -14709,8 +14297,7 @@ "version": "3.0.22", "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", - "license": "CC0-1.0", - "peer": true + "license": "CC0-1.0" }, "node_modules/appium-safari-driver/node_modules/spdy": { "version": "4.0.2", @@ -14718,7 +14305,6 @@ "integrity": "sha512-r46gZQZQV+Kl9oItvl1JZZqJKGr+oEkB08A6BzkiR7593/7IbtuncXHd2YoYeTsG4157ZssMu9KYvUHLcjcDoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "handle-thing": "^2.0.0", @@ -14736,7 +14322,6 @@ "integrity": "sha512-hsLVFE5SjA6TCisWeJXFKniGGOpBgMLmerfO2aCyCU5s7nJ/rpAepqmFifv/GCbSbueEeAJJnmSQ2rKC/g8Fcw==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "debug": "^4.1.0", "detect-node": "^2.0.4", @@ -14752,7 +14337,6 @@ "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -14767,7 +14351,6 @@ "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", "integrity": "sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -14777,7 +14360,6 @@ "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -14787,7 +14369,6 @@ "resolved": "https://registry.npmjs.org/stream-buffers/-/stream-buffers-2.2.0.tgz", "integrity": "sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==", "license": "Unlicense", - "peer": true, "engines": { "node": ">= 0.10.0" } @@ -14797,7 +14378,6 @@ "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", "license": "MIT", - "peer": true, "dependencies": { "duplexer": "~0.1.1", "through": "~2.3.4" @@ -14808,7 +14388,6 @@ "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", "license": "MIT", - "peer": true, "dependencies": { "events-universal": "^1.0.0", "fast-fifo": "^1.3.2", @@ -14820,7 +14399,6 @@ "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", "license": "MIT", - "peer": true, "dependencies": { "safe-buffer": "~5.2.0" } @@ -14843,15 +14421,13 @@ "url": "https://feross.org/support" } ], - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14867,7 +14443,6 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -14882,7 +14457,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14896,7 +14470,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1" }, @@ -14909,7 +14482,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14919,7 +14491,6 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "license": "MIT", - "peer": true, "engines": { "node": ">=8" } @@ -14929,7 +14500,6 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "license": "MIT", - "peer": true, "dependencies": { "has-flag": "^4.0.0" }, @@ -14945,7 +14515,6 @@ "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.4" }, @@ -14958,7 +14527,6 @@ "resolved": "https://registry.npmjs.org/tagged-tag/-/tagged-tag-1.0.0.tgz", "integrity": "sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==", "license": "MIT", - "peer": true, "engines": { "node": ">=20" }, @@ -14971,7 +14539,6 @@ "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.7.tgz", "integrity": "sha512-qJj60CXt7IU1Ffyc3NJMjh6EkuCFej46zUqJ4J7pqYlThyd9bO0XBTmcOIhSzZJVWfsLks0+nle/j538YAW9RQ==", "license": "MIT", - "peer": true, "dependencies": { "b4a": "^1.6.4", "fast-fifo": "^1.2.0", @@ -14998,7 +14565,6 @@ "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", "license": "Apache-2.0", - "peer": true, "dependencies": { "b4a": "^1.6.4" } @@ -15007,22 +14573,19 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/text-hex/-/text-hex-1.0.0.tgz", "integrity": "sha512-uuVGNWzgJ4yhRaNSiubPY7OjISw4sw4E5Uv0wbjp+OzcbmVU/rsT8ujgcXJhn9ypzsgr5vlzpPqP+MBBKcGvbg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", "license": "MIT", - "peer": true, "engines": { "node": ">=0.6" } @@ -15032,7 +14595,6 @@ "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 14.0.0" } @@ -15042,7 +14604,6 @@ "resolved": "https://registry.npmjs.org/truncate-utf8-bytes/-/truncate-utf8-bytes-1.0.2.tgz", "integrity": "sha512-95Pu1QXQvruGEhv62XCMO3Mm90GscOCClvrIUwCM0PYOXK3kaF3l3sIHxx71ThJfcbM2O5Au6SO3AWCSEfW4mQ==", "license": "WTFPL", - "peer": true, "dependencies": { "utf8-byte-length": "^1.0.1" } @@ -15059,7 +14620,6 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-5.3.0.tgz", "integrity": "sha512-d9CwU93nN0IA1QL+GSNDdwLAu1Ew5ZjTwupvedwg3WdfoH6pIDvYQ2hV0Uc2nKBLPq7NB5apCx57MLS5qlmO5g==", "license": "(MIT OR CC0-1.0)", - "peer": true, "dependencies": { "tagged-tag": "^1.0.0" }, @@ -15075,7 +14635,6 @@ "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "license": "MIT", - "peer": true, "dependencies": { "content-type": "^1.0.5", "media-typer": "^1.1.0", @@ -15090,7 +14649,6 @@ "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==", "license": "MIT or GPL-2.0", - "peer": true, "engines": { "node": ">= 0.4.0" } @@ -15100,7 +14658,6 @@ "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -15109,15 +14666,13 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", "integrity": "sha512-Xn0w3MtiQ6zoz2vFyUVruaCL53O/DwUvkEeOvj+uulMm0BkUGYWmBYVyElqZaSLhY6ZD0ulfU3aBra2aVT4xfA==", - "license": "(WTFPL OR MIT)", - "peer": true + "license": "(WTFPL OR MIT)" }, "node_modules/appium-safari-driver/node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/uuid": { "version": "13.0.0", @@ -15137,7 +14692,6 @@ "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", "license": "Apache-2.0", - "peer": true, "dependencies": { "spdx-correct": "^3.0.0", "spdx-expression-parse": "^3.0.0" @@ -15148,7 +14702,6 @@ "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", "license": "MIT", - "peer": true, "engines": { "node": ">= 0.8" } @@ -15159,7 +14712,6 @@ "integrity": "sha512-O84QOnr0icsbFGLS0O3bI5FswxzRr8/gHwWkDlQFskhSPryQXvrTMxjxGP4+iWYoauLoBvfDpkrOauZ+0iZpDA==", "license": "MIT", "optional": true, - "peer": true, "dependencies": { "minimalistic-assert": "^1.0.0" } @@ -15169,7 +14721,6 @@ "resolved": "https://registry.npmjs.org/wcwidth/-/wcwidth-1.0.1.tgz", "integrity": "sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg==", "license": "MIT", - "peer": true, "dependencies": { "defaults": "^1.0.3" } @@ -15194,7 +14745,6 @@ "resolved": "https://registry.npmjs.org/winston/-/winston-3.18.3.tgz", "integrity": "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww==", "license": "MIT", - "peer": true, "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", @@ -15217,7 +14767,6 @@ "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "license": "MIT", - "peer": true, "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", @@ -15232,7 +14781,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15247,7 +14795,6 @@ "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "license": "MIT", - "peer": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -15262,7 +14809,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15281,7 +14827,6 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "license": "MIT", - "peer": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -15298,15 +14843,13 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "license": "ISC", - "peer": true + "license": "ISC" }, "node_modules/appium-safari-driver/node_modules/ws": { "version": "8.18.3", "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "license": "MIT", - "peer": true, "engines": { "node": ">=10.0.0" }, @@ -15328,7 +14871,6 @@ "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-15.1.1.tgz", "integrity": "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg==", "license": "MIT", - "peer": true, "engines": { "node": ">=8.0" } @@ -15338,7 +14880,6 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "license": "ISC", - "peer": true, "engines": { "node": ">=10" } @@ -15348,7 +14889,6 @@ "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.2.tgz", "integrity": "sha512-mplynKqc1C2hTVYxd0PU2xQAc22TI1vShAYGksCCfxbn/dFwnHTNi1bvYsBTkhdUNtGIf5xNOg938rrSSYvS9A==", "license": "ISC", - "peer": true, "bin": { "yaml": "bin.mjs" }, @@ -15364,7 +14904,6 @@ "resolved": "https://registry.npmjs.org/yargs/-/yargs-18.0.0.tgz", "integrity": "sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==", "license": "MIT", - "peer": true, "dependencies": { "cliui": "^9.0.1", "escalade": "^3.1.1", @@ -15382,7 +14921,6 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-22.0.0.tgz", "integrity": "sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==", "license": "ISC", - "peer": true, "engines": { "node": "^20.19.0 || ^22.12.0 || >=23" } @@ -15391,15 +14929,13 @@ "version": "10.6.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.6.0.tgz", "integrity": "sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==", - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/appium-safari-driver/node_modules/yargs/node_modules/string-width": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/string-width/-/string-width-7.2.0.tgz", "integrity": "sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==", "license": "MIT", - "peer": true, "dependencies": { "emoji-regex": "^10.3.0", "get-east-asian-width": "^1.0.0", @@ -15417,7 +14953,6 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^6.0.1" }, @@ -15433,7 +14968,6 @@ "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", "license": "MIT", - "peer": true, "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" @@ -15447,7 +14981,6 @@ "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", "license": "MIT", - "peer": true, "engines": { "node": "*" } @@ -15457,7 +14990,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -15470,7 +15002,6 @@ "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", "license": "MIT", - "peer": true, "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", @@ -16640,36 +16171,12 @@ } }, "node_modules/doc-detective-common": { - "version": "3.6.1", - "resolved": "https://registry.npmjs.org/doc-detective-common/-/doc-detective-common-3.6.1.tgz", - "integrity": "sha512-Vnc/1W3UwsG6c5HNl8PGLpycDECeBdc4VyCr1LasWt8GHDeO6YvASUC6IW0QS6zEp1DsdJgl/Nfa1GSbWKAhjg==", - "license": "AGPL-3.0-only", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^15.1.3", - "ajv": "^8.17.1", - "ajv-errors": "^3.0.0", - "ajv-formats": "^3.0.1", - "ajv-keywords": "^5.1.0", - "axios": "^1.13.2", - "yaml": "^2.8.2" - } + "resolved": "../common", + "link": true }, "node_modules/doc-detective-resolver": { - "version": "3.6.2", - "resolved": "https://registry.npmjs.org/doc-detective-resolver/-/doc-detective-resolver-3.6.2.tgz", - "integrity": "sha512-jLFVXFR82vkF6FapYm1ivOMFAov1LXj/dHaHulKaUXVIqx58ADEgtoL8aRWy0cZCSA59xVwcfZrxhUONVsb2Ug==", - "license": "AGPL-3.0-only", - "dependencies": { - "@apidevtools/json-schema-ref-parser": "^15.1.3", - "adm-zip": "^0.5.16", - "ajv": "^8.17.1", - "axios": "^1.13.2", - "doc-detective-common": "^3.6.1", - "dotenv": "^17.2.3", - "fast-xml-parser": "^5.3.3", - "json-schema-faker": "^0.5.9", - "posthog-node": "^5.18.1" - } + "resolved": "../resolver", + "link": true }, "node_modules/dom-serializer": { "version": "2.0.0", @@ -17979,6 +17486,7 @@ "node_modules/jsep": { "version": "1.4.0", "license": "MIT", + "peer": true, "engines": { "node": ">= 10.16.0" } @@ -18702,6 +18210,22 @@ "node": ">= 0.4.0" } }, + "node_modules/node-addon-api": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.1.tgz", + "integrity": "sha512-5m3bsyrjFWE1xf7nz7YXdN4udnVtXK6/Yfgn5qnahL6bCkf2yKt4k3nuTKAtT4r3IG8JNR2ncsIMdZuAzJjHQQ==", + "license": "MIT" + }, + "node_modules/node-pty": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", + "integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^7.1.0" + } + }, "node_modules/normalize-package-data": { "version": "2.5.0", "license": "BSD-2-Clause", diff --git a/package.json b/package.json index 2791f4b5..99227147 100644 --- a/package.json +++ b/package.json @@ -36,11 +36,12 @@ "appium-geckodriver": "^2.1.6", "appium-safari-driver": "^4.1.6", "axios": "^1.13.2", - "doc-detective-common": "^3.6.1", - "doc-detective-resolver": "^3.6.2", + "doc-detective-common": "file:../common", + "doc-detective-resolver": "file:../resolver", "dotenv": "^17.2.3", "geckodriver": "^6.1.0", "jq-web": "^0.6.2", + "node-pty": "^1.1.0", "json-schema-faker": "^0.5.9", "pixelmatch": "^7.1.0", "pngjs": "^7.0.0", diff --git a/src/config.js b/src/config.js index 8415e316..f0c318d2 100644 --- a/src/config.js +++ b/src/config.js @@ -320,7 +320,7 @@ async function getAvailableApps({ config }) { const chrome = installedBrowsers.find( (browser) => browser.browser === "chrome" ); - const chromeVersion = chrome.buildId; + const chromeVersion = chrome?.buildId; const chromedriver = installedBrowsers.find( (browser) => browser.browser === "chromedriver" ); diff --git a/src/scopes/cleanup.js b/src/scopes/cleanup.js new file mode 100644 index 00000000..48ad963e --- /dev/null +++ b/src/scopes/cleanup.js @@ -0,0 +1,91 @@ +/** + * Cleanup Handlers - Graceful shutdown for scoped processes + * + * Sets up process signal handlers to ensure all scoped processes + * are terminated when the main process exits. + */ + +/** + * Setup cleanup handlers for graceful shutdown + * @param {ScopeRegistry} registry - The scope registry to cleanup + * @returns {Function} Function to remove the handlers + */ +function setupCleanupHandlers(registry) { + let isCleaningUp = false; + + const cleanup = async () => { + if (isCleaningUp) return; + isCleaningUp = true; + + try { + await registry.cleanup(); + } catch (e) { + // Ignore errors during cleanup + } + }; + + // Signal handlers + const sigintHandler = async () => { + await cleanup(); + process.exit(130); // 128 + SIGINT (2) + }; + + const sigtermHandler = async () => { + await cleanup(); + process.exit(143); // 128 + SIGTERM (15) + }; + + const exitHandler = () => { + // Synchronous cleanup on exit - can't await + if (!isCleaningUp) { + isCleaningUp = true; + try { + // Best effort synchronous cleanup + for (const name of registry.list()) { + const scope = registry.get(name); + if (scope && scope.process && typeof scope.process.kill === "function") { + try { + scope.process.kill(); + } catch (e) { + // Ignore + } + } + } + } catch (e) { + // Ignore + } + } + }; + + const uncaughtHandler = async (err) => { + console.error("Uncaught exception:", err); + await cleanup(); + process.exit(1); + }; + + const unhandledHandler = async (reason, promise) => { + console.error("Unhandled rejection:", reason); + await cleanup(); + process.exit(1); + }; + + // Register handlers + process.on("SIGINT", sigintHandler); + process.on("SIGTERM", sigtermHandler); + process.on("exit", exitHandler); + process.on("uncaughtException", uncaughtHandler); + process.on("unhandledRejection", unhandledHandler); + + // Return function to remove handlers + return () => { + process.off("SIGINT", sigintHandler); + process.off("SIGTERM", sigtermHandler); + process.off("exit", exitHandler); + process.off("uncaughtException", uncaughtHandler); + process.off("unhandledRejection", unhandledHandler); + }; +} + +module.exports = { + setupCleanupHandlers, +}; diff --git a/src/scopes/code.js b/src/scopes/code.js new file mode 100644 index 00000000..1390cd4f --- /dev/null +++ b/src/scopes/code.js @@ -0,0 +1,18 @@ +/** + * Code Scope - REPL-backed code execution sessions (placeholder) + * + * Future implementation for interactive code REPL sessions. + * Currently throws "not implemented" error. + */ + +/** + * Create a code REPL scope + * @throws {Error} Always throws - not yet implemented + */ +async function createCodeScope(options) { + throw new Error("Code scopes are not yet implemented"); +} + +module.exports = { + createCodeScope, +}; diff --git a/src/scopes/index.js b/src/scopes/index.js new file mode 100644 index 00000000..9d011269 --- /dev/null +++ b/src/scopes/index.js @@ -0,0 +1,32 @@ +/** + * Scopes Module - Session management for long-running processes + * + * Provides utilities for creating, managing, and cleaning up named scopes + * that persist across test steps. + */ + +const { ScopeRegistry } = require("./registry"); +const { createTerminalScope, terminateScope } = require("./terminal"); +const { waitForConditions, parsePattern, matchesPattern } = require("./waitUntil"); +const { setupCleanupHandlers } = require("./cleanup"); +const { createCodeScope } = require("./code"); + +module.exports = { + // Registry + ScopeRegistry, + + // Terminal scopes + createTerminalScope, + terminateScope, + + // Wait utilities + waitForConditions, + parsePattern, + matchesPattern, + + // Cleanup + setupCleanupHandlers, + + // Code scopes (placeholder) + createCodeScope, +}; diff --git a/src/scopes/registry.js b/src/scopes/registry.js new file mode 100644 index 00000000..70fdc4bc --- /dev/null +++ b/src/scopes/registry.js @@ -0,0 +1,116 @@ +/** + * Scope Registry - Manages named scopes for long-running processes + * + * A scope represents a running process with its associated output buffers. + * Scopes allow steps to reference and interact with processes across multiple test steps. + */ + +/** + * ScopeRegistry manages named scopes for terminal/shell sessions + */ +class ScopeRegistry { + constructor() { + this.scopes = new Map(); + } + + /** + * Check if a scope exists + * @param {string} name - The scope name + * @returns {boolean} True if scope exists + */ + has(name) { + return this.scopes.has(name); + } + + /** + * Get a scope by name + * @param {string} name - The scope name + * @returns {Object|undefined} The scope object or undefined + */ + get(name) { + return this.scopes.get(name); + } + + /** + * Create a new scope + * @param {string} name - The scope name (must be unique and non-empty) + * @param {Object} process - The process object (must have pid property) + * @throws {Error} If name is empty or scope already exists + */ + create(name, process) { + if (!name || name.trim() === "") { + throw new Error("Scope name cannot be empty"); + } + if (this.scopes.has(name)) { + throw new Error(`Scope '${name}' already exists`); + } + this.scopes.set(name, { + process, + stdout: "", + stderr: "", + createdAt: Date.now(), + }); + } + + /** + * Delete a scope by name + * @param {string} name - The scope name + */ + delete(name) { + this.scopes.delete(name); + } + + /** + * Append data to a scope's stdout buffer + * @param {string} name - The scope name + * @param {string} data - The data to append + */ + appendStdout(name, data) { + const scope = this.scopes.get(name); + if (scope) { + scope.stdout += data; + } + } + + /** + * Append data to a scope's stderr buffer + * @param {string} name - The scope name + * @param {string} data - The data to append + */ + appendStderr(name, data) { + const scope = this.scopes.get(name); + if (scope) { + scope.stderr += data; + } + } + + /** + * List all scope names + * @returns {string[]} Array of scope names + */ + list() { + return Array.from(this.scopes.keys()); + } + + /** + * Cleanup all scopes - terminates all processes and clears the registry + * @returns {Promise} + */ + async cleanup() { + const promises = []; + for (const [name, scope] of this.scopes) { + if (scope.process && typeof scope.process.kill === "function") { + try { + scope.process.kill(); + } catch (e) { + // Ignore errors during cleanup + } + } + promises.push(Promise.resolve()); + } + await Promise.all(promises); + this.scopes.clear(); + } +} + +module.exports = { ScopeRegistry }; diff --git a/src/scopes/terminal.js b/src/scopes/terminal.js new file mode 100644 index 00000000..51e59a52 --- /dev/null +++ b/src/scopes/terminal.js @@ -0,0 +1,173 @@ +/** + * Terminal Scope - PTY-backed terminal sessions using node-pty + * + * Creates pseudo-terminal processes that support full terminal emulation, + * allowing interactive commands and proper signal handling. + */ + +const pty = require("node-pty"); +const os = require("os"); + +/** + * Create a terminal scope with a PTY-backed process + * @param {Object} options - Configuration options + * @param {string} options.command - The command to run + * @param {string[]} [options.args=[]] - Command arguments + * @param {string} [options.cwd] - Working directory + * @param {Object} [options.env] - Environment variables (merged with process.env) + * @param {boolean} [options.waitForExit=false] - If true, wait for process to exit + * @param {number} [options.cols=80] - Terminal columns + * @param {number} [options.rows=24] - Terminal rows + * @returns {Promise} Result object with process, stdout, stderr + */ +async function createTerminalScope(options) { + const { + command, + args = [], + cwd = process.cwd(), + env = {}, + waitForExit = false, + cols = 80, + rows = 24, + } = options; + + // Determine shell based on platform + const shell = os.platform() === "win32" ? "cmd.exe" : command; + const shellArgs = os.platform() === "win32" ? ["/c", command, ...args] : args; + + // Merge environment variables + const mergedEnv = { ...process.env, ...env }; + + return new Promise((resolve, reject) => { + let ptyProcess; + + try { + ptyProcess = pty.spawn(shell, shellArgs, { + name: "xterm-color", + cols, + rows, + cwd, + env: mergedEnv, + }); + } catch (err) { + reject(err); + return; + } + + let stdout = ""; + let stderr = ""; // In PTY, stderr merges with stdout, but we keep for API compatibility + let exited = false; + let exitCode = null; + + // Data handler - captures output + const onDataHandler = (data) => { + stdout += data; + }; + ptyProcess.onData(onDataHandler); + + // Exit handler + ptyProcess.onExit(({ exitCode: code }) => { + exited = true; + exitCode = code; + }); + + if (waitForExit) { + // Wait for process to exit + const checkExit = () => { + if (exited) { + resolve({ + process: ptyProcess, + stdout, + stderr, + exitCode, + }); + } else { + setTimeout(checkExit, 10); + } + }; + checkExit(); + } else { + // Return immediately with running process + // Give a tiny delay to ensure process has started + setTimeout(() => { + resolve({ + process: ptyProcess, + stdout, + stderr, + get exitCode() { + return exitCode; + }, + }); + }, 50); + } + }); +} + +/** + * Terminate a terminal scope process + * @param {Object} process - The PTY process to terminate + * @param {Object} [options] - Termination options + * @param {number} [options.timeout=5000] - Time to wait before force kill (ms) + * @returns {Promise} + */ +async function terminateScope(process, options = {}) { + const { timeout = 5000 } = options; + + if (!process) { + return; + } + + return new Promise((resolve) => { + let resolved = false; + let forceKillTimeout; + + const cleanup = () => { + if (!resolved) { + resolved = true; + if (forceKillTimeout) { + clearTimeout(forceKillTimeout); + } + resolve(); + } + }; + + // Listen for exit + if (process.onExit) { + process.onExit(() => { + cleanup(); + }); + } + + // Try graceful termination first (SIGTERM) + try { + if (typeof process.kill === "function") { + process.kill(); + } + } catch (e) { + // Process may already be dead + cleanup(); + return; + } + + // Force kill after timeout if still running + forceKillTimeout = setTimeout(() => { + try { + if (typeof process.kill === "function") { + // Send SIGKILL on Unix, or just call kill again on Windows + process.kill("SIGKILL"); + } + } catch (e) { + // Ignore errors + } + cleanup(); + }, timeout); + + // Also set a max timeout to prevent hanging + setTimeout(cleanup, timeout + 100); + }); +} + +module.exports = { + createTerminalScope, + terminateScope, +}; diff --git a/src/scopes/waitUntil.js b/src/scopes/waitUntil.js new file mode 100644 index 00000000..c1ebc9d1 --- /dev/null +++ b/src/scopes/waitUntil.js @@ -0,0 +1,141 @@ +/** + * Wait Until - Utilities for waiting on output conditions + * + * Supports waiting for specific strings or regex patterns in stdout/stderr + * from scoped processes. + */ + +/** + * Parse a pattern string that may be a regex (e.g., "/pattern/flags") or plain string + * @param {string} pattern - The pattern to parse + * @returns {Object} Object with { isRegex, regex?, string? } + */ +function parsePattern(pattern) { + // Check if it's a regex pattern: /pattern/ or /pattern/flags + const regexMatch = pattern.match(/^\/(.+)\/([gimsuvy]*)$/); + + if (regexMatch) { + const [, regexPattern, flags] = regexMatch; + try { + return { + isRegex: true, + regex: new RegExp(regexPattern, flags), + }; + } catch (e) { + throw new Error(`Invalid regex pattern '${pattern}': ${e.message}`); + } + } + + return { + isRegex: false, + string: pattern, + }; +} + +/** + * Check if a buffer matches a pattern + * @param {string} buffer - The buffer to check + * @param {Object} parsedPattern - Parsed pattern from parsePattern() + * @returns {boolean} True if pattern matches + */ +function matchesPattern(buffer, parsedPattern) { + if (parsedPattern.isRegex) { + return parsedPattern.regex.test(buffer); + } + return buffer.includes(parsedPattern.string); +} + +/** + * Wait for conditions to be met in a scope's output + * @param {ScopeRegistry} registry - The scope registry + * @param {string} scopeName - The name of the scope to monitor + * @param {Object} conditions - The conditions to wait for + * @param {string} [conditions.stdout] - String or regex pattern to match in stdout + * @param {string} [conditions.stderr] - String or regex pattern to match in stderr + * @param {number} [conditions.timeout=30000] - Timeout in milliseconds + * @param {Object} [conditions.terminalResult] - Terminal result object to monitor for early exit + * @returns {Promise} Resolves to true when conditions are met + * @throws {Error} If timeout is reached, process exits early, or pattern is invalid + */ +async function waitForConditions(registry, scopeName, conditions) { + const { + stdout: stdoutPattern, + stderr: stderrPattern, + timeout = 30000, + terminalResult = null, + } = conditions; + + // Parse patterns upfront to catch errors early + const parsedStdout = stdoutPattern ? parsePattern(stdoutPattern) : null; + const parsedStderr = stderrPattern ? parsePattern(stderrPattern) : null; + + const startTime = Date.now(); + const pollInterval = 50; // ms + + return new Promise((resolve, reject) => { + const check = () => { + const scope = registry.get(scopeName); + + if (!scope) { + reject(new Error(`Scope '${scopeName}' not found`)); + return; + } + + // Check stdout condition + let stdoutMatched = !parsedStdout; // True if no condition + if (parsedStdout && matchesPattern(scope.stdout, parsedStdout)) { + stdoutMatched = true; + } + + // Check stderr condition + let stderrMatched = !parsedStderr; // True if no condition + if (parsedStderr && matchesPattern(scope.stderr, parsedStderr)) { + stderrMatched = true; + } + + // Both conditions must be met + if (stdoutMatched && stderrMatched) { + resolve(true); + return; + } + + // Check if process exited early (before conditions were met) + if (terminalResult) { + const exitCode = terminalResult.exitCode; + if (exitCode !== null && exitCode !== undefined) { + const waitedFor = []; + if (parsedStdout) waitedFor.push(`stdout: "${stdoutPattern}"`); + if (parsedStderr) waitedFor.push(`stderr: "${stderrPattern}"`); + reject(new Error( + `Process exited with code ${exitCode} before condition was met (${waitedFor.join(", ")}). ` + + `Output: "${scope.stdout.slice(-200)}"` + )); + return; + } + } + + // Check timeout + if (Date.now() - startTime >= timeout) { + const waitedFor = []; + if (parsedStdout) waitedFor.push(`stdout: "${stdoutPattern}"`); + if (parsedStderr) waitedFor.push(`stderr: "${stderrPattern}"`); + reject(new Error( + `Timeout waiting for conditions (${waitedFor.join(", ")}) after ${timeout}ms. ` + + `Current stdout: "${scope.stdout.slice(-200)}"` + )); + return; + } + + // Continue polling + setTimeout(check, pollInterval); + }; + + check(); + }); +} + +module.exports = { + waitForConditions, + parsePattern, + matchesPattern, +}; diff --git a/src/tests.js b/src/tests.js index 6118ebb7..e335a7b4 100644 --- a/src/tests.js +++ b/src/tests.js @@ -20,6 +20,7 @@ const { httpRequest } = require("./tests/httpRequest"); const { clickElement } = require("./tests/click"); const { runCode } = require("./tests/runCode"); const { dragAndDropElement } = require("./tests/dragAndDrop"); +const terminateScope = require("./tests/terminateScope"); const path = require("path"); const { spawn } = require("child_process"); const { randomUUID } = require("crypto"); @@ -27,6 +28,7 @@ const { setAppiumHome } = require("./appium"); const { resolveExpression } = require("./expressions"); const { getEnvironment, getAvailableApps } = require("./config"); const { uploadChangedFiles } = require("./integrations"); +const { ScopeRegistry } = require("./scopes"); exports.runSpecs = runSpecs; exports.runViaApi = runViaApi; @@ -519,6 +521,8 @@ async function runSpecs({ resolvedTests }) { log(config, "debug", `CONTEXT:\n${JSON.stringify(context, null, 2)}`); let driver; + // Create scope registry for this context + const scopeRegistry = new ScopeRegistry(); // Ensure context contains a 'steps' property if (!context.steps) { context.steps = []; @@ -647,6 +651,7 @@ async function runSpecs({ resolvedTests }) { step: step, driver: driver, metaValues: metaValues, + scopeRegistry: scopeRegistry, options: { openApiDefinitions: context.openApi || [], }, @@ -743,6 +748,17 @@ async function runSpecs({ resolvedTests }) { ); } } + + // Clean up any remaining scopes for this context + try { + await scopeRegistry.cleanup(); + } catch (error) { + log( + config, + "warning", + `Failed to cleanup scopes: ${error.message}` + ); + } } // Parse context results to calc test result @@ -833,6 +849,7 @@ async function runStep({ step, driver, metaValues = {}, + scopeRegistry, options = {}, }) { let actionResult; @@ -887,20 +904,23 @@ async function runStep({ }); config.recording = actionResult.recording; } else if (typeof step.runCode !== "undefined") { - actionResult = await runCode({ config: config, step: step }); + actionResult = await runCode({ config: config, step: step, scopeRegistry: scopeRegistry }); } else if (typeof step.runShell !== "undefined") { - actionResult = await runShell({ config: config, step: step }); + actionResult = await runShell({ config: config, step: step, scopeRegistry: scopeRegistry }); } else if (typeof step.screenshot !== "undefined") { actionResult = await saveScreenshot({ config: config, step: step, driver: driver, }); + } else if (typeof step.terminateScope !== "undefined") { + actionResult = await terminateScope(config, step, { scopeRegistry: scopeRegistry }); } else if (typeof step.type !== "undefined") { actionResult = await typeKeys({ config: config, step: step, driver: driver, + scopeRegistry: scopeRegistry, }); } else if (typeof step.wait !== "undefined") { actionResult = await wait({ step: step, driver: driver }); diff --git a/src/tests/runCode.js b/src/tests/runCode.js index 5a745db9..5c03a844 100644 --- a/src/tests/runCode.js +++ b/src/tests/runCode.js @@ -37,7 +37,7 @@ function createTempScript(code, language) { } // Run gather, compile, and run code. -async function runCode({ config, step }) { +async function runCode({ config, step, scopeRegistry }) { const result = { status: "PASS", description: "Executed code.", @@ -113,10 +113,19 @@ async function runCode({ config, step }) { args: [scriptPath, ...step.runCode.args], }, }; + + // Pass through scope and waitUntil if specified + if (step.runCode.scope) { + shellStep.runShell.scope = step.runCode.scope; + } + if (step.runCode.waitUntil) { + shellStep.runShell.waitUntil = step.runCode.waitUntil; + } + delete shellStep.runCode; // Execute script using runShell - const shellResult = await runShell({ config: config, step: shellStep }); + const shellResult = await runShell({ config: config, step: shellStep, scopeRegistry }); // Copy results result.status = shellResult.status; diff --git a/src/tests/runShell.js b/src/tests/runShell.js index 8e1925eb..5d19499c 100644 --- a/src/tests/runShell.js +++ b/src/tests/runShell.js @@ -4,13 +4,17 @@ const { log, calculateFractionalDifference, } = require("../utils"); +const { + createTerminalScope, + waitForConditions, +} = require("../scopes"); const fs = require("fs"); const path = require("path"); exports.runShell = runShell; // Run a shell command. -async function runShell({ config, step }) { +async function runShell({ config, step, scopeRegistry }) { // Promisify and execute command const result = { status: "PASS", @@ -48,7 +52,214 @@ async function runShell({ config, step }) { timeout: step.runShell.timeout || 60000, }; - // Execute command + // Check if this is a scoped command + if (step.runShell.scope) { + return await runScopedCommand({ config, step, scopeRegistry, result }); + } + + // Non-scoped command - original behavior + return await runNonScopedCommand({ config, step, result }); +} + +/** + * Run a scoped command using terminal scope + * + * Behavior: + * - If waitUntil is NOT specified: Wait for command to complete (exit), respecting timeout. + * Check exit code and clean up scope after completion. + * - If waitUntil IS specified: Wait for the condition to be met, then return early. + * The process continues running in the background for subsequent steps to interact with. + */ +async function runScopedCommand({ config, step, scopeRegistry, result }) { + const scopeName = step.runShell.scope; + const waitUntil = step.runShell.waitUntil; + const timeout = step.runShell.timeout; + + log(config, "debug", `Running scoped command in scope: ${scopeName}`); + + // Check if scope registry is available + if (!scopeRegistry) { + result.status = "FAIL"; + result.description = "Scope registry not available. Scoped commands require scope support."; + return result; + } + + // Check if scope already exists + if (scopeRegistry.has(scopeName)) { + result.status = "FAIL"; + result.description = `Scope '${scopeName}' already exists. Terminate it first or use a different name.`; + return result; + } + + try { + // Create terminal scope + const terminalResult = await createTerminalScope({ + command: step.runShell.command, + args: step.runShell.args, + cwd: step.runShell.workingDirectory, + waitForExit: false, + }); + + // Register the scope with the terminalResult for exit code access + scopeRegistry.create(scopeName, terminalResult.process, { terminalResult }); + + // Initialize with any output already captured + if (terminalResult.stdout) { + scopeRegistry.appendStdout(scopeName, terminalResult.stdout); + } + + // Set up output capture for future output + terminalResult.process.onData((data) => { + scopeRegistry.appendStdout(scopeName, data); + }); + + log(config, "debug", `Created scope '${scopeName}' with PID ${terminalResult.process.pid}`); + + if (waitUntil && waitUntil.stdio) { + // WITH waitUntil: Wait for condition, allow process to continue running + // But also monitor for early exit - if process exits before condition is met, fail immediately + log(config, "debug", `Waiting for condition in scope '${scopeName}' (timeout: ${timeout}ms)`); + + try { + await waitForConditions(scopeRegistry, scopeName, { + stdout: waitUntil.stdio.stdout, + stderr: waitUntil.stdio.stderr, + timeout: timeout, + terminalResult: terminalResult, // Pass terminalResult to detect early exit + }); + + log(config, "debug", `Condition met in scope '${scopeName}', process continues running`); + + // Get current output + const scope = scopeRegistry.get(scopeName); + result.outputs.stdio.stdout = scope ? scope.stdout : ""; + result.outputs.stdio.stderr = scope ? scope.stderr : ""; + result.description = `Scoped command '${scopeName}' ready (process continues running).`; + + } catch (waitError) { + // Early exit or timeout - terminate process and clean up + await cleanupScope(scopeRegistry, scopeName); + + result.status = "FAIL"; + // Use the error message directly - it will indicate if it was early exit or timeout + result.description = `Failed waiting for condition in scope '${scopeName}': ${waitError.message}`; + return result; + } + } else { + // WITHOUT waitUntil: Wait for command to complete (exit) + log(config, "debug", `Waiting for command to complete in scope '${scopeName}' (timeout: ${timeout}ms)`); + + try { + const exitResult = await waitForExit(terminalResult, timeout); + + // Get final output + const scope = scopeRegistry.get(scopeName); + result.outputs.stdio.stdout = scope ? scope.stdout : ""; + result.outputs.stdio.stderr = scope ? scope.stderr : ""; + result.outputs.exitCode = exitResult.exitCode; + + // Clean up scope since command completed + scopeRegistry.delete(scopeName); + + // Check exit code + if (!step.runShell.exitCodes.includes(exitResult.exitCode)) { + result.status = "FAIL"; + result.description = `Scoped command returned exit code ${exitResult.exitCode}. Expected one of ${JSON.stringify(step.runShell.exitCodes)}`; + return result; + } + + result.description = `Scoped command '${scopeName}' completed with exit code ${exitResult.exitCode}.`; + + } catch (exitError) { + // Timeout - terminate process and clean up + await cleanupScope(scopeRegistry, scopeName); + + result.status = "FAIL"; + result.description = `Command in scope '${scopeName}' did not complete within ${timeout}ms. Use 'waitUntil' for long-running processes.`; + return result; + } + } + + // Check stdio pattern if specified + if (step.runShell.stdio) { + if (!checkStdioPattern(step.runShell.stdio, result.outputs.stdio.stdout, result.outputs.stdio.stderr)) { + result.status = "FAIL"; + result.description = `Couldn't find expected output (${step.runShell.stdio}) in scope output.`; + } + } + + return result; + } catch (error) { + result.status = "FAIL"; + result.description = `Failed to run scoped command: ${error.message}`; + return result; + } +} + +/** + * Wait for a process to exit within a timeout by polling the terminalResult's exitCode getter + * @param {Object} terminalResult - The terminal result object with exitCode getter + * @param {number} timeout - Timeout in milliseconds + * @returns {Promise<{exitCode: number}>} + */ +function waitForExit(terminalResult, timeout) { + return new Promise((resolve, reject) => { + const startTime = Date.now(); + const pollInterval = 50; // Check every 50ms + + const checkExit = () => { + const exitCode = terminalResult.exitCode; + + // exitCode is defined (including 0) when process has exited + if (exitCode !== null && exitCode !== undefined) { + resolve({ exitCode }); + return; + } + + // Check if timed out + if (Date.now() - startTime >= timeout) { + reject(new Error(`Timeout after ${timeout}ms`)); + return; + } + + // Poll again + setTimeout(checkExit, pollInterval); + }; + + checkExit(); + }); +} + +/** + * Clean up a scope by terminating process and removing from registry + */ +async function cleanupScope(scopeRegistry, scopeName) { + try { + const scope = scopeRegistry.get(scopeName); + if (scope && scope.process) { + scope.process.kill(); + } + scopeRegistry.delete(scopeName); + } catch (e) { + // Ignore cleanup errors + } +} + +/** + * Check if stdio matches a pattern (string or regex) + */ +function checkStdioPattern(pattern, stdout, stderr) { + if (pattern.startsWith("/") && pattern.endsWith("/")) { + const regex = new RegExp(pattern.slice(1, -1)); + return regex.test(stdout) || regex.test(stderr); + } + return stdout.includes(pattern) || stderr.includes(pattern); +} + +/** + * Run a non-scoped command (original behavior) + */ +async function runNonScopedCommand({ config, step, result }) { const timeout = step.runShell.timeout; const options = {}; if (step.runShell.workingDirectory) diff --git a/src/tests/terminateScope.js b/src/tests/terminateScope.js new file mode 100644 index 00000000..334291c3 --- /dev/null +++ b/src/tests/terminateScope.js @@ -0,0 +1,73 @@ +/** + * terminateScope Action - Terminates a named scope and its associated process + */ + +const { terminateScope: terminateScopeProcess } = require("../scopes"); +const { log } = require("../utils"); + +/** + * Execute a terminateScope action + * @param {Object} config - The configuration object + * @param {Object} step - The step object containing terminateScope action + * @param {Object} context - The execution context + * @param {Object} context.scopeRegistry - The scope registry + * @returns {Promise} Result object with status and description + */ +async function terminateScope(config, step, context = {}) { + const { scopeRegistry } = context; + const action = step.terminateScope; + + // Handle both string and object forms + const scopeName = typeof action === "string" ? action : action.scope; + + log(config, "debug", `Terminating scope: ${scopeName}`); + + // Validate scope registry + if (!scopeRegistry) { + return { + status: "FAIL", + description: "Scope registry not available. This action requires scope support.", + outputs: {}, + }; + } + + // Check if scope exists + if (!scopeRegistry.has(scopeName)) { + return { + status: "FAIL", + description: `Scope '${scopeName}' does not exist or was already terminated.`, + outputs: {}, + }; + } + + try { + const scope = scopeRegistry.get(scopeName); + + // Terminate the process + if (scope.process) { + await terminateScopeProcess(scope.process); + } + + // Remove from registry + scopeRegistry.delete(scopeName); + + log(config, "debug", `Successfully terminated scope: ${scopeName}`); + + return { + status: "PASS", + description: `Terminated scope '${scopeName}'.`, + outputs: { + stdout: scope.stdout || "", + stderr: scope.stderr || "", + }, + }; + } catch (error) { + return { + status: "FAIL", + description: `Failed to terminate scope '${scopeName}': ${error.message}`, + outputs: {}, + }; + } +} + +module.exports = terminateScope; diff --git a/src/tests/typeKeys.js b/src/tests/typeKeys.js index c9f011ca..e4459cad 100644 --- a/src/tests/typeKeys.js +++ b/src/tests/typeKeys.js @@ -1,5 +1,6 @@ const { validate } = require("doc-detective-common"); const { Key } = require("webdriverio"); +const { log } = require("../utils"); const { findElementByCriteria, } = require("./findStrategies"); @@ -66,8 +67,43 @@ const specialKeyMap = { $ZANKAKU_HANDKAKU$: Key.ZenkakuHankaku, }; -// Type a sequence of keys in the active element. -async function typeKeys({ config, step, driver }) { +// Map special keys to terminal escape sequences +const terminalSpecialKeyMap = { + $ENTER$: "\n", + $RETURN$: "\r", + $TAB$: "\t", + $ESCAPE$: "\x1b", + $BACKSPACE$: "\x7f", + $SPACE$: " ", + $DELETE$: "\x1b[3~", + $ARROW_UP$: "\x1b[A", + $ARROW_DOWN$: "\x1b[B", + $ARROW_RIGHT$: "\x1b[C", + $ARROW_LEFT$: "\x1b[D", + $HOME$: "\x1b[H", + $END$: "\x1b[F", + $PAGE_UP$: "\x1b[5~", + $PAGE_DOWN$: "\x1b[6~", + $INSERT$: "\x1b[2~", + $F1$: "\x1bOP", + $F2$: "\x1bOQ", + $F3$: "\x1bOR", + $F4$: "\x1bOS", + $F5$: "\x1b[15~", + $F6$: "\x1b[17~", + $F7$: "\x1b[18~", + $F8$: "\x1b[19~", + $F9$: "\x1b[20~", + $F10$: "\x1b[21~", + $F11$: "\x1b[23~", + $F12$: "\x1b[24~", + $CTRL_C$: "\x03", + $CTRL_D$: "\x04", + $CTRL_Z$: "\x1a", +}; + +// Type a sequence of keys in the active element or a named scope. +async function typeKeys({ config, step, driver, scopeRegistry }) { let result = { status: "PASS", description: "Typed keys." }; // Validate step payload @@ -106,6 +142,90 @@ async function typeKeys({ config, step, driver }) { return result; } + // Check if this is a scope-targeted type action + const scopeName = step.type.scope; + + if (scopeName) { + // Type to a named scope + return await typeToScope({ config, step, scopeRegistry, result }); + } + + // Browser-based typing + return await typeToBrowser({ config, step, driver, result }); +} + +/** + * Type keys to a named scope (terminal) + */ +async function typeToScope({ config, step, scopeRegistry, result }) { + const scopeName = step.type.scope; + + log(config, "debug", `Typing keys to scope: ${scopeName}`); + + // Check if scope registry is available + if (!scopeRegistry) { + result.status = "FAIL"; + result.description = "Scope registry not available. Typing to scopes requires scope support."; + return result; + } + + // Check if scope exists + if (!scopeRegistry.has(scopeName)) { + result.status = "FAIL"; + result.description = `Scope '${scopeName}' does not exist.`; + return result; + } + + const scope = scopeRegistry.get(scopeName); + + if (!scope.process || typeof scope.process.write !== "function") { + result.status = "FAIL"; + result.description = `Scope '${scopeName}' does not support writing (no write method).`; + return result; + } + + try { + // Convert keys to terminal input + const terminalInput = convertKeysToTerminalInput(step.type.keys); + + // Write to the scope's process + scope.process.write(terminalInput); + + log(config, "debug", `Wrote ${terminalInput.length} characters to scope '${scopeName}'`); + + result.description = `Typed keys to scope '${scopeName}'.`; + return result; + } catch (error) { + result.status = "FAIL"; + result.description = `Failed to type to scope '${scopeName}': ${error.message}`; + return result; + } +} + +/** + * Convert key array to terminal input string + */ +function convertKeysToTerminalInput(keys) { + let output = ""; + + for (const key of keys) { + if (key.startsWith("$") && key.endsWith("$") && terminalSpecialKeyMap[key]) { + output += terminalSpecialKeyMap[key]; + } else if (key.startsWith("$") && key.endsWith("$")) { + // Unknown special key - skip it + continue; + } else { + output += key; + } + } + + return output; +} + +/** + * Type keys to browser element + */ +async function typeToBrowser({ config, step, driver, result }) { // Find element to type into if any criteria are specified let element = null; const hasElementCriteria = step.type.selector || step.type.elementText || @@ -193,3 +313,5 @@ async function typeKeys({ config, step, driver }) { // PASS return result; } + +module.exports = { typeKeys }; diff --git a/test/artifacts/scope_basic.spec.json b/test/artifacts/scope_basic.spec.json new file mode 100644 index 00000000..c2413345 --- /dev/null +++ b/test/artifacts/scope_basic.spec.json @@ -0,0 +1,134 @@ +{ + "tests": [ + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'READY' && sleep 5"], + "scope": "basic-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "READY" + } + } + } + }, + { + "terminateScope": { + "scope": "basic-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SERVER_STARTED' && while true; do sleep 1; done"], + "scope": "args-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SERVER_STARTED" + } + } + } + }, + { + "runShell": "echo 'Other work while scope runs'" + }, + { + "terminateScope": { + "scope": "args-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'BG_READY' && sleep 10"], + "scope": "background-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "BG_READY" + } + } + } + }, + { + "runShell": "echo 'Background process started'" + }, + { + "terminateScope": { + "scope": "background-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SCOPE_A_READY' && sleep 10"], + "scope": "multi-scope-a", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SCOPE_A_READY" + } + } + } + }, + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SCOPE_B_READY' && sleep 10"], + "scope": "multi-scope-b", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SCOPE_B_READY" + } + } + } + }, + { + "runShell": "echo 'Both scopes are running'" + }, + { + "terminateScope": { + "scope": "multi-scope-a" + } + }, + { + "terminateScope": { + "scope": "multi-scope-b" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'Short-lived scoped command' && exit 0"], + "scope": "foreground-scope", + "timeout": 5000 + } + }, + { + "runShell": "echo 'After foreground command completed'" + } + ] + } + ] +} diff --git a/test/artifacts/scope_errors.spec.json b/test/artifacts/scope_errors.spec.json new file mode 100644 index 00000000..df457381 --- /dev/null +++ b/test/artifacts/scope_errors.spec.json @@ -0,0 +1,94 @@ +{ + "tests": [ + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'FIRST' && sleep 10"], + "scope": "duplicate-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "FIRST" + } + } + } + }, + { + "runShell": "echo 'Scope is running, will terminate now'" + }, + { + "terminateScope": { + "scope": "duplicate-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": "echo 'Setting up test for special characters'" + }, + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SPECIAL_READY' && sleep 5"], + "scope": "scope-with-dashes_and_underscores", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SPECIAL_READY" + } + } + } + }, + { + "terminateScope": { + "scope": "scope-with-dashes_and_underscores" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'QUICK_EXIT'"], + "scope": "early-exit-scope", + "timeout": 5000 + } + }, + { + "runShell": "echo 'After quick exit - no terminateScope needed'" + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'BG_SILENT' && sleep 10"], + "scope": "silent-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "BG_SILENT" + } + } + } + }, + { + "runShell": "echo 'Silent scope is running in background'" + }, + { + "terminateScope": { + "scope": "silent-scope" + } + } + ] + } + ] +} diff --git a/test/artifacts/scope_lifecycle.spec.json b/test/artifacts/scope_lifecycle.spec.json new file mode 100644 index 00000000..6363babf --- /dev/null +++ b/test/artifacts/scope_lifecycle.spec.json @@ -0,0 +1,140 @@ +{ + "tests": [ + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'BACKGROUND_STARTED' && sleep 30"], + "scope": "persist-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "BACKGROUND_STARTED" + } + } + } + }, + { + "runShell": "echo 'Step 2: doing work'" + }, + { + "runShell": "echo 'Step 3: more work'" + }, + { + "runShell": "echo 'Step 4: still more work'" + }, + { + "terminateScope": { + "scope": "persist-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'FIRST_INSTANCE' && sleep 10"], + "scope": "reusable-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "FIRST_INSTANCE" + } + } + } + }, + { + "terminateScope": { + "scope": "reusable-scope" + } + }, + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'SECOND_INSTANCE' && sleep 10"], + "scope": "reusable-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "SECOND_INSTANCE" + } + } + } + }, + { + "terminateScope": { + "scope": "reusable-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'AUTO_CLEANUP_TEST' && sleep 60"], + "scope": "auto-cleanup-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "AUTO_CLEANUP_TEST" + } + } + } + }, + { + "runShell": "echo 'Test ends here - scope should be auto-cleaned'" + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'INDEPENDENT_TEST' && sleep 10"], + "scope": "independent-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "INDEPENDENT_TEST" + } + } + } + }, + { + "terminateScope": { + "scope": "independent-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo \"PWD: $(pwd)\" && echo 'DIR_READY' && sleep 10"], + "scope": "workdir-scope", + "workingDirectory": "/tmp", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "DIR_READY" + } + } + } + }, + { + "terminateScope": { + "scope": "workdir-scope" + } + } + ] + } + ] +} diff --git a/test/artifacts/scope_typeKeys.spec.json b/test/artifacts/scope_typeKeys.spec.json new file mode 100644 index 00000000..e93ac19a --- /dev/null +++ b/test/artifacts/scope_typeKeys.spec.json @@ -0,0 +1,165 @@ +{ + "tests": [ + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'READY' && cat"], + "scope": "type-basic-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "READY" + } + } + } + }, + { + "type": { + "keys": "hello world", + "scope": "type-basic-scope" + } + }, + { + "type": { + "keys": "$ENTER$", + "scope": "type-basic-scope" + } + }, + { + "terminateScope": { + "scope": "type-basic-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'STARTED' && cat"], + "scope": "special-keys-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "STARTED" + } + } + } + }, + { + "type": { + "keys": ["line1", "$ENTER$", "line2", "$ENTER$"], + "scope": "special-keys-scope" + } + }, + { + "terminateScope": { + "scope": "special-keys-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'READY' && cat"], + "scope": "tab-key-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "READY" + } + } + } + }, + { + "type": { + "keys": ["column1", "$TAB$", "column2", "$ENTER$"], + "scope": "tab-key-scope" + } + }, + { + "terminateScope": { + "scope": "tab-key-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo '>>> ' && while read line; do echo \"You typed: $line\"; echo '>>> '; done"], + "scope": "repl-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": ">>>" + } + } + } + }, + { + "type": { + "keys": ["first command", "$ENTER$"], + "scope": "repl-scope" + } + }, + { + "wait": 500 + }, + { + "type": { + "keys": ["second command", "$ENTER$"], + "scope": "repl-scope" + } + }, + { + "terminateScope": { + "scope": "repl-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'RUNNING' && trap 'echo INTERRUPTED' INT && while true; do sleep 1; done"], + "scope": "ctrl-c-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "RUNNING" + } + } + } + }, + { + "wait": 500 + }, + { + "type": { + "keys": "$CTRL_C$", + "scope": "ctrl-c-scope" + } + }, + { + "wait": 500 + }, + { + "terminateScope": { + "scope": "ctrl-c-scope" + } + } + ] + } + ] +} diff --git a/test/artifacts/scope_waitUntil.spec.json b/test/artifacts/scope_waitUntil.spec.json new file mode 100644 index 00000000..488243d7 --- /dev/null +++ b/test/artifacts/scope_waitUntil.spec.json @@ -0,0 +1,136 @@ +{ + "tests": [ + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "sleep 0.5 && echo 'Server listening on port 3000'"], + "scope": "string-pattern-scope", + "timeout": 5000, + "waitUntil": { + "stdio": { + "stdout": "listening on port" + } + } + } + }, + { + "terminateScope": { + "scope": "string-pattern-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "sleep 0.3 && echo 'Process ID: 12345 started'"], + "scope": "regex-pattern-scope", + "timeout": 5000, + "waitUntil": { + "stdio": { + "stdout": "/Process ID: \\d+ started/" + } + } + } + }, + { + "terminateScope": { + "scope": "regex-pattern-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "sleep 0.3 && echo 'APPLICATION READY'"], + "scope": "regex-case-scope", + "timeout": 5000, + "waitUntil": { + "stdio": { + "stdout": "/application ready/i" + } + } + } + }, + { + "terminateScope": { + "scope": "regex-case-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'Line 1' && sleep 0.2 && echo 'Line 2' && sleep 0.2 && echo 'READY_SIGNAL' && sleep 5"], + "scope": "multiline-scope", + "timeout": 5000, + "waitUntil": { + "stdio": { + "stdout": "READY_SIGNAL" + } + } + } + }, + { + "terminateScope": { + "scope": "multiline-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "echo 'IMMEDIATE' && sleep 5"], + "scope": "fast-output-scope", + "timeout": 3000, + "waitUntil": { + "stdio": { + "stdout": "IMMEDIATE" + } + } + } + }, + { + "terminateScope": { + "scope": "fast-output-scope" + } + } + ] + }, + { + "steps": [ + { + "runShell": { + "command": "sh", + "args": ["-c", "sleep 1 && echo 'DELAYED_READY' && sleep 10"], + "scope": "delayed-scope", + "timeout": 5000, + "waitUntil": { + "stdio": { + "stdout": "DELAYED_READY" + } + } + } + }, + { + "terminateScope": { + "scope": "delayed-scope" + } + } + ] + } + ] +} diff --git a/test/scopes.test.js b/test/scopes.test.js new file mode 100644 index 00000000..243ec5e1 --- /dev/null +++ b/test/scopes.test.js @@ -0,0 +1,1756 @@ +/** + * Scope-based session management tests + * Tests for creating, managing, and cleaning up named scopes for long-running processes + */ + +const assert = require("assert").strict; +const path = require("path"); + +// ============================================================================= +// REGISTRY TESTS +// ============================================================================= + +describe("Scope Registry", function () { + this.timeout(10000); // 10 second timeout for scope tests + + let ScopeRegistry; + + before(function () { + // Import will fail initially - this is expected (RED phase) + try { + const scopes = require("../src/scopes"); + ScopeRegistry = scopes.ScopeRegistry; + } catch (e) { + // Module doesn't exist yet - expected in RED phase + } + }); + + describe("Basic CRUD operations", function () { + it("should create a new scope registry", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + assert.ok(registry, "registry should be created"); + assert.equal(typeof registry.has, "function", "has should be a function"); + assert.equal(typeof registry.get, "function", "get should be a function"); + assert.equal(typeof registry.create, "function", "create should be a function"); + assert.equal(typeof registry.delete, "function", "delete should be a function"); + }); + + it("should register a new scope with a name", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + + assert.ok(registry.has("my-scope"), "scope should exist after creation"); + }); + + it("should retrieve a scope by name", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + const scope = registry.get("my-scope"); + + assert.ok(scope, "should retrieve scope"); + assert.equal(scope.process.pid, 12345, "should have correct process"); + }); + + it("should return undefined for non-existent scope", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + + const scope = registry.get("non-existent"); + + assert.equal(scope, undefined, "should return undefined"); + }); + + it("should delete a scope by name", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + assert.ok(registry.has("my-scope"), "scope should exist"); + + registry.delete("my-scope"); + assert.ok(!registry.has("my-scope"), "scope should not exist after deletion"); + }); + + it("should not throw when deleting non-existent scope", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + + assert.doesNotThrow(() => { + registry.delete("non-existent"); + }, "should not throw for non-existent scope"); + }); + + it("should throw error when creating scope with empty name", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + assert.throws(() => { + registry.create("", mockProcess); + }, /scope name/i, "should throw for empty name"); + }); + + it("should throw error when creating scope with existing name", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + + assert.throws(() => { + registry.create("my-scope", mockProcess); + }, /already exists/i, "should throw for duplicate name"); + }); + }); + + describe("Output buffering", function () { + it("should initialize scope with empty stdout buffer", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + const scope = registry.get("my-scope"); + + assert.equal(scope.stdout, "", "stdout should be empty string"); + }); + + it("should initialize scope with empty stderr buffer", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + const scope = registry.get("my-scope"); + + assert.equal(scope.stderr, "", "stderr should be empty string"); + }); + + it("should append to stdout buffer", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + registry.appendStdout("my-scope", "line 1\n"); + registry.appendStdout("my-scope", "line 2\n"); + + const scope = registry.get("my-scope"); + assert.equal(scope.stdout, "line 1\nline 2\n", "stdout should accumulate"); + }); + + it("should append to stderr buffer", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + const mockProcess = { pid: 12345 }; + + registry.create("my-scope", mockProcess); + registry.appendStderr("my-scope", "error 1\n"); + registry.appendStderr("my-scope", "error 2\n"); + + const scope = registry.get("my-scope"); + assert.equal(scope.stderr, "error 1\nerror 2\n", "stderr should accumulate"); + }); + + it("should not throw when appending to non-existent scope", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + + assert.doesNotThrow(() => { + registry.appendStdout("non-existent", "data"); + registry.appendStderr("non-existent", "data"); + }, "should not throw for non-existent scope"); + }); + }); + + describe("Scope listing and cleanup", function () { + it("should list all scope names", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + + registry.create("scope-1", { pid: 1 }); + registry.create("scope-2", { pid: 2 }); + registry.create("scope-3", { pid: 3 }); + + const names = registry.list(); + + assert.ok(Array.isArray(names), "should return array"); + assert.equal(names.length, 3, "should have 3 scopes"); + assert.ok(names.includes("scope-1"), "should include scope-1"); + assert.ok(names.includes("scope-2"), "should include scope-2"); + assert.ok(names.includes("scope-3"), "should include scope-3"); + }); + + it("should return empty array when no scopes", function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + + const names = registry.list(); + + assert.ok(Array.isArray(names), "should return array"); + assert.equal(names.length, 0, "should be empty"); + }); + + it("should cleanup all scopes", async function () { + if (!ScopeRegistry) this.skip(); + const registry = new ScopeRegistry(); + + // Create mock processes with kill method + const killed = []; + const mockProcess1 = { + pid: 1, + kill: () => { killed.push(1); } + }; + const mockProcess2 = { + pid: 2, + kill: () => { killed.push(2); } + }; + + registry.create("scope-1", mockProcess1); + registry.create("scope-2", mockProcess2); + + await registry.cleanup(); + + assert.equal(registry.list().length, 0, "all scopes should be removed"); + assert.deepEqual(killed.sort(), [1, 2], "all processes should be killed"); + }); + }); +}); + +// ============================================================================= +// TERMINAL SCOPE TESTS +// ============================================================================= + +describe("Terminal Scope", function () { + this.timeout(30000); // 30 second timeout for terminal tests + + let createTerminalScope, terminateScope; + + before(function () { + try { + const scopes = require("../src/scopes"); + createTerminalScope = scopes.createTerminalScope; + terminateScope = scopes.terminateScope; + } catch (e) { + // Module doesn't exist yet - expected in RED phase + } + }); + + describe("Creating terminal scopes", function () { + it("should create a terminal scope with a command", async function () { + if (!createTerminalScope) this.skip(); + + const result = await createTerminalScope({ + command: "echo", + args: ["hello"], + }); + + assert.ok(result, "should return result"); + assert.ok(result.process, "should have process"); + assert.ok(result.process.pid, "process should have pid"); + }); + + it("should create a terminal scope with working directory", async function () { + if (!createTerminalScope) this.skip(); + + const result = await createTerminalScope({ + command: "pwd", + args: [], + cwd: "/tmp", + }); + + assert.ok(result, "should return result"); + assert.ok(result.process, "should have process"); + }); + + it("should create a terminal scope with environment variables", async function () { + if (!createTerminalScope) this.skip(); + + const result = await createTerminalScope({ + command: "printenv", + args: ["MY_TEST_VAR"], + env: { MY_TEST_VAR: "test-value" }, + }); + + assert.ok(result, "should return result"); + }); + + it("should return stdout from the command", async function () { + if (!createTerminalScope) this.skip(); + + const result = await createTerminalScope({ + command: "echo", + args: ["test output"], + waitForExit: true, + }); + + assert.ok(result.stdout, "should have stdout"); + assert.ok(result.stdout.includes("test output"), "stdout should contain output"); + }); + + it("should capture stderr from the command", async function () { + if (!createTerminalScope) this.skip(); + + // Use a command that writes to stderr + const result = await createTerminalScope({ + command: "sh", + args: ["-c", "echo error >&2"], + waitForExit: true, + }); + + // In PTY, stderr often merges with stdout, but we should have output + assert.ok(result.stdout || result.stderr, "should have some output"); + }); + + it("should handle invalid command gracefully", async function () { + if (!createTerminalScope) this.skip(); + + // node-pty doesn't throw for invalid commands, it exits with error output + const result = await createTerminalScope({ + command: "nonexistent-command-12345", + args: [], + waitForExit: true, + }); + + // Should have error output or non-zero exit code + assert.ok( + result.stdout.includes("not found") || + result.stdout.includes("No such file") || + result.exitCode !== 0, + "should indicate command failure" + ); + }); + }); + + describe("Long-running processes", function () { + it("should start a long-running process without waiting for exit", async function () { + if (!createTerminalScope) this.skip(); + + const result = await createTerminalScope({ + command: "sleep", + args: ["60"], + waitForExit: false, + }); + + assert.ok(result.process, "should have process"); + assert.ok(result.process.pid, "process should have pid"); + + // Clean up + result.process.kill(); + }); + + it("should allow writing to stdin of running process", async function () { + if (!createTerminalScope) this.skip(); + + const result = await createTerminalScope({ + command: "cat", + args: [], + waitForExit: false, + }); + + assert.ok(result.process, "should have process"); + assert.ok(typeof result.process.write === "function", "should have write method"); + + // Write to stdin + result.process.write("hello\n"); + + // Clean up + result.process.kill(); + }); + }); + + describe("Terminating scopes", function () { + it("should terminate a running process gracefully", async function () { + if (!createTerminalScope || !terminateScope) this.skip(); + + const result = await createTerminalScope({ + command: "sleep", + args: ["60"], + waitForExit: false, + }); + + const pid = result.process.pid; + assert.ok(pid, "should have pid"); + + await terminateScope(result.process); + + // Process should be terminated + // Give it a moment to clean up + await new Promise(resolve => setTimeout(resolve, 100)); + }); + + it("should force kill if graceful termination fails", async function () { + if (!createTerminalScope || !terminateScope) this.skip(); + + // Start a process that ignores SIGTERM + const result = await createTerminalScope({ + command: "sh", + args: ["-c", "trap '' TERM; sleep 60"], + waitForExit: false, + }); + + const pid = result.process.pid; + assert.ok(pid, "should have pid"); + + // Should eventually kill even if SIGTERM is ignored + await terminateScope(result.process, { timeout: 500 }); + + await new Promise(resolve => setTimeout(resolve, 100)); + }); + + it("should not throw when terminating already-exited process", async function () { + if (!createTerminalScope || !terminateScope) this.skip(); + + const result = await createTerminalScope({ + command: "echo", + args: ["done"], + waitForExit: true, + }); + + // Process has already exited + await assert.doesNotReject( + terminateScope(result.process), + "should not throw for exited process" + ); + }); + }); +}); + +// ============================================================================= +// WAIT UNTIL TESTS +// ============================================================================= + +describe("Wait Until Conditions", function () { + this.timeout(30000); + + let waitForConditions, createTerminalScope, ScopeRegistry; + + before(function () { + try { + const scopes = require("../src/scopes"); + waitForConditions = scopes.waitForConditions; + createTerminalScope = scopes.createTerminalScope; + ScopeRegistry = scopes.ScopeRegistry; + } catch (e) { + // Module doesn't exist yet - expected in RED phase + } + }); + + describe("String matching", function () { + it("should wait for stdout to contain string", async function () { + if (!waitForConditions || !createTerminalScope || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + + // Start a process that outputs after a delay + const result = await createTerminalScope({ + command: "sh", + args: ["-c", "sleep 0.5 && echo 'SERVER READY'"], + waitForExit: false, + }); + + registry.create("test-scope", result.process); + + // Set up output capture + result.process.onData((data) => { + registry.appendStdout("test-scope", data); + }); + + const matched = await waitForConditions(registry, "test-scope", { + stdout: "SERVER READY", + timeout: 5000, + }); + + assert.ok(matched, "should find the string"); + + result.process.kill(); + }); + + it("should timeout if string not found", async function () { + if (!waitForConditions || !createTerminalScope || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + + const result = await createTerminalScope({ + command: "echo", + args: ["wrong output"], + waitForExit: false, + }); + + registry.create("test-scope", result.process); + + result.process.onData((data) => { + registry.appendStdout("test-scope", data); + }); + + await assert.rejects( + waitForConditions(registry, "test-scope", { + stdout: "NEVER FOUND", + timeout: 500, + }), + /timeout/i, + "should timeout" + ); + + result.process.kill(); + }); + }); + + describe("Regex matching", function () { + it("should wait for stdout to match regex pattern", async function () { + if (!waitForConditions || !createTerminalScope || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + + const result = await createTerminalScope({ + command: "sh", + args: ["-c", "sleep 0.2 && echo 'Server listening on port 3000'"], + waitForExit: false, + }); + + registry.create("test-scope", result.process); + + result.process.onData((data) => { + registry.appendStdout("test-scope", data); + }); + + const matched = await waitForConditions(registry, "test-scope", { + stdout: "/port \\d+/", + timeout: 5000, + }); + + assert.ok(matched, "should match regex"); + + result.process.kill(); + }); + + it("should support regex flags in pattern", async function () { + if (!waitForConditions || !createTerminalScope || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + + const result = await createTerminalScope({ + command: "sh", + args: ["-c", "sleep 0.2 && echo 'READY'"], + waitForExit: false, + }); + + registry.create("test-scope", result.process); + + result.process.onData((data) => { + registry.appendStdout("test-scope", data); + }); + + const matched = await waitForConditions(registry, "test-scope", { + stdout: "/ready/i", // Case insensitive + timeout: 5000, + }); + + assert.ok(matched, "should match with flags"); + + result.process.kill(); + }); + + it("should handle invalid regex gracefully", async function () { + if (!waitForConditions || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + registry.create("test-scope", { pid: 1 }); + registry.appendStdout("test-scope", "some output"); + + await assert.rejects( + waitForConditions(registry, "test-scope", { + stdout: "/[invalid/", + timeout: 500, + }), + /regex|pattern|invalid/i, + "should throw for invalid regex" + ); + }); + }); + + describe("Multiple conditions", function () { + it("should wait for both stdout and stderr conditions", async function () { + if (!waitForConditions || !createTerminalScope || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + + const result = await createTerminalScope({ + command: "sh", + args: ["-c", "sleep 0.2 && echo 'stdout ready' && echo 'stderr ready' >&2"], + waitForExit: false, + }); + + registry.create("test-scope", result.process); + + // In PTY mode, both go to stdout typically + result.process.onData((data) => { + registry.appendStdout("test-scope", data); + }); + + // Wait for stdout condition at minimum + const matched = await waitForConditions(registry, "test-scope", { + stdout: "ready", + timeout: 5000, + }); + + assert.ok(matched, "should match condition"); + + result.process.kill(); + }); + }); +}); + +// ============================================================================= +// CLEANUP HANDLER TESTS +// ============================================================================= + +describe("Cleanup Handlers", function () { + this.timeout(10000); + + let setupCleanupHandlers, ScopeRegistry; + + before(function () { + try { + const scopes = require("../src/scopes"); + setupCleanupHandlers = scopes.setupCleanupHandlers; + ScopeRegistry = scopes.ScopeRegistry; + } catch (e) { + // Module doesn't exist yet + } + }); + + it("should export setupCleanupHandlers function", function () { + if (!setupCleanupHandlers) this.skip(); + assert.equal(typeof setupCleanupHandlers, "function"); + }); + + it("should accept a registry parameter", function () { + if (!setupCleanupHandlers || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + + assert.doesNotThrow(() => { + const cleanup = setupCleanupHandlers(registry); + // Remove handlers after test + if (cleanup) cleanup(); + }); + }); + + it("should return a function to remove handlers", function () { + if (!setupCleanupHandlers || !ScopeRegistry) this.skip(); + + const registry = new ScopeRegistry(); + const removeHandlers = setupCleanupHandlers(registry); + + assert.equal(typeof removeHandlers, "function", "should return cleanup function"); + removeHandlers(); + }); +}); + +// ============================================================================= +// INTEGRATION TESTS - TERMINATE SCOPE ACTION +// ============================================================================= + +describe("terminateScope Action", function () { + this.timeout(30000); + + let terminateScopeAction; + + before(function () { + try { + terminateScopeAction = require("../src/tests/terminateScope"); + } catch (e) { + // Module doesn't exist yet + } + }); + + it("should export a function", function () { + if (!terminateScopeAction) this.skip(); + assert.equal(typeof terminateScopeAction, "function"); + }); + + it("should return FAIL when scope does not exist", async function () { + if (!terminateScopeAction) this.skip(); + + const { ScopeRegistry } = require("../src/scopes"); + const registry = new ScopeRegistry(); + + const step = { + terminateScope: { + scope: "non-existent-scope", + }, + }; + + const config = { logLevel: "silent" }; + + const result = await terminateScopeAction(config, step, { scopeRegistry: registry }); + + assert.equal(result.status, "FAIL", "should fail for non-existent scope"); + assert.ok(result.description.includes("not found") || result.description.includes("does not exist"), + "should mention scope not found"); + }); + + it("should return PASS when scope is terminated successfully", async function () { + if (!terminateScopeAction) this.skip(); + + const { ScopeRegistry, createTerminalScope } = require("../src/scopes"); + const registry = new ScopeRegistry(); + + // Create a scope with a running process + const terminalResult = await createTerminalScope({ + command: "sleep", + args: ["60"], + waitForExit: false, + }); + + registry.create("test-scope", terminalResult.process); + + const step = { + terminateScope: { + scope: "test-scope", + }, + }; + + const config = { logLevel: "silent" }; + + const result = await terminateScopeAction(config, step, { scopeRegistry: registry }); + + assert.equal(result.status, "PASS", "should pass for successful termination"); + assert.ok(!registry.has("test-scope"), "scope should be removed from registry"); + }); +}); + +// ============================================================================= +// INTEGRATION TESTS - FULL WORKFLOW +// ============================================================================= + +describe("Scope Integration Tests", function () { + this.timeout(60000); // 60 second timeout for integration tests + + const fs = require("fs"); + const { runTests } = require("../src"); + const artifactPath = path.resolve("./test/artifacts"); + + // Helper to check if result is null (schema validation failed - schema not yet updated) + function skipIfSchemaNotUpdated(result) { + if (result === null) { + this.skip("Schema doesn't support scope/waitUntil yet - update doc-detective-common"); + } + } + + describe("runShell with scope", function () { + it("should create a named scope for a background process", async function () { + // With the new behavior, background processes require waitUntil to stay running + // Without waitUntil, the command waits for exit and then cleans up the scope + const scopeTest = { + tests: [ + { + steps: [ + { + // Background process with waitUntil to keep it running + runShell: { + command: "sh", + args: ["-c", "echo 'STARTED' && sleep 60"], + scope: "background-sleep", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "STARTED", + }, + }, + }, + }, + { + terminateScope: { + scope: "background-sleep", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-scope-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(scopeTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + assert.equal(result.summary.steps.pass, 2, "both steps should pass"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should wait for output pattern before continuing", async function () { + const waitUntilTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'Starting...' && sleep 0.5 && echo 'READY'"], + scope: "server-process", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "READY", + }, + }, + }, + }, + { + runShell: "echo 'Server is ready!'", + }, + { + terminateScope: { + scope: "server-process", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-waituntil-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(waitUntilTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should fail step when waitUntil times out", async function () { + const timeoutTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'never ready'"], + scope: "failing-process", + timeout: 500, + waitUntil: { + stdio: { + stdout: "WILL_NEVER_APPEAR", + }, + }, + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-timeout-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(timeoutTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 1, "step should fail on timeout"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should allow regex patterns in waitUntil", async function () { + const regexTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "sleep 0.2 && echo 'Listening on port 8080' && sleep 10"], + scope: "regex-process", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "/port \\d+/", + }, + }, + }, + }, + { + terminateScope: { + scope: "regex-process", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-regex-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(regexTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + }); + + describe("typeKeys with scope", function () { + it("should send keys to a named scope", async function () { + // Skip this test on non-Unix systems + if (process.platform === "win32") { + this.skip(); + } + + // cat waits for input indefinitely, so we need waitUntil to let it run in background + const typeTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'CAT_READY' && cat"], + scope: "cat-process", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "CAT_READY", + }, + }, + }, + }, + { + type: { + keys: ["hello world$ENTER$"], + scope: "cat-process", + }, + }, + { + terminateScope: { + scope: "cat-process", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-typekeys-scope-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(typeTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should fail when typing to non-existent scope", async function () { + const typeFailTest = { + tests: [ + { + steps: [ + { + type: { + keys: ["hello"], + scope: "non-existent-scope", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-typekeys-fail-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(typeFailTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 1, "step should fail for non-existent scope"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should translate special keys like $ENTER$", async function () { + if (process.platform === "win32") { + this.skip(); + } + + // cat waits for input indefinitely, so we need waitUntil to let it run in background + const specialKeysTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'CAT_READY' && cat"], + scope: "special-keys-process", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "CAT_READY", + }, + }, + }, + }, + { + type: { + keys: ["line1$ENTER$line2$TAB$tabbed$ENTER$"], + scope: "special-keys-process", + }, + }, + { + terminateScope: { + scope: "special-keys-process", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-special-keys-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(specialKeysTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + }); + + describe("Scope lifecycle", function () { + it("should persist scope across multiple steps", async function () { + const persistTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'STARTED' && sleep 10"], + scope: "persistent-scope", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "STARTED", + }, + }, + }, + }, + { + runShell: "echo 'Step 2'", + }, + { + runShell: "echo 'Step 3'", + }, + { + terminateScope: { + scope: "persistent-scope", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-persist-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(persistTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + assert.equal(result.summary.steps.pass, 4, "all 4 steps should pass"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should clean up all scopes after test context ends", async function () { + // This test verifies cleanup happens even without explicit terminateScope + // Background processes require waitUntil to stay running + const cleanupTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'STARTED' && sleep 60"], + scope: "auto-cleanup-scope", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "STARTED", + }, + }, + }, + }, + // No terminateScope - should auto-cleanup at end of test + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-cleanup-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(cleanupTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + // Test should complete (not hang) because cleanup happens + assert.equal(result.summary.tests.pass + result.summary.tests.fail + result.summary.tests.skipped > 0, true); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should allow reusing scope name after termination", async function () { + // With the new behavior, short-lived commands complete and auto-cleanup + // For this test, we use background processes that need termination + const reuseTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'FIRST_READY' && sleep 60"], + scope: "reusable-scope", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "FIRST_READY", + }, + }, + }, + }, + { + terminateScope: { + scope: "reusable-scope", + }, + }, + { + runShell: { + command: "sh", + args: ["-c", "echo 'SECOND_READY' && sleep 60"], + scope: "reusable-scope", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "SECOND_READY", + }, + }, + }, + }, + { + terminateScope: { + scope: "reusable-scope", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-reuse-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(reuseTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + }); + + describe("Edge cases", function () { + it("should handle scope name with special characters", async function () { + // With the new behavior, short-lived commands complete and auto-cleanup + // No need to terminate since echo completes immediately + const specialNameTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "echo", + args: ["test"], + scope: "my-scope_v2", + timeout: 5000, + }, + }, + // No terminateScope needed - command completes and auto-cleans up + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-special-name-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(specialNameTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should fail gracefully when scope already exists", async function () { + // First command runs as background process (with waitUntil) + // Second command tries to use same scope name and should fail + const duplicateTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'STARTED' && sleep 60"], + scope: "duplicate-scope", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "STARTED", + }, + }, + }, + }, + { + runShell: { + command: "echo", + args: ["second"], + scope: "duplicate-scope", // Same name - should fail + timeout: 5000, + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-duplicate-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(duplicateTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + // Second step should fail because scope already exists + assert.equal(result.summary.steps.fail, 1, "one step should fail"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should handle process that exits immediately", async function () { + // With the new behavior, commands without waitUntil wait for completion + // and auto-cleanup. No terminateScope needed. + const quickExitTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "echo", + args: ["done"], + scope: "quick-exit", + timeout: 5000, + }, + }, + // No terminateScope - command completes and auto-cleans up + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-quick-exit-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(quickExitTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + // Verify the scoped command actually captured the output + const step = result.specs[0].tests[0].contexts[0].steps[0]; + assert.ok(step.outputs.stdio.stdout.includes("done"), "stdout should contain 'done'"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should capture exit code from scoped process", async function () { + // With the new behavior, commands without waitUntil wait for completion + // and capture exit code. No terminateScope needed. + const exitCodeTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "exit 0"], + scope: "exit-code-scope", + timeout: 5000, + }, + }, + // No terminateScope - command completes and auto-cleans up + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-exit-code-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(exitCodeTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + // Verify exit code was captured + const step = result.specs[0].tests[0].contexts[0].steps[0]; + assert.equal(step.outputs.exitCode, 0, "exit code should be captured"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + }); + + // ============================================================================= + // NEW BEHAVIOR: waitUntil determines if process runs in background or foreground + // ============================================================================= + + describe("Scope execution behavior", function () { + // Test: Without waitUntil, scoped command waits for process to exit + it("should wait for command to complete when waitUntil is NOT specified", async function () { + // This test verifies that without waitUntil, the step waits for the process to exit + // and captures the exit code before continuing to the next step + const waitForExitTest = { + tests: [ + { + steps: [ + { + // Short-lived command - should complete and return exit code + runShell: { + command: "sh", + args: ["-c", "echo 'done' && exit 0"], + scope: "foreground-scope", + timeout: 5000, + // NO waitUntil - should wait for exit + }, + }, + // This step should only run after the command exits + { + runShell: "echo 'after foreground command'", + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-wait-for-exit-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(waitForExitTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + assert.equal(result.summary.steps.pass, 2, "both steps should pass"); + + // Verify the scoped command captured the exit code + const scopedStep = result.specs[0].tests[0].contexts[0].steps[0]; + assert.equal(scopedStep.outputs.exitCode, 0, "exit code should be captured"); + assert.ok(scopedStep.outputs.stdio.stdout.includes("done"), "stdout should be captured"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + // Test: Without waitUntil, long-running command times out and fails + it("should fail with timeout when command does not exit and waitUntil is NOT specified", async function () { + const timeoutTest = { + tests: [ + { + steps: [ + { + // Long-running command with short timeout - should timeout and fail + runShell: { + command: "sleep", + args: ["60"], + scope: "timeout-scope", + timeout: 1000, // 1 second timeout + // NO waitUntil - should wait for exit, then timeout + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-timeout-no-waituntil-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(timeoutTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 1, "step should fail due to timeout"); + + // Verify the error message indicates timeout + const failedStep = result.specs[0].tests[0].contexts[0].steps[0]; + assert.equal(failedStep.result, "FAIL", "step should be marked as FAIL"); + assert.ok( + failedStep.resultDescription.includes("timeout") || + failedStep.resultDescription.includes("did not complete"), + "error should mention timeout or not completing" + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + // Test: Without waitUntil, non-zero exit code should fail the step + it("should fail when command exits with non-zero code and waitUntil is NOT specified", async function () { + const exitCodeFailTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "exit 1"], + scope: "exit-fail-scope", + timeout: 5000, + // NO waitUntil - should wait for exit and check exit code + // Default exitCodes is [0], so exit 1 should fail + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-exit-code-fail-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(exitCodeFailTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 1, "step should fail due to non-zero exit code"); + + const failedStep = result.specs[0].tests[0].contexts[0].steps[0]; + assert.equal(failedStep.result, "FAIL", "step should be marked as FAIL"); + assert.ok( + failedStep.resultDescription.includes("exit code"), + "error should mention exit code" + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + // Test: With waitUntil, process continues running in background + it("should allow process to continue running when waitUntil IS specified", async function () { + const backgroundTest = { + tests: [ + { + steps: [ + { + // Long-running command WITH waitUntil - should continue in background + runShell: { + command: "sh", + args: ["-c", "echo 'READY' && sleep 60"], + scope: "background-scope", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "READY", + }, + }, + }, + }, + // This step runs while the background process is still running + { + runShell: "echo 'running while background process active'", + }, + { + terminateScope: { + scope: "background-scope", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-background-process-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(backgroundTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + assert.equal(result.summary.steps.pass, 3, "all 3 steps should pass"); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + // Test: With waitUntil, timeout terminates the process + it("should terminate process when waitUntil times out", async function () { + const waitUntilTimeoutTest = { + tests: [ + { + steps: [ + { + // Process that never outputs the expected pattern + runShell: { + command: "sh", + args: ["-c", "echo 'WRONG_OUTPUT' && sleep 60"], + scope: "waituntil-timeout-scope", + timeout: 1000, // 1 second timeout + waitUntil: { + stdio: { + stdout: "NEVER_APPEARS", + }, + }, + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-waituntil-timeout-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(waitUntilTimeoutTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 1, "step should fail due to waitUntil timeout"); + + const failedStep = result.specs[0].tests[0].contexts[0].steps[0]; + assert.equal(failedStep.result, "FAIL", "step should be marked as FAIL"); + assert.ok( + failedStep.resultDescription.toLowerCase().includes("timeout"), + "error should mention timeout" + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + // Test: With waitUntil, if process exits before condition is met, fail immediately + it("should fail immediately when process exits before waitUntil condition is met", async function () { + const earlyExitTest = { + tests: [ + { + steps: [ + { + // Process exits before outputting expected pattern + runShell: { + command: "sh", + args: ["-c", "echo 'WRONG_OUTPUT' && exit 0"], + scope: "early-exit-scope", + timeout: 10000, // Long timeout - but should fail fast + waitUntil: { + stdio: { + stdout: "NEVER_APPEARS", + }, + }, + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-early-exit-waituntil-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(earlyExitTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + const startTime = Date.now(); + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + const elapsed = Date.now() - startTime; + + // Should fail fast, not wait for the full 10 second timeout + assert.ok(elapsed < 5000, `should fail fast (elapsed: ${elapsed}ms), not wait for timeout`); + + assert.equal(result.summary.steps.fail, 1, "step should fail"); + + const failedStep = result.specs[0].tests[0].contexts[0].steps[0]; + assert.equal(failedStep.result, "FAIL", "step should be marked as FAIL"); + assert.ok( + failedStep.resultDescription.toLowerCase().includes("exit") || + failedStep.resultDescription.toLowerCase().includes("condition"), + "error should mention exit or condition not met" + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + // Test: With waitUntil, if process exits with non-zero code, report the exit code + it("should report exit code when process exits with error before waitUntil condition is met", async function () { + const errorExitTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'Starting...' && exit 1"], + scope: "error-exit-scope", + timeout: 10000, + waitUntil: { + stdio: { + stdout: "READY", + }, + }, + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-error-exit-waituntil-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(errorExitTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + const startTime = Date.now(); + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + const elapsed = Date.now() - startTime; + + // Should fail fast + assert.ok(elapsed < 5000, `should fail fast (elapsed: ${elapsed}ms)`); + + assert.equal(result.summary.steps.fail, 1, "step should fail"); + + const failedStep = result.specs[0].tests[0].contexts[0].steps[0]; + assert.equal(failedStep.result, "FAIL", "step should be marked as FAIL"); + assert.ok( + failedStep.resultDescription.includes("exit code") || + failedStep.resultDescription.includes("exited"), + "error should mention exit code" + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + }); +}); From d6380f9f3663c493d71b51601bfd846c45268f0c Mon Sep 17 00:00:00 2001 From: hawkeyexl Date: Thu, 8 Jan 2026 18:53:01 -0800 Subject: [PATCH 2/2] Refactor terminal interaction and enhance output handling - Change shell command from 'sh' to 'node' in dev.spec.json for better compatibility. - Implement waitForOutputSettle function to ensure terminal output stabilizes before proceeding. - Add unit tests for typeKeys output settling behavior and interaction with Node REPL. - Validate that typed input is captured correctly in interactive terminal applications. --- dev/dev.spec.json | 94 +----- src/tests/typeKeys.js | 95 +++++- test/scopes.test.js | 652 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 751 insertions(+), 90 deletions(-) diff --git a/dev/dev.spec.json b/dev/dev.spec.json index 5484b13d..0e48107c 100644 --- a/dev/dev.spec.json +++ b/dev/dev.spec.json @@ -4,107 +4,27 @@ "steps": [ { "runShell": { - "command": "sh", - "args": ["-c", "echo 'READY' && sleep 5"], + "command": "node", "scope": "basic-scope", - "timeout": 3000, "waitUntil": { "stdio": { - "stdout": "READY" + "stdout": "Welcome to Node.js" } } } }, { - "terminateScope": { - "scope": "basic-scope" - } - } - ] - }, - { - "steps": [ - { - "runShell": { - "command": "sh", - "args": ["-c", "echo 'SERVER_STARTED' && while true; do sleep 1; done"], - "scope": "args-scope", - "timeout": 3000, - "waitUntil": { - "stdio": { - "stdout": "SERVER_STARTED" - } - } - } - }, - { - "runShell": "echo 'Other work while scope runs'" - }, - { - "terminateScope": { - "scope": "args-scope" - } - } - ] - }, - { - "steps": [ - { - "runShell": { - "command": "sleep", - "args": ["10"], - "scope": "no-wait-scope" - } - }, - { - "runShell": "echo 'Background process started'" - }, - { - "terminateScope": { - "scope": "no-wait-scope" - } - } - ] - }, - { - "steps": [ - { - "runShell": { - "command": "sh", - "args": ["-c", "echo 'SCOPE_A_READY' && sleep 10"], - "scope": "multi-scope-a", - "timeout": 3000, - "waitUntil": { - "stdio": { - "stdout": "SCOPE_A_READY" - } - } - } - }, - { - "runShell": { - "command": "sh", - "args": ["-c", "echo 'SCOPE_B_READY' && sleep 10"], - "scope": "multi-scope-b", - "timeout": 3000, - "waitUntil": { - "stdio": { - "stdout": "SCOPE_B_READY" - } - } + "type": { + "scope": "basic-scope", + "keys": ["console.log('hello')", "$ENTER$", ".exit"] } }, { - "runShell": "echo 'Both scopes are running'" + "wait": 500 }, { "terminateScope": { - "scope": "multi-scope-a" - } - }, - { - "terminateScope": { - "scope": "multi-scope-b" + "scope": "basic-scope" } } ] diff --git a/src/tests/typeKeys.js b/src/tests/typeKeys.js index e4459cad..0aad8c58 100644 --- a/src/tests/typeKeys.js +++ b/src/tests/typeKeys.js @@ -193,6 +193,12 @@ async function typeToScope({ config, step, scopeRegistry, result }) { log(config, "debug", `Wrote ${terminalInput.length} characters to scope '${scopeName}'`); + // Wait for PTY output to settle (no new data for settleTime ms) + // This ensures the terminal has finished processing and echoing the input + const settleTime = 50; // ms to wait for output to stabilize + const maxWaitTime = 5000; // max total wait time + await waitForOutputSettle(scopeRegistry, scopeName, settleTime, maxWaitTime); + result.description = `Typed keys to scope '${scopeName}'.`; return result; } catch (error) { @@ -202,18 +208,74 @@ async function typeToScope({ config, step, scopeRegistry, result }) { } } +/** + * Wait for PTY output to settle (no new data for settleTime ms) + * @param {Object} scopeRegistry - The scope registry + * @param {string} scopeName - Name of the scope to monitor + * @param {number} settleTime - Time in ms to wait with no new output + * @param {number} maxWaitTime - Maximum total wait time in ms + * @returns {Promise} Resolves when output has settled + */ +async function waitForOutputSettle(scopeRegistry, scopeName, settleTime, maxWaitTime) { + const startTime = Date.now(); + const pollInterval = 10; // ms between checks + + let lastStdoutLength = 0; + let lastChangeTime = Date.now(); + + return new Promise((resolve) => { + const check = () => { + const scope = scopeRegistry.get(scopeName); + if (!scope) { + resolve(); // Scope gone, nothing to wait for + return; + } + + const currentLength = scope.stdout.length; + + if (currentLength !== lastStdoutLength) { + // Output changed, reset settle timer + lastStdoutLength = currentLength; + lastChangeTime = Date.now(); + } + + const timeSinceLastChange = Date.now() - lastChangeTime; + const totalElapsed = Date.now() - startTime; + + if (timeSinceLastChange >= settleTime) { + // Output has settled + resolve(); + return; + } + + if (totalElapsed >= maxWaitTime) { + // Max wait time reached, continue anyway + resolve(); + return; + } + + setTimeout(check, pollInterval); + }; + + check(); + }); +} + /** * Convert key array to terminal input string + * Handles both separate special keys like ["hello", "$ENTER$"] + * and embedded special keys like ["hello$ENTER$world"] */ function convertKeysToTerminalInput(keys) { let output = ""; for (const key of keys) { + // Check if the entire key is a special key if (key.startsWith("$") && key.endsWith("$") && terminalSpecialKeyMap[key]) { output += terminalSpecialKeyMap[key]; - } else if (key.startsWith("$") && key.endsWith("$")) { - // Unknown special key - skip it - continue; + } else if (key.includes("$")) { + // May contain embedded special keys - parse them out + output += parseEmbeddedSpecialKeys(key); } else { output += key; } @@ -222,6 +284,33 @@ function convertKeysToTerminalInput(keys) { return output; } +/** + * Parse a string that may contain embedded special keys like "hello$ENTER$world" + */ +function parseEmbeddedSpecialKeys(str) { + let result = ""; + let i = 0; + + while (i < str.length) { + if (str[i] === "$") { + // Look for closing $ + const endIndex = str.indexOf("$", i + 1); + if (endIndex !== -1) { + const specialKey = str.slice(i, endIndex + 1); + if (terminalSpecialKeyMap[specialKey]) { + result += terminalSpecialKeyMap[specialKey]; + i = endIndex + 1; + continue; + } + } + } + result += str[i]; + i++; + } + + return result; +} + /** * Type keys to browser element */ diff --git a/test/scopes.test.js b/test/scopes.test.js index 243ec5e1..98841dd2 100644 --- a/test/scopes.test.js +++ b/test/scopes.test.js @@ -729,6 +729,146 @@ describe("terminateScope Action", function () { }); }); +// ============================================================================= +// TYPE KEYS OUTPUT SETTLE TESTS (Unit Tests) +// ============================================================================= + +describe("typeKeys output settle", function () { + this.timeout(30000); + + let typeKeys, ScopeRegistry, createTerminalScope; + + before(function () { + try { + typeKeys = require("../src/tests/typeKeys").typeKeys; + const scopes = require("../src/scopes"); + ScopeRegistry = scopes.ScopeRegistry; + createTerminalScope = scopes.createTerminalScope; + } catch (e) { + // Module doesn't exist yet + } + }); + + it("should wait for output to settle before completing", async function () { + if (!typeKeys || !ScopeRegistry || !createTerminalScope) this.skip(); + if (process.platform === "win32") this.skip(); + + const registry = new ScopeRegistry(); + + // Start a Node REPL + const terminalResult = await createTerminalScope({ + command: "node", + args: [], + waitForExit: false, + }); + + registry.create("test-repl", terminalResult.process); + + // Set up output capture + if (terminalResult.stdout) { + registry.appendStdout("test-repl", terminalResult.stdout); + } + terminalResult.process.onData((data) => { + registry.appendStdout("test-repl", data); + }); + + // Wait for Node REPL to be ready + await new Promise((resolve) => { + const checkReady = () => { + const scope = registry.get("test-repl"); + if (scope.stdout.includes("Welcome to Node.js")) { + resolve(); + } else { + setTimeout(checkReady, 50); + } + }; + checkReady(); + }); + + const step = { + type: { + keys: ["console.log('settle-test-output')$ENTER$"], + scope: "test-repl", + }, + }; + + const config = { logLevel: "silent" }; + + // typeKeys should wait for output to settle before returning + const result = await typeKeys({ config, step, scopeRegistry: registry }); + + // After typeKeys returns, the output should already contain our expected text + // because it waited for the PTY output to settle + const scope = registry.get("test-repl"); + assert.ok( + scope.stdout.includes("settle-test-output"), + `stdout should contain 'settle-test-output' after typeKeys returns (output settled), got: ${scope.stdout.slice(-200)}` + ); + assert.equal(result.status, "PASS", "typeKeys should pass"); + + // Cleanup + terminalResult.process.kill(); + }); + + it("should capture multi-line output after settle", async function () { + if (!typeKeys || !ScopeRegistry || !createTerminalScope) this.skip(); + if (process.platform === "win32") this.skip(); + + const registry = new ScopeRegistry(); + + // Start a Node REPL + const terminalResult = await createTerminalScope({ + command: "node", + args: [], + waitForExit: false, + }); + + registry.create("test-multiline", terminalResult.process); + + if (terminalResult.stdout) { + registry.appendStdout("test-multiline", terminalResult.stdout); + } + terminalResult.process.onData((data) => { + registry.appendStdout("test-multiline", data); + }); + + // Wait for Node REPL to be ready + await new Promise((resolve) => { + const checkReady = () => { + const scope = registry.get("test-multiline"); + if (scope.stdout.includes("Welcome to Node.js")) { + resolve(); + } else { + setTimeout(checkReady, 50); + } + }; + checkReady(); + }); + + // Type a command that produces multi-line output + const step = { + type: { + keys: ["[1,2,3].forEach(x => console.log('line-' + x))$ENTER$"], + scope: "test-multiline", + }, + }; + + const config = { logLevel: "silent" }; + const result = await typeKeys({ config, step, scopeRegistry: registry }); + + const scope = registry.get("test-multiline"); + + // All output lines should be captured + assert.ok(scope.stdout.includes("line-1"), `should contain line-1, got: ${scope.stdout.slice(-300)}`); + assert.ok(scope.stdout.includes("line-2"), `should contain line-2`); + assert.ok(scope.stdout.includes("line-3"), `should contain line-3`); + assert.equal(result.status, "PASS"); + + // Cleanup + terminalResult.process.kill(); + }); +}); + // ============================================================================= // INTEGRATION TESTS - FULL WORKFLOW // ============================================================================= @@ -1070,6 +1210,518 @@ describe("Scope Integration Tests", function () { } } }); + + it("should automatically wait for output to settle (no waitUntil needed)", async function () { + if (process.platform === "win32") { + this.skip(); + } + + // This test validates that type action automatically waits for output to settle: + // 1. Types to the process + // 2. Waits for PTY output to stabilize BEFORE completing the step + // 3. No separate wait step needed - it just works + const settleTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "node", + scope: "type-settle", + timeout: 10000, + waitUntil: { + stdio: { + stdout: "Welcome to Node.js", + }, + }, + }, + }, + { + type: { + keys: ["console.log('settle-test-output')$ENTER$"], + scope: "type-settle", + }, + }, + // NO wait step - the type action should auto-settle + { + terminateScope: { + scope: "type-settle", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-type-settle-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(settleTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + + // Verify the output was captured (proves auto-settle worked) + const terminateStep = result.specs[0].tests[0].contexts[0].steps[2]; + const stdout = terminateStep.outputs?.stdout || ""; + assert.ok( + stdout.includes("settle-test-output"), + `type auto-settle should have waited for output, got: ${stdout}` + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should interact with actual Node REPL", async function () { + // Skip on Windows where node REPL behaves differently + if (process.platform === "win32") { + this.skip(); + } + + // This test uses the ACTUAL Node REPL (not a simulated one) + // to validate that we can: + // 1. Start the Node REPL + // 2. Execute JavaScript expressions + // 3. Capture the REPL's response + const nodeReplTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "node", + scope: "node-repl", + timeout: 10000, + waitUntil: { + stdio: { + stdout: "Welcome to Node.js", + }, + }, + }, + }, + { + type: { + keys: ["2 + 2$ENTER$"], + scope: "node-repl", + }, + }, + { + wait: 500, + }, + { + type: { + keys: [".exit$ENTER$"], + scope: "node-repl", + }, + }, + { + wait: 500, + }, + { + terminateScope: { + scope: "node-repl", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-node-repl-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(nodeReplTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + + // Verify the REPL evaluated our expression + const terminateStep = result.specs[0].tests[0].contexts[0].steps[5]; + const stdout = terminateStep.outputs?.stdout || ""; + assert.ok( + stdout.includes("4"), + `Node REPL should output result of 2+2=4, got: ${stdout}` + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should execute console.log in Node REPL and capture output", async function () { + // Skip on Windows + if (process.platform === "win32") { + this.skip(); + } + + // Test console.log output capture - this is the exact use case from dev.spec.json + const consoleLogTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "node", + scope: "node-console", + timeout: 10000, + waitUntil: { + stdio: { + stdout: "Welcome to Node.js", + }, + }, + }, + }, + { + type: { + keys: ["console.log('hello from node')$ENTER$"], + scope: "node-console", + }, + }, + { + wait: 500, + }, + { + terminateScope: { + scope: "node-console", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-node-console-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(consoleLogTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + + // Verify the console.log output was captured + const terminateStep = result.specs[0].tests[0].contexts[0].steps[3]; + const stdout = terminateStep.outputs?.stdout || ""; + assert.ok( + stdout.includes("hello from node"), + `Node REPL should capture console.log output, got: ${stdout}` + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should handle multi-line JavaScript in Node REPL", async function () { + // Skip on Windows + if (process.platform === "win32") { + this.skip(); + } + + // Test multi-line input and function definition in Node REPL + const multiLineTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "node", + scope: "node-multiline", + timeout: 10000, + waitUntil: { + stdio: { + stdout: "Welcome to Node.js", + }, + }, + }, + }, + { + // Define a function + type: { + keys: ["function greet(name) { return 'Hello, ' + name + '!'; }$ENTER$"], + scope: "node-multiline", + }, + }, + { + wait: 300, + }, + { + // Call the function + type: { + keys: ["greet('World')$ENTER$"], + scope: "node-multiline", + }, + }, + { + wait: 500, + }, + { + terminateScope: { + scope: "node-multiline", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-node-multiline-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(multiLineTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + + // Verify the function returned the expected value + const terminateStep = result.specs[0].tests[0].contexts[0].steps[5]; + const stdout = terminateStep.outputs?.stdout || ""; + assert.ok( + stdout.includes("Hello, World!"), + `Node REPL should output function result, got: ${stdout}` + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + }); + + describe("Interactive terminal applications", function () { + it("should capture typed input in process output (cat echo test)", async function () { + if (process.platform === "win32") { + this.skip(); + } + + // This test validates that: + // 1. We can start an interactive process (cat) + // 2. Type input to it + // 3. The input appears in the captured output (cat echoes input) + const interactiveTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'CAT_READY' && cat"], + scope: "interactive-cat", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "CAT_READY", + }, + }, + }, + }, + { + type: { + keys: ["hello interactive$ENTER$"], + scope: "interactive-cat", + }, + }, + { + wait: 500, + }, + { + terminateScope: { + scope: "interactive-cat", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-interactive-cat-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(interactiveTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + + // Verify the typed input appears in the scope's captured output + const terminateStep = result.specs[0].tests[0].contexts[0].steps[3]; + const stdout = terminateStep.outputs?.stdout || ""; + assert.ok( + stdout.includes("hello interactive"), + `typed input should appear in captured output, got: ${stdout}` + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should support REPL-style interaction with response validation", async function () { + if (process.platform === "win32") { + this.skip(); + } + + // This test validates a REPL-like interaction: + // 1. Start a process that echoes back input with a prefix + // 2. Type commands + // 3. Verify the process responded to our input + const replTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'REPL_READY' && while read line; do echo \"RESPONSE: $line\"; done"], + scope: "repl-test", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "REPL_READY", + }, + }, + }, + }, + { + type: { + keys: ["test-command-123$ENTER$"], + scope: "repl-test", + }, + }, + { + wait: 500, + }, + { + terminateScope: { + scope: "repl-test", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-repl-interaction-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(replTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + + // Verify the REPL responded to our input + const terminateStep = result.specs[0].tests[0].contexts[0].steps[3]; + const stdout = terminateStep.outputs?.stdout || ""; + assert.ok( + stdout.includes("RESPONSE: test-command-123"), + `REPL should echo back our input with RESPONSE prefix, got: ${stdout}` + ); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); + + it("should handle multiple rounds of interaction", async function () { + if (process.platform === "win32") { + this.skip(); + } + + // Validates multi-turn interaction with an interactive process + const multiTurnTest = { + tests: [ + { + steps: [ + { + runShell: { + command: "sh", + args: ["-c", "echo 'CALC_READY' && while read line; do echo \"Got: $line\"; done"], + scope: "multi-turn", + timeout: 5000, + waitUntil: { + stdio: { + stdout: "CALC_READY", + }, + }, + }, + }, + { + type: { + keys: ["first-input$ENTER$"], + scope: "multi-turn", + }, + }, + { + wait: 300, + }, + { + type: { + keys: ["second-input$ENTER$"], + scope: "multi-turn", + }, + }, + { + wait: 300, + }, + { + type: { + keys: ["third-input$ENTER$"], + scope: "multi-turn", + }, + }, + { + wait: 300, + }, + { + terminateScope: { + scope: "multi-turn", + }, + }, + ], + }, + ], + }; + + const tempFilePath = path.resolve("./test/temp-multi-turn-test.json"); + fs.writeFileSync(tempFilePath, JSON.stringify(multiTurnTest, null, 2)); + const config = { input: tempFilePath, logLevel: "silent" }; + + let result; + try { + result = await runTests(config); + skipIfSchemaNotUpdated.call(this, result); + assert.equal(result.summary.steps.fail, 0, "no steps should fail"); + + // Verify all three inputs were processed + const terminateStep = result.specs[0].tests[0].contexts[0].steps[7]; + const stdout = terminateStep.outputs?.stdout || ""; + + assert.ok(stdout.includes("Got: first-input"), `should have processed first input, got: ${stdout}`); + assert.ok(stdout.includes("Got: second-input"), `should have processed second input, got: ${stdout}`); + assert.ok(stdout.includes("Got: third-input"), `should have processed third input, got: ${stdout}`); + } finally { + if (fs.existsSync(tempFilePath)) { + fs.unlinkSync(tempFilePath); + } + } + }); }); describe("Scope lifecycle", function () {