From 2dbfda1285c9bc99be106c3b5d8f768b15192516 Mon Sep 17 00:00:00 2001 From: Orange Mug Date: Tue, 9 May 2023 21:21:45 +0100 Subject: [PATCH] Added initial webdriver tests (#1337) Adds webdriver tests for testing from a users perspective via browser actions. We currently support local test runners for a bunch of actions on desktop `chrome`/`firefox`/`edge`/`safari` on macos. We also have a browserstack runner which we'll enable in another PR. ### Release Note - Adds initial webdriver tests --- .eslintignore | 3 + .eslintrc.js | 1 + .gitignore | 6 + apps/webdriver/CHANGELOG.md | 2 + apps/webdriver/README.md | 1 + apps/webdriver/package.json | 51 + apps/webdriver/scripts/dev.mjs | 199 + .../src/1-basic/BasicExampleWebdriver.tsx | 36 + apps/webdriver/src/index.css | 25 + apps/webdriver/src/index.tsx | 27 + apps/webdriver/tsconfig.json | 29 + apps/webdriver/www/index.html | 16 + e2e/CHANGELOG.md | 175 + e2e/README.md | 298 ++ e2e/downloads/README.md | 3 + e2e/package.json | 61 + e2e/screenshots/README.md | 3 + e2e/test-overview-ui.png | Bin 0 -> 215187 bytes e2e/test/helpers/constants.ts | 3 + e2e/test/helpers/index.ts | 4 + e2e/test/helpers/runtime.ts | 46 + e2e/test/helpers/ui/app.ts | 112 + e2e/test/helpers/ui/canvas.ts | 98 + e2e/test/helpers/ui/help.ts | 3 + e2e/test/helpers/ui/index.ts | 8 + e2e/test/helpers/ui/main.ts | 21 + e2e/test/helpers/ui/minimap.ts | 45 + e2e/test/helpers/ui/props.ts | 54 + e2e/test/helpers/ui/share.ts | 1 + e2e/test/helpers/ui/tools.ts | 21 + e2e/test/helpers/util.ts | 139 + e2e/test/helpers/webdriver.ts | 95 + e2e/test/mocha-ext.ts | 118 + e2e/test/specs/arrange.ts | 391 ++ e2e/test/specs/camera.ts | 295 ++ e2e/test/specs/constants.ts | 17 + e2e/test/specs/export.ts | 172 + e2e/test/specs/grouping.ts | 6 + e2e/test/specs/index.ts | 27 + e2e/test/specs/pages.ts | 15 + e2e/test/specs/reorder.ts | 122 + e2e/test/specs/screenshots.ts | 1 + e2e/test/specs/shortcuts.ts | 154 + e2e/test/specs/smoke.ts | 326 ++ e2e/test/specs/styling.ts | 315 ++ e2e/test/specs/text.ts | 243 + e2e/test/test-constants.ts | 12 + e2e/tsconfig.json | 36 + e2e/wdio.local.conf.js | 260 + e2e/wdio.nightly.conf.js | 280 ++ e2e/wdio.remote.conf.js | 271 ++ lazy.config.ts | 5 + package.json | 2 + public-yarn.lock | 4217 ++++++++++++++++- scripts/e2e-run-ci | 14 + scripts/e2e-run-tests | 9 + scripts/e2e-start-server | 3 + 57 files changed, 8760 insertions(+), 137 deletions(-) create mode 100644 apps/webdriver/CHANGELOG.md create mode 100755 apps/webdriver/README.md create mode 100755 apps/webdriver/package.json create mode 100644 apps/webdriver/scripts/dev.mjs create mode 100644 apps/webdriver/src/1-basic/BasicExampleWebdriver.tsx create mode 100644 apps/webdriver/src/index.css create mode 100644 apps/webdriver/src/index.tsx create mode 100755 apps/webdriver/tsconfig.json create mode 100644 apps/webdriver/www/index.html create mode 100644 e2e/CHANGELOG.md create mode 100644 e2e/README.md create mode 100644 e2e/downloads/README.md create mode 100644 e2e/package.json create mode 100644 e2e/screenshots/README.md create mode 100644 e2e/test-overview-ui.png create mode 100644 e2e/test/helpers/constants.ts create mode 100644 e2e/test/helpers/index.ts create mode 100644 e2e/test/helpers/runtime.ts create mode 100644 e2e/test/helpers/ui/app.ts create mode 100644 e2e/test/helpers/ui/canvas.ts create mode 100644 e2e/test/helpers/ui/help.ts create mode 100644 e2e/test/helpers/ui/index.ts create mode 100644 e2e/test/helpers/ui/main.ts create mode 100644 e2e/test/helpers/ui/minimap.ts create mode 100644 e2e/test/helpers/ui/props.ts create mode 100644 e2e/test/helpers/ui/share.ts create mode 100644 e2e/test/helpers/ui/tools.ts create mode 100644 e2e/test/helpers/util.ts create mode 100644 e2e/test/helpers/webdriver.ts create mode 100644 e2e/test/mocha-ext.ts create mode 100644 e2e/test/specs/arrange.ts create mode 100644 e2e/test/specs/camera.ts create mode 100644 e2e/test/specs/constants.ts create mode 100644 e2e/test/specs/export.ts create mode 100644 e2e/test/specs/grouping.ts create mode 100644 e2e/test/specs/index.ts create mode 100644 e2e/test/specs/pages.ts create mode 100644 e2e/test/specs/reorder.ts create mode 100644 e2e/test/specs/screenshots.ts create mode 100644 e2e/test/specs/shortcuts.ts create mode 100644 e2e/test/specs/smoke.ts create mode 100644 e2e/test/specs/styling.ts create mode 100644 e2e/test/specs/text.ts create mode 100644 e2e/test/test-constants.ts create mode 100644 e2e/tsconfig.json create mode 100644 e2e/wdio.local.conf.js create mode 100644 e2e/wdio.nightly.conf.js create mode 100644 e2e/wdio.remote.conf.js create mode 100755 scripts/e2e-run-ci create mode 100755 scripts/e2e-run-tests create mode 100755 scripts/e2e-start-server diff --git a/.eslintignore b/.eslintignore index 44cebb406..db9fe8476 100644 --- a/.eslintignore +++ b/.eslintignore @@ -18,3 +18,6 @@ **/next.config.js **/setupTests.js **/setupJest.js +apps/webdriver/www +apps/vscode/extension/editor +apps/examples/www \ No newline at end of file diff --git a/.eslintrc.js b/.eslintrc.js index 027c239d8..f4bd345aa 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,6 +5,7 @@ module.exports = { 'plugin:@typescript-eslint/recommended', 'plugin:@next/next/core-web-vitals', ], + ignorePatterns: ['e2e/wdio.*.js'], plugins: ['@typescript-eslint', 'no-only-tests', 'import', 'local', '@next/next', 'react-hooks'], settings: { next: { diff --git a/.gitignore b/.gitignore index bd4f0f5e0..44a2c920f 100644 --- a/.gitignore +++ b/.gitignore @@ -57,6 +57,7 @@ packages/assets/embed-icons packages/assets/fonts packages/assets/icons packages/assets/translations +apps/webdriver/www/ apps/examples/www/embed-icons apps/examples/www/fonts apps/examples/www/icons @@ -77,3 +78,8 @@ apps/examples/www/index.js .tsbuild apps/examples/build.esbuild.json + +e2e/screenshots +e2e/downloads +e2e/driver-logs +e2e/.wdio-vscode-service/ diff --git a/apps/webdriver/CHANGELOG.md b/apps/webdriver/CHANGELOG.md new file mode 100644 index 000000000..115a6b294 --- /dev/null +++ b/apps/webdriver/CHANGELOG.md @@ -0,0 +1,2 @@ +# @tldraw/webdriver + diff --git a/apps/webdriver/README.md b/apps/webdriver/README.md new file mode 100755 index 000000000..03d286ff8 --- /dev/null +++ b/apps/webdriver/README.md @@ -0,0 +1 @@ +# @tldraw/webdriver diff --git a/apps/webdriver/package.json b/apps/webdriver/package.json new file mode 100755 index 000000000..d8259b86d --- /dev/null +++ b/apps/webdriver/package.json @@ -0,0 +1,51 @@ +{ + "name": "webdriver", + "description": "A tiny little drawing app (for webdriver).", + "version": "2.0.0-alpha.11", + "private": true, + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "homepage": "https://tldraw.dev", + "repository": { + "type": "git", + "url": "https://github.com/tldraw/tldraw" + }, + "bugs": { + "url": "https://github.com/tldraw/tldraw/issues" + }, + "keywords": [ + "tldraw", + "drawing", + "app", + "development", + "whiteboard", + "canvas", + "infinite" + ], + "scripts": { + "dev-webdriver": "concurrently --names \"tsc,esbuild\" \"yarn run -T tsx ../../scripts/typecheck.ts --build --watch --preserveWatchOutput\" \"node ./scripts/dev.mjs\"", + "lint": "yarn run -T tsx ../../scripts/lint.ts" + }, + "devDependencies": { + "@types/ip": "^1.1.0", + "@types/node-forge": "^1.3.1", + "browserslist-to-esbuild": "^1.2.0", + "concurrently": "^7.4.0", + "esbuild": "^0.16.7", + "ip": "^1.1.8", + "kleur": "^4.1.5", + "lazyrepo": "0.0.0-alpha.26", + "node-forge": "^1.3.1", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "dependencies": { + "@tldraw/assets": "workspace:*", + "@tldraw/tldraw": "workspace:*", + "react-router-dom": "^6.9.0", + "signia": "0.1.4", + "signia-react": "0.1.4" + } +} diff --git a/apps/webdriver/scripts/dev.mjs b/apps/webdriver/scripts/dev.mjs new file mode 100644 index 000000000..bdaec49cf --- /dev/null +++ b/apps/webdriver/scripts/dev.mjs @@ -0,0 +1,199 @@ +// @ts-nocheck +/* eslint-disable */ + +import browserslist from 'browserslist-to-esbuild' +import crypto from 'crypto' +import esbuild from 'esbuild' +import { createServer as createNonSslServer, request } from 'http' +import { createServer as createSslServer } from 'https' +import ip from 'ip' +import chalk from 'kleur' +import forge from 'node-forge' +import * as url from 'url' + +const LOG_REQUEST_PATHS = false + +export const generateCert = ({ altNameIPs, altNameURIs, validityDays }) => { + const keys = forge.pki.rsa.generateKeyPair(2048) + const cert = forge.pki.createCertificate() + cert.publicKey = keys.publicKey + + // NOTE: serialNumber is the hex encoded value of an ASN.1 INTEGER. + // Conforming CAs should ensure serialNumber is: + // - no more than 20 octets + // - non-negative (prefix a '00' if your value starts with a '1' bit) + cert.serialNumber = '01' + crypto.randomBytes(19).toString('hex') // 1 octet = 8 bits = 1 byte = 2 hex chars + cert.validity.notBefore = new Date() + cert.validity.notAfter = new Date( + new Date().getTime() + 1000 * 60 * 60 * 24 * (validityDays ?? 1) + ) + const attrs = [ + { + name: 'countryName', + value: 'AU', + }, + { + shortName: 'ST', + value: 'Some-State', + }, + { + name: 'organizationName', + value: 'Temporary Testing Department Ltd', + }, + ] + cert.setSubject(attrs) + cert.setIssuer(attrs) + + // add alt names so that the browser won't complain + cert.setExtensions([ + { + name: 'subjectAltName', + altNames: [ + ...(altNameURIs !== undefined ? altNameURIs.map((uri) => ({ type: 6, value: uri })) : []), + ...(altNameIPs !== undefined ? altNameIPs.map((uri) => ({ type: 7, ip: uri })) : []), + ], + }, + ]) + // self-sign certificate + cert.sign(keys.privateKey) + + // convert a Forge certificate and private key to PEM + const pem = forge.pki.certificateToPem(cert) + const privateKey = forge.pki.privateKeyToPem(keys.privateKey) + + return { + cert: pem, + privateKey, + } +} + +const { log } = console + +const dirname = url.fileURLToPath(new URL('.', import.meta.url)) + +const PORT = 5420 +const SSL_PORT = 5421 +const ENABLE_SSL = process.env.ENABLE_SSL === '1' +const ENABLE_NETWORK_CACHING = process.env.ENABLE_NETWORK_CACHING === '1' +const OUT_DIR = dirname + '/../www/' + +const clients = [] + +async function main() { + await esbuild.build({ + entryPoints: ['src/index.tsx'], + outdir: OUT_DIR, + bundle: true, + minify: false, + sourcemap: true, + incremental: true, + format: 'cjs', + external: ['*.woff'], + target: browserslist(['defaults']), + define: { + process: '{ "env": { "NODE_ENV": "development"} }', + }, + loader: { + '.woff2': 'file', + '.svg': 'file', + '.json': 'file', + '.png': 'file', + }, + watch: { + onRebuild(error) { + log('rebuilt') + if (error) { + log(error) + } + clients.forEach((res) => res.write('data: update\n\n')) + clients.length = 0 + }, + }, + }) + + esbuild.serve({ servedir: OUT_DIR, port: 8009 }, {}).then(({ host, port: esbuildPort }) => { + const handler = async (req, res) => { + const { url, method, headers } = req + if (req.url === '/esbuild') + return clients.push( + res.writeHead(200, { + 'Content-Type': 'text/event-stream', + 'Cache-Control': 'no-cache', + Connection: 'keep-alive', + }) + ) + + function forwardRequest(url) { + const path = (url?.split('/').pop()?.indexOf('.') ?? -1) > -1 ? url : `/index.html` //for PWA with router + + if (LOG_REQUEST_PATHS) { + console.log('[%s]=', method, path) + } + + const req2 = request( + { hostname: host, port: esbuildPort, path, method, headers }, + (prxRes) => { + const newHeaders = { + ...prxRes.headers, + } + + if (ENABLE_NETWORK_CACHING) { + const hrInSeconds = 60*60*60 + newHeaders['cache-control'] = `max-age=${hrInSeconds}`; + } + + if (url === '/index.js') { + const jsReloadCode = + ' (() => new EventSource("/esbuild").onmessage = () => location.reload())();' + + newHeaders['content-length'] = parseInt(prxRes.headers['content-length'] ?? '0', 10) + jsReloadCode.length, + + res.writeHead(prxRes.statusCode ?? 0, newHeaders) + res.write(jsReloadCode) + } else { + res.writeHead(prxRes.statusCode ?? 0, newHeaders) + } + prxRes.pipe(res, { end: true }) + } + ) + + req.pipe(req2, { end: true }) + } + + forwardRequest(url ?? '/') + } + + const nonSslServer = createNonSslServer(handler) + nonSslServer.on('error', function (e) { + // Handle your error here + console.log(e) + }) + nonSslServer.listen(PORT, () => { + log(`Running on:\n`) + log(chalk.bold().cyan(` http://localhost:${PORT}`)) + log(`\nNetwork:\n`) + log(chalk.bold().cyan(` http://${ip.address()}:${PORT}`)) + }) + + if (ENABLE_SSL) { + const cert = generateCert({ + altNameIPs: ['127.0.0.1'], + altNameURIs: ['localhost'], + validityDays: 2, + }) + const sslServer = createSslServer({ key: cert.privateKey, cert: cert.cert }, handler) + sslServer.on('error', function (e) { + // Handle your error here + console.log(e) + }) + sslServer.listen(SSL_PORT, () => { + log(`Running on:\n`) + log(chalk.bold().cyan(` https://localhost:${SSL_PORT}`)) + log(`\nNetwork:\n`) + log(chalk.bold().cyan(` https://${ip.address()}:${SSL_PORT}`)) + }) + } + }) +} + +main() diff --git a/apps/webdriver/src/1-basic/BasicExampleWebdriver.tsx b/apps/webdriver/src/1-basic/BasicExampleWebdriver.tsx new file mode 100644 index 000000000..537bbfabe --- /dev/null +++ b/apps/webdriver/src/1-basic/BasicExampleWebdriver.tsx @@ -0,0 +1,36 @@ +import { hardReset, Tldraw } from '@tldraw/tldraw' +/* eslint-disable import/no-internal-modules */ +import { getAssetUrlsByImport } from '@tldraw/assets/imports' +import '@tldraw/tldraw/editor.css' +import '@tldraw/tldraw/ui.css' +/* eslint-enable import/no-internal-modules */ + +import { useEffect, useState } from 'react' + +declare global { + interface Window { + webdriverReset: () => void + } +} + +const assetUrls = getAssetUrlsByImport() + +// NOTE: This is currently very similar to `apps/examples/src/1-basic/BasicExample.tsx` +// and should probably stay that way to make writing new tests easier as it's +// what we're most familiar with +export default function Example() { + const [instanceKey, setInstanceKey] = useState(0) + + useEffect(() => { + window.webdriverReset = () => { + hardReset({ shouldReload: false }) + setInstanceKey(instanceKey + 1) + } + }, [instanceKey]) + + return ( +
+ +
+ ) +} diff --git a/apps/webdriver/src/index.css b/apps/webdriver/src/index.css new file mode 100644 index 000000000..ad1897e84 --- /dev/null +++ b/apps/webdriver/src/index.css @@ -0,0 +1,25 @@ +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500&display=swap'); + +html, +body { + padding: 0; + margin: 0; + font-family: 'Inter', sans-serif; + overscroll-behavior: none; + touch-action: none; + min-height: 100vh; + /* mobile viewport bug fix */ + min-height: -webkit-fill-available; + height: 100%; +} + +html, +* { + box-sizing: border-box; +} + +.tldraw__editor { + position: fixed; + inset: 0px; + overflow: hidden; +} diff --git a/apps/webdriver/src/index.tsx b/apps/webdriver/src/index.tsx new file mode 100644 index 000000000..cc522a93d --- /dev/null +++ b/apps/webdriver/src/index.tsx @@ -0,0 +1,27 @@ +import { DefaultErrorFallback, ErrorBoundary } from '@tldraw/tldraw' +import { StrictMode } from 'react' +import { createRoot } from 'react-dom/client' +import { RouterProvider, createBrowserRouter } from 'react-router-dom' +import ExampleBasic from './1-basic/BasicExampleWebdriver' +import './index.css' + +const router = createBrowserRouter([ + { + path: '/', + element: , + }, +]) + +const rootElement = document.getElementById('root') +const root = createRoot(rootElement!) + +root.render( + + } + onError={(error) => console.error(error)} + > + + + +) diff --git a/apps/webdriver/tsconfig.json b/apps/webdriver/tsconfig.json new file mode 100755 index 000000000..8e86ccf32 --- /dev/null +++ b/apps/webdriver/tsconfig.json @@ -0,0 +1,29 @@ +{ + "compilerOptions": { + "target": "ESNext", + "useDefineForClassFields": true, + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "allowJs": true, + "checkJs": true, + "skipLibCheck": true, + "esModuleInterop": true, + "removeComments": true, + "allowSyntheticDefaultImports": true, + "strict": true, + "forceConsistentCasingInFileNames": true, + "module": "ESNext", + "moduleResolution": "Node", + "resolveJsonModule": true, + "noEmit": true, + "jsx": "react-jsx", + "incremental": true, + "baseUrl": ".", + "composite": true, + "sourceMap": false, + "importHelpers": false, + "skipDefaultLibCheck": true, + "experimentalDecorators": true + }, + "include": ["src", "scripts"], + "references": [{ "path": "../../packages/tldraw" }] +} diff --git a/apps/webdriver/www/index.html b/apps/webdriver/www/index.html new file mode 100644 index 000000000..32648dcf4 --- /dev/null +++ b/apps/webdriver/www/index.html @@ -0,0 +1,16 @@ + + + + + + + + + tldraw + + + + +
+ + diff --git a/e2e/CHANGELOG.md b/e2e/CHANGELOG.md new file mode 100644 index 000000000..6f9a233da --- /dev/null +++ b/e2e/CHANGELOG.md @@ -0,0 +1,175 @@ +# @tldraw/e2e + +## 2.0.0-alpha.8 + +### Patch Changes + +- Release day! + +## 2.0.0-alpha.7 + +### Patch Changes + +- Bug fixes. + +## 2.0.0-alpha.6 + +### Patch Changes + +- Add licenses. + +## 2.0.0-alpha.5 + +### Patch Changes + +- Add CSS files to tldraw/tldraw. + +## 2.0.0-alpha.4 + +### Patch Changes + +- Add children to tldraw/tldraw + +## 2.0.0-alpha.3 + +### Patch Changes + +- Change permissions. + +## 2.0.0-alpha.2 + +### Patch Changes + +- Add tldraw, editor + +## 0.1.0-alpha.11 + +### Patch Changes + +- Fix stale reactors. + +## 0.1.0-alpha.10 + +### Patch Changes + +- Fix type export bug. + +## 0.1.0-alpha.9 + +### Patch Changes + +- Fix import bugs. + +## 0.1.0-alpha.8 + +### Patch Changes + +- Changes validation requirements, exports validation helpers. + +## 0.1.0-alpha.7 + +### Patch Changes + +- - Pre-pre-release update + +## 0.0.2-alpha.1 + +### Patch Changes + +- Fix error with HMR + +## 0.0.2-alpha.0 + +### Patch Changes + +- Initial release + +## 0.0.1-alpha.0 + +### Patch Changes + +- Initial release + +## 0.0.1-alpha.13 + +### Patch Changes + +- - + +## 0.0.1-alpha.12 + +### Patch Changes + +- - + +## 0.0.1-alpha.11 + +### Patch Changes + +- - + +## 0.0.1-alpha.10 + +### Patch Changes + +- - + +## 0.0.1-alpha.9 + +### Patch Changes + +- - + +## 0.0.1-alpha.8 + +### Patch Changes + +- - + +## 0.0.1-alpha.7 + +### Patch Changes + +- - + +## 0.0.1-alpha.6 + +### Patch Changes + +- 16495ef7: - + +## 0.0.1-alpha.5 + +### Patch Changes + +- Changed a few things. + +## 0.0.1-alpha.4 + +### Patch Changes + +- Remove bundling + +## 0.0.1-alpha.3 + +### Patch Changes + +- Fix UI package. + +## 0.0.1-alpha.2 + +### Patch Changes + +- Add modules to missing packages + +## 0.0.1-alpha.1 + +### Patch Changes + +- Add module + +## 0.0.1-alpha.0 + +### Patch Changes + +- Add css to dist diff --git a/e2e/README.md b/e2e/README.md new file mode 100644 index 000000000..f60a632f2 --- /dev/null +++ b/e2e/README.md @@ -0,0 +1,298 @@ +# Webdriver + +This docs describes Webdriver testing within the app. + +Webdriver testing can be tricky because you're sending commands to an actual browser running on either you machine or browserstack. This can be slow but it's currently the only way to test things within a wide range of browsers without emulation. This give us the best chance of having a stable app across a range of browsers without excessive manual testing + +> **A note on stability**: Webdriver tests are a lot more flakey than other types of testing, the major benefit is that you can run them on real devices, so we can hopefully get a good smoke test of various real devices. You can also screenshot those devices during test runs, to check look. You however probably don't want to write too many webdriver tests, they are best placed for smoke testing and testing stuff that's very browser specific. + +To run the tests you first must start the server with + +```sh +./scripts/e2e-start-server +``` + +You can then either test locally with + +```sh +./scripts/e2e-run-tests local +``` + +By default it'll just run the chrome tests. You can also specify other browsers to run like this + +```sh +# Note edge, safari are a work in progress and will just be skipped for now. +./scripts/e2e-run-tests local firefox,chrome,edge,safari +``` + +Or to test remotely via browserstack + +```sh +./scripts/e2e-run-tests remote +``` + +There are three parts to the testing API + +- `runtime` — the tldraw `App` instance in the browser window. This calls methods in the JS runtime of the app +- `ui` — methods to interacting with the apps UI elements +- `util` — some general helper methods + +The `ui` is further broken up into + +![test overview ui](test-overview-ui.png) + +tldraw room for above is at + +## Nightly tests +We run all the tests on all browsers via browserstack each night at 02:00am. You can search https://github.com/tldraw/tldraw-lite/actions/workflows/webdriver-nightly.yml for the results of the previous evenings tests + +## On demand tests +To run tests on demand via github actions you can head to https://github.com/tldraw/tldraw-lite/actions/workflows/webdriver-on-demand.yml and select **run workflow** with you desired options and press the **run workflow** button + +https://user-images.githubusercontent.com/235915/233660925-866d2db3-66f9-4e6c-b19a-8da597c8b512.mov + +## Ongoing issues + +You can see the on-going test issues in the project at + +Performance is a little slow remotely mostly to do with + +- startup time of the browser, see +- app reset/refresh time, see + +## Writing new tests + +There are target tests with the `.todo(...)` suffix, an example might be + +https://github.com/tldraw/tldraw-lite/blob/7ff0dd111bb9bf9b931d53f61722842780d9793e/e2e/test/specs/shortcuts.ts#L114 + +The missing tests (`.todo`) follow a lot of the same patterns that already exist in the codebase. Firefox is skipped quite a lot in the test runners, this is due to issues with the app and tests, so those also need to be resolved. You can search for `FIXME` in the `./e2e` directory to find those. + +## Test writing guide + +Below explains the process of writing a new test. Most actions don't have anything specific for a particular browser, however most actions do have specifics per-layout and viewport size. Lets walk through and explain a very simple test, the draw shortcut. + +When pressing `d` in the browser on a desktop environment we should be changing the current state to `draw.idle`. Below is the test comments in line. + +```js +// These are helpers that we use in our tests to interact with our app via the browser. +import { runtime, ui } from '../helpers' + +// The runner uses mocha, `env` is an addition added by mocha-ext and `it` is +// extended to support skipping of tests in certain environments. +import { describe, env, it } from '../mocha-ext' + +// Always group tests, this make them easy to run in isolation by changing +// `describe(...)` to `describe.only(...)` +describe('shortcuts', () => { + // The `env` command is used to mark groups of tests as skippable under + // certain conditions, here we're only running these tests under desktop + // environments. Note that is **REALLY** important we don't just wrap this + // in a `if(FOO) {...}` block. Otherwise `.only` would break under certain + // circumstances + env({ device: 'desktop' }, () => { + // We haven't written this test yet, we can mark those with a `.todo` + // This appears as `[TODO] {test name}` in the test output. + it.todo('select — V', async () => { + await ui.app.setup() + await browser.keys(['v']) + }) + + // Here is our draw shortcut test, all tests are going to be async as + // commands are sent to the browser over HTTP + it('draw — D', async () => { + // We must always `setup` our app. This starts the browser (if it + // isn't already) and does a hard reset. Note **NEVER** do this in + // a mocha `before(...)` hook. Else you'll end up starting the browser + // (doing heavy work) whether or not you actually run the test suite. + // I believe this is a mocha/webdriver integration bug. + await ui.app.setup() + + // `browser` is a global from + // This is the default way to interact with the browser. There are + // also lots of helpers in `import { ui } from '../helpers'` module. + // Here we're instructing the browser to enter `d` to the currently + // selected element + await browser.keys(['d']) + + // Although we've pressed `d` we don't know how long that's going to + // take to process in the browser. A slow environment might take + // sometime to update the runtime. So we use `waitUntil` to wait + // for the change. **DON'T** use `util.sleep(...)` here as this will + // only work if the browser/env is fast and will generally slow down + // the test runner and make things flakey. + await browser.waitUntil(async () => { + // `runtime` executes JS in the JS-runtime on the device. This + // is to test for conditions to be met. + return await runtime.isIn('draw.idle') + }) + }) +``` + +To run the above tests, we'd probably want to first isolate the test and `only` run a single test, or test group. Change + +```diff +- it('draw — D', async () => { ++ it.only('draw — D', async () => { +``` + +Now lets start the dev server and run the tests locally. In one terminal session run + +```sh +./scripts/e2e-start-server +``` + +In another terminal session run + +```sh +./scripts/e2e-run-tests local +``` + +The test should run twice in chrome. Once in desktop chrome and once chrome mobile emulation mode. The results should look something like + +``` +------------------------------------------------------------------ +[chrome 109.0.5414.119 mac os x #0-0] Running: chrome (v109.0.5414.119) on mac os x +[chrome 109.0.5414.119 mac os x #0-0] Session ID: 599e14341d743b938056560f3a467361 +[chrome 109.0.5414.119 mac os x #0-0] +[chrome 109.0.5414.119 mac os x #0-0] » /test/specs/index.ts +[chrome 109.0.5414.119 mac os x #0-0] shortcuts +[chrome 109.0.5414.119 mac os x #0-0] ✓ draw — D (ignored only: desktop) +[chrome 109.0.5414.119 mac os x #0-0] +[chrome 109.0.5414.119 mac os x #0-0] 1 passing (59ms) +------------------------------------------------------------------ +[chrome 109.0.5414.119 mac os x #1-0] Running: chrome (v109.0.5414.119) on mac os x +[chrome 109.0.5414.119 mac os x #1-0] Session ID: 2b813d4f8793f910c1a02f771438e3f7 +[chrome 109.0.5414.119 mac os x #1-0] +[chrome 109.0.5414.119 mac os x #1-0] » /test/specs/index.ts +[chrome 109.0.5414.119 mac os x #1-0] shortcuts +[chrome 109.0.5414.119 mac os x #1-0] ✓ draw — D +[chrome 109.0.5414.119 mac os x #1-0] +[chrome 109.0.5414.119 mac os x #1-0] 1 passing (2s) + + +Spec Files: 2 passed, 2 total (100% completed) in 00:00:05 +``` + +Yay, passing tests 🎉 + +However we're not done quite yet. Next up, we need to test them across the rest of our supported browsers. With the `e2e-start-server` still running + +```sh +./scripts/e2e-run-tests remote +``` + +This will start a tunnel from browserstack to your local machine, running the tests against the local server. You can head to browserstack to see the completed test suite + +In you terminal you should see the results + +``` +------------------------------------------------------------------ +[chrome 110.0.5481.78 windows #0-0] Running: chrome (v110.0.5481.78) on windows +[chrome 110.0.5481.78 windows #0-0] Session ID: 5123d74060f5dac19a960c50476b32007d71b758 +[chrome 110.0.5481.78 windows #0-0] +[chrome 110.0.5481.78 windows #0-0] » /test/specs/index.ts +[chrome 110.0.5481.78 windows #0-0] shortcuts +[chrome 110.0.5481.78 windows #0-0] ✓ draw — D +[chrome 110.0.5481.78 windows #0-0] +[chrome 110.0.5481.78 windows #0-0] 1 passing (36.1s) +------------------------------------------------------------------ +[msedge 109.0.1518.49 WINDOWS #1-0] Running: msedge (v109.0.1518.49) on WINDOWS +[msedge 109.0.1518.49 WINDOWS #1-0] Session ID: fd44f7771955021532c16a151b2ccb7657d53cda +[msedge 109.0.1518.49 WINDOWS #1-0] +[msedge 109.0.1518.49 WINDOWS #1-0] » /test/specs/index.ts +[msedge 109.0.1518.49 WINDOWS #1-0] shortcuts +[msedge 109.0.1518.49 WINDOWS #1-0] ✓ draw — D +[msedge 109.0.1518.49 WINDOWS #1-0] +[msedge 109.0.1518.49 WINDOWS #1-0] 1 passing (9.2s) +------------------------------------------------------------------ +[firefox 109.0 WINDOWS #2-0] Running: firefox (v109.0) on WINDOWS +[firefox 109.0 WINDOWS #2-0] Session ID: b590ddccd02ee3a9f3f0ebb92c19d2c5f81421b9 +[firefox 109.0 WINDOWS #2-0] +[firefox 109.0 WINDOWS #2-0] » /test/specs/index.ts +[firefox 109.0 WINDOWS #2-0] shortcuts +[firefox 109.0 WINDOWS #2-0] ✓ draw — D +[firefox 109.0 WINDOWS #2-0] +[firefox 109.0 WINDOWS #2-0] 1 passing (14.4s) +------------------------------------------------------------------ +[chrome 110.0.5481.77 MAC #3-0] Running: chrome (v110.0.5481.77) on MAC +[chrome 110.0.5481.77 MAC #3-0] Session ID: 12d8c60cfb3a20b516b300191e12c4d71952d607 +[chrome 110.0.5481.77 MAC #3-0] +[chrome 110.0.5481.77 MAC #3-0] » /test/specs/index.ts +[chrome 110.0.5481.77 MAC #3-0] shortcuts +[chrome 110.0.5481.77 MAC #3-0] ✓ draw — D +[chrome 110.0.5481.77 MAC #3-0] +[chrome 110.0.5481.77 MAC #3-0] 1 passing (10.9s) +------------------------------------------------------------------ +[firefox 109.0 MAC #4-0] Running: firefox (v109.0) on MAC +[firefox 109.0 MAC #4-0] Session ID: be2fae1429977f192be311c7a1f6a177c0ff6170 +[firefox 109.0 MAC #4-0] +[firefox 109.0 MAC #4-0] » /test/specs/index.ts +[firefox 109.0 MAC #4-0] shortcuts +[firefox 109.0 MAC #4-0] ✓ draw — D +[firefox 109.0 MAC #4-0] +[firefox 109.0 MAC #4-0] 1 passing (9.5s) +------------------------------------------------------------------ +[msedge 109.0.1518.49 MAC #5-0] Running: msedge (v109.0.1518.49) on MAC +[msedge 109.0.1518.49 MAC #5-0] Session ID: 0e59617ae8ac7e5403270c204e1108aa0b2a6c62 +[msedge 109.0.1518.49 MAC #5-0] +[msedge 109.0.1518.49 MAC #5-0] » /test/specs/index.ts +[msedge 109.0.1518.49 MAC #5-0] shortcuts +[msedge 109.0.1518.49 MAC #5-0] ✓ draw — D +[msedge 109.0.1518.49 MAC #5-0] +[msedge 109.0.1518.49 MAC #5-0] 1 passing (8s) +------------------------------------------------------------------ +[2B111FDH2007PR Android 13 #6-0] Running: 2B111FDH2007PR on Android 13 executing chrome +[2B111FDH2007PR Android 13 #6-0] Session ID: 4317967dd4e85cff839a7965a34707a2c839e748 +[2B111FDH2007PR Android 13 #6-0] +[2B111FDH2007PR Android 13 #6-0] » /test/specs/index.ts +[2B111FDH2007PR Android 13 #6-0] shortcuts +[2B111FDH2007PR Android 13 #6-0] ✓ draw — D (ignored only: desktop) +[2B111FDH2007PR Android 13 #6-0] +[2B111FDH2007PR Android 13 #6-0] 1 passing (1.6s) +------------------------------------------------------------------ +[R5CR10PDY5Y Android 11 #7-0] Running: R5CR10PDY5Y on Android 11 +[R5CR10PDY5Y Android 11 #7-0] Session ID: 696e16707b7d9bfdcfc04d3dca08e4579a91af18 +[R5CR10PDY5Y Android 11 #7-0] +[R5CR10PDY5Y Android 11 #7-0] » /test/specs/index.ts +[R5CR10PDY5Y Android 11 #7-0] shortcuts +[R5CR10PDY5Y Android 11 #7-0] ✓ draw — D (ignored only: desktop) +[R5CR10PDY5Y Android 11 #7-0] +[R5CR10PDY5Y Android 11 #7-0] 1 passing (1.8s) +------------------------------------------------------------------ +[R3CR909M44J Android 11 #8-0] Running: R3CR909M44J on Android 11 executing chrome +[R3CR909M44J Android 11 #8-0] Session ID: 797cf525cd07f287281a2051075e0b5e5edc9fd2 +[R3CR909M44J Android 11 #8-0] +[R3CR909M44J Android 11 #8-0] » /test/specs/index.ts +[R3CR909M44J Android 11 #8-0] shortcuts +[R3CR909M44J Android 11 #8-0] ✓ draw — D (ignored only: desktop) +[R3CR909M44J Android 11 #8-0] +[R3CR909M44J Android 11 #8-0] 1 passing (2.1s) + + +Spec Files: 9 passed, 9 total (100% completed) in 00:03:51 +``` + +Now you can remove the `.only` and open a PR and rejoice in your new found skills. + +```diff +- it.only('draw — D', async () => { ++ it('draw — D', async () => { +``` + +Existing tests are a good guide for writing more advance tests, hopefully this should give you a start 🤞 + + +## Notes + +### `msedgedriver` +You might notice that `msedgedriver` on version 91, and hasn't been updated in a while. + +``` +"msedgedriver": "^91.0.0", +``` + +This module isn't actually used but is required for `wdio-edgedriver-service` to start where we pass a custom path via the `edgedriverCustomPath` option. + +### `safaridriver` +Locally safari webdriver tests are currently somewhat buggy, there appear to be some tests that don't complete. Please take on this task if you have time 🙂 diff --git a/e2e/downloads/README.md b/e2e/downloads/README.md new file mode 100644 index 000000000..238ce806c --- /dev/null +++ b/e2e/downloads/README.md @@ -0,0 +1,3 @@ +# downloads + +Location for temporary downloads. diff --git a/e2e/package.json b/e2e/package.json new file mode 100644 index 000000000..9f485f994 --- /dev/null +++ b/e2e/package.json @@ -0,0 +1,61 @@ +{ + "name": "@tldraw/e2e", + "version": "2.0.0-alpha.8", + "private": true, + "packageManager": "yarn@3.5.0", + "author": { + "name": "tldraw GB Ltd.", + "email": "hello@tldraw.com" + }, + "homepage": "https://tldraw.dev", + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/tldraw/tldraw" + }, + "bugs": { + "url": "https://github.com/tldraw/tldraw/issues" + }, + "keywords": [ + "tldraw", + "drawing", + "app", + "development", + "whiteboard", + "canvas", + "infinite" + ], + "scripts": { + "test:local": "TS_NODE_PROJECT=./tsconfig.json wdio run wdio.local.conf.js", + "test:remote": "TS_NODE_PROJECT=./tsconfig.json wdio run wdio.remote.conf.js", + "test:nightly": "TS_NODE_PROJECT=./tsconfig.json wdio run wdio.nightly.conf.js", + "lint": "yarn run -T tsx ../scripts/lint.ts" + }, + "devDependencies": { + "@sitespeed.io/edgedriver": "^112.0.1722-34", + "@tldraw/editor": "workspace:*", + "@tldraw/primitives": "workspace:*", + "@types/mocha": "^10.0.1", + "@types/sharp": "^0.31.1", + "@wdio/browserstack-service": "^8.1.3", + "@wdio/cli": "^8.1.3", + "@wdio/globals": "^8.1.3", + "@wdio/local-runner": "^8.1.2", + "@wdio/mocha-framework": "^8.1.2", + "@wdio/spec-reporter": "^8.1.2", + "chromedriver": "^112.0.0", + "geckodriver": "^3.2.0", + "lazyrepo": "0.0.0-alpha.26", + "msedgedriver": "^91.0.0", + "pixelmatch": "^5.3.0", + "pngjs": "^6.0.0", + "sharp": "^0.31.2", + "ts-node-dev": "^2.0.0", + "ua-parser-js": "^1.0.33", + "wdio-chromedriver-service": "^8.0.1", + "wdio-edgedriver-service": "^2.1.2", + "wdio-geckodriver-service": "^4.1.1", + "wdio-safaridriver-service": "^2.1.0", + "wdio-vscode-service": "^5.0.0" + } +} diff --git a/e2e/screenshots/README.md b/e2e/screenshots/README.md new file mode 100644 index 000000000..6b169ef01 --- /dev/null +++ b/e2e/screenshots/README.md @@ -0,0 +1,3 @@ +# screenshots + +Location for temporary screenshots. diff --git a/e2e/test-overview-ui.png b/e2e/test-overview-ui.png new file mode 100644 index 0000000000000000000000000000000000000000..36ae34935e9069dc9fd020c4c63dddceb65b5289 GIT binary patch literal 215187 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 z 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�+hle9-o)q1Pz0)U80*z~?}oie@d$>+nt~#gDAubZRrq`#>?VX8s(QpbDXGk08}5W=_fe+^9nc8;;$eT$tBj;(Mp9t@~|8)oln@EFtV^=Cx!rADkFc z++A8&RT}pS3|m%Ng^_>g`Ssy*_SXlBpGy{@%*2uHUnHi^GTtKD?_;nL#wBh`ACVVt z*}n$1)k5ibp*Jry_e1dW>RHAP=Izl47kS0D-JuR z{EmtG>Q;~7i*s0&@nmCY0P^(2~Kn4oKbKHS}Mju#-$ZVzVGH?AGjD{wl zI}NLiTR5qF7X?R)MQD@WpKZy9lSN>;Eu-tkuPyXbeh&nZK$R9q021m9&pW@+t{{5ab8LYncV&}Fa~81lMBXq_3p5LP8v zV4pojG*fI=qQo!*HlU*c+}71!Ox1CSu5~6cS*o97%xCF)0heN0a~cc0X5-I)@!}m^ zD+;;G7REvo`7F>+xK;-@MaGm}+FSd@RE?el^WRO2IPwXk561s@tZ{}g!-sBch@F~W z=0?>g4gQBp9_H=LKtE9x^#EjXjbf@!<*(1p0JwMpnp=0_+F%C#vZ0VTpo%XRm>R(jKYcG|6?^_2rAu;vr%GnPvv{*!Je6?76lD(Wa}^(`Nid)Xbe^s?hX}0 z&Ghi6HHp+efD4)ZR|tR@BdYX#cJ;|>y()Z<1_$uysV=)FA6soYK9UD zRa39^)MEP0nI62+=$Nydf6h4A5AXG9szcfM3FA3kVx@OOIOUsK_Spxz;6BY zgOYQij>BQK4QO}g5(~ZR44wY$CBA1?;R#V2pitH}0m(Cqcxu$tMY4`%$EsE5L`_ void + } +} + +export async function isIn(path: string) { + return await browser.execute((path) => window.app.isIn(path), path) +} + +export async function propsForNextShape() { + return await browser.execute(() => window.app.instanceState.propsForNextShape) +} + +export function getAllShapes() { + return browser.execute(() => window.app.shapesArray) +} +export async function getShapesOfType(...types: TLShape['type'][]) { + return await browser.execute((types) => { + return window.app.store + .allRecords() + .filter((s) => s.typeName === 'shape' && types.includes(s.type)) + }, types) +} + +export async function selectionBounds(): Promise { + return await browser.execute(() => { + return window.app.selectionBounds + }) +} + +export async function getCamera() { + return await browser.execute(() => { + const { x, y, z } = window.app.camera + return { x, y, z } + }) +} +export async function hardReset() { + await browser.execute(() => { + window.webdriverReset() + }) +} diff --git a/e2e/test/helpers/ui/app.ts b/e2e/test/helpers/ui/app.ts new file mode 100644 index 000000000..ca93356d3 --- /dev/null +++ b/e2e/test/helpers/ui/app.ts @@ -0,0 +1,112 @@ +import { ui, util } from '..' +import * as runtime from '../runtime' + +export function wd(key: string) { + return `*[data-wd="${key}"]` +} + +export async function pointWithinActiveArea(x: number, y: number) { + const offsetX = 0 + const offsetY = 52 + return { x: x + offsetX, y: y + offsetY } +} + +export async function waitForReady() { + await browser.waitUntil(() => { + return browser.execute(() => { + return window.tldrawReady + }) + }) + + // Make sure the window is focused... maybe + await ui.canvas.click(100, 100) + + // Note: We need to not trigger the double click handler here so we need to wait a little bit. + await util.sleep(300) +} + +export async function hardReset() { + await runtime.hardReset() + await waitForReady() +} + +export async function open() { + await browser.url(global.webdriverTestUrl ?? `https://localhost:5420/`) + /** + * HACK: vscode doesn't support `browser.setWindowSize` so we use the + * default size. + * + * This will break things currently if run on a small screen. + */ + if (global.tldrawOptions.windowSize !== 'default') { + const windowSize = global.tldrawOptions.windowSize ?? [1200, 1200] + await browser.setWindowSize(windowSize[0], windowSize[1]) + } + + await waitForReady() + global.isWindowOpen = true +} + +export async function shapesAsImgData() { + return await browser.execute(async () => { + return await window.app + .getSvg([...window.app.shapeIds], { padding: 0, background: true }) + .then(async (svg) => { + const svgStr = new XMLSerializer().serializeToString(svg) + const svgImage = document.createElement('img') + document.body.appendChild(svgImage) + svgImage.src = URL.createObjectURL( + new Blob([svgStr], { + type: 'image/svg+xml', + }) + ) + + const dpr = window.devicePixelRatio + + return await new Promise<{ + width: number + height: number + dpr: number + data: string + }>((resolve) => { + svgImage.onload = () => { + const width = parseInt(svg.getAttribute('width')) + const height = parseInt(svg.getAttribute('height')) + + const canvas = document.createElement('canvas') + canvas.width = width * dpr + canvas.height = height * dpr + const canvasCtx = canvas.getContext('2d') + canvasCtx.drawImage(svgImage, 0, 0, width * dpr, height * dpr) + const imgData = canvas.toDataURL('image/png') + resolve({ + dpr: dpr, + width: width * dpr, + height: height * dpr, + data: imgData, + }) + } + }) + }) + }) +} + +global.isWindowOpen = false + +export async function setup() { + if (!global.isWindowOpen) { + await open() + } else { + await hardReset() + } +} + +export async function getElementByWd(...selectors: string[]) { + for (const possibleSelector of selectors) { + const element = wd(possibleSelector) + const isDisplayed = await $(element).isDisplayed() + if (isDisplayed) { + return $(element) + } + } +} diff --git a/e2e/test/helpers/ui/canvas.ts b/e2e/test/helpers/ui/canvas.ts new file mode 100644 index 000000000..bf48872d7 --- /dev/null +++ b/e2e/test/helpers/ui/canvas.ts @@ -0,0 +1,98 @@ +import { MOVE_DEFAULTS } from '../constants' +import { getElementByWd, pointWithinActiveArea, wd } from './app' + +export async function brush(x1: number, y1: number, x2: number, y2: number) { + const start = await pointWithinActiveArea(x1, y1) + const end = await pointWithinActiveArea(x2, y2) + + await browser + .action('pointer') + .move({ ...start, ...MOVE_DEFAULTS }) + .down() + .move(end) + .up() + .perform() +} + +export async function draw(points: { x: number; y: number }[]) { + const mappedPoints = [] + for (const point of points) { + mappedPoints.push(await pointWithinActiveArea(point.x, point.y)) + } + + let chain = browser.action('pointer') + for (const [index, mappedPoint] of mappedPoints.entries()) { + if (index === 0) { + chain = chain.move({ ...mappedPoint, ...MOVE_DEFAULTS }).down() + } else { + chain = chain.move({ ...mappedPoint, ...MOVE_DEFAULTS }) + } + } + await chain.perform() +} + +export async function click(x1: number, y1: number) { + const start = await pointWithinActiveArea(x1, y1) + await browser + .action('pointer') + .move({ ...start, ...MOVE_DEFAULTS }) + .down() + .up() + .perform() +} + +export async function doubleClick(x1: number, y1: number) { + const start = await pointWithinActiveArea(x1, y1) + await browser + .action('pointer') + .move({ ...start, ...MOVE_DEFAULTS }) + .down() + .up() + .down() + .up() + .perform() +} + +export async function dragBy(target: WebdriverIO.Element, dx: number, dy: number) { + const loc = await target.getLocation() + const size = await target.getSize() + const locX = Math.floor(loc.x) + Math.floor(size.width / 2) + const locY = Math.floor(loc.y) + Math.floor(size.height / 2) + + const startX = locX + const startY = locY + const endX = locX + dx + const endY = locY + dy + + await browser.actions([ + browser + .action('pointer') + .move({ x: startX, y: startY, ...MOVE_DEFAULTS }) + .down('left') + .move({ x: endX, y: endY, ...MOVE_DEFAULTS }) + .up(), + ]) +} + +export async function contextMenu(x: number, y: number, path: string[] = []) { + await browser + .action('pointer') + .move({ x, y, ...MOVE_DEFAULTS }) + .down('right') + .up() + .perform() + // await $(wd('active-area')).click({button: 2, x, y}) + + for await (const item of path) { + await $(wd(`menu-item.${item}`)).waitForExist() + await $(wd(`menu-item.${item}`)).click() + } +} + +export async function clickTextInput() { + await (await $(wd(`canvas`) + ' textarea')).click() +} + +export async function selectionHandle(...possibleSelectors: string[]) { + return getElementByWd(...possibleSelectors.map((s) => `selection.${s}`)) +} diff --git a/e2e/test/helpers/ui/help.ts b/e2e/test/helpers/ui/help.ts new file mode 100644 index 000000000..8b9f14c3e --- /dev/null +++ b/e2e/test/helpers/ui/help.ts @@ -0,0 +1,3 @@ +export async function menu(_path: string[] = []) { + // TODO... +} diff --git a/e2e/test/helpers/ui/index.ts b/e2e/test/helpers/ui/index.ts new file mode 100644 index 000000000..30504e3ea --- /dev/null +++ b/e2e/test/helpers/ui/index.ts @@ -0,0 +1,8 @@ +export * as app from './app' +export * as canvas from './canvas' +export * as help from './help' +export * as main from './main' +export * as minimap from './minimap' +export * as props from './props' +export * as share from './share' +export * as tools from './tools' diff --git a/e2e/test/helpers/ui/main.ts b/e2e/test/helpers/ui/main.ts new file mode 100644 index 000000000..8f6ff9346 --- /dev/null +++ b/e2e/test/helpers/ui/main.ts @@ -0,0 +1,21 @@ +import { wd } from './app' + +export async function menu(path = []) { + await $(wd('main.menu')).click() + + for await (const item of path) { + await $(wd(`menu-item.${item}`)).click() + } +} + +export async function actionMenu(path = []) { + await $(wd('main.action-menu')).click() + + for await (const item of path) { + await $(wd(`menu-item.${item}`)).click() + } +} + +export async function click(key: string) { + await $(wd(`main.${key}`)).click() +} diff --git a/e2e/test/helpers/ui/minimap.ts b/e2e/test/helpers/ui/minimap.ts new file mode 100644 index 000000000..4544c9ce8 --- /dev/null +++ b/e2e/test/helpers/ui/minimap.ts @@ -0,0 +1,45 @@ +import { wd } from './app' + +export function $element() { + return $(wd('minimap')) +} + +export async function zoomIn() { + const button = wd(`minimap.zoom-in`) + const toggle = wd(`minimap.toggle`) + + if (await $(button).isExisting()) { + await $(button).click() + } else if (await $(wd(`minimap.toggle`)).isExisting()) { + await $(toggle).click() + await $(button).click() + } + + return this +} + +export async function zoomOut() { + const button = wd(`minimap.zoom-out`) + const toggle = wd(`minimap.toggle`) + + if (await $(button).isExisting()) { + await $(button).click() + } else if (await $(wd(`minimap.toggle`)).isExisting()) { + await $(toggle).click() + await $(button).click() + } + + return this +} + +export async function menuButton() { + return await $(wd('minimap.zoom-menu')) +} + +export async function menu(path: string[] = []) { + await $(wd('minimap.zoom-menu')).click() + + for await (const item of path) { + await $(wd(`minimap.zoom-menu.${item}`)).click() + } +} diff --git a/e2e/test/helpers/ui/props.ts b/e2e/test/helpers/ui/props.ts new file mode 100644 index 000000000..ee818ba33 --- /dev/null +++ b/e2e/test/helpers/ui/props.ts @@ -0,0 +1,54 @@ +import { wd } from './app' + +export async function ifMobileOpenStylesMenu() { + if (globalThis.tldrawOpts.ui === 'mobile') { + await $(wd(`mobile.styles`)).click() + } +} + +export async function selectColor(color: string) { + await $(wd(`style.color.${color}`)).click() +} + +export async function selectFill(fill: string) { + await $(wd(`style.fill.${fill}`)).click() +} + +export async function selectStroke(stroke: string) { + await $(wd(`style.dash.${stroke}`)).click() +} + +export async function selectSize(size: string) { + await $(wd(`style.size.${size}`)).click() +} + +export async function selectSpline(type: string) { + await $(wd(`style.spline`)).click() + await $(wd(`style.spline.${type}`)).click() +} + +export async function selectOpacity(_opacity: number) { + // TODO... +} + +export async function selectShape() { + // TODO... +} + +export async function selectFont(font: string) { + await $(wd(`font.${font}`)).click() +} + +export async function selectAlign(alignment: string) { + await $(wd(`align.${alignment}`)).click() +} + +export async function selectArrowheadStart(type: string) { + await $(wd(`style.arrowheads.start`)).click() + await $(wd(`style.arrowheads.start.${type}`)).click() +} + +export async function selectArrowheadEnd(type: string) { + await $(wd(`style.arrowheads.end`)).click() + await $(wd(`style.arrowheads.end.${type}`)).click() +} diff --git a/e2e/test/helpers/ui/share.ts b/e2e/test/helpers/ui/share.ts new file mode 100644 index 000000000..f84f9d32e --- /dev/null +++ b/e2e/test/helpers/ui/share.ts @@ -0,0 +1 @@ +export async function menu(_path: string[] = []) {} diff --git a/e2e/test/helpers/ui/tools.ts b/e2e/test/helpers/ui/tools.ts new file mode 100644 index 000000000..d440a8ffd --- /dev/null +++ b/e2e/test/helpers/ui/tools.ts @@ -0,0 +1,21 @@ +import { wd } from './app' + +export function $element() { + return $(wd('tools')) +} + +export async function click(toolName: string) { + // Check if `tools.mobile-more` exists + // Check ifisExisting() + const toolSelector = wd(`tools.${toolName}`) + const moreSelector = wd(`tools.more`) + + if (await $(toolSelector).isExisting()) { + await $(toolSelector).click() + } else if (await $(moreSelector).isExisting()) { + await $(moreSelector).click() + await $(toolSelector).click() + } + + return this +} diff --git a/e2e/test/helpers/util.ts b/e2e/test/helpers/util.ts new file mode 100644 index 000000000..30f3c156c --- /dev/null +++ b/e2e/test/helpers/util.ts @@ -0,0 +1,139 @@ +import fs from 'fs' +import { ui } from '.' + +export async function textToClipboard(text: string) { + const html = `

${text}

` + const url = `data:text/html;base64,${btoa(html)}` + + await browser.newWindow('', { + windowName: 'copy_target', + // windowFeatures: 'width=420,height=230,resizable,scrollbars=yes,status=1', + }) + await browser.url(url) + await $('#copy_target').waitForExist() + + await $('#copy_target').click() + + // For some reason the import isn't working... + // From + const cmd = 'WDIO_CONTROL' + + // Select all + await browser.action('key').down(cmd).down('a').up(cmd).up('a').perform() + + await browser.execute(() => new Promise((resolve) => setTimeout(resolve, 3000))) + + // Copy + await browser.action('key').down(cmd).down('c').up(cmd).up('c').perform() + + await browser.closeWindow() + + const handles = await browser.getWindowHandles() + await browser.switchToWindow(handles[0]) +} + +const LOCAL_DOWNLOAD_DIR = process.env.DOWNLOADS_DIR + ? process.env.DOWNLOADS_DIR + '/' + : __dirname + '/../../downloads/' +export async function getDownloadFile(fileName: string) { + if (global.webdriverService === 'browserstack') { + // In browserstack we must grab it from the service + // + // Note this only works on desktop devices. + const base64String = await browser.executeScript( + `browserstack_executor: {"action": "getFileContent", "arguments": {"fileName": "${fileName}"}}`, + [] + ) + const buffer = Buffer.from(base64String, 'base64') + return buffer + } else { + // Locally we can grab the file from the `LOCAL_DOWNLOAD_DIR` + return await fs.promises.readFile(LOCAL_DOWNLOAD_DIR + fileName) + } +} + +export async function imageToClipboard(_buffer: Buffer) { + // TODO... +} + +export async function htmlToClipboard(_html: string) { + // TODO... +} + +export async function nativeCopy() { + // CMD+C + await browser.action('key').down('WDIO_CONTROL').down('c').up('WDIO_CONTROL').up('c').perform() +} + +export async function nativePaste() { + // CMD+V + await browser.action('key').down('WDIO_CONTROL').down('v').up('WDIO_CONTROL').up('v').perform() +} + +export async function deleteAllShapesOnPage() { + await ui.main.menu(['edit', 'select-all']) + await ui.main.menu(['edit', 'delete']) +} + +export async function sleep(ms: number) { + await browser.execute((ms) => new Promise((resolve) => setTimeout(resolve, ms)), ms) +} + +export async function clearClipboard() { + return await browser.execute(async () => { + if (navigator.clipboard.write) { + await navigator.clipboard.write([ + new ClipboardItem({ + 'text/plain': new Blob(['CLEAR'], { type: 'text/plain' }), + }), + ]) + } else { + await navigator.clipboard.writeText('') + } + }) +} + +export async function grantPermissions(permissions: 'clipboard-read'[]) { + for (const permission of permissions) { + await browser.setPermissions( + { + name: permission, + }, + 'granted' + ) + } +} + +// Checks that the clipboard is no-longer "clear" see 'clearClipboard' above +export async function waitForClipboardContents() { + return await browser.waitUntil(async () => { + return await browser.execute(async () => { + const results = await navigator.clipboard.read() + if (results.length < 0) { + return true + } + return ( + !results[0].types.includes('text/plain') || + (await (await results[0].getType('text/plain')).text()) !== 'CLEAR' + ) + }) + }) +} + +export async function clipboardContents() { + return await browser.execute(async () => { + const results = await navigator.clipboard.read() + const contents = [] + + for (const result of results) { + const item = {} + for (const type of result.types) { + item[type] = type.match(/^text/) + ? await (await result.getType(type)).text() + : await (await result.getType(type)).arrayBuffer() + } + contents.push(item) + } + return contents + }) +} diff --git a/e2e/test/helpers/webdriver.ts b/e2e/test/helpers/webdriver.ts new file mode 100644 index 000000000..93dae29b7 --- /dev/null +++ b/e2e/test/helpers/webdriver.ts @@ -0,0 +1,95 @@ +import { Box2d } from '@tldraw/primitives' +import fs from 'fs' +import pixelmatch from 'pixelmatch' +import pngjs from 'pngjs' +import sharp from 'sharp' +import { app } from './ui' + +const PNG = pngjs.PNG + +type takeRegionScreenshotOpts = { + writeTo: { + path: string + prefix: string + } +} +type takeRegionScreenshotResult = { + browserBuffer: Buffer + exportBuffer: Buffer + fullWidth: any + fullHeight: any +} +export async function takeRegionScreenshot( + bbox: Box2d, + opts?: takeRegionScreenshotOpts +): Promise { + const { data, dpr } = await app.shapesAsImgData() + + const base64 = await browser.takeScreenshot() + const bufferInput = Buffer.from(data.replace('data:image/png;base64,', ''), 'base64') + + const { data: exportBuffer, info: origInfo } = await sharp(bufferInput) + .png() + .toBuffer({ resolveWithObject: true }) + + const fullWidth = Math.floor(origInfo.width) + const fullHeight = Math.floor(origInfo.height) + + const binary = Buffer.from(base64, 'base64') + + const browserBuffer = await sharp(binary) + .extract({ + left: Math.floor(bbox.x * dpr), + top: Math.floor(bbox.y * dpr), + width: Math.floor(bbox.w * dpr), + height: Math.floor(bbox.h * dpr), + }) + .resize({ width: fullWidth, height: fullHeight }) + .png() + .toBuffer() + + if (opts.writeTo) { + const { path: writeToPath, prefix: writeToPrefix } = opts.writeTo + await fs.promises.writeFile( + `${__dirname}/../../screenshots/${writeToPrefix}-app.png`, + exportBuffer + ) + await fs.promises.writeFile(`${writeToPath}/${writeToPrefix}-svg.png`, exportBuffer) + } + + return { + browserBuffer, + exportBuffer, + fullWidth, + fullHeight, + } +} + +type diffScreenshotOpts = { + writeTo: { + path: string + prefix: string + } +} +export async function diffScreenshot( + screenshotRegion: takeRegionScreenshotResult, + opts?: diffScreenshotOpts +) { + const { exportBuffer, browserBuffer, fullWidth, fullHeight } = screenshotRegion + const diff = new PNG({ width: fullWidth, height: fullHeight }) + const img1 = PNG.sync.read(browserBuffer) + const img2 = PNG.sync.read(exportBuffer) + const pxielDiff = pixelmatch(img1.data, img2.data, diff.data, fullWidth, fullHeight, { + threshold: 0.6, + }) + + if (opts.writeTo) { + const { path: writeToPath, prefix: writeToPrefix } = opts.writeTo + + await fs.promises.writeFile(`${writeToPath}/${writeToPrefix}-diff.png`, PNG.sync.write(diff)) + } + + return { + pxielDiff, + } +} diff --git a/e2e/test/mocha-ext.ts b/e2e/test/mocha-ext.ts new file mode 100644 index 000000000..68d9317f4 --- /dev/null +++ b/e2e/test/mocha-ext.ts @@ -0,0 +1,118 @@ +const mochaIt = global.it +const describe = global.describe + +const DEFAULT_ENV_HANDLER = () => ({ + shouldIgnore: false, + skipMessage: '', +}) + +type EnvOpts = { + device?: 'mobile' | 'desktop' + skipBrowsers?: ('firefox' | 'safari' | 'edge' | 'samsung' | 'chrome' | 'vscode')[] + input?: ('mouse' | 'touch')[] + os?: 'windows' | 'macos' | 'linux' | 'android' | 'ios' | 'ipados' + ui?: 'mobile' | 'desktop' + ignoreWhen?: () => boolean +} + +// Gets set by env(...) and read in it(...) +let envMethod = DEFAULT_ENV_HANDLER + +/** This is a mocha extension to allow us to run tests only for specific environments */ +const env = (opts: EnvOpts, handler: () => void) => { + envMethod = () => { + // @ts-ignore + const { tldrawOptions } = global + let skipMessage = '(ignored)' + let shouldIgnore = false + + if (opts.device && tldrawOptions.device !== opts.device) { + shouldIgnore = true + skipMessage = `(ignored only: ${opts.device})` + } + if (opts.skipBrowsers && opts.skipBrowsers.includes(tldrawOptions.browser)) { + shouldIgnore = true + skipMessage = `(ignored browser)` + } + if (opts.input && !tldrawOptions.input?.find((item) => opts.input.includes(item))) { + shouldIgnore = true + skipMessage = `(ignored only: ${opts.input.join(', ')})` + } + if (opts.ui && tldrawOptions.ui !== opts.ui) { + shouldIgnore = true + skipMessage = `(ignored only: ${opts.ui})` + } + if (opts.os && tldrawOptions.os !== opts.os) { + shouldIgnore = true + skipMessage = `(ignored only: ${opts.os})` + } + if (opts.ignoreWhen && opts.ignoreWhen()) { + shouldIgnore = true + } + + return { skipMessage, shouldIgnore } + } + + handler() + envMethod = DEFAULT_ENV_HANDLER +} + +/** Same usage as the mocha it(...) method */ +const it = (msg: string, handler: () => void) => { + const { shouldIgnore, skipMessage } = envMethod() + if (shouldIgnore) { + mochaIt(msg + ' ' + skipMessage, () => {}) + } else { + mochaIt(msg, handler) + } +} + +/** Same usage as the mocha it.only(...) method */ +// eslint-disable-next-line no-only-tests/no-only-tests +it.only = (msg: string, handler: () => void) => { + const { shouldIgnore, skipMessage } = envMethod() + if (shouldIgnore) { + mochaIt.only(msg + ' ' + skipMessage, () => {}) + } else { + mochaIt.only(msg, handler) + } +} + +/** Same usage as the mocha it.skip(...) method */ +it.skip = (msg: string, handler: () => void) => { + const { shouldIgnore, skipMessage } = envMethod() + if (shouldIgnore) { + mochaIt.skip(msg + ' ' + skipMessage, () => {}) + } else { + mochaIt.skip(msg, handler) + } +} + +/** Same usage as the mocha it.skip(...) method */ +it.todo = (msg: string, _handler?: () => void) => { + mochaIt.skip('[TODO] ' + msg, () => {}) +} + +it.ok = it + +it.fails = (msg: string, handler: () => void | Promise) => { + const { shouldIgnore, skipMessage } = envMethod() + if (shouldIgnore) { + mochaIt('[FAILS] ' + msg + ' ' + skipMessage, () => {}) + } else { + mochaIt('[FAILS] ' + msg, async () => { + let failed = false + try { + await handler() + } catch (err: any) { + failed = true + } + + if (!failed) { + throw new Error('This expected to fail, did you fix it?') + } + }) + } +} + +export { env, describe, it } diff --git a/e2e/test/specs/arrange.ts b/e2e/test/specs/arrange.ts new file mode 100644 index 000000000..3ec1bf569 --- /dev/null +++ b/e2e/test/specs/arrange.ts @@ -0,0 +1,391 @@ +import { runtime, ui } from '../helpers' +import { describe, it } from '../mocha-ext' + +const createShapes = async (pos0 = [20, 20], pos1 = [70, 70], pos2 = [120, 120]) => { + const size = 30 + + await ui.tools.click('rectangle') + await ui.canvas.brush(pos0[0], pos0[1], pos0[0] + size, pos0[1] + size) + + await ui.tools.click('rectangle') + await ui.canvas.brush(pos1[0], pos1[1], pos1[0] + size, pos1[1] + size) + + await ui.tools.click('rectangle') + await ui.canvas.brush(pos2[0], pos2[1], pos2[0] + size, pos2[1] + size) + + return await runtime.getAllShapes() +} + +const MODES = [/*'context', */ 'action'] + +describe('arrange', () => { + describe('align-left', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(11, 11) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'align-left']) + } else if (mode === 'action') { + await ui.main.actionMenu(['align-left']) + } + + const shapes = await runtime.getAllShapes() + expect(origShapes[0].x).toBe(shapes[0].x) + expect(origShapes[0].x).toBe(shapes[1].x) + expect(origShapes[0].x).toBe(shapes[2].x) + }) + } + }) + + describe('align-center-horizontal', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'align-center-horizontal']) + } else if (mode === 'action') { + await ui.main.actionMenu(['align-center-horizontal']) + } + const shapes = await runtime.getAllShapes() + expect(origShapes[1].x).toBe(shapes[0].x) + expect(origShapes[1].x).toBe(shapes[1].x) + expect(origShapes[1].x).toBe(shapes[2].x) + }) + } + }) + + describe('align-right', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'align-right']) + } else if (mode === 'action') { + await ui.main.actionMenu(['align-right']) + } + + const shapes = await runtime.getAllShapes() + expect(origShapes[2].x).toBe(shapes[0].x) + expect(origShapes[2].x).toBe(shapes[1].x) + expect(origShapes[2].x).toBe(shapes[2].x) + }) + } + }) + + describe('align-top', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'align-top']) + } else if (mode === 'action') { + await ui.main.actionMenu(['align-top']) + } + + const shapes = await runtime.getAllShapes() + expect(origShapes[0].y).toBe(shapes[0].y) + expect(origShapes[0].y).toBe(shapes[1].y) + expect(origShapes[0].y).toBe(shapes[2].y) + }) + } + }) + + describe('align-center-vertical', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'align-center-vertical']) + } else if (mode === 'action') { + await ui.main.actionMenu(['align-center-vertical']) + } + + const shapes = await runtime.getAllShapes() + expect(origShapes[1].y).toBe(shapes[0].y) + expect(origShapes[1].y).toBe(shapes[1].y) + expect(origShapes[1].y).toBe(shapes[2].y) + }) + } + }) + + describe('align-bottom', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + await ui.app.pointWithinActiveArea(11, 11) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(11, 11) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'align-bottom']) + } else if (mode === 'action') { + await ui.main.actionMenu(['align-bottom']) + } + + const shapes = await runtime.getAllShapes() + expect(origShapes[2].y).toBe(shapes[0].y) + expect(origShapes[2].y).toBe(shapes[1].y) + expect(origShapes[2].y).toBe(shapes[2].y) + }) + } + }) + + describe('distribute-horizontal', () => { + for (const mode of MODES) { + const createHorzShapes = async () => { + await ui.tools.click('rectangle') + await ui.canvas.brush(20, 20, 50, 50) + + await ui.tools.click('rectangle') + await ui.canvas.brush(90, 70, 120, 100) + + await ui.tools.click('rectangle') + await ui.canvas.brush(120, 120, 150, 150) + + return await runtime.getAllShapes() + } + + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createHorzShapes() + + await ui.canvas.brush(10, 10, 170, 170) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'distribute-horizontal']) + } else if (mode === 'action') { + await ui.main.actionMenu(['distribute-horizontal']) + } + + const shapes = await runtime.getAllShapes() + expect(origShapes[0].x).toBe(shapes[0].x) + expect(origShapes[0].x + 50).toBe(shapes[1].x) + expect(origShapes[0].x + 100).toBe(shapes[2].x) + }) + } + }) + + describe('distribute-vertical', () => { + for (const mode of MODES) { + const createVertShapes = async () => { + await ui.tools.click('rectangle') + await ui.canvas.brush(20, 20, 50, 50) + + await ui.tools.click('rectangle') + await ui.canvas.brush(70, 90, 100, 120) + + await ui.tools.click('rectangle') + await ui.canvas.brush(120, 120, 150, 150) + + return await runtime.getAllShapes() + } + + it(`${mode} menu`, async () => { + await ui.app.setup() + const origShapes = await createVertShapes() + + await ui.canvas.brush(10, 10, 170, 170) + + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'distribute-vertical']) + } else if (mode === 'action') { + await ui.main.actionMenu(['distribute-vertical']) + } + + const shapes = await runtime.getAllShapes() + expect(origShapes[0].x).toBe(shapes[0].x) + expect(origShapes[0].x + 50).toBe(shapes[1].x) + expect(origShapes[0].x + 100).toBe(shapes[2].x) + }) + } + }) + + describe('stretch-horizontal', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'stretch-horizontal']) + } else if (mode === 'action') { + await ui.main.actionMenu(['stretch-horizontal']) + } + + const shapes = await runtime.getAllShapes() + expect(shapes[0].props).toHaveProperty('w', 130) + expect(shapes[1].props).toHaveProperty('w', 130) + expect(shapes[2].props).toHaveProperty('w', 130) + }) + } + }) + + describe('stretch-vertical', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + await createShapes() + + await ui.canvas.brush(10, 10, 170, 170) + if (mode === 'context') { + const point = await ui.app.pointWithinActiveArea(21, 21) + await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'stretch-vertical']) + } else if (mode === 'action') { + await ui.main.actionMenu(['stretch-vertical']) + } + + const shapes = await runtime.getAllShapes() + expect(shapes[0].props).toHaveProperty('h', 130) + expect(shapes[1].props).toHaveProperty('h', 130) + expect(shapes[2].props).toHaveProperty('h', 130) + }) + } + }) + + // describe('flip-horizontal', () => { + // for (const mode of ['context']) { + // it(`${mode} menu`, async () => { + // await ui.app.setup() + // const origShapes = await createShapes() + + // // TODO: Move back to front + // await ui.canvas.brush(0, 0, 150, 150) + // const point = await ui.app.pointWithinActiveArea(11, 11) + // if (mode === 'context') { + // const point = await ui.app.pointWithinActiveArea(11, 11) + // await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'flip-horizontal']) + // } else if (mode === 'action') { + // await ui.main.actionMenu(['flip-horizontal']) + // } + + // const shapes = await runtime.getAllShapes() + // expect(shapes[0].x).toBe(origShapes[2].x) + // expect(shapes[1].x).toBe(origShapes[1].x) + // expect(shapes[2].x).toBe(origShapes[0].x) + // }) + // } + // }) + + // describe('flip-vertical', () => { + // for (const mode of ['context']) { + // it(`${mode} menu`, async () => { + // await ui.app.setup() + // const origShapes = await createShapes() + + // // TODO: Move back to front + // await ui.canvas.brush(0, 0, 150, 150) + // const point = await ui.app.pointWithinActiveArea(11, 11) + // if (mode === 'context') { + // const point = await ui.app.pointWithinActiveArea(11, 11) + // await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'flip-vertical']) + // } else if (mode === 'action') { + // await ui.main.actionMenu(['flip-vertical']) + // } + + // const shapes = await runtime.getAllShapes() + // expect(shapes[0].y).toBe(origShapes[2].y) + // expect(shapes[1].y).toBe(origShapes[1].y) + // expect(shapes[2].y).toBe(origShapes[0].y) + // }) + // } + // }) + + // describe('pack', () => { + // for (const mode of ['context']) { + // it(`${mode} menu`, async () => { + // await ui.app.setup() + // const origShapes = await createShapes() + + // // TODO: Move back to front + // await ui.canvas.brush(0, 0, 150, 150) + // const point = await ui.app.pointWithinActiveArea(11, 11) + // if (mode === 'context') { + // const point = await ui.app.pointWithinActiveArea(11, 11) + // await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'pack']) + // } else if (mode === 'action') { + // await ui.main.actionMenu(['pack']) + // } + + // const shapes = await runtime.getAllShapes() + // expect(shapes.length).toBe(3) + // expect(shapes[0].x).toBe(339) + // expect(shapes[0].y).toBe(122) + // expect(shapes[1].x).toBe(385) + // expect(shapes[1].y).toBe(122) + // expect(shapes[2].x).toBe(431) + // expect(shapes[2].y).toBe(122) + // }) + // } + // }) + + // describe('stack-vertical', () => { + // for (const mode of MODES) { + // it(`${mode} menu`, async () => { + // await ui.app.setup() + // const origShapes = await createShapes([10, 10], [60, 16], [110, 24]) + + // await ui.canvas.brush(0, 0, 150, 150) + // if (mode === 'context') { + // const point = await ui.app.pointWithinActiveArea(11, 11) + // await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'stack-vertical']) + // } else if (mode === 'action') { + // await ui.main.actionMenu(['stack-vertical']) + // } + + // const shapes = await runtime.getAllShapes() + // console.log(shapes) + // expect(shapes[0].y).toBe(72) + // expect(shapes[1].y).toBe(102) + // expect(shapes[2].y).toBe(132) + // }) + // } + // }) + + // describe('stack-horizontal', () => { + // for (const mode of MODES) { + // it(`${mode} menu`, async () => { + // await ui.app.setup() + // const origShapes = await createShapes([10, 10], [16, 60], [24, 110]) + + // await ui.canvas.brush(0, 0, 150, 150) + // if (mode === 'context') { + // const point = await ui.app.pointWithinActiveArea(11, 11) + // await ui.canvas.contextMenu(point.x, point.y, ['arrange', 'stack-horizontal']) + // } else if (mode === 'action') { + // await ui.main.actionMenu(['stack-horizontal']) + // } + + // const shapes = await runtime.getAllShapes() + // console.log(shapes) + // expect(shapes[0].x).toBe(335) + // expect(shapes[1].x).toBe(365) + // expect(shapes[2].x).toBe(395) + // }) + // } + // }) +}) diff --git a/e2e/test/specs/camera.ts b/e2e/test/specs/camera.ts new file mode 100644 index 000000000..a435797b8 --- /dev/null +++ b/e2e/test/specs/camera.ts @@ -0,0 +1,295 @@ +import { MOVE_DEFAULTS, runtime, ui } from '../helpers' +import { describe, env, it } from '../mocha-ext' + +describe('camera', () => { + env( + { + skipBrowsers: ['firefox'], + }, + () => { + describe('panning', () => { + it('hand tool', async () => { + await ui.app.setup() + + const c1 = await runtime.getCamera() + await ui.tools.click('hand') + await browser.actions([ + browser + .action('pointer') + .move({ x: 200, y: 200, ...MOVE_DEFAULTS }) + .down('left') + .move({ x: 300, y: 300, ...MOVE_DEFAULTS }) + .up(), + ]) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c1.z).toBe(c2.z) + }) + + env( + { + input: ['mouse'], + }, + () => { + // Failed in + it('wheel', async () => { + await ui.app.setup() + + const c1 = await runtime.getCamera() + await browser + .action('wheel') + .scroll({ x: 200, y: 200, deltaX: 100, deltaY: 100, duration: 100 }) + .perform() + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c1.z).toBe(c2.z) + }) + + // REMOTE:OK + it('spacebar', async () => { + await ui.app.setup() + + const c1 = await runtime.getCamera() + await browser.actions([ + browser.action('key').down(' '), + browser + .action('pointer') + .move({ x: 200, y: 200, ...MOVE_DEFAULTS }) + .down('left') + .move({ x: 300, y: 300, ...MOVE_DEFAULTS }) + .up(), + ]) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c1.z).toBe(c2.z) + }) + } + ) + }) + + describe('zooming', () => { + env( + { + ui: 'desktop', + input: ['mouse'], + skipBrowsers: ['firefox'], + }, + () => { + it('wheel', async () => { + await ui.app.setup() + const c1 = await runtime.getCamera() + await browser.actions([ + // For some reason the import isn't working... + // From + browser.action('key').down('WDIO_CONTROL'), + browser + .action('wheel') + .scroll({ x: 200, y: 200, deltaX: 100, deltaY: 100, duration: 100 }), + ]) + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c1.z).not.toBe(c2.z) + }) + } + ) + + env( + { + input: ['touch'], + }, + () => { + it('pinch-in', async () => { + await ui.app.setup() + + const c1 = await runtime.getCamera() + await browser.actions([ + browser + .action('pointer', { parameters: { pointerType: 'touch' } }) + .move({ x: 200, y: 200, ...MOVE_DEFAULTS }) + .down('left') + .move({ x: 300, y: 300, ...MOVE_DEFAULTS }) + .up(), + browser + .action('pointer', { parameters: { pointerType: 'touch' } }) + .move({ x: 200, y: 200, ...MOVE_DEFAULTS }) + .down('left') + .move({ x: 100, y: 100, ...MOVE_DEFAULTS }) + .up(), + ]) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c1.z).toBeLessThan(c2.z) + }) + + it('pinch-out', async () => { + await ui.app.setup() + + const c1 = await runtime.getCamera() + await browser.actions([ + browser + .action('pointer', { parameters: { pointerType: 'touch' } }) + .move({ x: 300, y: 300, ...MOVE_DEFAULTS }) + .down('left') + .move({ x: 220, y: 220, ...MOVE_DEFAULTS }) + .up(), + browser + .action('pointer', { parameters: { pointerType: 'touch' } }) + .move({ x: 100, y: 100, ...MOVE_DEFAULTS }) + .down('left') + .move({ x: 180, y: 180, ...MOVE_DEFAULTS }) + .up(), + ]) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c1.z).toBeGreaterThan(c2.z) + }) + } + ) + + env( + { + ui: 'desktop', + }, + () => { + describe('minimap', () => { + describe('buttons', () => { + // REMOTE:OK + it('zoom in', async () => { + await ui.app.setup() + const c1 = await runtime.getCamera() + await ui.minimap.zoomIn() + + await browser.waitUntil(async () => { + const text = await (await ui.minimap.menuButton()).getText() + return text === '200%' + }) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c2.z).toBe(2) + }) + + it('zoom out', async () => { + await ui.app.setup() + const c1 = await runtime.getCamera() + await ui.minimap.zoomOut() + + await browser.waitUntil(async () => { + const text = await (await ui.minimap.menuButton()).getText() + return text === '50%' + }) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c2.z).toBeCloseTo(0.5) + }) + }) + + describe('menu', () => { + // REMOTE:OK + it('zoom in', async () => { + await ui.app.setup() + const c1 = await runtime.getCamera() + await ui.minimap.menu(['zoom-in']) + + await browser.waitUntil(async () => { + const text = await (await ui.minimap.menuButton()).getText() + return text === '200%' + }) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c2.z).toBeCloseTo(2) + }) + + // REMOTE:OK + it('zoom out', async () => { + await ui.app.setup() + const c1 = await runtime.getCamera() + await ui.minimap.menu(['zoom-out']) + + await browser.waitUntil(async () => { + const text = await (await ui.minimap.menuButton()).getText() + return text === '50%' + }) + + const c2 = await runtime.getCamera() + expect(c1).not.toMatchObject(c2 as any) + expect(c2.z).toBeCloseTo(0.5) + }) + + // REMOTE:OK + it('zoom 100%', async () => { + await ui.app.setup() + await browser.execute(() => { + window.app.setCamera(0, 0, 0.5) + }) + const c1 = await runtime.getCamera() + expect(c1.z).toBeCloseTo(0.5) + + await ui.minimap.menu(['zoom-to-100']) + await browser.waitUntil(async () => { + const text = await (await ui.minimap.menuButton()).getText() + return text === '100%' + }) + + const c2 = await runtime.getCamera() + expect(c2.z).toBeCloseTo(1) + }) + + it('zoom to fit', async () => { + await ui.app.setup() + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + + await browser.execute(() => { + window.app.setCamera(0, 0, 0.5) + }) + const c1 = await runtime.getCamera() + expect(c1.z).toBeCloseTo(0.5) + + await ui.minimap.menu(['zoom-to-fit']) + await browser.waitUntil(async () => { + const text = await (await ui.minimap.menuButton()).getText() + return text === '800%' + }) + + const c2 = await runtime.getCamera() + expect(c2.z).toBeCloseTo(8) + }) + + it('zoom to selection', async () => { + await ui.app.setup() + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + + await browser.execute(() => { + window.app.setCamera(0, 0, 0.5) + }) + const c1 = await runtime.getCamera() + expect(c1.z).toBeCloseTo(0.5) + + await ui.minimap.menu(['zoom-to-selection']) + await browser.waitUntil(async () => { + const text = await (await ui.minimap.menuButton()).getText() + return text === '100%' + }) + + const c2 = await runtime.getCamera() + expect(c2.z).toBeCloseTo(1) + }) + }) + }) + } + ) + }) + } + ) +}) diff --git a/e2e/test/specs/constants.ts b/e2e/test/specs/constants.ts new file mode 100644 index 000000000..ce5be7e6c --- /dev/null +++ b/e2e/test/specs/constants.ts @@ -0,0 +1,17 @@ +export const SHAPES = [ + { type: 'geo', tool: 'rectangle' }, + // { type: 'geo', tool: 'ellipse' }, + // { type: 'geo', tool: 'triangle' }, + // { type: 'geo', tool: 'diamond' }, + // { type: 'geo', tool: 'pentagon' }, + // { type: 'geo', tool: 'hexagon' }, + // { type: 'geo', tool: 'octagon' }, + // { type: 'geo', tool: 'star' }, + // { type: 'geo', tool: 'rhombus' }, + // { type: 'geo', tool: 'oval' }, + // { type: 'geo', tool: 'trapezoid' }, + // { type: 'geo', tool: 'arrow-right' }, + // { type: 'geo', tool: 'arrow-left' }, + // { type: 'geo', tool: 'arrow-up' }, + // { type: 'geo', tool: 'arrow-down' }, +] diff --git a/e2e/test/specs/export.ts b/e2e/test/specs/export.ts new file mode 100644 index 000000000..7cdbf8701 --- /dev/null +++ b/e2e/test/specs/export.ts @@ -0,0 +1,172 @@ +import { runtime, ui, util } from '../helpers' +import { describe, env, it } from '../mocha-ext' + +describe('export', () => { + before(async () => { + await ui.app.open() + }) + + const createShape = async () => { + await ui.tools.click('text') + await ui.canvas.brush(70, 200, 250, 200) + await browser.keys('testing') + } + + describe.skip('export-as', () => { + for (const mode of ['main' /*, 'context'*/]) { + describe(`${mode} menu`, () => { + env( + // It turns out we can't grab the file on mobile devices... urgh! + { + device: 'desktop', + skipBrowsers: ['firefox'], + }, + () => { + const fileNameFromShape = async (shape) => { + return await browser.execute((shapeId) => { + return window.app.getShapeById(shapeId)?.id.replace(/:/, '_') + }, shape.id) + } + + it('svg', async () => { + await util.grantPermissions(['clipboard-read']) + await util.clearClipboard() + await ui.app.setup() + await createShape() + + if (mode === 'context') { + await ui.canvas.contextMenu(100, 120, ['export-as', 'export-as-svg']) + } else if (mode === 'main') { + await ui.main.menu(['edit', 'export-as', 'export-as-svg']) + } + + // FIXME: This shouldn't be a timer... but what to do??? + await browser.execute(() => new Promise((resolve) => setTimeout(resolve, 3000))) + const allShapes = await runtime.getAllShapes() + const fileName = await fileNameFromShape(allShapes[0]) + const file = await util.getDownloadFile(fileName + '.svg') + + // TODO: Also check the buffer is correct here... + expect(file).toExist() + }) + + it('png', async () => { + await util.grantPermissions(['clipboard-read']) + await util.clearClipboard() + await ui.app.setup() + await createShape() + + if (mode === 'context') { + await ui.canvas.contextMenu(100, 120, ['export-as', 'export-as-png']) + } else if (mode === 'main') { + await ui.main.menu(['edit', 'export-as', 'export-as-png']) + } + + // FIXME: This shouldn't be a timer... but what to do??? + await browser.execute(() => new Promise((resolve) => setTimeout(resolve, 3000))) + const allShapes = await runtime.getAllShapes() + const fileName = await fileNameFromShape(allShapes[0]) + const file = await util.getDownloadFile(fileName + '.png') + + // TODO: Also check the buffer is correct here... + expect(file).toExist() + }) + + it('json', async () => { + await util.grantPermissions(['clipboard-read']) + await util.clearClipboard() + await ui.app.setup() + await createShape() + + if (mode === 'context') { + await ui.canvas.contextMenu(100, 120, ['export-as', 'export-as-json']) + } else if (mode === 'main') { + await ui.main.menu(['edit', 'export-as', 'export-as-json']) + } + + // FIXME: This shouldn't be a timer... but what to do??? + await browser.execute(() => new Promise((resolve) => setTimeout(resolve, 3000))) + const allShapes = await runtime.getAllShapes() + const fileName = await fileNameFromShape(allShapes[0]) + const file = await util.getDownloadFile(fileName + '.json') + + // TODO: Also check the buffer is correct here... + expect(file).toExist() + }) + } + ) + }) + } + }) + + describe('copy-as', () => { + for (const mode of ['main' /*, 'context'*/]) { + describe(`${mode} menu`, () => { + env( + { + // NOTE: Will be abled once mobile browsers support the '/permissions' API endpoint. + device: 'desktop', + // FIXME + skipBrowsers: ['firefox', 'vscode'], + }, + () => { + it('svg', async () => { + await util.grantPermissions(['clipboard-read']) + await ui.app.setup() + await util.clearClipboard() + await createShape() + + if (mode === 'context') { + await ui.canvas.contextMenu(100, 120, ['copy-as', 'copy-as-svg']) + } else if (mode === 'main') { + await ui.main.menu(['edit', 'copy-as', 'copy-as-svg']) + } + + await util.waitForClipboardContents() + const clipboardContents = await util.clipboardContents() + expect(clipboardContents.length).toEqual(1) + expect(clipboardContents[0]['text/plain']).toMatch(/ { + await util.grantPermissions(['clipboard-read']) + await ui.app.setup() + await util.clearClipboard() + await createShape() + + if (mode === 'context') { + await ui.canvas.contextMenu(100, 120, ['copy-as', 'copy-as-png']) + } else if (mode === 'main') { + await ui.main.menu(['edit', 'copy-as', 'copy-as-png']) + } + + await util.waitForClipboardContents() + const clipboardContents = await util.clipboardContents() + expect(clipboardContents.length).toEqual(1) + expect(clipboardContents[0]['image/png']).toBeDefined() + }) + + it('json', async () => { + await util.grantPermissions(['clipboard-read']) + await ui.app.setup() + await util.clearClipboard() + await createShape() + + if (mode === 'context') { + await ui.canvas.contextMenu(100, 120, ['copy-as', 'copy-as-json']) + } else if (mode === 'main') { + await ui.main.menu(['edit', 'copy-as', 'copy-as-json']) + } + + await util.waitForClipboardContents() + const clipboardContents = await util.clipboardContents() + expect(clipboardContents.length).toEqual(1) + expect(clipboardContents[0]['text/plain']).toBeDefined() + expect(clipboardContents[0]['text/plain']).toMatch(/^{/) + }) + } + ) + }) + } + }) +}) diff --git a/e2e/test/specs/grouping.ts b/e2e/test/specs/grouping.ts new file mode 100644 index 000000000..fee35e9a7 --- /dev/null +++ b/e2e/test/specs/grouping.ts @@ -0,0 +1,6 @@ +import { describe } from '../mocha-ext' + +describe('grouping', () => { + describe('group', () => {}) + describe('ungroup', () => {}) +}) diff --git a/e2e/test/specs/index.ts b/e2e/test/specs/index.ts new file mode 100644 index 000000000..bbc99e27c --- /dev/null +++ b/e2e/test/specs/index.ts @@ -0,0 +1,27 @@ +import { ui } from '../helpers/index' +import './arrange' +import './camera' +import './export' +import './grouping' +import './pages' +import './reorder' +import './screenshots' +import './shortcuts' +import './smoke' +import './styling' +import './text' + +before(async () => { + await browser.waitUntil( + async () => { + try { + await ui.app.open() + } catch (err) { + console.error(err) + return false + } + return true + }, + { timeout: 30 * 1000 } + ) +}) diff --git a/e2e/test/specs/pages.ts b/e2e/test/specs/pages.ts new file mode 100644 index 000000000..78e422228 --- /dev/null +++ b/e2e/test/specs/pages.ts @@ -0,0 +1,15 @@ +import { describe } from '../mocha-ext' + +describe('pages', () => { + describe('switch-page', () => {}) + describe('create', () => {}) + describe('edit-pages', () => { + describe('go-to-page', () => {}) + describe('duplicate', () => {}) + describe('move', () => {}) + describe('delete', () => {}) + describe('close', () => {}) + describe('rename-page', () => {}) + describe('create-page', () => {}) + }) +}) diff --git a/e2e/test/specs/reorder.ts b/e2e/test/specs/reorder.ts new file mode 100644 index 000000000..88d7e40e9 --- /dev/null +++ b/e2e/test/specs/reorder.ts @@ -0,0 +1,122 @@ +import { runtime, ui } from '../helpers' +import { app } from '../helpers/ui' +import { describe, it } from '../mocha-ext' + +const sortByIndex = (a, b) => { + if (a.index < b.index) { + return -1 + } else if (a.index > b.index) { + return 1 + } + return 0 +} + +describe('reorder', () => { + const createShapes = async () => { + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + + await ui.tools.click('rectangle') + await ui.canvas.brush(30, 30, 80, 80) + + await ui.tools.click('rectangle') + await ui.canvas.brush(50, 50, 100, 100) + + return (await runtime.getAllShapes()).sort(sortByIndex).map((s) => s.id) + } + + const MODES = [/*'context', */ 'action'] + + describe('bring-to-front', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const ids = await createShapes() + + // TODO: Move back to front + await ui.canvas.click(11, 11) + const point = await app.pointWithinActiveArea(11, 11) + + if (mode === 'action') { + await ui.main.actionMenu(['bring-to-front']) + } else if (mode === 'context') { + await ui.canvas.contextMenu(point.x, point.y, ['reorder', 'bring-to-front']) + } + + const shapes = (await runtime.getAllShapes()).sort(sortByIndex) + expect(shapes[0].id).toBe(ids[1]) + expect(shapes[1].id).toBe(ids[2]) + expect(shapes[2].id).toBe(ids[0]) + }) + } + }) + + describe('bring-forward', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const ids = await createShapes() + + // TODO: Move back to front + await ui.canvas.click(11, 11) + const point = await app.pointWithinActiveArea(11, 11) + if (mode === 'action') { + await ui.main.actionMenu(['bring-forward']) + } else if (mode === 'context') { + await ui.canvas.contextMenu(point.x, point.y, ['reorder', 'bring-forward']) + } + + const shapes = (await runtime.getAllShapes()).sort(sortByIndex) + expect(shapes[0].id).toBe(ids[1]) + expect(shapes[1].id).toBe(ids[0]) + expect(shapes[2].id).toBe(ids[2]) + }) + } + }) + + describe('send-backward', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const ids = await createShapes() + + // TODO: Move back to front + await ui.canvas.click(51, 51) + const point = await app.pointWithinActiveArea(51, 51) + if (mode === 'action') { + await ui.main.actionMenu(['send-backward']) + } else if (mode === 'context') { + await ui.canvas.contextMenu(point.x, point.y, ['reorder', 'send-backward']) + } + + const shapes = (await runtime.getAllShapes()).sort(sortByIndex) + expect(shapes[0].id).toBe(ids[0]) + expect(shapes[1].id).toBe(ids[2]) + expect(shapes[2].id).toBe(ids[1]) + }) + } + }) + + describe('send-to-back', () => { + for (const mode of MODES) { + it(`${mode} menu`, async () => { + await ui.app.setup() + const ids = await createShapes() + + // TODO: Move back to front + await ui.canvas.click(51, 51) + const point = await app.pointWithinActiveArea(51, 51) + if (mode === 'action') { + await ui.main.actionMenu(['send-to-back']) + } else if (mode === 'context') { + await ui.canvas.contextMenu(point.x, point.y, ['reorder', 'send-to-back']) + } + + const shapes = (await runtime.getAllShapes()).sort(sortByIndex) + expect(shapes[0].id).toBe(ids[2]) + expect(shapes[1].id).toBe(ids[0]) + expect(shapes[2].id).toBe(ids[1]) + }) + } + }) +}) diff --git a/e2e/test/specs/screenshots.ts b/e2e/test/specs/screenshots.ts new file mode 100644 index 000000000..336ce12bb --- /dev/null +++ b/e2e/test/specs/screenshots.ts @@ -0,0 +1 @@ +export {} diff --git a/e2e/test/specs/shortcuts.ts b/e2e/test/specs/shortcuts.ts new file mode 100644 index 000000000..6f4177bbe --- /dev/null +++ b/e2e/test/specs/shortcuts.ts @@ -0,0 +1,154 @@ +import { runtime, ui } from '../helpers' +import { describe, env, it } from '../mocha-ext' + +describe('basic keyboard shortcuts', () => { + env({ device: 'desktop' }, () => { + // If this one works, the others will work as well. + it('draw — D', async () => { + await ui.app.setup() + await browser.keys(['d']) + await browser.waitUntil(async () => { + return await runtime.isIn('draw.idle') + }) + }) + + // Tools + it.todo('select — V', async () => { + await ui.app.setup() + await browser.keys(['v']) + }) + + it('draw — D', async () => { + await ui.app.setup() + await browser.keys(['d']) + await browser.waitUntil(async () => { + return await runtime.isIn('draw.idle') + }) + }) + + it('eraser — E', async () => { + await ui.app.setup() + await browser.keys(['e']) + await browser.waitUntil(async () => { + return await runtime.isIn('eraser.idle') + }) + }) + + it('hand — H', async () => { + await ui.app.setup() + await browser.keys(['h']) + await browser.waitUntil(async () => { + return await runtime.isIn('hand.idle') + }) + }) + + it('rectangle — R', async () => { + await ui.app.setup() + await browser.keys(['r']) + await browser.waitUntil(async () => { + return ( + (await runtime.isIn('geo.idle')) && + (await runtime.propsForNextShape()).geo === 'rectangle' + ) + }) + }) + + it('ellipse — O', async () => { + await ui.app.setup() + await browser.keys(['o']) + await browser.waitUntil(async () => { + return ( + (await runtime.isIn('geo.idle')) && (await runtime.propsForNextShape()).geo === 'ellipse' + ) + }) + }) + + it.fails('diamond — P', async () => { + await ui.app.setup() + await browser.keys(['p']) + await browser.waitUntil(async () => { + return ( + (await runtime.isIn('geo.idle')) && (await runtime.propsForNextShape()).geo === 'diamond' + ) + }) + }) + + it('arrow — A', async () => { + await ui.app.setup() + await browser.keys(['a']) + await browser.waitUntil(async () => { + return await runtime.isIn('arrow.idle') + }) + }) + + it('line — L', async () => { + await ui.app.setup() + await browser.keys(['l']) + await browser.waitUntil(async () => { + return await runtime.isIn('line.idle') + }) + }) + + it('text — T', async () => { + await ui.app.setup() + await browser.keys(['t']) + await browser.waitUntil(async () => { + return await runtime.isIn('text.idle') + }) + }) + + it('frame — F', async () => { + await ui.app.setup() + await browser.keys(['f']) + await browser.waitUntil(async () => { + return await runtime.isIn('frame.idle') + }) + }) + + it('sticky — N', async () => { + await ui.app.setup() + await browser.keys(['n']) + await browser.waitUntil(async () => { + return await runtime.isIn('note.idle') + }) + }) + + // View + it.todo('zoom-in — ⌘+', () => {}) + it.todo('zoom-in — ⌘-', () => {}) + it.todo('zoom-in — ⌘0', () => {}) + it.todo('zoom-in — ⌘1', () => {}) + it.todo('zoom-in — ⌘2', () => {}) + it.todo('zoom-in — ⌘/', () => {}) + it.todo('zoom-in — ⌘.', () => {}) + it.todo("zoom-in — ⌘'", () => {}) + + // Transform + it.todo('flip-h — ⇧H', () => {}) + it.todo('flip-v — ⇧V', () => {}) + it.todo('lock/unlock — ⌘L', () => {}) + it.todo('move-to-front — ]', () => {}) + it.todo('move-forward — ⌥]', () => {}) + it.todo('move-backward — ⌥[', () => {}) + it.todo('move-to-back — [', () => {}) + it.todo('group — ⌘G', () => {}) + it.todo('ungroup — ⌘⇧G', () => {}) + + // File + it.todo('new-project — ⌘N', () => {}) + it.todo('open — ⌘O', () => {}) + it.todo('save — ⌘S', () => {}) + it.todo('save-as — ⌘⇧S', () => {}) + it.todo('upload-media — ⌘I', () => {}) + + // Edit + it.todo('undo — ⌘Z', () => {}) + it.todo('redo — ⌘⇧Z', () => {}) + it.todo('cut — ⌘X', () => {}) + it.todo('copy — ⌘C', () => {}) + it.todo('paste — ⌘V', () => {}) + it.todo('select-all — ⌘A', () => {}) + it.todo('delete — ⌫', () => {}) + it.todo('duplicate — ⌘D', () => {}) + }) +}) diff --git a/e2e/test/specs/smoke.ts b/e2e/test/specs/smoke.ts new file mode 100644 index 000000000..5c7f730f8 --- /dev/null +++ b/e2e/test/specs/smoke.ts @@ -0,0 +1,326 @@ +import { TLGeoShape } from '@tldraw/editor' +import { runtime, ui, util } from '../helpers' +import { describe, env, it } from '../mocha-ext' +import { SHAPES } from './constants' + +describe('smoke', () => { + env( + { + // FIXME + skipBrowsers: ['firefox'], + }, + () => { + it('startup in correct state', async () => { + await ui.app.setup() + + await ui.tools.click('rectangle') + await ui.canvas.brush(110, 210, 160, 260) + + await browser.waitUntil(async () => { + const isInGeoIdle = await browser.execute(() => window.app.isIn('select.idle')) + return isInGeoIdle === true + }) + expect(await browser.execute(() => window.app.isIn('select.idle'))).toBe(true) + expect(await browser.execute(() => window.app.shapesArray.length)).toBe(1) + }) + + it('click/tap create/delete some shapes', async () => { + await ui.app.setup() + + for (const shape of SHAPES) { + await ui.tools.click(shape.tool) + await ui.canvas.click(10, 10) + await ui.tools.click(shape.tool) + await ui.canvas.click(110, 210) + await ui.tools.click(shape.tool) + await ui.canvas.click(210, 310) + + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(3) + expect(allShapes.every((s) => s.type === shape.type)) + + await ui.main.menu(['edit', 'select-all']) + await ui.main.menu(['edit', 'delete']) + } + }) + + it('brush create/delete some shapes', async () => { + await ui.app.setup() + + for (const shape of SHAPES) { + await ui.tools.click(shape.tool) + await ui.canvas.brush(10, 10, 60, 160) + await ui.tools.click(shape.tool) + await ui.canvas.brush(110, 210, 160, 260) + await ui.tools.click(shape.tool) + await ui.canvas.brush(210, 310, 260, 360) + + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(3) + expect(allShapes.every((s) => s.type === shape.type)) + + await ui.main.menu(['edit', 'select-all']) + await ui.main.menu(['edit', 'delete']) + } + // ----------------------------- + + // Text + // await ui.tools.click('text') + // // TODO: This fails if you don't hide the modal first + // await ui.canvas.brush(10, 200, 250, 200) + // await tldraw.app.activeInput() + // await browser.keys("testing"); + + // await ui.tools.click('text') + // await ui.canvas.brush(10, 200, 250, 200) + // await tldraw.app.activeInput() + // await browser.keys("testing"); + + // await ui.tools.click('text') + // await ui.canvas.brush(10, 300, 250, 300) + // await tldraw.app.activeInput() + // await browser.keys("testing"); + + // const allShapes = await runtime.getShapesOfType(); + // expect(allShapes.length).toBe(3) + // expect(allShapes.every(s => s.type === 'text')); + // expect(allShapes.every(s => s.props.text === 'testing')); + + // await cleanup(); + + // // Note + // await ui.tools.click('note') + // await ui.canvas.click(100, 100) + // await ui.tools.click('note') + // await ui.canvas.click(210, 210) + // await ui.tools.click('note') + // await ui.canvas.click(310, 310) + + // for (const [index, [x, y]] of [ + // [70, 70], + // [180, 180], + // [280, 280], + // ].entries()) { + // // TODO: This only works if there is a small delay + // await ui.canvas.doubleClick(x, y) + // await browser.keys([`test${index}`]) + // await browser.action('key').down('\uE03D').down('\uE007').perform(true) + // await util.sleep(20) + // await browser.action('key').up('\uE007').up('\uE03D').perform(true) + // } + + // const allNoteShapes = await runtime.getAllShapes() + // expect(allNoteShapes.length).toBe(3) + // expect(allNoteShapes.every((s) => s.type === 'note')) + // expect(allNoteShapes[0].props.text).toBe('test0') + // expect(allNoteShapes[1].props.text).toBe('test1') + // expect(allNoteShapes[2].props.text).toBe('test2') + + // await ui.main.menu(['edit', 'select-all']) + // await ui.main.menu(['edit', 'delete']) + + // Image + // TODO + + // Frame + // FIXME: Fails on mobile + // await ui.tools.click('frame') + // await ui.canvas.brush(10, 10, 60, 160) + // await ui.tools.click('frame') + // await ui.canvas.brush(110, 210, 160, 260) + // await ui.tools.click('frame') + // await ui.canvas.brush(210, 310, 260, 360) + + // await ui.canvas.doubleClick(10, 0) + // await browser.keys([ + // 'test1', + // '\uE007', // ENTER + // ]) + + // await ui.canvas.doubleClick(110, 200) + // await browser.keys([ + // 'test2', + // '\uE007', // ENTER + // ]) + + // await ui.canvas.doubleClick(210, 300) + // await browser.keys([ + // 'test3', + // '\uE007', // ENTER + // ]) + + // const allShapes = await runtime.getAllShapes() + // expect(allShapes.length).toBe(3) + // expect(allShapes.every((s) => s.type === 'frame')) + // expect(allShapes[0].props.name).toBe('test1') + // expect(allShapes[1].props.name).toBe('test2') + // expect(allShapes[2].props.name).toBe('test3') + + // await ui.main.menu(['edit', 'select-all']) + // await ui.main.menu(['edit', 'delete']) + }) + + it.skip('[TODO] resize some shapes', async () => { + await ui.app.setup() + + // await ui.canvas.brush(10, 10, 100, 100) + + for (const size of [30, 50, 70]) { + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 10 + size, 10 + size) + + await ui.main.menu(['edit', 'select-all']) + + const handle = await ui.canvas.selectionHandle('resize.bottom-right') + await ui.canvas.dragBy(handle, 20, 20) + + const allShapes = (await runtime.getAllShapes()) as TLGeoShape[] + expect(allShapes.length).toBe(1) + expect(allShapes[0].props.w).toBe(size + 20) + expect(allShapes[0].props.h).toBe(size + 20) + + await util.deleteAllShapesOnPage() + } + }) + + // REMOTE:OK + it.skip('[TODO] rotate some shapes', async () => { + await ui.app.setup() + + for (const size of [70, 90, 100]) { + await ui.tools.click('rectangle') + await ui.canvas.brush(100, 120, 100 + size, 120 + size) + + await ui.main.menu(['edit', 'select-all']) + + const handle = await ui.canvas.selectionHandle('rotate.mobile', 'rotate.top-right') + await ui.canvas.dragBy(handle, size / 2, size / 2) + + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + + const rotation = allShapes[0].rotation + + // TODO: This isn't exact I assume because pixel issues with the DPR and webdriver + expect(rotation > 0).toBe(true) + + await util.deleteAllShapesOnPage() + } + }) + + // FIXME: Ok once resolved + it.skip('undo/redo', async () => { + await ui.app.setup() + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 110, 60, 160) + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 210, 60, 260) + + expect((await runtime.getAllShapes()).length).toBe(3) + + await ui.main.click('undo') + expect((await runtime.getAllShapes()).length).toBe(2) + + await ui.main.click('undo') + expect((await runtime.getAllShapes()).length).toBe(1) + + await ui.main.click('undo') + expect((await runtime.getAllShapes()).length).toBe(0) + + await ui.main.click('undo') + expect((await runtime.getAllShapes()).length).toBe(0) + + await ui.main.click('redo') + expect((await runtime.getAllShapes()).length).toBe(1) + + await ui.main.click('redo') + expect((await runtime.getAllShapes()).length).toBe(2) + + await ui.main.click('redo') + expect((await runtime.getAllShapes()).length).toBe(3) + + await ui.main.click('redo') + expect((await runtime.getAllShapes()).length).toBe(3) + }) + + it.skip('reorder', async () => { + await ui.app.setup() + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + + await ui.tools.click('rectangle') + await ui.canvas.brush(30, 30, 80, 80) + + await ui.tools.click('rectangle') + await ui.canvas.brush(50, 50, 100, 100) + + throw new Error('TODO: Not done yet') + + // await tldraw.canvas.contextMenu([x,y], ["reorder", "move-to-front"]); + // // Assert order + + // await tldraw.canvas.contextMenu([x,y], ["reorder", "move-to-front"]); + // // Assert order + + // await tldraw.canvas.contextMenu([x,y], ["reorder", "move-to-front"]); + // // Assert order + }) + + it.skip('move page', async () => { + await ui.app.setup() + + // await tldraw.main.pages.create() + // await tldraw.main.pages.create() + + // const pages = await tldraw.app.getPages() + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + + await ui.tools.click('rectangle') + await ui.canvas.brush(10, 10, 60, 60) + }) + + // REMOTE:OK + it('group/ungroup', async () => { + await ui.app.setup() + + await ui.tools.click('rectangle') + await ui.canvas.brush(100, 100, 150, 150) + await ui.tools.click('rectangle') + await ui.canvas.brush(200, 200, 250, 250) + await ui.tools.click('rectangle') + await ui.canvas.brush(300, 300, 350, 350) + + await ui.main.menu(['edit', 'select-all']) + await ui.main.menu(['edit', 'group']) + + const groupShapesBefore = await runtime.getShapesOfType('group') + const geoShapesBefore = await runtime.getShapesOfType('geo') + expect(groupShapesBefore.length).toBe(1) + expect(geoShapesBefore.length).toBe(3) + + await ui.main.menu(['edit', 'select-all']) + await ui.main.menu(['edit', 'ungroup']) + + const allShapes = await runtime.getAllShapes() + const groupShapesAfter = allShapes.filter( + (s) => s.typeName === 'shape' && s.type === 'group' + ) + const geoShapesAfter = allShapes.filter((s) => s.typeName === 'shape' && s.type === 'geo') + + expect(groupShapesAfter.length).toBe(0) + expect(geoShapesAfter.length).toBe(3) + }) + } + ) +}) diff --git a/e2e/test/specs/styling.ts b/e2e/test/specs/styling.ts new file mode 100644 index 000000000..615c4a67e --- /dev/null +++ b/e2e/test/specs/styling.ts @@ -0,0 +1,315 @@ +import { TLShape } from '@tldraw/editor' +import { runtime, ui, util } from '../helpers' +import { describe, it } from '../mocha-ext' +import { SHAPES } from './constants' + +const LITE_MODE = true + +const assertColors = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const colors = LITE_MODE + ? ['black'] + : [ + 'black', + 'grey', + 'light-violet', + 'violet', + 'blue', + 'light-blue', + 'yellow', + 'orange', + 'green', + 'light-green', + 'light-red', + 'red', + ] + + await ui.props.ifMobileOpenStylesMenu() + for (const color of colors) { + await ui.props.selectColor(color) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect('color' in allShapes[0].props && allShapes[0].props.color).toBe(color) + } + } +} + +const assertOpacity = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const opacities = LITE_MODE ? [0.5] : [0.1, 0.25, 0.5, 0.75, 1] + + await ui.props.ifMobileOpenStylesMenu() + for (const opacity of opacities) { + await ui.props.selectOpacity(opacity) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect('opacity' in allShapes[0].props && allShapes[0].props.opacity).toBe(opacity) + } + } +} + +const assertFill = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const fills = LITE_MODE ? ['solid'] : ['none', 'semi', 'solid', 'pattern'] + + await ui.props.ifMobileOpenStylesMenu() + for (const fill of fills) { + await ui.props.selectFill(fill) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].props).toHaveProperty('fill', fill) + } + } +} + +const assertFont = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const fonts = LITE_MODE ? ['sans'] : ['draw', 'sans', 'serif', 'mono'] + + await ui.props.ifMobileOpenStylesMenu() + for (const font of fonts) { + await ui.props.selectFont(font) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].props).toHaveProperty('font', font) + } + } +} + +const assertAlign = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const alignments = LITE_MODE ? ['middle'] : ['start', 'middle', 'end'] + + await ui.props.ifMobileOpenStylesMenu() + for (const alignment of alignments) { + await ui.props.selectAlign(alignment) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].props).toHaveProperty('align', alignment) + } + } +} + +const assertStroke = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const strokes = LITE_MODE ? ['dashed'] : ['draw', 'dashed', 'dotted', 'solid'] + + await ui.props.ifMobileOpenStylesMenu() + for (const stroke of strokes) { + await ui.props.selectStroke(stroke) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].props).toHaveProperty('dash', stroke) + } + } +} + +const assertSize = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const sizes = LITE_MODE ? ['xl'] : ['s', 'm', 'l', 'xl'] + + await ui.props.ifMobileOpenStylesMenu() + for (const size of sizes) { + await ui.props.selectSize(size) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].props).toHaveProperty('size', size) + } + } +} + +const assertSpline = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const types = LITE_MODE ? ['line'] : ['line', 'cubic'] + + await ui.props.ifMobileOpenStylesMenu() + for (const type of types) { + await ui.props.selectSpline(type) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].props).toHaveProperty('spline', type) + } + } +} + +const assertArrowheads = (createShape: () => Promise) => { + return async () => { + await ui.app.setup() + await createShape() + const types = LITE_MODE + ? ['triangle'] + : ['none', 'arrow', 'triangle', 'square', 'dot', 'diamond', 'inverted', 'bar'] + + await ui.props.ifMobileOpenStylesMenu() + for (const startType of types) { + for (const endType of types) { + await ui.props.selectArrowheadStart(startType) + await ui.props.selectArrowheadEnd(endType) + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].props).toHaveProperty('arrowheadStart', startType) + expect(allShapes[0].props).toHaveProperty('arrowheadEnd', endType) + } + } + } +} + +describe.skip('styling', () => { + describe('draw', () => { + const createShape = async () => { + await ui.tools.click('draw') + await ui.canvas.draw([ + { x: 50, y: 50 }, + { x: 300, y: 50 }, + { x: 300, y: 300 }, + { x: 50, y: 300 }, + { x: 50, y: 50 }, + ]) + await ui.tools.click('select') + + return (await runtime.getAllShapes())[0] + } + it('color', assertColors(createShape)) + it.todo('opacity', assertOpacity(createShape)) + it('fill', assertFill(createShape)) + it('stroke', assertStroke(createShape)) + it('size', assertSize(createShape)) + }) + + describe('arrow', () => { + const createShape = async () => { + await ui.tools.click('arrow') + await ui.canvas.brush(50, 50, 200, 200) + + await ui.canvas.doubleClick((200 - 50) / 2, (200 - 50) / 2) + await browser.keys(['test']) + await browser.action('key').down('\uE03D').down('\uE007').perform(true) + await util.sleep(20) + await browser.action('key').up('\uE007').up('\uE03D').perform() + + return (await runtime.getAllShapes())[0] + } + it('color', assertColors(createShape)) + it.todo('opacity', assertOpacity(createShape)) + it('fill', assertFill(createShape)) + it('stroke', assertStroke(createShape)) + it('size', assertSize(createShape)) + it('arrowheads', assertArrowheads(createShape)) + it('font', assertFont(createShape)) + }) + + describe('line', () => { + const createShape = async () => { + await ui.tools.click('line') + await ui.canvas.brush(50, 50, 200, 200) + + return (await runtime.getAllShapes())[0] + } + it('color', assertColors(createShape)) + it.todo('opacity', assertOpacity(createShape)) + it('stroke', assertStroke(createShape)) + it('size', assertSize(createShape)) + it('spline', assertSpline(createShape)) + }) + + SHAPES.map((shapeDef) => { + describe(shapeDef.tool, () => { + const createShape = async () => { + await ui.tools.click(shapeDef.tool) + await ui.canvas.brush(60, 60, 210, 210) + + await ui.canvas.doubleClick(60 + (210 - 60) / 2, 60 + (210 - 60) / 2) + await browser.keys(['test']) + await browser.action('key').down('\uE03D').down('\uE007').perform(true) + await util.sleep(20) + await browser.action('key').up('\uE007').up('\uE03D').perform(true) + + return (await runtime.getAllShapes())[0] + } + + it('color', assertColors(createShape)) + it.todo('opacity', () => {}) + it('fill', assertFill(createShape)) + it('stroke', assertStroke(createShape)) + it('size', assertSize(createShape)) + it('font', assertFont(createShape)) + it('align', assertAlign(createShape)) + }) + }) + + describe('text', () => { + const createShape = async () => { + await ui.tools.click('select') + await ui.tools.click('text') + await ui.canvas.click(100, 100) + await browser.keys('testing') + + return (await runtime.getAllShapes())[0] + } + it('color', assertColors(createShape)) + it.todo('opacity', assertOpacity(createShape)) + it('size', assertSize(createShape)) + it('font', assertFont(createShape)) + it('align', assertAlign(createShape)) + }) + + describe('frame', () => { + const createShape = async () => { + await ui.tools.click('frame') + await ui.canvas.brush(10, 10, 60, 160) + + await ui.canvas.doubleClick(10, 0) + await browser.keys([ + 'test', + '\uE007', // ENTER + ]) + + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].type).toBe('frame') + expect(allShapes[0].props).toHaveProperty('name', 'test') + + return allShapes[0] + } + it.todo('opacity', assertOpacity(createShape)) + }) + + describe.skip('note', () => { + const createShape = async () => { + await ui.tools.click('note') + await ui.canvas.click(100, 100) + await browser.keys(['test']) + await browser.action('key').down('\uE03D').down('\uE007').perform(true) + await util.sleep(20) + await browser.action('key').up('\uE007').up('\uE03D').perform(true) + + const allShapes = await runtime.getAllShapes() + expect(allShapes.length).toBe(1) + expect(allShapes[0].type).toBe('note') + expect(allShapes[0].props).toHaveProperty('text', 'test') + + return allShapes[0] + } + it('color', assertColors(createShape)) + it.todo('opacity', assertOpacity(createShape)) + it('size', assertSize(createShape)) + it('font', assertFont(createShape)) + it('align', assertAlign(createShape)) + }) +}) diff --git a/e2e/test/specs/text.ts b/e2e/test/specs/text.ts new file mode 100644 index 000000000..ff50efb99 --- /dev/null +++ b/e2e/test/specs/text.ts @@ -0,0 +1,243 @@ +import { runtime, ui } from '../helpers' +import { diffScreenshot, takeRegionScreenshot } from '../helpers/webdriver' +import { describe, env, it } from '../mocha-ext' + +describe('text', () => { + env( + { + // This can be removed once bugs resolved on mobile. + // Tracked in + device: 'desktop', + }, + () => { + const tests = [ + { + name: 'multiline (align center)', + fails: true, + handler: async () => { + await ui.tools.click('select') + await ui.tools.click('text') + await ui.canvas.brush(100, 0, 150, 150) + await browser.keys('testing\ntesting\n1, 2, 3') + }, + }, + // { + // name: 'diacritics (align center)', + // fails: false, + // handler: async () => { + // await ui.tools.click('text') + // await ui.canvas.brush(50, 100, 150, 150) + // await browser.keys('âéīôù') + // }, + // }, + ] + + for (const test of tests) { + const { name } = test + const slugName = name.replace(/ /g, '-').replace(/[)(]/g, '') + const prefix = [ + global.webdriverService, + global.tldrawOptions.os, + global.tldrawOptions.browser, + global.tldrawOptions.ui, + slugName, + ].join('-') + + const cleanUp = async () => { + await ui.main.menu(['edit', 'select-all']) + await ui.main.menu(['edit', 'delete']) + } + + const testHandler = async () => { + await ui.app.setup() + await test.handler() + + await ui.main.menu(['edit', 'select-all']) + const selectionBounds = await runtime.selectionBounds() + await ui.main.menu(['edit', 'select-none']) + + const screenshotResults = await takeRegionScreenshot(selectionBounds, { + writeTo: { + path: `${__dirname}/../../screenshots/`, + prefix, + }, + }) + + const { pxielDiff } = await diffScreenshot(screenshotResults, { + writeTo: { + path: `${__dirname}/../../screenshots/`, + prefix, + }, + }) + + await cleanUp() + expect(pxielDiff).toBeLessThan(70) + } + + it[test.fails ? 'fails' : 'ok']('text: ' + test.name, testHandler) + } + } + ) +}) + +describe('text measurement', () => { + const measureTextOptions = { + text: 'testing', + width: 'fit-content', + fontFamily: 'var(--tl-font-draw)', + fontSize: 24, + lineHeight: 1.35, + fontWeight: 'normal', + fontStyle: 'normal', + padding: '0px', + maxWidth: 'auto', + } + + const getTextLinesOptions = { + text: 'testing', + width: 100, + height: 1000, + wrap: true, + padding: 0, + fontSize: 24, + fontWeight: 'normal', + fontFamily: 'var(--tl-font-draw)', + fontStyle: 'normal', + lineHeight: 1.35, + textAlign: 'start' as 'start' | 'middle' | 'end', + } + + env({}, () => { + it('should measure text', async () => { + await ui.app.setup() + const { w, h } = await browser.execute((options) => { + return window.app.textMeasure.measureText({ + ...options, + }) + }, measureTextOptions) + + expect(w).toBeCloseTo(85.828125, 1) + expect(h).toBeCloseTo(32.3984375, 1) + }) + + it('should get a single text line', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['testing']) + }) + + it('should wrap a word when it has to', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + width: 50, + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['test', 'ing']) + }) + + it('should wrap between words when it has to', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + text: 'testing testing', + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['testing', 'testing']) + }) + + it('should strip whitespace at line breaks', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + text: 'testing testing', + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['testing', 'testing']) + }) + + it('should strip whitespace at the end of wrapped lines', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + text: 'testing testing ', + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['testing', 'testing']) + }) + + it('should strip whitespace at the end of unwrapped lines', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + width: 200, + text: 'testing testing ', + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['testing testing']) + }) + + it('should strip whitespace from the start of an unwrapped line', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + width: 200, + text: ' testing testing', + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['testing testing']) + }) + + it('should place starting whitespace on its own line if it has to', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + text: ' testing testing', + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['', 'testing', 'testing']) + }) + + it('should place ending whitespace on its own line if it has to', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + text: 'testing testing ', + }) + }, getTextLinesOptions) + + expect(lines).toEqual(['testing', 'testing']) + }) + it('should return an empty array if the text is empty', async () => { + await ui.app.setup() + const lines = await browser.execute((options) => { + return window.app.textMeasure.getTextLines({ + ...options, + text: '', + }) + }, getTextLinesOptions) + + expect(lines).toEqual([]) + }) + }) +}) diff --git a/e2e/test/test-constants.ts b/e2e/test/test-constants.ts new file mode 100644 index 000000000..3574286c9 --- /dev/null +++ b/e2e/test/test-constants.ts @@ -0,0 +1,12 @@ +export const IS_MAC_OS = process.platform === 'darwin' + +export const WINDOW_SIZES = { + MOBILE_PORTRAIT: { + width: 414, + height: 796, + }, + DESKTOP: { + width: 1200, + height: 800, + }, +} diff --git a/e2e/tsconfig.json b/e2e/tsconfig.json new file mode 100644 index 000000000..9e864b6cf --- /dev/null +++ b/e2e/tsconfig.json @@ -0,0 +1,36 @@ +{ + "include": ["test"], + "exclude": ["node_modules", "dist", ".tsbuild*"], + "compilerOptions": { + "jsx": "react-jsx", + "skipLibCheck": true, + "resolveJsonModule": true, + "allowSyntheticDefaultImports": true, + "importHelpers": true, + "lib": ["dom", "esnext"], + "esModuleInterop": true, + "target": "ESNext", + "moduleResolution": "node16", + "types": [ + "node", + "@wdio/globals/types", + "@wdio/mocha-framework", + "@types/mocha", + "wdio-vscode-service" + ], + "noEmitOnError": false, + "noEmit": true + }, + "ts-node": { + "compilerOptions": { + "moduleResolution": "node", + "module": "CommonJS" + } + }, + "references": [ + { "path": "../packages/editor" }, + { "path": "../packages/tlschema" }, + { "path": "../packages/tlstore" }, + { "path": "../packages/primitives" } + ] +} diff --git a/e2e/wdio.local.conf.js b/e2e/wdio.local.conf.js new file mode 100644 index 000000000..6a512ac45 --- /dev/null +++ b/e2e/wdio.local.conf.js @@ -0,0 +1,260 @@ +const edgeDriver = require('@sitespeed.io/edgedriver') + +const CURRENT_OS = { + win32: 'windows', + linux: 'linux', + darwin: 'macos', +}[process.platform] + +global.webdriverService = 'local' +global.webdriverTestUrl = process.env.TEST_URL ?? 'http://localhost:5420/' + +let capabilities +if (process.env.CI === 'true') { + capabilities = [ + // { + // maxInstances: 1, + // browserName: 'chrome', + // acceptInsecureCerts: true, + // 'goog:chromeOptions': { + // mobileEmulation: { + // deviceName: 'iPhone XR', + // }, + // prefs: { + // download: { + // default_directory: __dirname + '/downloads/', + // prompt_for_download: false, + // }, + // }, + // }, + // 'tldraw:options': { + // browser: 'chrome', + // os: CURRENT_OS, + // ui: 'mobile', + // device: 'mobile', + // input: ['touch'], + // }, + // }, + { + maxInstances: 1, + browserName: 'chrome', + acceptInsecureCerts: true, + 'goog:chromeOptions': { + prefs: { + download: { + default_directory: __dirname + '/downloads/', + prompt_for_download: false, + }, + }, + }, + 'tldraw:options': { + browser: 'chrome', + os: CURRENT_OS, + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + ] +} else { + capabilities = [ + { + maxInstances: 1, + browserName: 'chrome', + acceptInsecureCerts: true, + 'goog:chromeOptions': { + // Network emulation requires device mode, which is only enabled when mobile emulation is on + mobileEmulation: { + deviceName: 'iPhone XR', + }, + prefs: { + download: { + default_directory: __dirname + '/downloads/', + prompt_for_download: false, + }, + }, + }, + 'tldraw:options': { + browser: 'chrome', + os: CURRENT_OS, + ui: 'mobile', + device: 'mobile', + input: ['touch'], + }, + }, + { + maxInstances: 1, + browserName: 'vscode', + browserVersion: 'stable', + acceptInsecureCerts: true, + 'wdio:vscodeOptions': { + extensionPath: __dirname + '../bublic/apps/vscode/extension/dist/web', + userSettings: { + 'editor.fontSize': 14, + }, + }, + 'tldraw:options': { + browser: 'vscode', + os: CURRENT_OS, + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + windowSize: 'default', + }, + }, + { + maxInstances: 1, + browserName: 'chrome', + acceptInsecureCerts: true, + 'goog:chromeOptions': { + prefs: { + download: { + default_directory: __dirname + '/downloads/', + prompt_for_download: false, + }, + }, + }, + 'tldraw:options': { + browser: 'chrome', + os: CURRENT_OS, + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + maxInstances: 1, + browserName: 'safari', + acceptInsecureCerts: true, + 'tldraw:options': { + browser: 'safari', + os: CURRENT_OS, + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + maxInstances: 1, + browserName: 'firefox', + acceptInsecureCerts: true, + 'tldraw:options': { + browser: 'firefox', + os: CURRENT_OS, + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + maxInstances: 1, + browserName: 'MicrosoftEdge', + acceptInsecureCerts: true, + 'tldraw:options': { + browser: 'edge', + os: CURRENT_OS, + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + ] +} + +let browsers = (process.env.BROWSERS || 'chrome').split(',').map((b) => b.trim()) +const validBrowsers = ['chrome', 'safari', 'firefox', 'edge', 'vscode'] +const skippedBrowsers = [] + +if (browsers.includes('safari')) { + console.log( + 'NOTE: In safari you need to run `safaridriver --enable`, see for details.' + ) +} + +for (const browser of browsers) { + if (!validBrowsers.includes(browser)) { + throw new Error(`'${browser}' not a valid browser name`) + } + if (skippedBrowsers.includes(browser)) { + console.error(`'${browser}' not currently supported`) + } +} + +capabilities = capabilities.filter((capability) => { + return browsers.includes(capability['tldraw:options'].browser) +}) + +exports.config = { + specs: ['./test/specs/index.ts'], + hostname: process.env.DOCKER_HOST || 'localhost', + exclude: [], + services: process.env.DOCKER_HOST + ? [] + : [ + ['vscode', { verboseLogging: true }], + [ + 'geckodriver', + { + outputDir: './driver-logs', + logFileName: 'wdio-geckodriver.log', + }, + ], + [ + 'safaridriver', + { + outputDir: './driver-logs', + logFileName: 'wdio-safaridriver.log', + }, + ], + [ + 'chromedriver', + { + logFileName: 'wdio-chromedriver.log', + outputDir: './driver-logs', + args: ['--silent'], + // NOTE: Must be on a different port that 7676 otherwise it conflicts with 'vscode' service. + port: 7677, + }, + ], + // HACK: If we don't have edge as a capability but we do have + // this service then `wdio-edgedriver-service` throws an scary + // error (which doesn't actually effect anything) + ...(!browsers.includes('edge') + ? [] + : [ + [ + 'edgedriver', + { + port: 17556, // default for EdgeDriver + logFileName: 'wdio-edgedriver.log', + outputDir: './driver-logs', + edgedriverCustomPath: edgeDriver.binPath(), + }, + ], + ]), + ], + maxInstances: 1, + capabilities: capabilities, + logLevel: process.env.WD_LOG_LEVEL ?? 'error', + bail: 0, + baseUrl: 'http://localhost', + waitforTimeout: 10000, + connectionRetryTimeout: 120000, + connectionRetryCount: 3, + framework: 'mocha', + reporters: ['spec'], + mochaOpts: { + ui: 'bdd', + timeout: 60000, + }, + beforeSession: (_config, capabilities) => { + global.tldrawOptions = capabilities['tldraw:options'] + }, + autoCompileOpts: { + autoCompile: true, + tsNodeOpts: { + transpileOnly: true, + project: './tsconfig.json', + }, + }, +} diff --git a/e2e/wdio.nightly.conf.js b/e2e/wdio.nightly.conf.js new file mode 100644 index 000000000..6190b4bff --- /dev/null +++ b/e2e/wdio.nightly.conf.js @@ -0,0 +1,280 @@ +const BUILD_NAME = `test-suite-${new Date().toISOString()}` + +global.webdriverService = 'browserstack' +global.webdriverTestUrl = 'http://localhost:5420/' + +exports.config = { + user: process.env.BROWSERSTACK_USER, + key: process.env.BROWSERSTACK_KEY, + hostname: 'hub.browserstack.com', + specs: ['./test/specs/index.ts'], + services: [ + [ + 'browserstack', + { + browserstackLocal: true, + opts: { + verbose: 'true', + }, + }, + ], + ], + exclude: [], + maxInstances: 1, + waitforInterval: 200, + /** + * Capabilities can be configured via + * + * The once commented out currently fail on because of insecure certs, details + */ + capabilities: [ + /** + * ==================================================================== + * Windows 11 + * ==================================================================== + */ + { + 'bstack:options': { + os: 'Windows', + osVersion: '11', + browserVersion: 'latest', + seleniumVersion: '3.14.0', + }, + browserName: 'Chrome', + 'tldraw:options': { + browser: 'chrome', + os: 'windows', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'Windows', + osVersion: '11', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Edge', + 'tldraw:options': { + browser: 'edge', + os: 'windows', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'Windows', + osVersion: '11', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Firefox', + 'tldraw:options': { + browser: 'firefox', + os: 'windows', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + /** + * ==================================================================== + * MacOS + * ==================================================================== + */ + // { + // 'bstack:options' : { + // "os" : "OS X", + // "osVersion" : "Ventura", + // "browserVersion" : "16.0", + // "seleniumVersion" : "4.6.0", + // }, + // "acceptInsecureCerts" : "true", + // "browserName" : "Safari", + // }, + { + 'bstack:options': { + os: 'OS X', + osVersion: 'Ventura', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + browserName: 'Chrome', + 'tldraw:options': { + browser: 'chrome', + os: 'macos', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'OS X', + osVersion: 'Ventura', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Firefox', + 'tldraw:options': { + browser: 'firefox', + os: 'macos', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'OS X', + osVersion: 'Ventura', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Edge', + 'tldraw:options': { + browser: 'edge', + os: 'macos', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + /** + // * ==================================================================== + // * Android + // * ==================================================================== + // */ + { + 'bstack:options': { + osVersion: '13.0', + deviceName: 'Google Pixel 7', + appiumVersion: '1.22.0', + }, + browserName: 'chrome', + 'tldraw:options': { + appium: true, + browser: 'chrome', + os: 'android', + ui: 'mobile', + device: 'mobile', + input: ['touch'], + }, + }, + { + 'bstack:options': { + osVersion: '11.0', + deviceName: 'Samsung Galaxy S21', + appiumVersion: '1.22.0', + }, + acceptInsecureCerts: 'true', + browserName: 'samsung', + 'tldraw:options': { + appium: true, + browser: 'samsung', + os: 'android', + ui: 'mobile', + device: 'mobile', + input: ['touch'], + }, + }, + { + 'bstack:options': { + osVersion: '11.0', + deviceName: 'Samsung Galaxy S21', + appiumVersion: '1.22.0', + }, + acceptInsecureCerts: 'true', + browserName: 'chrome', + 'tldraw:options': { + appium: true, + browser: 'chrome', + os: 'android', + ui: 'mobile', + device: 'mobile', + input: ['touch'], + }, + }, + /** + * ==================================================================== + * iOS + * ==================================================================== + */ + // { + // 'bstack:options': { + // "osVersion" : "16", + // "deviceName" : "iPhone 14", + // "appiumVersion": "1.22.0" + // }, + // "acceptInsecureCerts" : "true", + // "browserName" : "safari", + // }, + // { + // 'bstack:options': { + // "osVersion" : "16", + // "deviceName" : "iPad Pro 12.9 2022", + // "appiumVersion": "1.22.0" + // }, + // "acceptInsecureCerts" : "true", + // "browserName" : "safari", + // }, + ] + .map((capability) => { + return { + ...capability, + acceptInsecureCerts: true, + 'bstack:options': { + ...capability['bstack:options'], + projectName: 'smoke-tests', + buildName: BUILD_NAME, + consoleLogs: 'verbose', + }, + 'tldraw:options': { + ...capability['tldraw:options'], + }, + } + }) + .filter((capability) => { + const { os, browser } = capability['tldraw:options'] + const envOsKey = `WD_OS_${os.toUpperCase()}` + const envBrowserKey = `WD_BROWSER_${browser.toUpperCase()}` + const envOsValue = process.env[envOsKey] + const envBrowserValue = process.env[envBrowserKey] + return !(envOsValue === 'false' || envBrowserValue === 'false') + }), + bail: 0, + waitforTimeout: 10000, + connectionRetryTimeout: 120000, + connectionRetryCount: 3, + framework: 'mocha', + reporters: ['spec'], + mochaOpts: { + ui: 'bdd', + timeout: 5 * 60 * 1000, + }, + logLevel: process.env.WD_LOG_LEVEL ?? 'info', + coloredLogs: true, + screenshotPath: './errorShots/', + waitforTimeout: 30000, + connectionRetryTimeout: 90000, + connectionRetryCount: 3, + beforeSession: (_config, capabilities) => { + global.tldrawOptions = capabilities['tldraw:options'] + }, + autoCompileOpts: { + autoCompile: true, + tsNodeOpts: { + transpileOnly: true, + swc: true, + project: './tsconfig.json', + }, + }, +} diff --git a/e2e/wdio.remote.conf.js b/e2e/wdio.remote.conf.js new file mode 100644 index 000000000..133d3addd --- /dev/null +++ b/e2e/wdio.remote.conf.js @@ -0,0 +1,271 @@ +const BUILD_NAME = `test-suite-${new Date().toISOString()}` + +global.webdriverService = 'browserstack' +global.webdriverTestUrl = 'http://localhost:5420/' + +exports.config = { + user: process.env.BROWSERSTACK_USER, + key: process.env.BROWSERSTACK_KEY, + hostname: 'hub.browserstack.com', + specs: ['./test/specs/index.ts'], + services: [ + [ + 'browserstack', + { + browserstackLocal: true, + opts: { + verbose: 'true', + }, + }, + ], + ], + exclude: [], + maxInstances: 1, + waitforInterval: 200, + /** + * Capabilities can be configured via + * + * The once commented out currently fail on because of insecure certs, details + */ + capabilities: [ + /** + * ==================================================================== + * Windows 11 + * ==================================================================== + */ + { + 'bstack:options': { + os: 'Windows', + osVersion: '11', + browserVersion: 'latest', + seleniumVersion: '3.14.0', + }, + browserName: 'Chrome', + 'tldraw:options': { + browser: 'chrome', + os: 'windows', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'Windows', + osVersion: '11', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Edge', + 'tldraw:options': { + browser: 'edge', + os: 'windows', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'Windows', + osVersion: '11', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Firefox', + 'tldraw:options': { + browser: 'firefox', + os: 'windows', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + /** + * ==================================================================== + * MacOS + * ==================================================================== + */ + // { + // 'bstack:options' : { + // "os" : "OS X", + // "osVersion" : "Ventura", + // "browserVersion" : "16.0", + // "seleniumVersion" : "4.6.0", + // }, + // "acceptInsecureCerts" : "true", + // "browserName" : "Safari", + // }, + { + 'bstack:options': { + os: 'OS X', + osVersion: 'Ventura', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + browserName: 'Chrome', + 'tldraw:options': { + browser: 'chrome', + os: 'macos', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'OS X', + osVersion: 'Ventura', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Firefox', + 'tldraw:options': { + browser: 'firefox', + os: 'macos', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + { + 'bstack:options': { + os: 'OS X', + osVersion: 'Ventura', + browserVersion: 'latest', + seleniumVersion: '4.6.0', + }, + acceptInsecureCerts: 'true', + browserName: 'Edge', + 'tldraw:options': { + browser: 'edge', + os: 'macos', + ui: 'desktop', + device: 'desktop', + input: ['mouse'], + }, + }, + /** + // * ==================================================================== + // * Android + // * ==================================================================== + // */ + { + 'bstack:options': { + osVersion: '13.0', + deviceName: 'Google Pixel 7', + appiumVersion: '1.22.0', + }, + browserName: 'chrome', + 'tldraw:options': { + appium: true, + browser: 'chrome', + os: 'android', + ui: 'mobile', + device: 'mobile', + input: ['touch'], + }, + }, + { + 'bstack:options': { + osVersion: '11.0', + deviceName: 'Samsung Galaxy S21', + appiumVersion: '1.22.0', + }, + acceptInsecureCerts: 'true', + browserName: 'samsung', + 'tldraw:options': { + appium: true, + browser: 'samsung', + os: 'android', + ui: 'mobile', + device: 'mobile', + input: ['touch'], + }, + }, + { + 'bstack:options': { + osVersion: '11.0', + deviceName: 'Samsung Galaxy S21', + appiumVersion: '1.22.0', + }, + acceptInsecureCerts: 'true', + browserName: 'chrome', + 'tldraw:options': { + appium: true, + browser: 'chrome', + os: 'android', + ui: 'mobile', + device: 'mobile', + input: ['touch'], + }, + }, + /** + * ==================================================================== + * iOS + * ==================================================================== + */ + // { + // 'bstack:options': { + // "osVersion" : "16", + // "deviceName" : "iPhone 14", + // "appiumVersion": "1.22.0" + // }, + // "acceptInsecureCerts" : "true", + // "browserName" : "safari", + // }, + // { + // 'bstack:options': { + // "osVersion" : "16", + // "deviceName" : "iPad Pro 12.9 2022", + // "appiumVersion": "1.22.0" + // }, + // "acceptInsecureCerts" : "true", + // "browserName" : "safari", + // }, + ].map((capability) => { + return { + ...capability, + acceptInsecureCerts: true, + 'bstack:options': { + ...capability['bstack:options'], + projectName: 'smoke-tests', + buildName: BUILD_NAME, + consoleLogs: 'verbose', + }, + 'tldraw:options': { + ...capability['tldraw:options'], + }, + } + }), + bail: 0, + waitforTimeout: 10000, + connectionRetryTimeout: 120000, + connectionRetryCount: 3, + framework: 'mocha', + reporters: ['spec'], + mochaOpts: { + ui: 'bdd', + timeout: 5 * 60 * 1000, + }, + logLevel: process.env.WD_LOG_LEVEL ?? 'info', + coloredLogs: true, + screenshotPath: './errorShots/', + waitforTimeout: 30000, + connectionRetryTimeout: 90000, + connectionRetryCount: 3, + beforeSession: (_config, capabilities) => { + global.tldrawOptions = capabilities['tldraw:options'] + }, + autoCompileOpts: { + autoCompile: true, + tsNodeOpts: { + transpileOnly: true, + swc: true, + project: './tsconfig.json', + }, + }, +} diff --git a/lazy.config.ts b/lazy.config.ts index 6ab9f2ac3..da26bb25e 100644 --- a/lazy.config.ts +++ b/lazy.config.ts @@ -16,6 +16,11 @@ export function generateSharedScripts(bublic: '' | '/bublic') 'dev-vscode': { runsAfter: { 'build:vscode-editor': {} }, }, + 'dev-webdriver': { + execution: 'independent', + runsAfter: { 'refresh-assets': {}, prebuild: {}, 'build:vscode-editor': {} }, + cache: 'none', + }, test: { baseCommand: 'yarn run -T jest', runsAfter: { 'refresh-assets': {} }, diff --git a/package.json b/package.json index 383b25814..42da40025 100644 --- a/package.json +++ b/package.json @@ -28,6 +28,7 @@ "apps/*", "packages/*", "apps/vscode/*", + "e2e", "config", "scripts" ], @@ -40,6 +41,7 @@ "dev": "lazy run dev --filter='{,bublic/}apps/examples' --filter='{,bublic/}packages/tldraw'", "dev-docs": "lazy run dev-docs", "dev-vscode": "code ./apps/vscode/extension && lazy run dev --filter='{,bublic/}apps/vscode/{extension,editor}'", + "dev-webdriver": "lazy run dev-webdriver --filter='apps/webdriver'", "build-types": "lazy inherit", "build-api": "lazy build-api", "build-package": "lazy build-package", diff --git a/public-yarn.lock b/public-yarn.lock index c6f800bce..61ef3a812 100644 --- a/public-yarn.lock +++ b/public-yarn.lock @@ -1455,6 +1455,15 @@ __metadata: languageName: node linkType: hard +"@babel/runtime@npm:^7.21.0": + version: 7.21.5 + resolution: "@babel/runtime@npm:7.21.5" + dependencies: + regenerator-runtime: ^0.13.11 + checksum: 358f2779d3187f5c67ad302e8f8d435412925d0b991d133c7d4a7b1ddd5a3fda1b6f34537cb64628dfd96a27ae46df105bed3895b8d754b88cacdded8d1129dd + languageName: node + linkType: hard + "@babel/template@npm:^7.18.10, @babel/template@npm:^7.20.7, @babel/template@npm:^7.3.3": version: 7.20.7 resolution: "@babel/template@npm:7.20.7" @@ -2278,6 +2287,85 @@ __metadata: languageName: node linkType: hard +"@fastify/accept-negotiator@npm:^1.0.0": + version: 1.1.0 + resolution: "@fastify/accept-negotiator@npm:1.1.0" + checksum: 5c8f263680af0aece8c1fdea4d4c094a7f82cc5ed90b709357eb52a01e3388d1ac74a17e5a1d5d53f2d3ca93ae50d283ee451a6435b2cbe1b9847fff4d7d0732 + languageName: node + linkType: hard + +"@fastify/ajv-compiler@npm:^3.5.0": + version: 3.5.0 + resolution: "@fastify/ajv-compiler@npm:3.5.0" + dependencies: + ajv: ^8.11.0 + ajv-formats: ^2.1.1 + fast-uri: ^2.0.0 + checksum: 5e5b16469f8d586473d0b32e3a9cf38c0d86ef2a6fb7ea12ed7f3665642bd8eb2dde9adcc317814369cb5a58210bfdac35996fa87d1cc23e88bbc799f0b128b0 + languageName: node + linkType: hard + +"@fastify/cors@npm:^8.2.1": + version: 8.2.1 + resolution: "@fastify/cors@npm:8.2.1" + dependencies: + fastify-plugin: ^4.0.0 + mnemonist: 0.39.5 + checksum: 219edf11ad8397c7c00ac605c2c084f7dd929a13d95255013612275d195971bd23de1a8cf23304ded8bbe0066e2ea3c7f3c5b7ee0b14af2daed2071f0ed5609a + languageName: node + linkType: hard + +"@fastify/deepmerge@npm:^1.0.0": + version: 1.3.0 + resolution: "@fastify/deepmerge@npm:1.3.0" + checksum: 33ec927905dca320d7ae9535a1521909f7c82339706345324ab6287ad100589a799b8257c15b0e582c7bb74e2aa4883d82ba0228d7b116aa8789ada4f78d6974 + languageName: node + linkType: hard + +"@fastify/error@npm:^3.0.0": + version: 3.2.0 + resolution: "@fastify/error@npm:3.2.0" + checksum: e538ef76fd2dedd0584691e0c891997321a2050092b11089a70090f5a0edab0dc8ab069747aa6025782280824e2348548e051c8e77558baec699bd44e581e187 + languageName: node + linkType: hard + +"@fastify/fast-json-stringify-compiler@npm:^4.3.0": + version: 4.3.0 + resolution: "@fastify/fast-json-stringify-compiler@npm:4.3.0" + dependencies: + fast-json-stringify: ^5.7.0 + checksum: 2734afabe2539d3e15d2bd9f8dfee756d9cd969f7303dc085dd91c744ff61742bb0d3ebd3b561cf3c32be54567048a634b4962f943eb6bd9ed3fbd71cbf6a4fa + languageName: node + linkType: hard + +"@fastify/send@npm:^2.0.0": + version: 2.1.0 + resolution: "@fastify/send@npm:2.1.0" + dependencies: + "@lukeed/ms": ^2.0.1 + escape-html: ~1.0.3 + fast-decode-uri-component: ^1.0.1 + http-errors: 2.0.0 + mime: ^3.0.0 + checksum: c0eeddd35c53167e41ee7c25ccbb964e5a1e4a17c827da6486db656581f83f162237d6f8104b698a4a136d8ea054f2d028bfa4c7d1c4126db51584680f43e1f1 + languageName: node + linkType: hard + +"@fastify/static@npm:^6.10.1": + version: 6.10.1 + resolution: "@fastify/static@npm:6.10.1" + dependencies: + "@fastify/accept-negotiator": ^1.0.0 + "@fastify/send": ^2.0.0 + content-disposition: ^0.5.3 + fastify-plugin: ^4.0.0 + glob: ^8.0.1 + p-limit: ^3.1.0 + readable-stream: ^4.0.0 + checksum: c86a6e8d3b0bb8a8f649e0f6b378024896892b71a6a477352089106f4ad863bdd52f30cfd1853a9fd80f639d10d1a9bf03fc091d73562738e4ddddcc29146c14 + languageName: node + linkType: hard + "@floating-ui/core@npm:^0.7.3": version: 0.7.3 resolution: "@floating-ui/core@npm:0.7.3" @@ -2339,6 +2427,20 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: ^5.1.2 + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: ^7.0.1 + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: ^8.1.0 + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 4a473b9b32a7d4d3cfb7a614226e555091ff0c5a29a1734c28c72a182c2f6699b26fc6b5c2131dfd841e86b185aea714c72201d7c98c2fba5f17709333a67aeb + languageName: node + linkType: hard + "@istanbuljs/load-nyc-config@npm:^1.0.0": version: 1.1.0 resolution: "@istanbuljs/load-nyc-config@npm:1.1.0" @@ -2736,6 +2838,13 @@ __metadata: languageName: node linkType: hard +"@lukeed/ms@npm:^2.0.1": + version: 2.0.1 + resolution: "@lukeed/ms@npm:2.0.1" + checksum: c7b46933bf7bad3e024dcbbe2ad6201392b4ed2a05a717c0ef7e96a03fb885d44f08b4b749c392cc51c2736a6a45a08c77f1863ace1c072928fbfd9908a13db3 + languageName: node + linkType: hard + "@mapbox/node-pre-gyp@npm:^1.0.5": version: 1.0.10 resolution: "@mapbox/node-pre-gyp@npm:1.0.10" @@ -3265,6 +3374,13 @@ __metadata: languageName: node linkType: hard +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 6ad6a00fc4f2f2cfc6bff76fb1d88b8ee20bc0601e18ebb01b6d4be583733a860239a521a7fbca73b612e66705078809483549d2b18f370eb346c5155c8e4a0f + languageName: node + linkType: hard + "@pkgr/utils@npm:^2.3.1": version: 2.3.1 resolution: "@pkgr/utils@npm:2.3.1" @@ -3279,6 +3395,29 @@ __metadata: languageName: node linkType: hard +"@puppeteer/browsers@npm:0.5.0": + version: 0.5.0 + resolution: "@puppeteer/browsers@npm:0.5.0" + dependencies: + debug: 4.3.4 + extract-zip: 2.0.1 + https-proxy-agent: 5.0.1 + progress: 2.0.3 + proxy-from-env: 1.1.0 + tar-fs: 2.1.1 + unbzip2-stream: 1.4.3 + yargs: 17.7.1 + peerDependencies: + typescript: ">= 4.7.4" + peerDependenciesMeta: + typescript: + optional: true + bin: + browsers: lib/cjs/main-cli.js + checksum: d75fde03be4be106ca907834739251c2bb0b33a09fa23315c5dbe8b8b4cfed2f1b26af62e1dbe5fccc227e9bc87b51da0815461b982477eb01439bfdd6e7b01a + languageName: node + linkType: hard + "@radix-ui/number@npm:1.0.0": version: 1.0.0 resolution: "@radix-ui/number@npm:1.0.0" @@ -4035,6 +4174,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/is@npm:^0.7.0": + version: 0.7.0 + resolution: "@sindresorhus/is@npm:0.7.0" + checksum: decc50f6fe80b75c981bcff0a585c05259f5e04424a46a653ac9a7e065194145c463ca81001e3a229bd203f59474afadb5b1fa0af5507723f87f2dd45bd3897c + languageName: node + linkType: hard + "@sindresorhus/is@npm:^4.0.0": version: 4.6.0 resolution: "@sindresorhus/is@npm:4.6.0" @@ -4042,6 +4188,13 @@ __metadata: languageName: node linkType: hard +"@sindresorhus/is@npm:^5.2.0": + version: 5.3.0 + resolution: "@sindresorhus/is@npm:5.3.0" + checksum: b31cebabcdece3d5322de2a4dbc8c0f004e04147a00f2606787bcaf5655ad4b1954f6727fc6914c524009b2b9a2cc01c42835b55f651ce69fd2a0083b60bb852 + languageName: node + linkType: hard + "@sindresorhus/slugify@npm:^2.2.0": version: 2.2.0 resolution: "@sindresorhus/slugify@npm:2.2.0" @@ -4097,6 +4250,16 @@ __metadata: languageName: node linkType: hard +"@sitespeed.io/edgedriver@npm:^112.0.1722-34": + version: 112.0.1722-34 + resolution: "@sitespeed.io/edgedriver@npm:112.0.1722-34" + dependencies: + node-downloader-helper: 2.1.6 + node-stream-zip: 1.15.0 + checksum: 0bf4722d0b89f962a3cf498651c8796a013b86849a734327f0d853a917294df01b72fe315a0bb40c8d05c59a3b7619bdfbe00ccf0721a091b4c891e26e7690cb + languageName: node + linkType: hard + "@swc/core-darwin-arm64@npm:1.3.55": version: 1.3.55 resolution: "@swc/core-darwin-arm64@npm:1.3.55" @@ -4241,6 +4404,22 @@ __metadata: languageName: node linkType: hard +"@szmarczak/http-timer@npm:^5.0.1": + version: 5.0.1 + resolution: "@szmarczak/http-timer@npm:5.0.1" + dependencies: + defer-to-connect: ^2.0.1 + checksum: fc9cb993e808806692e4a3337c90ece0ec00c89f4b67e3652a356b89730da98bc824273a6d67ca84d5f33cd85f317dcd5ce39d8cc0a2f060145a608a7cb8ce92 + languageName: node + linkType: hard + +"@testim/chrome-version@npm:^1.1.3": + version: 1.1.3 + resolution: "@testim/chrome-version@npm:1.1.3" + checksum: 0874590ae515c2e9e80d62130cd9be070932b60724cef93217c6d2d62f2776a2a9cbc4ef3548e674f57236a4c75f322ce0df7b5ecfecbc8d8b5e3eeaee92391c + languageName: node + linkType: hard + "@testing-library/dom@npm:^8.0.0": version: 8.20.0 resolution: "@testing-library/dom@npm:8.20.0" @@ -4368,6 +4547,38 @@ __metadata: languageName: unknown linkType: soft +"@tldraw/e2e@workspace:e2e": + version: 0.0.0-use.local + resolution: "@tldraw/e2e@workspace:e2e" + dependencies: + "@sitespeed.io/edgedriver": ^112.0.1722-34 + "@tldraw/editor": "workspace:*" + "@tldraw/primitives": "workspace:*" + "@types/mocha": ^10.0.1 + "@types/sharp": ^0.31.1 + "@wdio/browserstack-service": ^8.1.3 + "@wdio/cli": ^8.1.3 + "@wdio/globals": ^8.1.3 + "@wdio/local-runner": ^8.1.2 + "@wdio/mocha-framework": ^8.1.2 + "@wdio/spec-reporter": ^8.1.2 + chromedriver: ^112.0.0 + geckodriver: ^3.2.0 + lazyrepo: 0.0.0-alpha.26 + msedgedriver: ^91.0.0 + pixelmatch: ^5.3.0 + pngjs: ^6.0.0 + sharp: ^0.31.2 + ts-node-dev: ^2.0.0 + ua-parser-js: ^1.0.33 + wdio-chromedriver-service: ^8.0.1 + wdio-edgedriver-service: ^2.1.2 + wdio-geckodriver-service: ^4.1.1 + wdio-safaridriver-service: ^2.1.0 + wdio-vscode-service: ^5.0.0 + languageName: unknown + linkType: soft + "@tldraw/editor@workspace:*, @tldraw/editor@workspace:packages/editor": version: 0.0.0-use.local resolution: "@tldraw/editor@workspace:packages/editor" @@ -4888,6 +5099,13 @@ __metadata: languageName: node linkType: hard +"@types/gitconfiglocal@npm:^2.0.1": + version: 2.0.1 + resolution: "@types/gitconfiglocal@npm:2.0.1" + checksum: a36531a2fe0811839a490b40ec316d065ac704a5c0824ba29d343e07124f61c79da8543b73f65562501d8746b31c69e151ed4d13b958a5fae9a79e20f01b1250 + languageName: node + linkType: hard + "@types/glob@npm:^7.1.1": version: 7.2.0 resolution: "@types/glob@npm:7.2.0" @@ -4933,13 +5151,22 @@ __metadata: languageName: node linkType: hard -"@types/http-cache-semantics@npm:*": +"@types/http-cache-semantics@npm:*, @types/http-cache-semantics@npm:^4.0.1": version: 4.0.1 resolution: "@types/http-cache-semantics@npm:4.0.1" checksum: 1048aacf627829f0d5f00184e16548205cd9f964bf0841c29b36bc504509230c40bc57c39778703a1c965a6f5b416ae2cbf4c1d4589c889d2838dd9dbfccf6e9 languageName: node linkType: hard +"@types/ip@npm:^1.1.0": + version: 1.1.0 + resolution: "@types/ip@npm:1.1.0" + dependencies: + "@types/node": "*" + checksum: 05e1ef8b525e2f2d73f5cde7ef1e0e7aa1b027946e066b4063f9c8c2fde59a13762ec703e6df32e2db31db1dc3a54f46a7516bd6d970200d175ec7e9700bb83e + languageName: node + linkType: hard + "@types/is-ci@npm:^3.0.0": version: 3.0.0 resolution: "@types/is-ci@npm:3.0.0" @@ -5046,7 +5273,7 @@ __metadata: languageName: node linkType: hard -"@types/keyv@npm:^3.1.4": +"@types/keyv@npm:^3.1.1, @types/keyv@npm:^3.1.4": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" dependencies: @@ -5126,6 +5353,13 @@ __metadata: languageName: node linkType: hard +"@types/mocha@npm:^10.0.0, @types/mocha@npm:^10.0.1": + version: 10.0.1 + resolution: "@types/mocha@npm:10.0.1" + checksum: 224ea9fce7b1734ccdb9aa99a622d902a538ce1847bca7fd22c5fb38adcf3ed536f50f48f587085db988a4bb3c2eb68f4b98e1cd6a38bc5547bd3bbbedc54495 + languageName: node + linkType: hard + "@types/ms@npm:*": version: 0.7.31 resolution: "@types/ms@npm:0.7.31" @@ -5143,6 +5377,15 @@ __metadata: languageName: node linkType: hard +"@types/node-forge@npm:^1.3.1": + version: 1.3.2 + resolution: "@types/node-forge@npm:1.3.2" + dependencies: + "@types/node": "*" + checksum: f532326a616e946e5f6733d1461e7b8d31911bbdfbc480a6337c422053d79c1e22a0776d114a325439e26ae382a640f939d0abf156e24a3c3ca5079d04aa73f6 + languageName: node + linkType: hard + "@types/node@npm:*, @types/node@npm:^18.13.0": version: 18.15.12 resolution: "@types/node@npm:18.15.12" @@ -5178,6 +5421,20 @@ __metadata: languageName: node linkType: hard +"@types/node@npm:^18.0.0": + version: 18.16.4 + resolution: "@types/node@npm:18.16.4" + checksum: 21d575391c88be9f3568675de59d571597cfc122efa32939ae2697c2218c6ccfb6ba0d4b5d9301905e6c4964f43add42569249b15d2e12d68b0aedaf891ec9a5 + languageName: node + linkType: hard + +"@types/normalize-package-data@npm:^2.4.1": + version: 2.4.1 + resolution: "@types/normalize-package-data@npm:2.4.1" + checksum: e87bccbf11f95035c89a132b52b79ce69a1e3652fe55962363063c9c0dae0fe2477ebc585e03a9652adc6f381d24ba5589cc5e51849df4ced3d3e004a7d40ed5 + languageName: node + linkType: hard + "@types/parse-json@npm:^4.0.0": version: 4.0.0 resolution: "@types/parse-json@npm:4.0.0" @@ -5299,6 +5556,24 @@ __metadata: languageName: node linkType: hard +"@types/sharp@npm:^0.31.1": + version: 0.31.1 + resolution: "@types/sharp@npm:0.31.1" + dependencies: + "@types/node": "*" + checksum: 226871181fc88b5ef8a6bc32c1e14a3426cc45480ed49536c45bb5c166c089169b8fe3e5c57aea8c34cc40b08311a95d5582c1a2f540f4425eb66fea3d6e0489 + languageName: node + linkType: hard + +"@types/split2@npm:^3.2.1": + version: 3.2.1 + resolution: "@types/split2@npm:3.2.1" + dependencies: + "@types/node": "*" + checksum: 3c625ad8bdf937650f8533af85b4de22b21dc474e0867bb36dcf91da8bef0af80e18821736581c22e2d402b8c14548e00d0b5dc885a62feae72a5eec04b36d05 + languageName: node + linkType: hard + "@types/stack-utils@npm:^2.0.0": version: 2.0.1 resolution: "@types/stack-utils@npm:2.0.1" @@ -5320,6 +5595,13 @@ __metadata: languageName: node linkType: hard +"@types/tcp-port-used@npm:^1.0.1": + version: 1.0.1 + resolution: "@types/tcp-port-used@npm:1.0.1" + checksum: 5b9df0fed67d214d14dd74c29c6d8f3d4d74533ec73d5150ccea92fea80ffe0f25424fa38dcefec58dee600d1038461f527397fe69ffef11542326385570759a + languageName: node + linkType: hard + "@types/testing-library__jest-dom@npm:^5.9.1": version: 5.14.5 resolution: "@types/testing-library__jest-dom@npm:5.14.5" @@ -5350,6 +5632,13 @@ __metadata: languageName: node linkType: hard +"@types/which@npm:^2.0.1": + version: 2.0.2 + resolution: "@types/which@npm:2.0.2" + checksum: 8626a3c2f6db676c449142e1082e33ea0c9d88b8a2bd796366b944891e6da0088b2aa83d3fa9c79e6696f7381a851fc76d43bd353eb6c4d98a7775b4ae0a96a5 + languageName: node + linkType: hard + "@types/wicg-file-system-access@npm:^2020.9.5": version: 2020.9.6 resolution: "@types/wicg-file-system-access@npm:2020.9.6" @@ -5357,6 +5646,15 @@ __metadata: languageName: node linkType: hard +"@types/ws@npm:^8.5.3, @types/ws@npm:^8.5.4": + version: 8.5.4 + resolution: "@types/ws@npm:8.5.4" + dependencies: + "@types/node": "*" + checksum: fefbad20d211929bb996285c4e6f699b12192548afedbe4930ab4384f8a94577c9cd421acaad163cacd36b88649509970a05a0b8f20615b30c501ed5269038d1 + languageName: node + linkType: hard + "@types/yargs-parser@npm:*": version: 21.0.0 resolution: "@types/yargs-parser@npm:21.0.0" @@ -5382,6 +5680,15 @@ __metadata: languageName: node linkType: hard +"@types/yauzl@npm:^2.9.1": + version: 2.10.0 + resolution: "@types/yauzl@npm:2.10.0" + dependencies: + "@types/node": "*" + checksum: 55d27ae5d346ea260e40121675c24e112ef0247649073848e5d4e03182713ae4ec8142b98f61a1c6cbe7d3b72fa99bbadb65d8b01873e5e605cdc30f1ff70ef2 + languageName: node + linkType: hard + "@typescript-eslint/eslint-plugin@npm:^5.10.2, @typescript-eslint/eslint-plugin@npm:^5.57.0": version: 5.59.0 resolution: "@typescript-eslint/eslint-plugin@npm:5.59.0" @@ -5769,6 +6076,228 @@ __metadata: languageName: node linkType: hard +"@vscode/test-electron@npm:^2.3.0": + version: 2.3.0 + resolution: "@vscode/test-electron@npm:2.3.0" + dependencies: + http-proxy-agent: ^4.0.1 + https-proxy-agent: ^5.0.0 + jszip: ^3.10.1 + semver: ^7.3.8 + checksum: 745679a14ca651a901dff301fae20d0636ce16357dbde511cece726179d27e4fa4c42acf5abcb64c1768f8afe141b2a5884375d1fc35a086d770a19c0260b21b + languageName: node + linkType: hard + +"@wdio/browserstack-service@npm:^8.1.3": + version: 8.10.0 + resolution: "@wdio/browserstack-service@npm:8.10.0" + dependencies: + "@types/gitconfiglocal": ^2.0.1 + "@wdio/logger": 8.6.6 + "@wdio/reporter": 8.10.0 + "@wdio/types": 8.10.0 + browserstack-local: ^1.5.1 + form-data: ^4.0.0 + git-repo-info: ^2.1.1 + gitconfiglocal: ^2.1.0 + got: ^12.1.0 + uuid: ^8.3.2 + webdriverio: 8.10.0 + peerDependencies: + "@wdio/cli": ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0 + checksum: 93e19782e68f4780e5fc1e55053f7ba3c9cc968698a8e6130cdfed47cdf12b1bac3147b0edf31d4d763f88b606b92030d617ab296081a0f0880a953752b6aae6 + languageName: node + linkType: hard + +"@wdio/cli@npm:^8.1.3": + version: 8.10.0 + resolution: "@wdio/cli@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + "@wdio/config": 8.10.0 + "@wdio/globals": 8.10.0 + "@wdio/logger": 8.6.6 + "@wdio/protocols": 8.8.1 + "@wdio/types": 8.10.0 + "@wdio/utils": 8.10.0 + async-exit-hook: ^2.0.1 + chalk: ^5.0.1 + chokidar: ^3.5.3 + cli-spinners: ^2.6.1 + ejs: ^3.1.8 + execa: ^7.0.0 + import-meta-resolve: ^3.0.0 + inquirer: 9.1.5 + lodash.flattendeep: ^4.4.0 + lodash.pickby: ^4.6.0 + lodash.union: ^4.6.0 + read-pkg-up: 9.1.0 + recursive-readdir: ^2.2.2 + webdriverio: 8.10.0 + yargs: ^17.5.1 + yarn-install: ^1.0.0 + bin: + wdio: bin/wdio.js + checksum: c073329836ace551aa75e4696bacf404b9a699bbb37da0e92445fd196a7c2a06a05ae2b276dcbb60fa5c820d310395412f2dc1bf56cba197b3c043218e36e84d + languageName: node + linkType: hard + +"@wdio/config@npm:8.10.0": + version: 8.10.0 + resolution: "@wdio/config@npm:8.10.0" + dependencies: + "@wdio/logger": 8.6.6 + "@wdio/types": 8.10.0 + "@wdio/utils": 8.10.0 + decamelize: ^6.0.0 + deepmerge-ts: ^5.0.0 + glob: ^10.2.2 + import-meta-resolve: ^3.0.0 + read-pkg-up: ^9.1.0 + checksum: 4391eeee973dcb01cdd76774ee9abdaad05c6d466943a41545d47a920a346c50dbfe67b27b0efb531242fd693ed2bbee3df1dfc88cf143044986148c85e36a19 + languageName: node + linkType: hard + +"@wdio/globals@npm:8.10.0, @wdio/globals@npm:^8.1.3, @wdio/globals@npm:^8.8.8": + version: 8.10.0 + resolution: "@wdio/globals@npm:8.10.0" + dependencies: + expect-webdriverio: ^4.0.1 + webdriverio: 8.10.0 + dependenciesMeta: + expect-webdriverio: + optional: true + webdriverio: + optional: true + checksum: e4a54d3bb622eee3d5bf53a3f5858d87b2fa6fabd78497da72de2159b8b8866b6602345b222265a88b9f29e7487075a926f25051e8c1b905abb078bf515610c5 + languageName: node + linkType: hard + +"@wdio/local-runner@npm:^8.1.2": + version: 8.10.0 + resolution: "@wdio/local-runner@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + "@wdio/logger": 8.6.6 + "@wdio/repl": 8.6.6 + "@wdio/runner": 8.10.0 + "@wdio/types": 8.10.0 + async-exit-hook: ^2.0.1 + split2: ^4.1.0 + stream-buffers: ^3.0.2 + checksum: 242bc53606b236b18fefcadccf92592a6b213c4c43e5ed2b14a555873057e3ada28477a972408d355cb78c5d7d221a5622b055d4fb67f768761b3a48a9f0673f + languageName: node + linkType: hard + +"@wdio/logger@npm:8.6.6, @wdio/logger@npm:^8.1.0, @wdio/logger@npm:^8.6.6": + version: 8.6.6 + resolution: "@wdio/logger@npm:8.6.6" + dependencies: + chalk: ^5.1.2 + loglevel: ^1.6.0 + loglevel-plugin-prefix: ^0.8.4 + strip-ansi: ^6.0.0 + checksum: b17effd00f0b5f4450b83e4d65e0f29bc60bc19a53b9c44fc3569e14bd53bcf6c0dfc8da517ddf639da503c1e68491ec20f97fddbb78b34d8b20136f7a60a4fe + languageName: node + linkType: hard + +"@wdio/mocha-framework@npm:^8.1.2": + version: 8.10.0 + resolution: "@wdio/mocha-framework@npm:8.10.0" + dependencies: + "@types/mocha": ^10.0.0 + "@types/node": ^18.0.0 + "@wdio/logger": 8.6.6 + "@wdio/types": 8.10.0 + "@wdio/utils": 8.10.0 + mocha: ^10.0.0 + checksum: 1dd18d633b43f40cb85a6eba2ec8d2a9e4bfba657da2dbee9142ce1b75a11ef47a514dab96973d8d4d2b0b2caac07f6a84b30819bcb7de51f3d82013d2e1a80b + languageName: node + linkType: hard + +"@wdio/protocols@npm:8.8.1": + version: 8.8.1 + resolution: "@wdio/protocols@npm:8.8.1" + checksum: 1be0bdef4cea1f0b6e70eb2027d4ee327f9e6c43f9f48df37924ec094644a559e1d66a87ee7bd22ef8c27f59c6382653bd2ed6ee90b4840777221e6ad730fb39 + languageName: node + linkType: hard + +"@wdio/repl@npm:8.6.6": + version: 8.6.6 + resolution: "@wdio/repl@npm:8.6.6" + dependencies: + "@types/node": ^18.0.0 + checksum: fdc24557caaeb925cac762c874c50bd46961eb1bc3b1b47f885fd03ed2341455b0ff14252fbce32523d5d6f056046b6f218eb05df211c7efd4f4243dcfdb1940 + languageName: node + linkType: hard + +"@wdio/reporter@npm:8.10.0": + version: 8.10.0 + resolution: "@wdio/reporter@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + "@wdio/logger": 8.6.6 + "@wdio/types": 8.10.0 + diff: ^5.0.0 + object-inspect: ^1.12.0 + supports-color: 9.3.1 + checksum: f51978de9d9cb0d699569c71f585972aa3a66ad2632cef0abd6f72e38ef937234c71d0e518033eb6fa08a8762489fa06e2059915c97758c75e12490a4117eaca + languageName: node + linkType: hard + +"@wdio/runner@npm:8.10.0": + version: 8.10.0 + resolution: "@wdio/runner@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + "@wdio/config": 8.10.0 + "@wdio/globals": 8.10.0 + "@wdio/logger": 8.6.6 + "@wdio/types": 8.10.0 + "@wdio/utils": 8.10.0 + deepmerge-ts: ^5.0.0 + expect-webdriverio: ^4.0.1 + gaze: ^1.1.2 + webdriver: 8.10.0 + webdriverio: 8.10.0 + checksum: 508fa6574ba940b20848c17879bfcf9548adca0200345181749676f004c8110c597f22c8fb8b595c589c9406005b71c8661a8bb198196cdff0fe950cdc53e476 + languageName: node + linkType: hard + +"@wdio/spec-reporter@npm:^8.1.2": + version: 8.10.0 + resolution: "@wdio/spec-reporter@npm:8.10.0" + dependencies: + "@wdio/reporter": 8.10.0 + "@wdio/types": 8.10.0 + chalk: ^5.1.2 + easy-table: ^1.2.0 + pretty-ms: ^7.0.0 + checksum: ed08f1e4d6a0da4ee414a16d9bc2903f802f65785e9652838ae4800fcce10031d720b137eeaf3c7352aa2843c4e28c8043415f03f2ed4821796c225a88156cc1 + languageName: node + linkType: hard + +"@wdio/types@npm:8.10.0": + version: 8.10.0 + resolution: "@wdio/types@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + checksum: 59b7d4651888f394145232a10978a79c46d4b8682bf80a6c7c8288bff7828d96c21924c6d378c43647b6af756280ccdb06da540268a91eb334fa4a008bbf037d + languageName: node + linkType: hard + +"@wdio/utils@npm:8.10.0": + version: 8.10.0 + resolution: "@wdio/utils@npm:8.10.0" + dependencies: + "@wdio/logger": 8.6.6 + "@wdio/types": 8.10.0 + import-meta-resolve: ^3.0.0 + p-iteration: ^1.1.8 + checksum: 9d29382492e3009af7f1b60b6902234400f4412eaf6cc03e33d2bf8d382e293ec154f82b80206b306f8d3c2402cbb5bb075ffbb1ec8903ba36928d27026c9b34 + languageName: node + linkType: hard + "@web3-storage/multipart-parser@npm:^1.0.0": version: 1.0.0 resolution: "@web3-storage/multipart-parser@npm:1.0.0" @@ -5790,6 +6319,22 @@ __metadata: languageName: node linkType: hard +"abort-controller@npm:^3.0.0": + version: 3.0.0 + resolution: "abort-controller@npm:3.0.0" + dependencies: + event-target-shim: ^5.0.0 + checksum: 170bdba9b47b7e65906a28c8ce4f38a7a369d78e2271706f020849c1bfe0ee2067d4261df8bbb66eb84f79208fd5b710df759d64191db58cfba7ce8ef9c54b75 + languageName: node + linkType: hard + +"abstract-logging@npm:^2.0.1": + version: 2.0.1 + resolution: "abstract-logging@npm:2.0.1" + checksum: 6967d15e5abbafd17f56eaf30ba8278c99333586fa4f7935fd80e93cfdc006c37fcc819c5d63ee373a12e6cb2d0417f7c3c6b9e42b957a25af9937d26749415e + languageName: node + linkType: hard + "accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" @@ -5861,6 +6406,13 @@ __metadata: languageName: node linkType: hard +"adm-zip@npm:0.5.9": + version: 0.5.9 + resolution: "adm-zip@npm:0.5.9" + checksum: 4909bc04119fdd5e8f8ba43826e50623e5c427cf0a939c809d4f9456a6a03c6aa0544e82088739de9ba16b7649f71b99ccbbddc7b2c38bd6143f9c4726cc7ed9 + languageName: node + linkType: hard + "agent-base@npm:6, agent-base@npm:^6.0.0, agent-base@npm:^6.0.2": version: 6.0.2 resolution: "agent-base@npm:6.0.2" @@ -5898,6 +6450,20 @@ __metadata: languageName: node linkType: hard +"ajv-formats@npm:^2.1.1": + version: 2.1.1 + resolution: "ajv-formats@npm:2.1.1" + dependencies: + ajv: ^8.0.0 + peerDependencies: + ajv: ^8.0.0 + peerDependenciesMeta: + ajv: + optional: true + checksum: 4a287d937f1ebaad4683249a4c40c0fa3beed30d9ddc0adba04859026a622da0d317851316ea64b3680dc60f5c3c708105ddd5d5db8fe595d9d0207fd19f90b7 + languageName: node + linkType: hard + "ajv@npm:8.6.3": version: 8.6.3 resolution: "ajv@npm:8.6.3" @@ -5910,7 +6476,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.0.0, ajv@npm:^6.10.0, ajv@npm:^6.12.4, ajv@npm:~6.12.6": +"ajv@npm:^6.0.0, ajv@npm:^6.10.0, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:~6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -5922,6 +6488,18 @@ __metadata: languageName: node linkType: hard +"ajv@npm:^8.0.0, ajv@npm:^8.10.0, ajv@npm:^8.11.0": + version: 8.12.0 + resolution: "ajv@npm:8.12.0" + dependencies: + fast-deep-equal: ^3.1.1 + json-schema-traverse: ^1.0.0 + require-from-string: ^2.0.2 + uri-js: ^4.2.2 + checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 + languageName: node + linkType: hard + "ansi-colors@npm:4.1.1": version: 4.1.1 resolution: "ansi-colors@npm:4.1.1" @@ -5954,6 +6532,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^2.0.0": + version: 2.1.1 + resolution: "ansi-regex@npm:2.1.1" + checksum: 190abd03e4ff86794f338a31795d262c1dfe8c91f7e01d04f13f646f1dcb16c5800818f886047876f1272f065570ab86b24b99089f8b68a0e11ff19aed4ca8f1 + languageName: node + linkType: hard + "ansi-regex@npm:^4.1.0": version: 4.1.1 resolution: "ansi-regex@npm:4.1.1" @@ -5975,6 +6560,13 @@ __metadata: languageName: node linkType: hard +"ansi-styles@npm:^2.2.1": + version: 2.2.1 + resolution: "ansi-styles@npm:2.2.1" + checksum: ebc0e00381f2a29000d1dac8466a640ce11943cef3bda3cd0020dc042e31e1058ab59bf6169cd794a54c3a7338a61ebc404b7c91e004092dd20e028c432c9c2c + languageName: node + linkType: hard + "ansi-styles@npm:^3.2.0, ansi-styles@npm:^3.2.1": version: 3.2.1 resolution: "ansi-styles@npm:3.2.1" @@ -6024,6 +6616,62 @@ __metadata: languageName: node linkType: hard +"arch@npm:^2.2.0": + version: 2.2.0 + resolution: "arch@npm:2.2.0" + checksum: e21b7635029fe8e9cdd5a026f9a6c659103e63fff423834323cdf836a1bb240a72d0c39ca8c470f84643385cf581bd8eda2cad8bf493e27e54bd9783abe9101f + languageName: node + linkType: hard + +"archive-type@npm:^4.0.0": + version: 4.0.0 + resolution: "archive-type@npm:4.0.0" + dependencies: + file-type: ^4.2.0 + checksum: 271f0d118294dd0305831f0700b635e8a9475f97693212d548eee48017f917e14349a25ad578f8e13486ba4b7cde1972d53e613d980e8738cfccea5fc626c76f + languageName: node + linkType: hard + +"archiver-utils@npm:^2.1.0": + version: 2.1.0 + resolution: "archiver-utils@npm:2.1.0" + dependencies: + glob: ^7.1.4 + graceful-fs: ^4.2.0 + lazystream: ^1.0.0 + lodash.defaults: ^4.2.0 + lodash.difference: ^4.5.0 + lodash.flatten: ^4.4.0 + lodash.isplainobject: ^4.0.6 + lodash.union: ^4.6.0 + normalize-path: ^3.0.0 + readable-stream: ^2.0.0 + checksum: 5665f40bde87ee82cb638177bdccca8cc6e55edea1b94338f7e6b56a1d9367b0d9a39e42b47866eaf84b8c67669a7d250900a226207ecc30fa163b52aae859a5 + languageName: node + linkType: hard + +"archiver@npm:^5.0.0": + version: 5.3.1 + resolution: "archiver@npm:5.3.1" + dependencies: + archiver-utils: ^2.1.0 + async: ^3.2.3 + buffer-crc32: ^0.2.1 + readable-stream: ^3.6.0 + readdir-glob: ^1.0.0 + tar-stream: ^2.2.0 + zip-stream: ^4.1.0 + checksum: 905b198ed04d26c951b80545d45c7f2e0432ef89977a93af8a762501d659886e39dda0fbffb0d517ff3fa450a3d09a29146e4273c2170624e1988f889fb5302c + languageName: node + linkType: hard + +"archy@npm:^1.0.0": + version: 1.0.0 + resolution: "archy@npm:1.0.0" + checksum: 504ae7af655130bab9f471343cfdb054feaec7d8e300e13348bc9fe9e660f83d422e473069584f73233c701ae37d1c8452ff2522f2a20c38849e0f406f1732ac + languageName: node + linkType: hard + "are-we-there-yet@npm:^2.0.0": version: 2.0.0 resolution: "are-we-there-yet@npm:2.0.0" @@ -6196,6 +6844,15 @@ __metadata: languageName: node linkType: hard +"asn1@npm:~0.2.3": + version: 0.2.6 + resolution: "asn1@npm:0.2.6" + dependencies: + safer-buffer: ~2.1.0 + checksum: 39f2ae343b03c15ad4f238ba561e626602a3de8d94ae536c46a4a93e69578826305366dc09fbb9b56aec39b4982a463682f259c38e59f6fa380cd72cd61e493d + languageName: node + linkType: hard + "asn1js@npm:^3.0.1, asn1js@npm:^3.0.5": version: 3.0.5 resolution: "asn1js@npm:3.0.5" @@ -6207,6 +6864,13 @@ __metadata: languageName: node linkType: hard +"assert-plus@npm:1.0.0, assert-plus@npm:^1.0.0": + version: 1.0.0 + resolution: "assert-plus@npm:1.0.0" + checksum: 19b4340cb8f0e6a981c07225eacac0e9d52c2644c080198765d63398f0075f83bbc0c8e95474d54224e297555ad0d631c1dcd058adb1ddc2437b41a6b424ac64 + languageName: node + linkType: hard + "assert@npm:^2.0.0": version: 2.0.0 resolution: "assert@npm:2.0.0" @@ -6269,6 +6933,13 @@ __metadata: languageName: node linkType: hard +"async-exit-hook@npm:^2.0.1": + version: 2.0.1 + resolution: "async-exit-hook@npm:2.0.1" + checksum: b72cbdd19ea90fa33a3a57b0dbff83e4bf2f4e4acd70b2b3847a588f9f16a45d38590ee13f285375dd919c224f60fa58dc3d315a87678d3aa24ff686d1c0200a + languageName: node + linkType: hard + "async-listen@npm:1.2.0": version: 1.2.0 resolution: "async-listen@npm:1.2.0" @@ -6290,6 +6961,13 @@ __metadata: languageName: node linkType: hard +"async@npm:^3.2.3": + version: 3.2.4 + resolution: "async@npm:3.2.4" + checksum: 43d07459a4e1d09b84a20772414aa684ff4de085cbcaec6eea3c7a8f8150e8c62aa6cd4e699fe8ee93c3a5b324e777d34642531875a0817a35697522c1b02e89 + languageName: node + linkType: hard + "asynckit@npm:^0.4.0": version: 0.4.0 resolution: "asynckit@npm:0.4.0" @@ -6297,6 +6975,13 @@ __metadata: languageName: node linkType: hard +"atomic-sleep@npm:^1.0.0": + version: 1.0.0 + resolution: "atomic-sleep@npm:1.0.0" + checksum: b95275afb2f80732f22f43a60178430c468906a415a7ff18bcd0feeebc8eec3930b51250aeda91a476062a90e07132b43a1794e8d8ffcf9b650e8139be75fa36 + languageName: node + linkType: hard + "author-regex@npm:^1.0.0": version: 1.0.0 resolution: "author-regex@npm:1.0.0" @@ -6333,6 +7018,17 @@ __metadata: languageName: node linkType: hard +"avvio@npm:^8.2.0": + version: 8.2.1 + resolution: "avvio@npm:8.2.1" + dependencies: + archy: ^1.0.0 + debug: ^4.0.0 + fastq: ^1.6.1 + checksum: 4c96922ea123d13b26cb78a071a8989fde62ee8580352b6d2f05b7976ed3d23efa663c12ee1be35501dfe65e12a769a2ea522bcdb7ca35a5ba4d86766467075a + languageName: node + linkType: hard + "await-to-js@npm:^3.0.0": version: 3.0.0 resolution: "await-to-js@npm:3.0.0" @@ -6340,6 +7036,20 @@ __metadata: languageName: node linkType: hard +"aws-sign2@npm:~0.7.0": + version: 0.7.0 + resolution: "aws-sign2@npm:0.7.0" + checksum: b148b0bb0778098ad8cf7e5fc619768bcb51236707ca1d3e5b49e41b171166d8be9fdc2ea2ae43d7decf02989d0aaa3a9c4caa6f320af95d684de9b548a71525 + languageName: node + linkType: hard + +"aws4@npm:^1.8.0": + version: 1.12.0 + resolution: "aws4@npm:1.12.0" + checksum: 68f79708ac7c335992730bf638286a3ee0a645cf12575d557860100767c500c08b30e24726b9f03265d74116417f628af78509e1333575e9f8d52a80edfe8cbc + languageName: node + linkType: hard + "axe-core@npm:^4.6.2": version: 4.7.0 resolution: "axe-core@npm:4.7.0" @@ -6347,6 +7057,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.2.1": + version: 1.4.0 + resolution: "axios@npm:1.4.0" + dependencies: + follow-redirects: ^1.15.0 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: 7fb6a4313bae7f45e89d62c70a800913c303df653f19eafec88e56cea2e3821066b8409bc68be1930ecca80e861c52aa787659df0ffec6ad4d451c7816b9386b + languageName: node + linkType: hard + "axobject-query@npm:^3.1.1": version: 3.1.1 resolution: "axobject-query@npm:3.1.1" @@ -6506,6 +7227,15 @@ __metadata: languageName: node linkType: hard +"bcrypt-pbkdf@npm:^1.0.0": + version: 1.0.2 + resolution: "bcrypt-pbkdf@npm:1.0.2" + dependencies: + tweetnacl: ^0.14.3 + checksum: 4edfc9fe7d07019609ccf797a2af28351736e9d012c8402a07120c4453a3b789a15f2ee1530dc49eee8f7eb9379331a8dd4b3766042b9e502f74a68e7f662291 + languageName: node + linkType: hard + "before-after-hook@npm:^2.2.0": version: 2.2.3 resolution: "before-after-hook@npm:2.2.3" @@ -6546,6 +7276,16 @@ __metadata: languageName: node linkType: hard +"bl@npm:^1.0.0": + version: 1.2.3 + resolution: "bl@npm:1.2.3" + dependencies: + readable-stream: ^2.3.5 + safe-buffer: ^5.1.1 + checksum: 123f097989ce2fa9087ce761cd41176aaaec864e28f7dfe5c7dab8ae16d66d9844f849c3ad688eb357e3c5e4f49b573e3c0780bb8bc937206735a3b6f8569a5f + languageName: node + linkType: hard + "bl@npm:^4.0.3, bl@npm:^4.1.0": version: 4.1.0 resolution: "bl@npm:4.1.0" @@ -6568,6 +7308,13 @@ __metadata: languageName: node linkType: hard +"bluebird@npm:3.7.2": + version: 3.7.2 + resolution: "bluebird@npm:3.7.2" + checksum: 869417503c722e7dc54ca46715f70e15f4d9c602a423a02c825570862d12935be59ed9c7ba34a9b31f186c017c23cac6b54e35446f8353059c101da73eac22ef + languageName: node + linkType: hard + "body-parser@npm:1.20.1": version: 1.20.1 resolution: "body-parser@npm:1.20.1" @@ -6660,7 +7407,16 @@ __metadata: languageName: node linkType: hard -"browserslist@npm:^4.21.3, browserslist@npm:^4.21.5": +"browserslist-to-esbuild@npm:^1.2.0": + version: 1.2.0 + resolution: "browserslist-to-esbuild@npm:1.2.0" + dependencies: + browserslist: ^4.17.3 + checksum: a6c1adc2ad8da0db23aa4f0b3c71e13561cbf61599e9acedc7890793d79aca250edb0f599095042e81587c88e87569bf3a1a68633026e81e4ac7548eb1c71b49 + languageName: node + linkType: hard + +"browserslist@npm:^4.17.3, browserslist@npm:^4.21.3, browserslist@npm:^4.21.5": version: 4.21.5 resolution: "browserslist@npm:4.21.5" dependencies: @@ -6674,6 +7430,19 @@ __metadata: languageName: node linkType: hard +"browserstack-local@npm:^1.5.1": + version: 1.5.2 + resolution: "browserstack-local@npm:1.5.2" + dependencies: + agent-base: ^6.0.2 + https-proxy-agent: ^5.0.1 + is-running: ^2.1.0 + ps-tree: =1.2.0 + temp-fs: ^0.9.9 + checksum: 63dd7e6beb8a8d8e3ae2b6ef742d4a99ce78383e40ecb631e24a09e72b84a882cda5afb205b92d0eb9d1e307860c1cd41021647e04d61641977bc665cd00acc2 + languageName: node + linkType: hard + "bser@npm:2.1.1": version: 2.1.1 resolution: "bser@npm:2.1.1" @@ -6683,13 +7452,37 @@ __metadata: languageName: node linkType: hard -"buffer-crc32@npm:~0.2.3": +"buffer-alloc-unsafe@npm:^1.1.0": + version: 1.1.0 + resolution: "buffer-alloc-unsafe@npm:1.1.0" + checksum: c5e18bf51f67754ec843c9af3d4c005051aac5008a3992938dda1344e5cfec77c4b02b4ca303644d1e9a6e281765155ce6356d85c6f5ccc5cd21afc868def396 + languageName: node + linkType: hard + +"buffer-alloc@npm:^1.2.0": + version: 1.2.0 + resolution: "buffer-alloc@npm:1.2.0" + dependencies: + buffer-alloc-unsafe: ^1.1.0 + buffer-fill: ^1.0.0 + checksum: 560cd27f3cbe73c614867da373407d4506309c62fe18de45a1ce191f3785ec6ca2488d802ff82065798542422980ca25f903db078c57822218182c37c3576df5 + languageName: node + linkType: hard + +"buffer-crc32@npm:^0.2.1, buffer-crc32@npm:^0.2.13, buffer-crc32@npm:~0.2.3": version: 0.2.13 resolution: "buffer-crc32@npm:0.2.13" checksum: 06252347ae6daca3453b94e4b2f1d3754a3b146a111d81c68924c22d91889a40623264e95e67955b1cb4a68cbedf317abeabb5140a9766ed248973096db5ce1c languageName: node linkType: hard +"buffer-fill@npm:^1.0.0": + version: 1.0.0 + resolution: "buffer-fill@npm:1.0.0" + checksum: c29b4723ddeab01e74b5d3b982a0c6828f2ded49cef049ddca3dac661c874ecdbcecb5dd8380cf0f4adbeb8cff90a7de724126750a1f1e5ebd4eb6c59a1315b1 + languageName: node + linkType: hard + "buffer-from@npm:^1.0.0": version: 1.1.2 resolution: "buffer-from@npm:1.1.2" @@ -6697,7 +7490,7 @@ __metadata: languageName: node linkType: hard -"buffer@npm:^5.5.0": +"buffer@npm:^5.2.1, buffer@npm:^5.5.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" dependencies: @@ -6717,6 +7510,15 @@ __metadata: languageName: node linkType: hard +"busboy@npm:^1.6.0": + version: 1.6.0 + resolution: "busboy@npm:1.6.0" + dependencies: + streamsearch: ^1.1.0 + checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e + languageName: node + linkType: hard + "bytes@npm:3.1.2": version: 3.1.2 resolution: "bytes@npm:3.1.2" @@ -6724,6 +7526,21 @@ __metadata: languageName: node linkType: hard +"cac@npm:^3.0.3": + version: 3.0.4 + resolution: "cac@npm:3.0.4" + dependencies: + camelcase-keys: ^3.0.0 + chalk: ^1.1.3 + indent-string: ^3.0.0 + minimist: ^1.2.0 + read-pkg-up: ^1.0.1 + suffix: ^0.1.0 + text-table: ^0.2.0 + checksum: ce5ba580277a7cd3ca53f7eca92171e72a4b986559d03f1eeed54d4a94799b5e4112bc637131d7aad3a8ed5d7531ad4a34de3db6ba55b52aa2bd4be899b440c5 + languageName: node + linkType: hard + "cac@npm:^6.7.14": version: 6.7.14 resolution: "cac@npm:6.7.14" @@ -6790,6 +7607,43 @@ __metadata: languageName: node linkType: hard +"cacheable-lookup@npm:^7.0.0": + version: 7.0.0 + resolution: "cacheable-lookup@npm:7.0.0" + checksum: 9e2856763fc0a7347ab34d704c010440b819d4bb5e3593b664381b7433e942dd22e67ee5581f12256f908e79b82d30b86ebbacf40a081bfe10ee93fbfbc2d6a9 + languageName: node + linkType: hard + +"cacheable-request@npm:^10.2.8": + version: 10.2.10 + resolution: "cacheable-request@npm:10.2.10" + dependencies: + "@types/http-cache-semantics": ^4.0.1 + get-stream: ^6.0.1 + http-cache-semantics: ^4.1.1 + keyv: ^4.5.2 + mimic-response: ^4.0.0 + normalize-url: ^8.0.0 + responselike: ^3.0.0 + checksum: 6f56cf6dc88c000936c89e386fdfd65c9a7833f6a4f73314f546287352efca50ef8c7ccc80c64d5c51fe104f5a60356366e190846f56abf3f2e90c1bacec7eee + languageName: node + linkType: hard + +"cacheable-request@npm:^2.1.1": + version: 2.1.4 + resolution: "cacheable-request@npm:2.1.4" + dependencies: + clone-response: 1.0.2 + get-stream: 3.0.0 + http-cache-semantics: 3.8.1 + keyv: 3.0.0 + lowercase-keys: 1.0.0 + normalize-url: 2.0.1 + responselike: 1.0.2 + checksum: 69c684cb3645f75af094e3ef6e7959ca5edff33d70737498de1a068d2f719a12786efdd82fe1e2254a1f332bb88cce088273bd78fad3e57cdef5034f3ded9432 + languageName: node + linkType: hard + "cacheable-request@npm:^7.0.2": version: 7.0.2 resolution: "cacheable-request@npm:7.0.2" @@ -6822,6 +7676,23 @@ __metadata: languageName: node linkType: hard +"camelcase-keys@npm:^3.0.0": + version: 3.0.0 + resolution: "camelcase-keys@npm:3.0.0" + dependencies: + camelcase: ^3.0.0 + map-obj: ^1.0.0 + checksum: 8fa4b4546556cbe2bd933f4283dbd0c806d20b5db6711b73c0efbfcb12976d6f04febb1b7640898af7a2cfb7f099a7d02b34d635461dcc2900569eb78570292f + languageName: node + linkType: hard + +"camelcase@npm:^3.0.0": + version: 3.0.0 + resolution: "camelcase@npm:3.0.0" + checksum: ae4fe1c17c8442a3a345a6b7d2393f028ab7a7601af0c352ad15d1ab97ca75112e19e29c942b2a214898e160194829b68923bce30e018d62149c6d84187f1673 + languageName: node + linkType: hard + "camelcase@npm:^5.0.0, camelcase@npm:^5.3.1": version: 5.3.1 resolution: "camelcase@npm:5.3.1" @@ -6843,6 +7714,13 @@ __metadata: languageName: node linkType: hard +"caseless@npm:~0.12.0": + version: 0.12.0 + resolution: "caseless@npm:0.12.0" + checksum: b43bd4c440aa1e8ee6baefee8063b4850fd0d7b378f6aabc796c9ec8cb26d27fb30b46885350777d9bd079c5256c0e1329ad0dc7c2817e0bb466810ebb353751 + languageName: node + linkType: hard + "ccount@npm:^2.0.0": version: 2.0.1 resolution: "ccount@npm:2.0.1" @@ -6850,13 +7728,26 @@ __metadata: languageName: node linkType: hard -"chalk@npm:5.2.0, chalk@npm:^5.0.0, chalk@npm:^5.2.0": +"chalk@npm:5.2.0, chalk@npm:^5.0.0, chalk@npm:^5.0.1, chalk@npm:^5.1.2, chalk@npm:^5.2.0": version: 5.2.0 resolution: "chalk@npm:5.2.0" checksum: 03d8060277de6cf2fd567dc25fcf770593eb5bb85f460ce443e49255a30ff1242edd0c90a06a03803b0466ff0687a939b41db1757bec987113e83de89a003caa languageName: node linkType: hard +"chalk@npm:^1.1.3": + version: 1.1.3 + resolution: "chalk@npm:1.1.3" + dependencies: + ansi-styles: ^2.2.1 + escape-string-regexp: ^1.0.2 + has-ansi: ^2.0.0 + strip-ansi: ^3.0.0 + supports-color: ^2.0.0 + checksum: 9d2ea6b98fc2b7878829eec223abcf404622db6c48396a9b9257f6d0ead2acf18231ae368d6a664a83f272b0679158da12e97b5229f794939e555cc574478acd + languageName: node + linkType: hard + "chalk@npm:^2.0.0, chalk@npm:^2.3.2, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" @@ -6878,7 +7769,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": +"chalk@npm:^4.0.0, chalk@npm:^4.0.2, chalk@npm:^4.1.0, chalk@npm:^4.1.1, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -6973,7 +7864,7 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.5.3, chokidar@npm:^3.4.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2": +"chokidar@npm:3.5.3, chokidar@npm:^3.4.0, chokidar@npm:^3.5.1, chokidar@npm:^3.5.2, chokidar@npm:^3.5.3": version: 3.5.3 resolution: "chokidar@npm:3.5.3" dependencies: @@ -7006,6 +7897,48 @@ __metadata: languageName: node linkType: hard +"chrome-launcher@npm:^0.15.0": + version: 0.15.2 + resolution: "chrome-launcher@npm:0.15.2" + dependencies: + "@types/node": "*" + escape-string-regexp: ^4.0.0 + is-wsl: ^2.2.0 + lighthouse-logger: ^1.0.0 + bin: + print-chrome-path: bin/print-chrome-path.js + checksum: e1f8131b9f7bd931248ea85f413c6cdb93a0d41440ff5bf0987f36afb081d2b2c7b60ba6062ee7ae2dd9b052143f6b275b38c9eb115d11b49c3ea8829bad7db0 + languageName: node + linkType: hard + +"chromedriver@npm:^112.0.0": + version: 112.0.1 + resolution: "chromedriver@npm:112.0.1" + dependencies: + "@testim/chrome-version": ^1.1.3 + axios: ^1.2.1 + compare-versions: ^5.0.1 + extract-zip: ^2.0.1 + https-proxy-agent: ^5.0.1 + proxy-from-env: ^1.1.0 + tcp-port-used: ^1.0.1 + bin: + chromedriver: bin/chromedriver + checksum: b86df7a173e41bfce5a3896cb1b4b5a0274da09f74b1638dee82fe4e62c153be84c93f5ae66f46b37ea8d9312cbe4a8362732217d862e2824106f5b89915e4f0 + languageName: node + linkType: hard + +"chromium-bidi@npm:0.4.7": + version: 0.4.7 + resolution: "chromium-bidi@npm:0.4.7" + dependencies: + mitt: 3.0.0 + peerDependencies: + devtools-protocol: "*" + checksum: eec7581e2eddd2c95014c6edc5aae0b036c79bbeadee05166436b16139b6932c902c5ce21d95ed919a592f58d3a47c5469dc5f3de2a300700b2748ab119ad65e + languageName: node + linkType: hard + "ci-info@npm:^3.1.0, ci-info@npm:^3.2.0, ci-info@npm:^3.8.0": version: 3.8.0 resolution: "ci-info@npm:3.8.0" @@ -7100,6 +8033,17 @@ __metadata: languageName: node linkType: hard +"clipboardy@npm:^3.0.0": + version: 3.0.0 + resolution: "clipboardy@npm:3.0.0" + dependencies: + arch: ^2.2.0 + execa: ^5.1.1 + is-wsl: ^2.2.0 + checksum: 2c292acb59705494cbe07d7df7c8becff4f01651514d32ebd80f4aec2d20946d8f3824aac67ecdf2d09ef21fdf0eb24b6a7f033c137ccdceedc4661c54455c94 + languageName: node + linkType: hard + "cliui@npm:^5.0.0": version: 5.0.0 resolution: "cliui@npm:5.0.0" @@ -7133,6 +8077,15 @@ __metadata: languageName: node linkType: hard +"clone-response@npm:1.0.2": + version: 1.0.2 + resolution: "clone-response@npm:1.0.2" + dependencies: + mimic-response: ^1.0.0 + checksum: 2d0e61547fc66276e0903be9654ada422515f5a15741691352000d47e8c00c226061221074ce2c0064d12e975e84a8687cfd35d8b405750cb4e772f87b256eda + languageName: node + linkType: hard + "clone-response@npm:^1.0.2": version: 1.0.3 resolution: "clone-response@npm:1.0.3" @@ -7195,13 +8148,23 @@ __metadata: languageName: node linkType: hard -"color-name@npm:^1.1.4, color-name@npm:~1.1.4": +"color-name@npm:^1.0.0, color-name@npm:^1.1.4, color-name@npm:~1.1.4": version: 1.1.4 resolution: "color-name@npm:1.1.4" checksum: b0445859521eb4021cd0fb0cc1a75cecf67fceecae89b63f62b201cca8d345baf8b952c966862a9d9a2632987d4f6581f0ec8d957dfacece86f0a7919316f610 languageName: node linkType: hard +"color-string@npm:^1.9.0": + version: 1.9.1 + resolution: "color-string@npm:1.9.1" + dependencies: + color-name: ^1.0.0 + simple-swizzle: ^0.2.2 + checksum: c13fe7cff7885f603f49105827d621ce87f4571d78ba28ef4a3f1a104304748f620615e6bf065ecd2145d0d9dad83a3553f52bb25ede7239d18e9f81622f1cc5 + languageName: node + linkType: hard + "color-support@npm:^1.1.2, color-support@npm:^1.1.3": version: 1.1.3 resolution: "color-support@npm:1.1.3" @@ -7211,6 +8174,16 @@ __metadata: languageName: node linkType: hard +"color@npm:^4.2.3": + version: 4.2.3 + resolution: "color@npm:4.2.3" + dependencies: + color-convert: ^2.0.1 + color-string: ^1.9.0 + checksum: 0579629c02c631b426780038da929cca8e8d80a40158b09811a0112a107c62e10e4aad719843b791b1e658ab4e800558f2e87ca4522c8b32349d497ecb6adeb4 + languageName: node + linkType: hard + "colorette@npm:^2.0.19": version: 2.0.20 resolution: "colorette@npm:2.0.20" @@ -7225,7 +8198,7 @@ __metadata: languageName: node linkType: hard -"combined-stream@npm:^1.0.8": +"combined-stream@npm:^1.0.6, combined-stream@npm:^1.0.8, combined-stream@npm:~1.0.6": version: 1.0.8 resolution: "combined-stream@npm:1.0.8" dependencies: @@ -7288,6 +8261,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^2.8.1": + version: 2.20.3 + resolution: "commander@npm:2.20.3" + checksum: ab8c07884e42c3a8dbc5dd9592c606176c7eb5c1ca5ff274bcf907039b2c41de3626f684ea75ccf4d361ba004bbaff1f577d5384c155f3871e456bdf27becf9e + languageName: node + linkType: hard + "commander@npm:^5.0.0": version: 5.1.0 resolution: "commander@npm:5.1.0" @@ -7316,6 +8296,25 @@ __metadata: languageName: node linkType: hard +"compare-versions@npm:^5.0.1": + version: 5.0.3 + resolution: "compare-versions@npm:5.0.3" + checksum: f66a4bb6ef8ff32031cc92c04dea4bbead039e72a7f6c7df7ef05f5a42ddca9202f8875b7449add54181e73b89f039662a8760c8db0ab036c4e8f653a7cd29c1 + languageName: node + linkType: hard + +"compress-commons@npm:^4.1.0": + version: 4.1.1 + resolution: "compress-commons@npm:4.1.1" + dependencies: + buffer-crc32: ^0.2.13 + crc32-stream: ^4.0.2 + normalize-path: ^3.0.0 + readable-stream: ^3.6.0 + checksum: 0176483211a7304a4a8aa52dbcc149a4c9181ac8a04bfbcc3d1a379174bf5fa56c3b15cec19e5ae3d31f1b1ce35ebb275b792b867000c77bac7162ce4e0ca268 + languageName: node + linkType: hard + "concat-map@npm:0.0.1": version: 0.0.1 resolution: "concat-map@npm:0.0.1" @@ -7323,6 +8322,18 @@ __metadata: languageName: node linkType: hard +"concat-stream@npm:^1.6.2": + version: 1.6.2 + resolution: "concat-stream@npm:1.6.2" + dependencies: + buffer-from: ^1.0.0 + inherits: ^2.0.3 + readable-stream: ^2.2.2 + typedarray: ^0.0.6 + checksum: 1ef77032cb4459dcd5187bd710d6fc962b067b64ec6a505810de3d2b8cc0605638551b42f8ec91edf6fcd26141b32ef19ad749239b58fae3aba99187adc32285 + languageName: node + linkType: hard + "concurrently@npm:7.0.0": version: 7.0.0 resolution: "concurrently@npm:7.0.0" @@ -7341,6 +8352,26 @@ __metadata: languageName: node linkType: hard +"concurrently@npm:^7.4.0": + version: 7.6.0 + resolution: "concurrently@npm:7.6.0" + dependencies: + chalk: ^4.1.0 + date-fns: ^2.29.1 + lodash: ^4.17.21 + rxjs: ^7.0.0 + shell-quote: ^1.7.3 + spawn-command: ^0.0.2-1 + supports-color: ^8.1.0 + tree-kill: ^1.2.2 + yargs: ^17.3.1 + bin: + conc: dist/bin/concurrently.js + concurrently: dist/bin/concurrently.js + checksum: f705c9a7960f1b16559ca64958043faeeef6385c0bf30a03d1375e15ab2d96dba4f8166f1bbbb1c85e8da35ca0ce3c353875d71dff2aa132b2357bb533b3332e + languageName: node + linkType: hard + "config@workspace:config": version: 0.0.0-use.local resolution: "config@workspace:config" @@ -7358,7 +8389,7 @@ __metadata: languageName: node linkType: hard -"content-disposition@npm:0.5.4": +"content-disposition@npm:0.5.4, content-disposition@npm:^0.5.2, content-disposition@npm:^0.5.3": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" dependencies: @@ -7395,7 +8426,7 @@ __metadata: languageName: node linkType: hard -"cookie@npm:0.5.0": +"cookie@npm:0.5.0, cookie@npm:^0.5.0": version: 0.5.0 resolution: "cookie@npm:0.5.0" checksum: 1f4bd2ca5765f8c9689a7e8954183f5332139eb72b6ff783d8947032ec1fdf43109852c178e21a953a30c0dd42257828185be01b49d1eb1a67fd054ca588a180 @@ -7425,6 +8456,13 @@ __metadata: languageName: node linkType: hard +"core-util-is@npm:1.0.2": + version: 1.0.2 + resolution: "core-util-is@npm:1.0.2" + checksum: 7a4c925b497a2c91421e25bf76d6d8190f0b2359a9200dbeed136e63b2931d6294d3b1893eda378883ed363cd950f44a12a401384c609839ea616befb7927dab + languageName: node + linkType: hard + "core-util-is@npm:~1.0.0": version: 1.0.3 resolution: "core-util-is@npm:1.0.3" @@ -7445,6 +8483,25 @@ __metadata: languageName: node linkType: hard +"crc-32@npm:^1.2.0": + version: 1.2.2 + resolution: "crc-32@npm:1.2.2" + bin: + crc32: bin/crc32.njs + checksum: ad2d0ad0cbd465b75dcaeeff0600f8195b686816ab5f3ba4c6e052a07f728c3e70df2e3ca9fd3d4484dc4ba70586e161ca5a2334ec8bf5a41bf022a6103ff243 + languageName: node + linkType: hard + +"crc32-stream@npm:^4.0.2": + version: 4.0.2 + resolution: "crc32-stream@npm:4.0.2" + dependencies: + crc-32: ^1.2.0 + readable-stream: ^3.4.0 + checksum: 1099559283b86e8a55390228b57ff4d57a74cac6aa8086aa4730f84317c9f93e914aeece115352f2d706a9df7ed75327ffacd86cfe23f040aef821231b528e76 + languageName: node + linkType: hard + "crc@npm:^4.3.2": version: 4.3.2 resolution: "crc@npm:4.3.2" @@ -7485,7 +8542,7 @@ __metadata: languageName: node linkType: hard -"cross-fetch@npm:^3.1.5": +"cross-fetch@npm:3.1.5, cross-fetch@npm:^3.1.5": version: 3.1.5 resolution: "cross-fetch@npm:3.1.5" dependencies: @@ -7494,7 +8551,17 @@ __metadata: languageName: node linkType: hard -"cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": +"cross-spawn@npm:^4.0.2": + version: 4.0.2 + resolution: "cross-spawn@npm:4.0.2" + dependencies: + lru-cache: ^4.0.1 + which: ^1.2.9 + checksum: 8ce57b3e11c5c798542a21ddfdc1edef33ab6fe001958b31f3340a6ff684e3334a8baad2751efa78b6200aad442cf12b939396d758b0dd5c42c9b782c28fe06e + languageName: node + linkType: hard + +"cross-spawn@npm:^7.0.0, cross-spawn@npm:^7.0.1, cross-spawn@npm:^7.0.2, cross-spawn@npm:^7.0.3": version: 7.0.3 resolution: "cross-spawn@npm:7.0.3" dependencies: @@ -7518,6 +8585,13 @@ __metadata: languageName: node linkType: hard +"css-shorthand-properties@npm:^1.1.1": + version: 1.1.1 + resolution: "css-shorthand-properties@npm:1.1.1" + checksum: 014b48e9fda528da7155cdf41e4ad9a0079ace4890e853d1d3ce4e41c2bb38c19e627d0be93dafe8b202c3a9fe83a6120b684e1405ee79b69ea8e248bd8833e9 + languageName: node + linkType: hard + "css-tree@npm:^2.2.1": version: 2.3.1 resolution: "css-tree@npm:2.3.1" @@ -7538,6 +8612,13 @@ __metadata: languageName: node linkType: hard +"css-value@npm:^0.0.1": + version: 0.0.1 + resolution: "css-value@npm:0.0.1" + checksum: 976a5832d1e5e5dc041903395a2842a382c7a0b150026f0f81671046f8125d4b86c7a9eed014a047c7a2111bc56d807d0e8d2e08b6e028798054593a9afc6b4d + languageName: node + linkType: hard + "css-what@npm:^5.0.1": version: 5.1.0 resolution: "css-what@npm:5.1.0" @@ -7621,6 +8702,15 @@ __metadata: languageName: node linkType: hard +"dashdash@npm:^1.12.0": + version: 1.14.1 + resolution: "dashdash@npm:1.14.1" + dependencies: + assert-plus: ^1.0.0 + checksum: 3634c249570f7f34e3d34f866c93f866c5b417f0dd616275decae08147dcdf8fccfaa5947380ccfb0473998ea3a8057c0b4cd90c875740ee685d0624b2983598 + languageName: node + linkType: hard + "data-uri-to-buffer@npm:3": version: 3.0.1 resolution: "data-uri-to-buffer@npm:3.0.1" @@ -7646,6 +8736,15 @@ __metadata: languageName: node linkType: hard +"date-fns@npm:^2.29.1": + version: 2.30.0 + resolution: "date-fns@npm:2.30.0" + dependencies: + "@babel/runtime": ^7.21.0 + checksum: f7be01523282e9bb06c0cd2693d34f245247a29098527d4420628966a2d9aad154bd0e90a6b1cf66d37adcb769cd108cf8a7bd49d76db0fb119af5cdd13644f4 + languageName: node + linkType: hard + "deasync@npm:^0.1.0": version: 0.1.28 resolution: "deasync@npm:0.1.28" @@ -7656,7 +8755,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:2.6.9": +"debug@npm:2.6.9, debug@npm:^2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" dependencies: @@ -7665,7 +8764,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.0, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -7677,6 +8776,18 @@ __metadata: languageName: node linkType: hard +"debug@npm:4.3.1": + version: 4.3.1 + resolution: "debug@npm:4.3.1" + dependencies: + ms: 2.1.2 + peerDependenciesMeta: + supports-color: + optional: true + checksum: 2c3352e37d5c46b0d203317cd45ea0e26b2c99f2d9dfec8b128e6ceba90dfb65425f5331bf3020fe9929d7da8c16758e737f4f3bfc0fce6b8b3d503bae03298b + languageName: node + linkType: hard + "debug@npm:4.3.3": version: 4.3.3 resolution: "debug@npm:4.3.3" @@ -7698,6 +8809,13 @@ __metadata: languageName: node linkType: hard +"decamelize@npm:6.0.0, decamelize@npm:^6.0.0": + version: 6.0.0 + resolution: "decamelize@npm:6.0.0" + checksum: 0066bc30798ec11e01adf0c19ad975caef86545d4bb6f70cfb90b7eb8e3cbf7974cf774ac2e6ea2586e4e07b1f654bfecc4e772c42128a79a89f8584fc546753 + languageName: node + linkType: hard + "decamelize@npm:^1.2.0": version: 1.2.0 resolution: "decamelize@npm:1.2.0" @@ -7728,6 +8846,22 @@ __metadata: languageName: node linkType: hard +"decode-uri-component@npm:^0.2.0": + version: 0.2.2 + resolution: "decode-uri-component@npm:0.2.2" + checksum: 95476a7d28f267292ce745eac3524a9079058bbb35767b76e3ee87d42e34cd0275d2eb19d9d08c3e167f97556e8a2872747f5e65cbebcac8b0c98d83e285f139 + languageName: node + linkType: hard + +"decompress-response@npm:^3.3.0": + version: 3.3.0 + resolution: "decompress-response@npm:3.3.0" + dependencies: + mimic-response: ^1.0.0 + checksum: 952552ac3bd7de2fc18015086b09468645c9638d98a551305e485230ada278c039c91116e946d07894b39ee53c0f0d5b6473f25a224029344354513b412d7380 + languageName: node + linkType: hard + "decompress-response@npm:^6.0.0": version: 6.0.0 resolution: "decompress-response@npm:6.0.0" @@ -7737,6 +8871,69 @@ __metadata: languageName: node linkType: hard +"decompress-tar@npm:^4.0.0, decompress-tar@npm:^4.1.0, decompress-tar@npm:^4.1.1": + version: 4.1.1 + resolution: "decompress-tar@npm:4.1.1" + dependencies: + file-type: ^5.2.0 + is-stream: ^1.1.0 + tar-stream: ^1.5.2 + checksum: 42d5360b558a28dd884e1bf809e3fea92b9910fda5151add004d4a64cc76ac124e8b3e9117e805f2349af9e49c331d873e6fc5ad86a00e575703fee632b0a225 + languageName: node + linkType: hard + +"decompress-tarbz2@npm:^4.0.0": + version: 4.1.1 + resolution: "decompress-tarbz2@npm:4.1.1" + dependencies: + decompress-tar: ^4.1.0 + file-type: ^6.1.0 + is-stream: ^1.1.0 + seek-bzip: ^1.0.5 + unbzip2-stream: ^1.0.9 + checksum: 519c81337730159a1f2d7072a6ee8523ffd76df48d34f14c27cb0a27f89b4e2acf75dad2f761838e5bc63230cea1ac154b092ecb7504be4e93f7d0e32ddd6aff + languageName: node + linkType: hard + +"decompress-targz@npm:^4.0.0": + version: 4.1.1 + resolution: "decompress-targz@npm:4.1.1" + dependencies: + decompress-tar: ^4.1.1 + file-type: ^5.2.0 + is-stream: ^1.1.0 + checksum: 22738f58eb034568dc50d370c03b346c428bfe8292fe56165847376b5af17d3c028fefca82db642d79cb094df4c0a599d40a8f294b02aad1d3ddec82f3fd45d4 + languageName: node + linkType: hard + +"decompress-unzip@npm:^4.0.1": + version: 4.0.1 + resolution: "decompress-unzip@npm:4.0.1" + dependencies: + file-type: ^3.8.0 + get-stream: ^2.2.0 + pify: ^2.3.0 + yauzl: ^2.4.2 + checksum: ba9f3204ab2415bedb18d796244928a18148ef40dbb15174d0d01e5991b39536b03d02800a8a389515a1523f8fb13efc7cd44697df758cd06c674879caefd62b + languageName: node + linkType: hard + +"decompress@npm:^4.2.1": + version: 4.2.1 + resolution: "decompress@npm:4.2.1" + dependencies: + decompress-tar: ^4.0.0 + decompress-tarbz2: ^4.0.0 + decompress-targz: ^4.0.0 + decompress-unzip: ^4.0.1 + graceful-fs: ^4.1.10 + make-dir: ^1.0.0 + pify: ^2.3.0 + strip-dirs: ^2.0.0 + checksum: 8247a31c6db7178413715fdfb35a482f019c81dfcd6e8e623d9f0382c9889ce797ce0144de016b256ed03298907a620ce81387cca0e69067a933470081436cb8 + languageName: node + linkType: hard + "dedent@npm:^0.7.0": version: 0.7.0 resolution: "dedent@npm:0.7.0" @@ -7790,6 +8987,13 @@ __metadata: languageName: node linkType: hard +"deepmerge-ts@npm:^5.0.0": + version: 5.1.0 + resolution: "deepmerge-ts@npm:5.1.0" + checksum: 6b57db93c2985e4a35f24b2451db31715050d143988b7d6346f4049c9aec21a6c289514b88d3ee3d6e0697e72ef5d96ff0bbb7cb75422d56fee55ee85c7168e7 + languageName: node + linkType: hard + "deepmerge@npm:^4.0.0, deepmerge@npm:^4.2.2": version: 4.3.1 resolution: "deepmerge@npm:4.3.1" @@ -7806,7 +9010,7 @@ __metadata: languageName: node linkType: hard -"defer-to-connect@npm:^2.0.0": +"defer-to-connect@npm:^2.0.0, defer-to-connect@npm:^2.0.1": version: 2.0.1 resolution: "defer-to-connect@npm:2.0.1" checksum: 8a9b50d2f25446c0bfefb55a48e90afd58f85b21bcf78e9207cd7b804354f6409032a1705c2491686e202e64fc05f147aa5aa45f9aa82627563f045937f5791b @@ -7842,6 +9046,21 @@ __metadata: languageName: node linkType: hard +"del@npm:^4.1.1": + version: 4.1.1 + resolution: "del@npm:4.1.1" + dependencies: + "@types/glob": ^7.1.1 + globby: ^6.1.0 + is-path-cwd: ^2.0.0 + is-path-in-cwd: ^2.0.0 + p-map: ^2.0.0 + pify: ^4.0.1 + rimraf: ^2.6.3 + checksum: 521f7da44bd79da841c06d573923d1f64f423aee8b8219c973478d3150ce1dcc024d03ad605929292adbff56d6448bca60d96dcdd2d8a53b46dbcb27e265c94b + languageName: node + linkType: hard + "delayed-stream@npm:~1.0.0": version: 1.0.0 resolution: "delayed-stream@npm:1.0.0" @@ -7891,7 +9110,7 @@ __metadata: languageName: node linkType: hard -"detect-libc@npm:^2.0.0": +"detect-libc@npm:^2.0.0, detect-libc@npm:^2.0.1": version: 2.0.1 resolution: "detect-libc@npm:2.0.1" checksum: ccb05fcabbb555beb544d48080179c18523a343face9ee4e1a86605a8715b4169f94d663c21a03c310ac824592f2ba9a5270218819bb411ad7be578a527593d7 @@ -7912,6 +9131,42 @@ __metadata: languageName: node linkType: hard +"devtools-protocol@npm:0.0.1107588": + version: 0.0.1107588 + resolution: "devtools-protocol@npm:0.0.1107588" + checksum: 9064fd643f39ae0adabb8f425b746899ff24371d89a5047d38752653259e6afcb6bcb2d9759ff727eb5885cfc0f9ba8eb384850a2af00694135622e88080e3e5 + languageName: node + linkType: hard + +"devtools-protocol@npm:^0.0.1138159": + version: 0.0.1138159 + resolution: "devtools-protocol@npm:0.0.1138159" + checksum: 8e0396ea7627cefb67f10f179efe107fbd36c1bdc29444e5f4f07b1b4a249fe9ea7d0404a3f414ab7ae8604b399dafd6709b6daedb0e0fdcbf093889ac5ec804 + languageName: node + linkType: hard + +"devtools@npm:8.10.0": + version: 8.10.0 + resolution: "devtools@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + "@wdio/config": 8.10.0 + "@wdio/logger": 8.6.6 + "@wdio/protocols": 8.8.1 + "@wdio/types": 8.10.0 + "@wdio/utils": 8.10.0 + chrome-launcher: ^0.15.0 + edge-paths: ^3.0.5 + import-meta-resolve: ^3.0.0 + puppeteer-core: 19.11.1 + query-selector-shadow-dom: ^1.0.0 + ua-parser-js: ^1.0.1 + uuid: ^9.0.0 + which: ^3.0.0 + checksum: 729d6f384c2138512dddc22c77507dbb0d8d28d6b65df32d91825fc5398226fc1e6474a0536a03b51acb317ba3c1f7aad350441f7da69ea7fc37fa846e2f68d9 + languageName: node + linkType: hard + "diff-sequences@npm:^28.1.1": version: 28.1.1 resolution: "diff-sequences@npm:28.1.1" @@ -8060,7 +9315,33 @@ __metadata: languageName: node linkType: hard -"duplexer@npm:^0.1.2": +"download@npm:^8.0.0": + version: 8.0.0 + resolution: "download@npm:8.0.0" + dependencies: + archive-type: ^4.0.0 + content-disposition: ^0.5.2 + decompress: ^4.2.1 + ext-name: ^5.0.0 + file-type: ^11.1.0 + filenamify: ^3.0.0 + get-stream: ^4.1.0 + got: ^8.3.1 + make-dir: ^2.1.0 + p-event: ^2.1.0 + pify: ^4.0.1 + checksum: 8a26b21eee8d23352265729dba8eea9f18cba0ebfa3e064041afffeefdfe508fc31e54a08bd0606ff8b0d548466bdb2e2e32b571a8f95227efa5b7c09c261a2f + languageName: node + linkType: hard + +"duplexer3@npm:^0.1.4": + version: 0.1.5 + resolution: "duplexer3@npm:0.1.5" + checksum: e677cb4c48f031ca728601d6a20bf6aed4c629d69ef9643cb89c67583d673c4ec9317cc6427501f38bd8c368d3a18f173987cc02bd99d8cf8fe3d94259a22a20 + languageName: node + linkType: hard + +"duplexer@npm:^0.1.2, duplexer@npm:~0.1.1": version: 0.1.2 resolution: "duplexer@npm:0.1.2" checksum: 62ba61a830c56801db28ff6305c7d289b6dc9f859054e8c982abd8ee0b0a14d2e9a8e7d086ffee12e868d43e2bbe8a964be55ddbd8c8957714c87373c7a4f9b0 @@ -8095,6 +9376,39 @@ __metadata: languageName: node linkType: hard +"easy-table@npm:^1.2.0": + version: 1.2.0 + resolution: "easy-table@npm:1.2.0" + dependencies: + ansi-regex: ^5.0.1 + wcwidth: ^1.0.1 + dependenciesMeta: + wcwidth: + optional: true + checksum: 66961b19751a68d2d30ce9b74ef750c374cc3112bbcac3d1ed5a939e43c035ecf6b1954098df2d5b05f1e853ab2b67de893794390dcbf0abe1f157fddeb52174 + languageName: node + linkType: hard + +"ecc-jsbn@npm:~0.1.1": + version: 0.1.2 + resolution: "ecc-jsbn@npm:0.1.2" + dependencies: + jsbn: ~0.1.0 + safer-buffer: ^2.1.0 + checksum: 22fef4b6203e5f31d425f5b711eb389e4c6c2723402e389af394f8411b76a488fa414d309d866e2b577ce3e8462d344205545c88a8143cc21752a5172818888a + languageName: node + linkType: hard + +"edge-paths@npm:^3.0.5": + version: 3.0.5 + resolution: "edge-paths@npm:3.0.5" + dependencies: + "@types/which": ^2.0.1 + which: ^2.0.2 + checksum: 76ea4380ad2e9c259b76493c33c335cb9043ab450f8fc8b26b8123c0b2d78325e1e824220ffc9380fa50d9ac8d82d9bf25af14a637f627eb2f7d9fd099421069 + languageName: node + linkType: hard + "edge-runtime@npm:2.1.4": version: 2.1.4 resolution: "edge-runtime@npm:2.1.4" @@ -8121,6 +9435,17 @@ __metadata: languageName: node linkType: hard +"ejs@npm:^3.1.8": + version: 3.1.9 + resolution: "ejs@npm:3.1.9" + dependencies: + jake: ^10.8.5 + bin: + ejs: bin/cli.js + checksum: af6f10eb815885ff8a8cfacc42c6b6cf87daf97a4884f87a30e0c3271fedd85d76a3a297d9c33a70e735b97ee632887f85e32854b9cdd3a2d97edf931519a35f + languageName: node + linkType: hard + "electron-to-chromium@npm:^1.4.284": version: 1.4.368 resolution: "electron-to-chromium@npm:1.4.368" @@ -8257,7 +9582,7 @@ __metadata: languageName: node linkType: hard -"error-ex@npm:^1.3.1": +"error-ex@npm:^1.2.0, error-ex@npm:^1.3.1": version: 1.3.2 resolution: "error-ex@npm:1.3.2" dependencies: @@ -8903,7 +10228,7 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:^1.0.5": +"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": version: 1.0.5 resolution: "escape-string-regexp@npm:1.0.5" checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 @@ -9451,6 +10776,28 @@ __metadata: languageName: node linkType: hard +"event-stream@npm:=3.3.4": + version: 3.3.4 + resolution: "event-stream@npm:3.3.4" + dependencies: + duplexer: ~0.1.1 + from: ~0 + map-stream: ~0.1.0 + pause-stream: 0.0.11 + split: 0.3 + stream-combiner: ~0.0.4 + through: ~2.3.1 + checksum: 80b467820b6daf824d9fb4345d2daf115a056e5c104463f2e98534e92d196a27f2df5ea2aa085624db26f4c45698905499e881d13bc7c01f7a13eac85be72a22 + languageName: node + linkType: hard + +"event-target-shim@npm:^5.0.0": + version: 5.0.1 + resolution: "event-target-shim@npm:5.0.1" + checksum: 1ffe3bb22a6d51bdeb6bf6f7cf97d2ff4a74b017ad12284cc9e6a279e727dc30a5de6bb613e5596ff4dc3e517841339ad09a7eec44266eccb1aa201a30448166 + languageName: node + linkType: hard + "eventemitter3@npm:^4.0.7": version: 4.0.7 resolution: "eventemitter3@npm:4.0.7" @@ -9458,6 +10805,13 @@ __metadata: languageName: node linkType: hard +"events@npm:^3.3.0": + version: 3.3.0 + resolution: "events@npm:3.3.0" + checksum: f6f487ad2198aa41d878fa31452f1a3c00958f46e9019286ff4787c84aac329332ab45c9cdc8c445928fc6d7ded294b9e005a7fce9426488518017831b272780 + languageName: node + linkType: hard + "examples.tldraw.com@workspace:apps/examples": version: 0.0.0-use.local resolution: "examples.tldraw.com@workspace:apps/examples" @@ -9475,7 +10829,7 @@ __metadata: languageName: unknown linkType: soft -"execa@npm:5.1.1, execa@npm:^5.0.0": +"execa@npm:5.1.1, execa@npm:^5.0.0, execa@npm:^5.1.1": version: 5.1.1 resolution: "execa@npm:5.1.1" dependencies: @@ -9530,6 +10884,23 @@ __metadata: languageName: node linkType: hard +"expect-webdriverio@npm:^4.0.1": + version: 4.2.3 + resolution: "expect-webdriverio@npm:4.2.3" + dependencies: + "@wdio/globals": ^8.8.8 + expect: ^29.5.0 + jest-matcher-utils: ^29.5.0 + webdriverio: ^8.8.8 + dependenciesMeta: + "@wdio/globals": + optional: true + webdriverio: + optional: true + checksum: 3a03a8356ee33cb8e36c7a0ad167b09ea5c3b9b090e55c22866f2639ce6e12a9a954901e023f9d2331266e087729b9107c882ad014d94dc18c21da6f3cb76d8a + languageName: node + linkType: hard + "expect@npm:^28.0.0, expect@npm:^28.1.3": version: 28.1.3 resolution: "expect@npm:28.1.3" @@ -9543,7 +10914,7 @@ __metadata: languageName: node linkType: hard -"expect@npm:^29.0.0": +"expect@npm:^29.0.0, expect@npm:^29.5.0": version: 29.5.0 resolution: "expect@npm:29.5.0" dependencies: @@ -9595,6 +10966,25 @@ __metadata: languageName: node linkType: hard +"ext-list@npm:^2.0.0": + version: 2.2.2 + resolution: "ext-list@npm:2.2.2" + dependencies: + mime-db: ^1.28.0 + checksum: 9b2426bea312e674eeced62c5f18407ab9a8653bbdfbde36492331c7973dab7fbf9e11d6c38605786168b42da333910314988097ca06eee61f1b9b57efae3f18 + languageName: node + linkType: hard + +"ext-name@npm:^5.0.0": + version: 5.0.0 + resolution: "ext-name@npm:5.0.0" + dependencies: + ext-list: ^2.0.0 + sort-keys-length: ^1.0.0 + checksum: f598269bd5de4295540ea7d6f8f6a01d82a7508f148b7700a05628ef6121648d26e6e5e942049e953b3051863df6b54bd8fe951e7877f185e34ace5d44370b33 + languageName: node + linkType: hard + "extend-shallow@npm:^2.0.1": version: 2.0.1 resolution: "extend-shallow@npm:2.0.1" @@ -9604,7 +10994,7 @@ __metadata: languageName: node linkType: hard -"extend@npm:^3.0.0": +"extend@npm:^3.0.0, extend@npm:~3.0.2": version: 3.0.2 resolution: "extend@npm:3.0.2" checksum: a50a8309ca65ea5d426382ff09f33586527882cf532931cb08ca786ea3146c0553310bda688710ff61d7668eba9f96b923fe1420cdf56a2c3eaf30fcab87b515 @@ -9622,6 +11012,51 @@ __metadata: languageName: node linkType: hard +"extract-zip@npm:2.0.1, extract-zip@npm:^2.0.1": + version: 2.0.1 + resolution: "extract-zip@npm:2.0.1" + dependencies: + "@types/yauzl": ^2.9.1 + debug: ^4.1.1 + get-stream: ^5.1.0 + yauzl: ^2.10.0 + dependenciesMeta: + "@types/yauzl": + optional: true + bin: + extract-zip: cli.js + checksum: 8cbda9debdd6d6980819cc69734d874ddd71051c9fe5bde1ef307ebcedfe949ba57b004894b585f758b7c9eeeea0e3d87f2dda89b7d25320459c2c9643ebb635 + languageName: node + linkType: hard + +"extract-zip@npm:^1.6.7": + version: 1.7.0 + resolution: "extract-zip@npm:1.7.0" + dependencies: + concat-stream: ^1.6.2 + debug: ^2.6.9 + mkdirp: ^0.5.4 + yauzl: ^2.10.0 + bin: + extract-zip: cli.js + checksum: 011bab660d738614555773d381a6ba4815d98c1cfcdcdf027e154ebcc9fc8c9ef637b3ea5c9b2144013100071ee41722ed041fc9aacc60f6198ef747cac0c073 + languageName: node + linkType: hard + +"extsprintf@npm:1.3.0": + version: 1.3.0 + resolution: "extsprintf@npm:1.3.0" + checksum: cee7a4a1e34cffeeec18559109de92c27517e5641991ec6bab849aa64e3081022903dd53084f2080d0d2530803aa5ee84f1e9de642c365452f9e67be8f958ce2 + languageName: node + linkType: hard + +"extsprintf@npm:^1.2.0": + version: 1.4.1 + resolution: "extsprintf@npm:1.4.1" + checksum: a2f29b241914a8d2bad64363de684821b6b1609d06ae68d5b539e4de6b28659715b5bea94a7265201603713b7027d35399d10b0548f09071c5513e65e8323d33 + languageName: node + linkType: hard + "fake-indexeddb@npm:^4.0.0": version: 4.0.1 resolution: "fake-indexeddb@npm:4.0.1" @@ -9631,6 +11066,27 @@ __metadata: languageName: node linkType: hard +"fast-content-type-parse@npm:^1.0.0": + version: 1.0.0 + resolution: "fast-content-type-parse@npm:1.0.0" + checksum: 9e9187be17bea18a2ee715c5737b983181cbe84f286a291db0595e421e04b578da10ca10845639be08664a4db6a793f7709822935cf38cfdf9ecba38d84ead9e + languageName: node + linkType: hard + +"fast-decode-uri-component@npm:^1.0.1": + version: 1.0.1 + resolution: "fast-decode-uri-component@npm:1.0.1" + checksum: 427a48fe0907e76f0e9a2c228e253b4d8a8ab21d130ee9e4bb8339c5ba4086235cf9576831f7b20955a752eae4b525a177ff9d5825dd8d416e7726939194fbee + languageName: node + linkType: hard + +"fast-deep-equal@npm:^2.0.1": + version: 2.0.1 + resolution: "fast-deep-equal@npm:2.0.1" + checksum: b701835a87985e0ec4925bdf1f0c1e7eb56309b5d12d534d5b4b69d95a54d65bb16861c081781ead55f73f12d6c60ba668713391ee7fbf6b0567026f579b7b0b + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -9678,6 +11134,20 @@ __metadata: languageName: node linkType: hard +"fast-json-stringify@npm:^5.7.0": + version: 5.7.0 + resolution: "fast-json-stringify@npm:5.7.0" + dependencies: + "@fastify/deepmerge": ^1.0.0 + ajv: ^8.10.0 + ajv-formats: ^2.1.1 + fast-deep-equal: ^3.1.3 + fast-uri: ^2.1.0 + rfdc: ^1.2.0 + checksum: ca834cd8afa654f50c8f14b8489ae74ccc1d2d695fd9eafc7b8eebe251e6d0305664e8dc9d50f3074a4a0572a34c2b96e8adb84096b813d7e6f1aafce34ecf9c + languageName: node + linkType: hard + "fast-levenshtein@npm:^2.0.6, fast-levenshtein@npm:~2.0.6": version: 2.0.6 resolution: "fast-levenshtein@npm:2.0.6" @@ -9685,7 +11155,61 @@ __metadata: languageName: node linkType: hard -"fastq@npm:^1.6.0": +"fast-querystring@npm:^1.0.0": + version: 1.1.1 + resolution: "fast-querystring@npm:1.1.1" + dependencies: + fast-decode-uri-component: ^1.0.1 + checksum: 86d2b75b9b299a552353532fb1a542f09730ee2a61e657d68710971d9a2afc9a3c5c7b7e106b6534f4cc506d2ff1c08ab0fda4ae614b4e7720798c9ac2a88e02 + languageName: node + linkType: hard + +"fast-redact@npm:^3.1.1": + version: 3.1.2 + resolution: "fast-redact@npm:3.1.2" + checksum: a30eb6b6830333ab213e0def55f46453ca777544dbd3a883016cb590a0eeb95e6fdf546553c1a13d509896bfba889b789991160a6d0996ceb19fce0a02e8b753 + languageName: node + linkType: hard + +"fast-uri@npm:^2.0.0, fast-uri@npm:^2.1.0": + version: 2.2.0 + resolution: "fast-uri@npm:2.2.0" + checksum: edac64d50628f21d562cdc19ea86f5af00902dbb09d2f96fff5974e5317157825e9aa163af9defd11a0818aac6ea2e9958597bed98dd041200a08a976809d08b + languageName: node + linkType: hard + +"fastify-plugin@npm:^4.0.0": + version: 4.5.0 + resolution: "fastify-plugin@npm:4.5.0" + checksum: 3cc36a43ec72ee9974d3cd1989027e77b277b0994e4fc02ed4e6492aabd83a0be66f14fdec56e60d9a41911ea1ff1ea70c9d957a87e46bcdb8fedd859f4988fe + languageName: node + linkType: hard + +"fastify@npm:^4.16.3": + version: 4.17.0 + resolution: "fastify@npm:4.17.0" + dependencies: + "@fastify/ajv-compiler": ^3.5.0 + "@fastify/error": ^3.0.0 + "@fastify/fast-json-stringify-compiler": ^4.3.0 + abstract-logging: ^2.0.1 + avvio: ^8.2.0 + fast-content-type-parse: ^1.0.0 + fast-json-stringify: ^5.7.0 + find-my-way: ^7.6.0 + light-my-request: ^5.6.1 + pino: ^8.5.0 + process-warning: ^2.0.0 + proxy-addr: ^2.0.7 + rfdc: ^1.3.0 + secure-json-parse: ^2.5.0 + semver: ^7.3.7 + tiny-lru: ^11.0.1 + checksum: 919018b384485452ad2100b172080a266327bdb89c68dbebb8443cc2352efe96fb3d1225d2924b1ca8f06175d5478fee5c18c8857fe6981b8c39328182ff48a1 + languageName: node + linkType: hard + +"fastq@npm:^1.6.0, fastq@npm:^1.6.1": version: 1.15.0 resolution: "fastq@npm:1.15.0" dependencies: @@ -9758,6 +11282,41 @@ __metadata: languageName: node linkType: hard +"file-type@npm:^11.1.0": + version: 11.1.0 + resolution: "file-type@npm:11.1.0" + checksum: 66c2086867291fda760a245534bec1fbf12817dc6fd3426c2b41f29a37c71bb61f1091505c98f03a446703321cc1d4a8e873ce631f5763fc53178645d9eb3f85 + languageName: node + linkType: hard + +"file-type@npm:^3.8.0": + version: 3.9.0 + resolution: "file-type@npm:3.9.0" + checksum: 1db70b2485ac77c4edb4b8753c1874ee6194123533f43c2651820f96b518f505fa570b093fedd6672eb105ba9fb89c62f84b6492e46788e39c3447aed37afa2d + languageName: node + linkType: hard + +"file-type@npm:^4.2.0": + version: 4.4.0 + resolution: "file-type@npm:4.4.0" + checksum: f3e0b38bef643a330b3d98e3aa9d6f0f32d2d80cb9341f5612187bd53ac84489a4dc66b354bd0cff6b60bff053c7ef21eb8923d62e9f1196ac627b63bd7875ef + languageName: node + linkType: hard + +"file-type@npm:^5.2.0": + version: 5.2.0 + resolution: "file-type@npm:5.2.0" + checksum: b2b21c7fc3cfb3c6a3a18b0d5d7233b74d8c17d82757655766573951daf42962a5c809e5fc3637675b237c558ebc67e4958fb2cc5a4ad407bc545aaa40001c74 + languageName: node + linkType: hard + +"file-type@npm:^6.1.0": + version: 6.2.0 + resolution: "file-type@npm:6.2.0" + checksum: 749540cefcd4959121eb83e373ed84e49b2e5a510aa5d598b725bd772dd306ae41fd00d3162ae3f6563b4db5cfafbbd0df321de3f20c17e20a8c56431ae55e58 + languageName: node + linkType: hard + "file-uri-to-path@npm:1.0.0": version: 1.0.0 resolution: "file-uri-to-path@npm:1.0.0" @@ -9772,6 +11331,33 @@ __metadata: languageName: node linkType: hard +"filelist@npm:^1.0.1": + version: 1.0.4 + resolution: "filelist@npm:1.0.4" + dependencies: + minimatch: ^5.0.1 + checksum: a303573b0821e17f2d5e9783688ab6fbfce5d52aaac842790ae85e704a6f5e4e3538660a63183d6453834dedf1e0f19a9dadcebfa3e926c72397694ea11f5160 + languageName: node + linkType: hard + +"filename-reserved-regex@npm:^2.0.0": + version: 2.0.0 + resolution: "filename-reserved-regex@npm:2.0.0" + checksum: 323a0020fd7f243238ffccab9d728cbc5f3a13c84b2c10e01efb09b8324561d7a51776be76f36603c734d4f69145c39a5d12492bf6142a28b50d7f90bd6190bc + languageName: node + linkType: hard + +"filenamify@npm:^3.0.0": + version: 3.0.0 + resolution: "filenamify@npm:3.0.0" + dependencies: + filename-reserved-regex: ^2.0.0 + strip-outer: ^1.0.0 + trim-repeated: ^1.0.0 + checksum: d419eaa1b8c331ab8616e1fffe33e4af135c60b5364320bbe015bc93ded89c6c301363f69593991de18a8f9dd278324c0a0d89fd554c30250306f4c16c956673 + languageName: node + linkType: hard + "fill-range@npm:^7.0.1": version: 7.0.1 resolution: "fill-range@npm:7.0.1" @@ -9796,6 +11382,17 @@ __metadata: languageName: node linkType: hard +"find-my-way@npm:^7.6.0": + version: 7.6.1 + resolution: "find-my-way@npm:7.6.1" + dependencies: + fast-deep-equal: ^3.1.3 + fast-querystring: ^1.0.0 + safe-regex2: ^2.0.0 + checksum: c6993162cf5c942a79ff22044178d19dbd990083d4a8b68632821228a3abb1288672e8939fd51e8ec58b4dc3bf71835434c5af43c0394b0d9c1e41084a90315e + languageName: node + linkType: hard + "find-replace@npm:^3.0.0": version: 3.0.0 resolution: "find-replace@npm:3.0.0" @@ -9815,6 +11412,16 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^1.0.0": + version: 1.1.2 + resolution: "find-up@npm:1.1.2" + dependencies: + path-exists: ^2.0.0 + pinkie-promise: ^2.0.0 + checksum: a2cb9f4c9f06ee3a1e92ed71d5aed41ac8ae30aefa568132f6c556fac7678a5035126153b59eaec68da78ac409eef02503b2b059706bdbf232668d7245e3240a + languageName: node + linkType: hard + "find-up@npm:^2.0.0": version: 2.1.0 resolution: "find-up@npm:2.1.0" @@ -9843,6 +11450,16 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^6.3.0": + version: 6.3.0 + resolution: "find-up@npm:6.3.0" + dependencies: + locate-path: ^7.1.0 + path-exists: ^5.0.0 + checksum: 9a21b7f9244a420e54c6df95b4f6fc3941efd3c3e5476f8274eb452f6a85706e7a6a90de71353ee4f091fcb4593271a6f92810a324ec542650398f928783c280 + languageName: node + linkType: hard + "flat-cache@npm:^3.0.4": version: 3.0.4 resolution: "flat-cache@npm:3.0.4" @@ -9869,6 +11486,16 @@ __metadata: languageName: node linkType: hard +"follow-redirects@npm:^1.15.0": + version: 1.15.2 + resolution: "follow-redirects@npm:1.15.2" + peerDependenciesMeta: + debug: + optional: true + checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190 + languageName: node + linkType: hard + "for-each@npm:^0.3.3": version: 0.3.3 resolution: "for-each@npm:0.3.3" @@ -9878,6 +11505,30 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.1.1 + resolution: "foreground-child@npm:3.1.1" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^4.0.1 + checksum: 139d270bc82dc9e6f8bc045fe2aae4001dc2472157044fdfad376d0a3457f77857fa883c1c8b21b491c6caade9a926a4bed3d3d2e8d3c9202b151a4cbbd0bcd5 + languageName: node + linkType: hard + +"forever-agent@npm:~0.6.1": + version: 0.6.1 + resolution: "forever-agent@npm:0.6.1" + checksum: 766ae6e220f5fe23676bb4c6a99387cec5b7b62ceb99e10923376e27bfea72f3c3aeec2ba5f45f3f7ba65d6616965aa7c20b15002b6860833bb6e394dea546a8 + languageName: node + linkType: hard + +"form-data-encoder@npm:^2.1.2": + version: 2.1.4 + resolution: "form-data-encoder@npm:2.1.4" + checksum: e0b3e5950fb69b3f32c273944620f9861f1933df9d3e42066e038e26dfb343d0f4465de9f27e0ead1a09d9df20bc2eed06a63c2ca2f8f00949e7202bae9e29dd + languageName: node + linkType: hard + "form-data@npm:^3.0.0": version: 3.0.1 resolution: "form-data@npm:3.0.1" @@ -9900,6 +11551,17 @@ __metadata: languageName: node linkType: hard +"form-data@npm:~2.3.2": + version: 2.3.3 + resolution: "form-data@npm:2.3.3" + dependencies: + asynckit: ^0.4.0 + combined-stream: ^1.0.6 + mime-types: ^2.1.12 + checksum: 10c1780fa13dbe1ff3100114c2ce1f9307f8be10b14bf16e103815356ff567b6be39d70fc4a40f8990b9660012dc24b0f5e1dde1b6426166eb23a445ba068ca3 + languageName: node + linkType: hard + "format@npm:^0.2.0": version: 0.2.2 resolution: "format@npm:0.2.2" @@ -9928,6 +11590,23 @@ __metadata: languageName: node linkType: hard +"from2@npm:^2.1.1": + version: 2.3.0 + resolution: "from2@npm:2.3.0" + dependencies: + inherits: ^2.0.1 + readable-stream: ^2.0.0 + checksum: 6080eba0793dce32f475141fb3d54cc15f84ee52e420ee22ac3ab0ad639dc95a1875bc6eb9c0e1140e94972a36a89dc5542491b85f1ab8df0c126241e0f1a61b + languageName: node + linkType: hard + +"from@npm:~0": + version: 0.1.7 + resolution: "from@npm:0.1.7" + checksum: b85125b7890489656eb2e4f208f7654a93ec26e3aefaf3bbbcc0d496fc1941e4405834fcc9fe7333192aa2187905510ace70417bbf9ac6f6f4784a731d986939 + languageName: node + linkType: hard + "fromentries@npm:^1.2.0, fromentries@npm:^1.3.2": version: 1.3.2 resolution: "fromentries@npm:1.3.2" @@ -9964,7 +11643,7 @@ __metadata: languageName: node linkType: hard -"fs-extra@npm:^11.1.0": +"fs-extra@npm:^11.1.0, fs-extra@npm:^11.1.1": version: 11.1.1 resolution: "fs-extra@npm:11.1.1" dependencies: @@ -10101,6 +11780,30 @@ __metadata: languageName: node linkType: hard +"gaze@npm:^1.1.2": + version: 1.1.3 + resolution: "gaze@npm:1.1.3" + dependencies: + globule: ^1.0.0 + checksum: d5fd375a029c07346154806a076bde21290598179d01ffbe7bc3e54092fa65814180bd27fc2b577582737733eec77cdbb7a572a4e73dff934dde60317223cde6 + languageName: node + linkType: hard + +"geckodriver@npm:^3.2.0": + version: 3.2.0 + resolution: "geckodriver@npm:3.2.0" + dependencies: + adm-zip: 0.5.9 + bluebird: 3.7.2 + got: 11.8.5 + https-proxy-agent: 5.0.1 + tar: 6.1.11 + bin: + geckodriver: bin/geckodriver + checksum: fe196312c97927be943aa97f2343ba15a6fb612b51a1e8ea96c0c8c6e8d50c81d33517e604e3db3777ada28bb9f5b0dad7e822ca4e841f16375a8f1d77df08ae + languageName: node + linkType: hard + "generic-names@npm:^4.0.0": version: 4.0.0 resolution: "generic-names@npm:4.0.0" @@ -10159,6 +11862,13 @@ __metadata: languageName: node linkType: hard +"get-port@npm:6.1.2, get-port@npm:^6.1.2": + version: 6.1.2 + resolution: "get-port@npm:6.1.2" + checksum: e3c3d591492a11393455ef220f24c812a28f7da56ec3e4a2512d931a1f196d42850b50ac6138349a44622eda6dc3c0ccd8495cd91376d968e2d9e6f6f849e0a9 + languageName: node + linkType: hard + "get-port@npm:^5.1.1": version: 5.1.1 resolution: "get-port@npm:5.1.1" @@ -10166,6 +11876,32 @@ __metadata: languageName: node linkType: hard +"get-stream@npm:3.0.0, get-stream@npm:^3.0.0": + version: 3.0.0 + resolution: "get-stream@npm:3.0.0" + checksum: 36142f46005ed74ce3a45c55545ec4e7da8e243554179e345a786baf144e5c4a35fb7bdc49fadfa9f18bd08000589b6fe364abdadfc4e1eb0e1b9914a6bb9c56 + languageName: node + linkType: hard + +"get-stream@npm:^2.2.0": + version: 2.3.1 + resolution: "get-stream@npm:2.3.1" + dependencies: + object-assign: ^4.0.1 + pinkie-promise: ^2.0.0 + checksum: d82c86556e131ba7bef00233aa0aa7a51230e6deac11a971ce0f47cd43e2a5e968a3e3914cd082f07cd0d69425653b2f96735b0a7d5c5c03fef3ab857a531367 + languageName: node + linkType: hard + +"get-stream@npm:^4.1.0": + version: 4.1.0 + resolution: "get-stream@npm:4.1.0" + dependencies: + pump: ^3.0.0 + checksum: 443e1914170c15bd52ff8ea6eff6dfc6d712b031303e36302d2778e3de2506af9ee964d6124010f7818736dcfde05c04ba7ca6cc26883106e084357a17ae7d73 + languageName: node + linkType: hard + "get-stream@npm:^5.1.0": version: 5.2.0 resolution: "get-stream@npm:5.2.0" @@ -10213,6 +11949,15 @@ __metadata: languageName: node linkType: hard +"getpass@npm:^0.1.1": + version: 0.1.7 + resolution: "getpass@npm:0.1.7" + dependencies: + assert-plus: ^1.0.0 + checksum: ab18d55661db264e3eac6012c2d3daeafaab7a501c035ae0ccb193c3c23e9849c6e29b6ac762b9c2adae460266f925d55a3a2a3a3c8b94be2f222df94d70c046 + languageName: node + linkType: hard + "git-hooks-list@npm:1.0.3": version: 1.0.3 resolution: "git-hooks-list@npm:1.0.3" @@ -10220,6 +11965,22 @@ __metadata: languageName: node linkType: hard +"git-repo-info@npm:^2.1.1": + version: 2.1.1 + resolution: "git-repo-info@npm:2.1.1" + checksum: 58cedacae81bbe8fedc81d226346c472d11357d1758140ab0ee5d0c3360ad5b7a9d8613ca6e8b50d089d073e5b3f2e2893536d0cb57bced5f558dc913d5e21c6 + languageName: node + linkType: hard + +"gitconfiglocal@npm:^2.1.0": + version: 2.1.0 + resolution: "gitconfiglocal@npm:2.1.0" + dependencies: + ini: ^1.3.2 + checksum: 4b4b44d992a6abf2900eec8cfe960dc36e0d3c2467d20ec69e0a0f13b6b7645b926daa004df42f94c34ad28a58529cf2522fa0bf261e4e7b95958fb451dcedda + languageName: node + linkType: hard + "github-from-package@npm:0.0.0": version: 0.0.0 resolution: "github-from-package@npm:0.0.0" @@ -10276,7 +12037,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.1.7": +"glob@npm:7.1.7, glob@npm:~7.1.1": version: 7.1.7 resolution: "glob@npm:7.1.7" dependencies: @@ -10304,7 +12065,22 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.6, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4": +"glob@npm:^10.2.2": + version: 10.2.2 + resolution: "glob@npm:10.2.2" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^2.0.3 + minimatch: ^9.0.0 + minipass: ^5.0.0 + path-scurry: ^1.7.0 + bin: + glob: dist/cjs/src/bin.js + checksum: 33cbbbea74deb605107715f2ee51937953271ff2f6ce712b57d95a714e2f1bf272fa2c2b0c5101097bf98d3e5d40856941af498b05bce07567aca1a6e3cc7ae9 + languageName: node + linkType: hard + +"glob@npm:^7.0.3, glob@npm:^7.0.5, glob@npm:^7.0.6, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -10418,6 +12194,19 @@ __metadata: languageName: node linkType: hard +"globby@npm:^6.1.0": + version: 6.1.0 + resolution: "globby@npm:6.1.0" + dependencies: + array-union: ^1.0.1 + glob: ^7.0.3 + object-assign: ^4.0.1 + pify: ^2.0.0 + pinkie-promise: ^2.0.0 + checksum: 18109d6b9d55643d2b98b59c3cfae7073ccfe39829632f353d516cc124d836c2ddebe48a23f04af63d66a621b6d86dd4cbd7e6af906f2458a7fe510ffc4bd424 + languageName: node + linkType: hard + "globby@npm:^7.1.1": version: 7.1.1 resolution: "globby@npm:7.1.1" @@ -10439,6 +12228,17 @@ __metadata: languageName: node linkType: hard +"globule@npm:^1.0.0": + version: 1.3.4 + resolution: "globule@npm:1.3.4" + dependencies: + glob: ~7.1.1 + lodash: ^4.17.21 + minimatch: ~3.0.2 + checksum: 258b6865c77d54fbd4c91dd6931d99baf81b1485fdf4bd2c053b1a10eab015163cb646e6c96812d5c8b027fb07adfc0b7c7fb13bbbb571f3c12ea60bd7fda2f5 + languageName: node + linkType: hard + "gopd@npm:^1.0.1": version: 1.0.1 resolution: "gopd@npm:1.0.1" @@ -10448,6 +12248,25 @@ __metadata: languageName: node linkType: hard +"got@npm:11.8.5": + version: 11.8.5 + resolution: "got@npm:11.8.5" + dependencies: + "@sindresorhus/is": ^4.0.0 + "@szmarczak/http-timer": ^4.0.5 + "@types/cacheable-request": ^6.0.1 + "@types/responselike": ^1.0.0 + cacheable-lookup: ^5.0.3 + cacheable-request: ^7.0.2 + decompress-response: ^6.0.0 + http2-wrapper: ^1.0.0-beta.5.2 + lowercase-keys: ^2.0.0 + p-cancelable: ^2.0.0 + responselike: ^2.0.0 + checksum: 2de8a1bbda4e9b6b2b72b2d2100bc055a59adc1740529e631f61feb44a8b9a1f9f8590941ed9da9df0090b6d6d0ed8ffee94cd9ac086ec3409b392b33440f7d2 + languageName: node + linkType: hard + "got@npm:^11.0.0": version: 11.8.6 resolution: "got@npm:11.8.6" @@ -10467,14 +12286,58 @@ __metadata: languageName: node linkType: hard -"graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": +"got@npm:^12.1.0": + version: 12.6.0 + resolution: "got@npm:12.6.0" + dependencies: + "@sindresorhus/is": ^5.2.0 + "@szmarczak/http-timer": ^5.0.1 + cacheable-lookup: ^7.0.0 + cacheable-request: ^10.2.8 + decompress-response: ^6.0.0 + form-data-encoder: ^2.1.2 + get-stream: ^6.0.1 + http2-wrapper: ^2.1.10 + lowercase-keys: ^3.0.0 + p-cancelable: ^3.0.0 + responselike: ^3.0.0 + checksum: 3621897067068dcb3578d05535cfb10f60aac07198032b3349a488f5741964e7f63d6e37c976840f1bcaaf42f5c049ed3c6d8e0d6c622b74639ca9319ad178a1 + languageName: node + linkType: hard + +"got@npm:^8.3.1": + version: 8.3.2 + resolution: "got@npm:8.3.2" + dependencies: + "@sindresorhus/is": ^0.7.0 + cacheable-request: ^2.1.1 + decompress-response: ^3.3.0 + duplexer3: ^0.1.4 + get-stream: ^3.0.0 + into-stream: ^3.1.0 + is-retry-allowed: ^1.1.0 + isurl: ^1.0.0-alpha5 + lowercase-keys: ^1.0.0 + mimic-response: ^1.0.0 + p-cancelable: ^0.4.0 + p-timeout: ^2.0.1 + pify: ^3.0.0 + safe-buffer: ^5.1.1 + timed-out: ^4.0.1 + url-parse-lax: ^3.0.0 + url-to-options: ^1.0.1 + checksum: ab05bfcb6de86dc0c3fba8d25cc51cb2b09851ff3f6f899c86cde8c63b30269f8823d69dbbc6d03f7c58bb069f55a3c5f60aba74aad6721938652d8f35fd3165 + languageName: node + linkType: hard + +"graceful-fs@npm:^4.1.10, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6, graceful-fs@npm:^4.2.9": version: 4.2.11 resolution: "graceful-fs@npm:4.2.11" checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 languageName: node linkType: hard -"grapheme-splitter@npm:^1.0.4": +"grapheme-splitter@npm:^1.0.2, grapheme-splitter@npm:^1.0.4": version: 1.0.4 resolution: "grapheme-splitter@npm:1.0.4" checksum: 0c22ec54dee1b05cd480f78cf14f732cb5b108edc073572c4ec205df4cd63f30f8db8025afc5debc8835a8ddeacf648a1c7992fe3dcd6ad38f9a476d84906620 @@ -10525,6 +12388,32 @@ __metadata: languageName: node linkType: hard +"har-schema@npm:^2.0.0": + version: 2.0.0 + resolution: "har-schema@npm:2.0.0" + checksum: d8946348f333fb09e2bf24cc4c67eabb47c8e1d1aa1c14184c7ffec1140a49ec8aa78aa93677ae452d71d5fc0fdeec20f0c8c1237291fc2bcb3f502a5d204f9b + languageName: node + linkType: hard + +"har-validator@npm:~5.1.3": + version: 5.1.5 + resolution: "har-validator@npm:5.1.5" + dependencies: + ajv: ^6.12.3 + har-schema: ^2.0.0 + checksum: b998a7269ca560d7f219eedc53e2c664cd87d487e428ae854a6af4573fc94f182fe9d2e3b92ab968249baec7ebaf9ead69cf975c931dc2ab282ec182ee988280 + languageName: node + linkType: hard + +"has-ansi@npm:^2.0.0": + version: 2.0.0 + resolution: "has-ansi@npm:2.0.0" + dependencies: + ansi-regex: ^2.0.0 + checksum: 1b51daa0214440db171ff359d0a2d17bc20061164c57e76234f614c91dbd2a79ddd68dfc8ee73629366f7be45a6df5f2ea9de83f52e1ca24433f2cc78c35d8ec + languageName: node + linkType: hard + "has-bigints@npm:^1.0.1, has-bigints@npm:^1.0.2": version: 1.0.2 resolution: "has-bigints@npm:1.0.2" @@ -10562,6 +12451,13 @@ __metadata: languageName: node linkType: hard +"has-symbol-support-x@npm:^1.4.1": + version: 1.4.2 + resolution: "has-symbol-support-x@npm:1.4.2" + checksum: ff06631d556d897424c00e8e79c10093ad34c93e88bb0563932d7837f148a4c90a4377abc5d8da000cb6637c0ecdb4acc9ae836c7cfd0ffc919986db32097609 + languageName: node + linkType: hard + "has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": version: 1.0.3 resolution: "has-symbols@npm:1.0.3" @@ -10569,6 +12465,15 @@ __metadata: languageName: node linkType: hard +"has-to-string-tag-x@npm:^1.2.0": + version: 1.4.1 + resolution: "has-to-string-tag-x@npm:1.4.1" + dependencies: + has-symbol-support-x: ^1.4.1 + checksum: 804c4505727be7770f8b2f5e727ce31c9affc5b83df4ce12344f44b68d557fefb31f77751dbd739de900653126bcd71f8842fac06f97a3fae5422685ab0ce6f0 + languageName: node + linkType: hard + "has-tostringtag@npm:^1.0.0": version: 1.0.0 resolution: "has-tostringtag@npm:1.0.0" @@ -10687,7 +12592,14 @@ __metadata: languageName: node linkType: hard -"hosted-git-info@npm:^4.0.2": +"hosted-git-info@npm:^2.1.4": + version: 2.8.9 + resolution: "hosted-git-info@npm:2.8.9" + checksum: c955394bdab888a1e9bb10eb33029e0f7ce5a2ac7b3f158099dc8c486c99e73809dca609f5694b223920ca2174db33d32b12f9a2a47141dc59607c29da5a62dd + languageName: node + linkType: hard + +"hosted-git-info@npm:^4.0.1, hosted-git-info@npm:^4.0.2": version: 4.1.0 resolution: "hosted-git-info@npm:4.1.0" dependencies: @@ -10731,7 +12643,14 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0": +"http-cache-semantics@npm:3.8.1": + version: 3.8.1 + resolution: "http-cache-semantics@npm:3.8.1" + checksum: b1108d37be478fa9b03890d4185217aac2256e9d2247ce6c6bd90bc5432687d68dc7710ba908cea6166fb983a849d902195241626cf175a3c62817a494c0f7f6 + languageName: node + linkType: hard + +"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0, http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 @@ -10773,6 +12692,17 @@ __metadata: languageName: node linkType: hard +"http-signature@npm:~1.2.0": + version: 1.2.0 + resolution: "http-signature@npm:1.2.0" + dependencies: + assert-plus: ^1.0.0 + jsprim: ^1.2.2 + sshpk: ^1.7.0 + checksum: 3324598712266a9683585bb84a75dec4fd550567d5e0dd4a0fff6ff3f74348793404d3eeac4918fa0902c810eeee1a86419e4a2e92a164132dfe6b26743fb47c + languageName: node + linkType: hard + "http2-wrapper@npm:^1.0.0-beta.5.2": version: 1.0.3 resolution: "http2-wrapper@npm:1.0.3" @@ -10783,7 +12713,17 @@ __metadata: languageName: node linkType: hard -"https-proxy-agent@npm:5, https-proxy-agent@npm:^5.0.0, https-proxy-agent@npm:^5.0.1": +"http2-wrapper@npm:^2.1.10": + version: 2.2.0 + resolution: "http2-wrapper@npm:2.2.0" + dependencies: + quick-lru: ^5.1.1 + resolve-alpn: ^1.2.0 + checksum: 6fd20e5cb6a58151715b3581e06a62a47df943187d2d1f69e538a50cccb7175dd334ecfde7900a37d18f3e13a1a199518a2c211f39860e81e9a16210c199cfaa + languageName: node + linkType: hard + +"https-proxy-agent@npm:5, https-proxy-agent@npm:5.0.1, https-proxy-agent@npm:^5.0.0, https-proxy-agent@npm:^5.0.1": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" dependencies: @@ -10880,6 +12820,13 @@ __metadata: languageName: node linkType: hard +"immediate@npm:~3.0.5": + version: 3.0.6 + resolution: "immediate@npm:3.0.6" + checksum: f9b3486477555997657f70318cc8d3416159f208bec4cca3ff3442fd266bc23f50f0c9bd8547e1371a6b5e82b821ec9a7044a4f7b944798b25aa3cc6d5e63e62 + languageName: node + linkType: hard + "import-cwd@npm:^3.0.0": version: 3.0.0 resolution: "import-cwd@npm:3.0.0" @@ -10927,6 +12874,13 @@ __metadata: languageName: node linkType: hard +"import-meta-resolve@npm:^3.0.0": + version: 3.0.0 + resolution: "import-meta-resolve@npm:3.0.0" + checksum: d0428cd14915ee0093b995dc5bbc70bd01cc668822f52b62af98f728e5d6a08724f07e6aa9f5fae002d5eecbf6ec2cdcd379bf4869dd1b353bd080693f91e394 + languageName: node + linkType: hard + "imurmurhash@npm:^0.1.4": version: 0.1.4 resolution: "imurmurhash@npm:0.1.4" @@ -10934,6 +12888,13 @@ __metadata: languageName: node linkType: hard +"indent-string@npm:^3.0.0": + version: 3.2.0 + resolution: "indent-string@npm:3.2.0" + checksum: a0b72603bba6c985d367fda3a25aad16423d2056b22a7e83ee2dd9ce0ce3d03d1e078644b679087aa7edf1cfb457f0d96d9eeadc0b12f38582088cc00e995d2f + languageName: node + linkType: hard + "indent-string@npm:^4.0.0": version: 4.0.0 resolution: "indent-string@npm:4.0.0" @@ -10965,7 +12926,7 @@ __metadata: languageName: node linkType: hard -"ini@npm:~1.3.0": +"ini@npm:^1.3.2, ini@npm:~1.3.0": version: 1.3.8 resolution: "ini@npm:1.3.8" checksum: dfd98b0ca3a4fc1e323e38a6c8eb8936e31a97a918d3b377649ea15bdb15d481207a0dda1021efbd86b464cae29a0d33c1d7dcaf6c5672bee17fa849bc50a1b3 @@ -10979,6 +12940,29 @@ __metadata: languageName: node linkType: hard +"inquirer@npm:9.1.5, inquirer@npm:^9.1.4": + version: 9.1.5 + resolution: "inquirer@npm:9.1.5" + dependencies: + ansi-escapes: ^6.0.0 + chalk: ^5.2.0 + cli-cursor: ^4.0.0 + cli-width: ^4.0.0 + external-editor: ^3.0.3 + figures: ^5.0.0 + lodash: ^4.17.21 + mute-stream: 1.0.0 + ora: ^6.1.2 + run-async: ^2.4.0 + rxjs: ^7.8.0 + string-width: ^5.1.2 + strip-ansi: ^7.0.1 + through: ^2.3.6 + wrap-ansi: ^8.1.0 + checksum: 8491fad532c781041abca63605505ddb4bfeaf6547c8a68aa3e9f5a72dec627e1f222d3dbb082dc741cbb031394c6ae3ca0c3d7d0ffde5087f235939996e63e5 + languageName: node + linkType: hard + "inquirer@npm:^8.2.1": version: 8.2.5 resolution: "inquirer@npm:8.2.5" @@ -11002,29 +12986,6 @@ __metadata: languageName: node linkType: hard -"inquirer@npm:^9.1.4": - version: 9.1.5 - resolution: "inquirer@npm:9.1.5" - dependencies: - ansi-escapes: ^6.0.0 - chalk: ^5.2.0 - cli-cursor: ^4.0.0 - cli-width: ^4.0.0 - external-editor: ^3.0.3 - figures: ^5.0.0 - lodash: ^4.17.21 - mute-stream: 1.0.0 - ora: ^6.1.2 - run-async: ^2.4.0 - rxjs: ^7.8.0 - string-width: ^5.1.2 - strip-ansi: ^7.0.1 - through: ^2.3.6 - wrap-ansi: ^8.1.0 - checksum: 8491fad532c781041abca63605505ddb4bfeaf6547c8a68aa3e9f5a72dec627e1f222d3dbb082dc741cbb031394c6ae3ca0c3d7d0ffde5087f235939996e63e5 - languageName: node - linkType: hard - "internal-slot@npm:^1.0.3, internal-slot@npm:^1.0.4, internal-slot@npm:^1.0.5": version: 1.0.5 resolution: "internal-slot@npm:1.0.5" @@ -11036,6 +12997,16 @@ __metadata: languageName: node linkType: hard +"into-stream@npm:^3.1.0": + version: 3.1.0 + resolution: "into-stream@npm:3.1.0" + dependencies: + from2: ^2.1.1 + p-is-promise: ^1.1.0 + checksum: e6e1a202227b20c446c251ef95348b3e8503cdc75aa2a09076f8821fc42c1b7fd43fabaeb8ed3cf9eb875942cfa4510b66949c5317997aa640921cc9bbadcd17 + languageName: node + linkType: hard + "invariant@npm:^2.2.4": version: 2.2.4 resolution: "invariant@npm:2.2.4" @@ -11054,7 +13025,14 @@ __metadata: languageName: node linkType: hard -"ip@npm:^1.1.5": +"ip-regex@npm:^4.1.0": + version: 4.3.0 + resolution: "ip-regex@npm:4.3.0" + checksum: 7ff904b891221b1847f3fdf3dbb3e6a8660dc39bc283f79eb7ed88f5338e1a3d1104b779bc83759159be266249c59c2160e779ee39446d79d4ed0890dfd06f08 + languageName: node + linkType: hard + +"ip@npm:^1.1.5, ip@npm:^1.1.8": version: 1.1.8 resolution: "ip@npm:1.1.8" checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb @@ -11120,6 +13098,13 @@ __metadata: languageName: node linkType: hard +"is-arrayish@npm:^0.3.1": + version: 0.3.2 + resolution: "is-arrayish@npm:0.3.2" + checksum: 977e64f54d91c8f169b59afcd80ff19227e9f5c791fa28fa2e5bce355cbaf6c2c356711b734656e80c9dd4a854dd7efcf7894402f1031dfc5de5d620775b4d5f + languageName: node + linkType: hard + "is-bigint@npm:^1.0.1": version: 1.0.4 resolution: "is-bigint@npm:1.0.4" @@ -11173,7 +13158,7 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.0, is-core-module@npm:^2.9.0": +"is-core-module@npm:^2.1.0, is-core-module@npm:^2.11.0, is-core-module@npm:^2.12.0, is-core-module@npm:^2.5.0, is-core-module@npm:^2.9.0": version: 2.12.0 resolution: "is-core-module@npm:2.12.0" dependencies: @@ -11326,6 +13311,13 @@ __metadata: languageName: node linkType: hard +"is-natural-number@npm:^4.0.1": + version: 4.0.1 + resolution: "is-natural-number@npm:4.0.1" + checksum: 3e5e3d52e0dfa4fea923b5d2b8a5cdbd9bf110c4598d30304b98528b02f40c9058a2abf1bae10bcbaf2bac18ace41cff7bc9673aff339f8c8297fae74ae0e75d + languageName: node + linkType: hard + "is-negative-zero@npm:^2.0.2": version: 2.0.2 resolution: "is-negative-zero@npm:2.0.2" @@ -11349,6 +13341,38 @@ __metadata: languageName: node linkType: hard +"is-object@npm:^1.0.1": + version: 1.0.2 + resolution: "is-object@npm:1.0.2" + checksum: 971219c4b1985b9751f65e4c8296d3104f0457b0e8a70849e848a4a2208bc47317d73b3b85d4a369619cb2df8284dc22584cb2695a7d99aca5e8d0aa64fc075a + languageName: node + linkType: hard + +"is-path-cwd@npm:^2.0.0": + version: 2.2.0 + resolution: "is-path-cwd@npm:2.2.0" + checksum: 46a840921bb8cc0dc7b5b423a14220e7db338072a4495743a8230533ce78812dc152548c86f4b828411fe98c5451959f07cf841c6a19f611e46600bd699e8048 + languageName: node + linkType: hard + +"is-path-in-cwd@npm:^2.0.0": + version: 2.1.0 + resolution: "is-path-in-cwd@npm:2.1.0" + dependencies: + is-path-inside: ^2.1.0 + checksum: 6b01b3f8c9172e9682ea878d001836a0cc5a78cbe6236024365d478c2c9e384da2417e5f21f2ad2da2761d0465309fc5baf6e71187d2a23f0058da69790f7f48 + languageName: node + linkType: hard + +"is-path-inside@npm:^2.1.0": + version: 2.1.0 + resolution: "is-path-inside@npm:2.1.0" + dependencies: + path-is-inside: ^1.0.2 + checksum: 6ca34dbd84d5c50a3ee1547afb6ada9b06d556a4ff42da9b303797e4acc3ac086516a4833030aa570f397f8c58dacabd57ee8e6c2ce8b2396a986ad2af10fcaf + languageName: node + linkType: hard + "is-path-inside@npm:^3.0.3": version: 3.0.3 resolution: "is-path-inside@npm:3.0.3" @@ -11363,6 +13387,13 @@ __metadata: languageName: node linkType: hard +"is-plain-obj@npm:^1.0.0": + version: 1.1.0 + resolution: "is-plain-obj@npm:1.1.0" + checksum: 0ee04807797aad50859652a7467481816cbb57e5cc97d813a7dcd8915da8195dc68c436010bf39d195226cde6a2d352f4b815f16f26b7bf486a5754290629931 + languageName: node + linkType: hard + "is-plain-obj@npm:^3.0.0": version: 3.0.0 resolution: "is-plain-obj@npm:3.0.0" @@ -11370,7 +13401,7 @@ __metadata: languageName: node linkType: hard -"is-plain-obj@npm:^4.0.0": +"is-plain-obj@npm:^4.0.0, is-plain-obj@npm:^4.1.0": version: 4.1.0 resolution: "is-plain-obj@npm:4.1.0" checksum: 6dc45da70d04a81f35c9310971e78a6a3c7a63547ef782e3a07ee3674695081b6ca4e977fbb8efc48dae3375e0b34558d2bcd722aec9bddfa2d7db5b041be8ce @@ -11410,6 +13441,20 @@ __metadata: languageName: node linkType: hard +"is-retry-allowed@npm:^1.1.0": + version: 1.2.0 + resolution: "is-retry-allowed@npm:1.2.0" + checksum: 50d700a89ae31926b1c91b3eb0104dbceeac8790d8b80d02f5c76d9a75c2056f1bb24b5268a8a018dead606bddf116b2262e5ac07401eb8b8783b266ed22558d + languageName: node + linkType: hard + +"is-running@npm:^2.1.0": + version: 2.1.0 + resolution: "is-running@npm:2.1.0" + checksum: b8804a041e532d218b123ddd34e1b5ff086ba03d100373dfda99179569b3fd146de228bef65d7448be4c4dfae3df84d9aa45b1e1b4a044899fc0521c68e32692 + languageName: node + linkType: hard + "is-set@npm:^2.0.1, is-set@npm:^2.0.2": version: 2.0.2 resolution: "is-set@npm:2.0.2" @@ -11426,6 +13471,13 @@ __metadata: languageName: node linkType: hard +"is-stream@npm:^1.1.0": + version: 1.1.0 + resolution: "is-stream@npm:1.1.0" + checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae + languageName: node + linkType: hard + "is-stream@npm:^2.0.0": version: 2.0.1 resolution: "is-stream@npm:2.0.1" @@ -11471,6 +13523,13 @@ __metadata: languageName: node linkType: hard +"is-typedarray@npm:~1.0.0": + version: 1.0.0 + resolution: "is-typedarray@npm:1.0.0" + checksum: 3508c6cd0a9ee2e0df2fa2e9baabcdc89e911c7bd5cf64604586697212feec525aa21050e48affb5ffc3df20f0f5d2e2cf79b08caa64e1ccc9578e251763aef7 + languageName: node + linkType: hard + "is-unicode-supported@npm:^0.1.0": version: 0.1.0 resolution: "is-unicode-supported@npm:0.1.0" @@ -11485,6 +13544,20 @@ __metadata: languageName: node linkType: hard +"is-url@npm:^1.2.4": + version: 1.2.4 + resolution: "is-url@npm:1.2.4" + checksum: 100e74b3b1feab87a43ef7653736e88d997eb7bd32e71fd3ebc413e58c1cbe56269699c776aaea84244b0567f2a7d68dfaa512a062293ed2f9fdecb394148432 + languageName: node + linkType: hard + +"is-utf8@npm:^0.2.0": + version: 0.2.1 + resolution: "is-utf8@npm:0.2.1" + checksum: 167ccd2be869fc228cc62c1a28df4b78c6b5485d15a29027d3b5dceb09b383e86a3522008b56dcac14b592b22f0a224388718c2505027a994fd8471465de54b3 + languageName: node + linkType: hard + "is-weakmap@npm:^2.0.1": version: 2.0.1 resolution: "is-weakmap@npm:2.0.1" @@ -11520,6 +13593,17 @@ __metadata: languageName: node linkType: hard +"is2@npm:^2.0.6": + version: 2.0.9 + resolution: "is2@npm:2.0.9" + dependencies: + deep-is: ^0.1.3 + ip-regex: ^4.1.0 + is-url: ^1.2.4 + checksum: be778a3bd0770799bd6d9b79916d2467a150a111088858dc00f6ea5a52b0e12d3a0a5cfd350d990bdb562552388be406707ee91ac6d40b96371c3a97aca1e579 + languageName: node + linkType: hard + "isarray@npm:0.0.1": version: 0.0.1 resolution: "isarray@npm:0.0.1" @@ -11548,6 +13632,13 @@ __metadata: languageName: node linkType: hard +"isstream@npm:~0.1.2": + version: 0.1.2 + resolution: "isstream@npm:0.1.2" + checksum: 1eb2fe63a729f7bdd8a559ab552c69055f4f48eb5c2f03724430587c6f450783c8f1cd936c1c952d0a927925180fcc892ebd5b174236cf1065d4bd5bdb37e963 + languageName: node + linkType: hard + "istanbul-lib-coverage@npm:^3.0.0, istanbul-lib-coverage@npm:^3.2.0": version: 3.2.0 resolution: "istanbul-lib-coverage@npm:3.2.0" @@ -11600,6 +13691,43 @@ __metadata: languageName: node linkType: hard +"isurl@npm:^1.0.0-alpha5": + version: 1.0.0 + resolution: "isurl@npm:1.0.0" + dependencies: + has-to-string-tag-x: ^1.2.0 + is-object: ^1.0.1 + checksum: 28a96e019269d57015fa5869f19dda5a3ed1f7b21e3e0c4ff695419bd0541547db352aa32ee4a3659e811a177b0e37a5bc1a036731e71939dd16b59808ab92bd + languageName: node + linkType: hard + +"jackspeak@npm:^2.0.3": + version: 2.2.0 + resolution: "jackspeak@npm:2.2.0" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: d8cd5be4f0e89cef04add5b0b068162a086bdb1ca68113ed729e99489b7865ca3edcc6430d6fd20c430e15382929ef5f3c7ec36e6aa7c17be23cac116f92dcff + languageName: node + linkType: hard + +"jake@npm:^10.8.5": + version: 10.8.5 + resolution: "jake@npm:10.8.5" + dependencies: + async: ^3.2.3 + chalk: ^4.0.2 + filelist: ^1.0.1 + minimatch: ^3.0.4 + bin: + jake: ./bin/cli.js + checksum: 56c913ecf5a8d74325d0af9bc17a233bad50977438d44864d925bb6c45c946e0fee8c4c1f5fe2225471ef40df5222e943047982717ebff0d624770564d3c46ba + languageName: node + linkType: hard + "java-properties@npm:^1.0.0": version: 1.0.2 resolution: "java-properties@npm:1.0.2" @@ -12217,6 +14345,13 @@ __metadata: languageName: node linkType: hard +"jsbn@npm:~0.1.0": + version: 0.1.1 + resolution: "jsbn@npm:0.1.1" + checksum: e5ff29c1b8d965017ef3f9c219dacd6e40ad355c664e277d31246c90545a02e6047018c16c60a00f36d561b3647215c41894f5d869ada6908a2e0ce4200c88f2 + languageName: node + linkType: hard + "jsdom@npm:^19.0.0": version: 19.0.0 resolution: "jsdom@npm:19.0.0" @@ -12323,6 +14458,13 @@ __metadata: languageName: node linkType: hard +"json-buffer@npm:3.0.0": + version: 3.0.0 + resolution: "json-buffer@npm:3.0.0" + checksum: 0cecacb8025370686a916069a2ff81f7d55167421b6aa7270ee74e244012650dd6bce22b0852202ea7ff8624fce50ff0ec1bdf95914ccb4553426e290d5a63fa + languageName: node + linkType: hard + "json-buffer@npm:3.0.1": version: 3.0.1 resolution: "json-buffer@npm:3.0.1" @@ -12368,6 +14510,13 @@ __metadata: languageName: node linkType: hard +"json-schema@npm:0.4.0": + version: 0.4.0 + resolution: "json-schema@npm:0.4.0" + checksum: 66389434c3469e698da0df2e7ac5a3281bcff75e797a5c127db7c5b56270e01ae13d9afa3c03344f76e32e81678337a8c912bdbb75101c62e487dc3778461d72 + languageName: node + linkType: hard + "json-stable-stringify-without-jsonify@npm:^1.0.1": version: 1.0.1 resolution: "json-stable-stringify-without-jsonify@npm:1.0.1" @@ -12375,6 +14524,13 @@ __metadata: languageName: node linkType: hard +"json-stringify-safe@npm:~5.0.1": + version: 5.0.1 + resolution: "json-stringify-safe@npm:5.0.1" + checksum: 48ec0adad5280b8a96bb93f4563aa1667fd7a36334f79149abd42446d0989f2ddc58274b479f4819f1f00617957e6344c886c55d05a4e15ebb4ab931e4a6a8ee + languageName: node + linkType: hard + "json5@npm:^1.0.2": version: 1.0.2 resolution: "json5@npm:1.0.2" @@ -12427,6 +14583,18 @@ __metadata: languageName: node linkType: hard +"jsprim@npm:^1.2.2": + version: 1.4.2 + resolution: "jsprim@npm:1.4.2" + dependencies: + assert-plus: 1.0.0 + extsprintf: 1.3.0 + json-schema: 0.4.0 + verror: 1.10.0 + checksum: 2ad1b9fdcccae8b3d580fa6ced25de930eaa1ad154db21bbf8478a4d30bbbec7925b5f5ff29b933fba9412b16a17bd484a8da4fdb3663b5e27af95dd693bab2a + languageName: node + linkType: hard + "jsx-ast-utils@npm:^2.4.1 || ^3.0.0, jsx-ast-utils@npm:^3.3.3": version: 3.3.3 resolution: "jsx-ast-utils@npm:3.3.3" @@ -12437,6 +14605,18 @@ __metadata: languageName: node linkType: hard +"jszip@npm:^3.10.1": + version: 3.10.1 + resolution: "jszip@npm:3.10.1" + dependencies: + lie: ~3.3.0 + pako: ~1.0.2 + readable-stream: ~2.3.6 + setimmediate: ^1.0.5 + checksum: abc77bfbe33e691d4d1ac9c74c8851b5761fba6a6986630864f98d876f3fcc2d36817dfc183779f32c00157b5d53a016796677298272a714ae096dfe6b1c8b60 + languageName: node + linkType: hard + "keytar@npm:^7.7.0": version: 7.9.0 resolution: "keytar@npm:7.9.0" @@ -12448,7 +14628,16 @@ __metadata: languageName: node linkType: hard -"keyv@npm:^4.0.0": +"keyv@npm:3.0.0": + version: 3.0.0 + resolution: "keyv@npm:3.0.0" + dependencies: + json-buffer: 3.0.0 + checksum: 5182775e546cdbb88dc583825bc0e990164709f31904a219e3321b3bf564a301ac4e5255ba95f7fba466548eba793b356a04a0242110173b199a37192b3b565f + languageName: node + linkType: hard + +"keyv@npm:^4.0.0, keyv@npm:^4.5.2": version: 4.5.2 resolution: "keyv@npm:4.5.2" dependencies: @@ -12478,6 +14667,13 @@ __metadata: languageName: node linkType: hard +"ky@npm:^0.33.0": + version: 0.33.3 + resolution: "ky@npm:0.33.3" + checksum: d1869e1f33c0165355f621b6726fcc1a9de20a31f4a826ca0cfd5753d83b9cba8723402d554a00194e0ee3959e0dda0638f4b99d54a3a7de928b55ff870b0bcc + languageName: node + linkType: hard + "language-subtag-registry@npm:~0.3.2": version: 0.3.22 resolution: "language-subtag-registry@npm:0.3.22" @@ -12520,6 +14716,15 @@ __metadata: languageName: node linkType: hard +"lazystream@npm:^1.0.0": + version: 1.0.1 + resolution: "lazystream@npm:1.0.1" + dependencies: + readable-stream: ^2.0.5 + checksum: 822c54c6b87701a6491c70d4fabc4cafcf0f87d6b656af168ee7bb3c45de9128a801cb612e6eeeefc64d298a7524a698dd49b13b0121ae50c2ae305f0dcc5310 + languageName: node + linkType: hard + "leven@npm:^3.1.0": version: 3.1.0 resolution: "leven@npm:3.1.0" @@ -12547,6 +14752,36 @@ __metadata: languageName: node linkType: hard +"lie@npm:~3.3.0": + version: 3.3.0 + resolution: "lie@npm:3.3.0" + dependencies: + immediate: ~3.0.5 + checksum: 33102302cf19766f97919a6a98d481e01393288b17a6aa1f030a3542031df42736edde8dab29ffdbf90bebeffc48c761eb1d064dc77592ca3ba3556f9fe6d2a8 + languageName: node + linkType: hard + +"light-my-request@npm:^5.6.1": + version: 5.9.1 + resolution: "light-my-request@npm:5.9.1" + dependencies: + cookie: ^0.5.0 + process-warning: ^2.0.0 + set-cookie-parser: ^2.4.1 + checksum: d501924ed3b4e04819145d6577c778a04573305762d6e6e8606307e6253a77a5c71e0ca60d79f37b76232d7cb9ccda11d47ca9969ee5934ca69179c7ee97c07f + languageName: node + linkType: hard + +"lighthouse-logger@npm:^1.0.0": + version: 1.3.0 + resolution: "lighthouse-logger@npm:1.3.0" + dependencies: + debug: ^2.6.9 + marky: ^1.2.2 + checksum: 82655f1862779dc5a917de62567a23dbf47bbde9a53abbdd4d72b2cf80d6c2595bc84de7ef836c94c76a2e4e3f3ec613a2e1ec021367959d397aeb5aae97a7cb + languageName: node + linkType: hard + "lilconfig@npm:2.1.0, lilconfig@npm:^2.0.5": version: 2.1.0 resolution: "lilconfig@npm:2.1.0" @@ -12614,6 +14849,19 @@ __metadata: languageName: node linkType: hard +"load-json-file@npm:^1.0.0": + version: 1.1.0 + resolution: "load-json-file@npm:1.1.0" + dependencies: + graceful-fs: ^4.1.2 + parse-json: ^2.2.0 + pify: ^2.0.0 + pinkie-promise: ^2.0.0 + strip-bom: ^2.0.0 + checksum: 0e4e4f380d897e13aa236246a917527ea5a14e4fc34d49e01ce4e7e2a1e08e2740ee463a03fb021c04f594f29a178f4adb994087549d7c1c5315fcd29bf9934b + languageName: node + linkType: hard + "load-json-file@npm:^4.0.0": version: 4.0.0 resolution: "load-json-file@npm:4.0.0" @@ -12682,6 +14930,15 @@ __metadata: languageName: node linkType: hard +"locate-path@npm:^7.1.0": + version: 7.2.0 + resolution: "locate-path@npm:7.2.0" + dependencies: + p-locate: ^6.0.0 + checksum: c1b653bdf29beaecb3d307dfb7c44d98a2a98a02ebe353c9ad055d1ac45d6ed4e1142563d222df9b9efebc2bcb7d4c792b507fad9e7150a04c29530b7db570f8 + languageName: node + linkType: hard + "lodash.camelcase@npm:^4.3.0": version: 4.3.0 resolution: "lodash.camelcase@npm:4.3.0" @@ -12696,6 +14953,13 @@ __metadata: languageName: node linkType: hard +"lodash.clonedeep@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeep@npm:4.5.0" + checksum: 92c46f094b064e876a23c97f57f81fbffd5d760bf2d8a1c61d85db6d1e488c66b0384c943abee4f6af7debf5ad4e4282e74ff83177c9e63d8ff081a4837c3489 + languageName: node + linkType: hard + "lodash.debounce@npm:^4.0.8": version: 4.0.8 resolution: "lodash.debounce@npm:4.0.8" @@ -12703,6 +14967,34 @@ __metadata: languageName: node linkType: hard +"lodash.defaults@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.defaults@npm:4.2.0" + checksum: 84923258235592c8886e29de5491946ff8c2ae5c82a7ac5cddd2e3cb697e6fbdfbbb6efcca015795c86eec2bb953a5a2ee4016e3735a3f02720428a40efbb8f1 + languageName: node + linkType: hard + +"lodash.difference@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.difference@npm:4.5.0" + checksum: ecee276aa578f300e79350805a14a51be8d1f12b3c1389a19996d8ab516f814211a5f65c68331571ecdad96522b863ccc484b55504ce8c9947212a29f8857d5a + languageName: node + linkType: hard + +"lodash.flatten@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.flatten@npm:4.4.0" + checksum: 0ac34a393d4b795d4b7421153d27c13ae67e08786c9cbb60ff5b732210d46f833598eee3fb3844bb10070e8488efe390ea53bb567377e0cb47e9e630bf0811cb + languageName: node + linkType: hard + +"lodash.flattendeep@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.flattendeep@npm:4.4.0" + checksum: 8521c919acac3d4bcf0aaf040c1ca9cb35d6c617e2d72e9b4d51c9a58b4366622cd6077441a18be626c3f7b28227502b3bf042903d447b056ee7e0b11d45c722 + languageName: node + linkType: hard + "lodash.get@npm:^4, lodash.get@npm:^4.4.2": version: 4.4.2 resolution: "lodash.get@npm:4.4.2" @@ -12717,6 +15009,13 @@ __metadata: languageName: node linkType: hard +"lodash.isplainobject@npm:^4.0.6": + version: 4.0.6 + resolution: "lodash.isplainobject@npm:4.0.6" + checksum: 29c6351f281e0d9a1d58f1a4c8f4400924b4c79f18dfc4613624d7d54784df07efaff97c1ff2659f3e085ecf4fff493300adc4837553104cef2634110b0d5337 + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -12724,6 +15023,13 @@ __metadata: languageName: node linkType: hard +"lodash.pickby@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.pickby@npm:4.6.0" + checksum: a554d898c15bcd3218e4005b95b5146210bd862010c7d242d17106ee36aed9b9209a858ce974136ab1faadd86a82297761c206fda7f1886278bac827145c5536 + languageName: node + linkType: hard + "lodash.throttle@npm:^4.1.1": version: 4.1.1 resolution: "lodash.throttle@npm:4.1.1" @@ -12731,6 +15037,13 @@ __metadata: languageName: node linkType: hard +"lodash.union@npm:^4.6.0": + version: 4.6.0 + resolution: "lodash.union@npm:4.6.0" + checksum: 1514dc6508b2614ec071a6470f36eb7a70f69bf1abb6d55bdfdc21069635a4517783654b28504c0f025059a7598d37529766888e6d5902b8ab28b712228f7b2a + languageName: node + linkType: hard + "lodash.uniq@npm:^4.5.0": version: 4.5.0 resolution: "lodash.uniq@npm:4.5.0" @@ -12738,6 +15051,13 @@ __metadata: languageName: node linkType: hard +"lodash.zip@npm:^4.2.0": + version: 4.2.0 + resolution: "lodash.zip@npm:4.2.0" + checksum: 41fd8dc1af8b38086369d4fdc81dd725715dcda36ec463d907b9c58f25e5ebb518376b0acec39ded96a6b1790a89c387b9a6b1627306f33fabaf987c8d5eac9e + languageName: node + linkType: hard + "lodash@npm:^4.17.15, lodash@npm:^4.17.21, lodash@npm:^4.17.4, lodash@npm:^4.7.0, lodash@npm:~4.17.15": version: 4.17.21 resolution: "lodash@npm:4.17.21" @@ -12777,6 +15097,20 @@ __metadata: languageName: node linkType: hard +"loglevel-plugin-prefix@npm:^0.8.4": + version: 0.8.4 + resolution: "loglevel-plugin-prefix@npm:0.8.4" + checksum: 5fe0632fa04263e083f87204107a06aa53e40a3537e08752539f5c0fd9a0ef112fe9ba6bdaed791502156c67a4ff7993a2b2871404615f0163f4c49649c362e4 + languageName: node + linkType: hard + +"loglevel@npm:^1.6.0": + version: 1.8.1 + resolution: "loglevel@npm:1.8.1" + checksum: a1a62db40291aaeaef2f612334c49e531bff71cc1d01a2acab689ab80d59e092f852ab164a5aedc1a752fdc46b7b162cb097d8a9eb2cf0b299511106c29af61d + languageName: node + linkType: hard + "longest-streak@npm:^3.0.0": version: 3.1.0 resolution: "longest-streak@npm:3.1.0" @@ -12795,6 +15129,20 @@ __metadata: languageName: node linkType: hard +"lowercase-keys@npm:1.0.0": + version: 1.0.0 + resolution: "lowercase-keys@npm:1.0.0" + checksum: 2370110c149967038fd5eb278f9b2d889eb427487c0e7fb417ab2ef4d93bacba1c8f226cf2ef1c2848b3191f37d84167d4342fbee72a1a122086680adecf362b + languageName: node + linkType: hard + +"lowercase-keys@npm:^1.0.0": + version: 1.0.1 + resolution: "lowercase-keys@npm:1.0.1" + checksum: 4d045026595936e09953e3867722e309415ff2c80d7701d067546d75ef698dac218a4f53c6d1d0e7368b47e45fd7529df47e6cb56fbb90523ba599f898b3d147 + languageName: node + linkType: hard + "lowercase-keys@npm:^2.0.0": version: 2.0.0 resolution: "lowercase-keys@npm:2.0.0" @@ -12802,6 +15150,13 @@ __metadata: languageName: node linkType: hard +"lowercase-keys@npm:^3.0.0": + version: 3.0.0 + resolution: "lowercase-keys@npm:3.0.0" + checksum: 67a3f81409af969bc0c4ca0e76cd7d16adb1e25aa1c197229587eaf8671275c8c067cd421795dbca4c81be0098e4c426a086a05e30de8a9c587b7a13c0c7ccc5 + languageName: node + linkType: hard + "lowlight@npm:^2.0.0": version: 2.8.1 resolution: "lowlight@npm:2.8.1" @@ -12813,6 +15168,16 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^4.0.1": + version: 4.1.5 + resolution: "lru-cache@npm:4.1.5" + dependencies: + pseudomap: ^1.0.2 + yallist: ^2.1.2 + checksum: 4bb4b58a36cd7dc4dcec74cbe6a8f766a38b7426f1ff59d4cf7d82a2aa9b9565cd1cb98f6ff60ce5cd174524868d7bc9b7b1c294371851356066ca9ac4cf135a + languageName: node + linkType: hard + "lru-cache@npm:^5.1.1": version: 5.1.1 resolution: "lru-cache@npm:5.1.1" @@ -12863,6 +15228,25 @@ __metadata: languageName: node linkType: hard +"make-dir@npm:^1.0.0": + version: 1.3.0 + resolution: "make-dir@npm:1.3.0" + dependencies: + pify: ^3.0.0 + checksum: c564f6e7bb5ace1c02ad56b3a5f5e07d074af0c0b693c55c7b2c2b148882827c8c2afc7b57e43338a9f90c125b58d604e8cf3e6990a48bf949dfea8c79668c0b + languageName: node + linkType: hard + +"make-dir@npm:^2.1.0": + version: 2.1.0 + resolution: "make-dir@npm:2.1.0" + dependencies: + pify: ^4.0.1 + semver: ^5.6.0 + checksum: 043548886bfaf1820323c6a2997e6d2fa51ccc2586ac14e6f14634f7458b4db2daf15f8c310e2a0abd3e0cddc64df1890d8fc7263033602c47bb12cbfcf86aab + languageName: node + linkType: hard + "make-dir@npm:^3.0.0, make-dir@npm:^3.1.0": version: 3.1.0 resolution: "make-dir@npm:3.1.0" @@ -12912,6 +15296,20 @@ __metadata: languageName: node linkType: hard +"map-obj@npm:^1.0.0": + version: 1.0.1 + resolution: "map-obj@npm:1.0.1" + checksum: 9949e7baec2a336e63b8d4dc71018c117c3ce6e39d2451ccbfd3b8350c547c4f6af331a4cbe1c83193d7c6b786082b6256bde843db90cb7da2a21e8fcc28afed + languageName: node + linkType: hard + +"map-stream@npm:~0.1.0": + version: 0.1.0 + resolution: "map-stream@npm:0.1.0" + checksum: 38abbe4eb883888031e6b2fc0630bc583c99396be16b8ace5794b937b682a8a081f03e8b15bfd4914d1bc88318f0e9ac73ba3512ae65955cd449f63256ddb31d + languageName: node + linkType: hard + "markdown-extensions@npm:^1.0.0": version: 1.1.1 resolution: "markdown-extensions@npm:1.1.1" @@ -12941,6 +15339,13 @@ __metadata: languageName: node linkType: hard +"marky@npm:^1.2.2": + version: 1.2.5 + resolution: "marky@npm:1.2.5" + checksum: 823b946677749551cdfc3b5221685478b5d1b9cc0dc03eff977c6f9a615fb05c67559f9556cb3c0fcb941a9ea0e195e37befd83026443396ccee8b724f54f4c5 + languageName: node + linkType: hard + "mdast-util-definitions@npm:^5.0.0": version: 5.1.2 resolution: "mdast-util-definitions@npm:5.1.2" @@ -13748,14 +16153,14 @@ __metadata: languageName: node linkType: hard -"mime-db@npm:1.52.0": +"mime-db@npm:1.52.0, mime-db@npm:^1.28.0": version: 1.52.0 resolution: "mime-db@npm:1.52.0" checksum: 0d99a03585f8b39d68182803b12ac601d9c01abfa28ec56204fa330bc9f3d1c5e14beb049bafadb3dbdf646dfb94b87e24d4ec7b31b7279ef906a8ea9b6a513f languageName: node linkType: hard -"mime-types@npm:^2.1.12, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": +"mime-types@npm:^2.1.12, mime-types@npm:~2.1.19, mime-types@npm:~2.1.24, mime-types@npm:~2.1.34": version: 2.1.35 resolution: "mime-types@npm:2.1.35" dependencies: @@ -13773,6 +16178,15 @@ __metadata: languageName: node linkType: hard +"mime@npm:^3.0.0": + version: 3.0.0 + resolution: "mime@npm:3.0.0" + bin: + mime: cli.js + checksum: f43f9b7bfa64534e6b05bd6062961681aeb406a5b53673b53b683f27fcc4e739989941836a355eef831f4478923651ecc739f4a5f6e20a76487b432bfd4db928 + languageName: node + linkType: hard + "mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -13801,6 +16215,13 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^4.0.0": + version: 4.0.0 + resolution: "mimic-response@npm:4.0.0" + checksum: 33b804cc961efe206efdb1fca6a22540decdcfce6c14eb5c0c50e5ae9022267ab22ce8f5568b1f7247ba67500fe20d523d81e0e9f009b321ccd9d472e78d1850 + languageName: node + linkType: hard + "min-indent@npm:^1.0.0": version: 1.0.1 resolution: "min-indent@npm:1.0.1" @@ -13817,6 +16238,15 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:5.0.1": + version: 5.0.1 + resolution: "minimatch@npm:5.0.1" + dependencies: + brace-expansion: ^2.0.1 + checksum: b34b98463da4754bc526b244d680c69d4d6089451ebe512edaf6dd9eeed0279399cfa3edb19233513b8f830bf4bfcad911dddcdf125e75074100d52f724774f0 + languageName: node + linkType: hard + "minimatch@npm:^3.0.3, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" @@ -13826,7 +16256,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:^5.0.1": +"minimatch@npm:^5.0.1, minimatch@npm:^5.1.0": version: 5.1.6 resolution: "minimatch@npm:5.1.6" dependencies: @@ -13844,6 +16274,24 @@ __metadata: languageName: node linkType: hard +"minimatch@npm:^9.0.0": + version: 9.0.0 + resolution: "minimatch@npm:9.0.0" + dependencies: + brace-expansion: ^2.0.1 + checksum: 7bd57899edd1d1b0560f50b5b2d1ea4ad2a366c5a2c8e0a943372cf2f200b64c256bae45a87a80915adbce27fa36526264296ace0da57b600481fe5ea3e372e5 + languageName: node + linkType: hard + +"minimatch@npm:~3.0.2": + version: 3.0.8 + resolution: "minimatch@npm:3.0.8" + dependencies: + brace-expansion: ^1.1.7 + checksum: 850cca179cad715133132693e6963b0db64ab0988c4d211415b087fc23a3e46321e2c5376a01bf5623d8782aba8bdf43c571e2e902e51fdce7175c7215c29f8b + languageName: node + linkType: hard + "minimist@npm:^1.2.0, minimist@npm:^1.2.3, minimist@npm:^1.2.5, minimist@npm:^1.2.6": version: 1.2.8 resolution: "minimist@npm:1.2.8" @@ -13935,6 +16383,13 @@ __metadata: languageName: node linkType: hard +"mitt@npm:3.0.0": + version: 3.0.0 + resolution: "mitt@npm:3.0.0" + checksum: f7be5049d27d18b1dbe9408452d66376fa60ae4a79fe9319869d1b90ae8cbaedadc7e9dab30b32d781411256d468be5538996bb7368941c09009ef6bbfa6bfc7 + languageName: node + linkType: hard + "mkdirp-classic@npm:^0.5.2, mkdirp-classic@npm:^0.5.3": version: 0.5.3 resolution: "mkdirp-classic@npm:0.5.3" @@ -13942,6 +16397,17 @@ __metadata: languageName: node linkType: hard +"mkdirp@npm:^0.5.1, mkdirp@npm:^0.5.4": + version: 0.5.6 + resolution: "mkdirp@npm:0.5.6" + dependencies: + minimist: ^1.2.6 + bin: + mkdirp: bin/cmd.js + checksum: 0c91b721bb12c3f9af4b77ebf73604baf350e64d80df91754dc509491ae93bf238581e59c7188360cec7cb62fc4100959245a42cfe01834efedc5e9d068376c2 + languageName: node + linkType: hard + "mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": version: 1.0.4 resolution: "mkdirp@npm:1.0.4" @@ -13963,6 +16429,47 @@ __metadata: languageName: node linkType: hard +"mnemonist@npm:0.39.5": + version: 0.39.5 + resolution: "mnemonist@npm:0.39.5" + dependencies: + obliterator: ^2.0.1 + checksum: 6669d687a434226924b2c84ee6eb7ce7d0f83dfc5caad8bcc164c73c0c11fb6d43cbe32636e710f068046f4b40a56c3032532554e93e02640aafc6ca3dd222e6 + languageName: node + linkType: hard + +"mocha@npm:^10.0.0": + version: 10.2.0 + resolution: "mocha@npm:10.2.0" + dependencies: + ansi-colors: 4.1.1 + browser-stdout: 1.3.1 + chokidar: 3.5.3 + debug: 4.3.4 + diff: 5.0.0 + escape-string-regexp: 4.0.0 + find-up: 5.0.0 + glob: 7.2.0 + he: 1.2.0 + js-yaml: 4.1.0 + log-symbols: 4.1.0 + minimatch: 5.0.1 + ms: 2.1.3 + nanoid: 3.3.3 + serialize-javascript: 6.0.0 + strip-json-comments: 3.1.1 + supports-color: 8.1.1 + workerpool: 6.2.1 + yargs: 16.2.0 + yargs-parser: 20.2.4 + yargs-unparser: 2.0.0 + bin: + _mocha: bin/_mocha + mocha: bin/mocha.js + checksum: 406c45eab122ffd6ea2003c2f108b2bc35ba036225eee78e0c784b6fa2c7f34e2b13f1dbacef55a4fdf523255d76e4f22d1b5aacda2394bd11666febec17c719 + languageName: node + linkType: hard + "mocha@npm:^9.1.1": version: 9.2.2 resolution: "mocha@npm:9.2.2" @@ -14042,6 +16549,21 @@ __metadata: languageName: node linkType: hard +"msedgedriver@npm:^91.0.0": + version: 91.0.0 + resolution: "msedgedriver@npm:91.0.0" + dependencies: + del: ^4.1.1 + extract-zip: ^1.6.7 + mkdirp: ^0.5.1 + request: ^2.88.0 + tcp-port-used: ^1.0.1 + bin: + msedgedriver: ./bin/msedgedriver + checksum: b8855457b83b27844d12717f28096d87b9353d6d485d29d678f022ebb89776d21682b18008fb9a340fd640d2bfb1dbe4fc1041bed02f4aa577693d09b9efbf8c + languageName: node + linkType: hard + "mute-stream@npm:0.0.8, mute-stream@npm:~0.0.4": version: 0.0.8 resolution: "mute-stream@npm:0.0.8" @@ -14065,6 +16587,15 @@ __metadata: languageName: node linkType: hard +"nanoid@npm:3.3.3": + version: 3.3.3 + resolution: "nanoid@npm:3.3.3" + bin: + nanoid: bin/nanoid.cjs + checksum: ada019402a07464a694553c61d2dca8a4353645a7d92f2830f0d487fedff403678a0bee5323a46522752b2eab95a0bc3da98b6cccaa7c0c55cd9975130e6d6f0 + languageName: node + linkType: hard + "nanoid@npm:^3.0.0, nanoid@npm:^3.3.4, nanoid@npm:^3.3.6": version: 3.3.6 resolution: "nanoid@npm:3.3.6" @@ -14265,6 +16796,24 @@ __metadata: languageName: node linkType: hard +"node-addon-api@npm:^5.0.0": + version: 5.1.0 + resolution: "node-addon-api@npm:5.1.0" + dependencies: + node-gyp: latest + checksum: 2508bd2d2981945406243a7bd31362fc7af8b70b8b4d65f869c61731800058fb818cc2fd36c8eac714ddd0e568cc85becf5e165cebbdf7b5024d5151bbc75ea1 + languageName: node + linkType: hard + +"node-downloader-helper@npm:2.1.6": + version: 2.1.6 + resolution: "node-downloader-helper@npm:2.1.6" + bin: + ndh: bin/ndh + checksum: 75b2c8f6ae003f54889bbd0fcc97f9449df47266a28ecc613f39c48a1ad3c1c7a53b609400d45e24a2c56d37f0b192abe856b6b8c31c4e5de68da6cdd1b90c40 + languageName: node + linkType: hard + "node-fetch@npm:2.6.7": version: 2.6.7 resolution: "node-fetch@npm:2.6.7" @@ -14293,6 +16842,13 @@ __metadata: languageName: node linkType: hard +"node-forge@npm:^1.3.1": + version: 1.3.1 + resolution: "node-forge@npm:1.3.1" + checksum: 08fb072d3d670599c89a1704b3e9c649ff1b998256737f0e06fbd1a5bf41cae4457ccaee32d95052d80bbafd9ffe01284e078c8071f0267dc9744e51c5ed42a9 + languageName: node + linkType: hard + "node-gyp-build@npm:^4.2.2": version: 4.6.0 resolution: "node-gyp-build@npm:4.6.0" @@ -14338,6 +16894,13 @@ __metadata: languageName: node linkType: hard +"node-stream-zip@npm:1.15.0": + version: 1.15.0 + resolution: "node-stream-zip@npm:1.15.0" + checksum: 0b73ffbb09490e479c8f47038d7cba803e6242618fbc1b71c26782009d388742ed6fb5ce6e9d31f528b410249e7eb1c6e7534e9d3792a0cafd99813ac5a35107 + languageName: node + linkType: hard + "nopt@npm:^5.0.0": version: 5.0.0 resolution: "nopt@npm:5.0.0" @@ -14360,6 +16923,30 @@ __metadata: languageName: node linkType: hard +"normalize-package-data@npm:^2.3.2": + version: 2.5.0 + resolution: "normalize-package-data@npm:2.5.0" + dependencies: + hosted-git-info: ^2.1.4 + resolve: ^1.10.0 + semver: 2 || 3 || 4 || 5 + validate-npm-package-license: ^3.0.1 + checksum: 7999112efc35a6259bc22db460540cae06564aa65d0271e3bdfa86876d08b0e578b7b5b0028ee61b23f1cae9fc0e7847e4edc0948d3068a39a2a82853efc8499 + languageName: node + linkType: hard + +"normalize-package-data@npm:^3.0.2": + version: 3.0.3 + resolution: "normalize-package-data@npm:3.0.3" + dependencies: + hosted-git-info: ^4.0.1 + is-core-module: ^2.5.0 + semver: ^7.3.4 + validate-npm-package-license: ^3.0.1 + checksum: bbcee00339e7c26fdbc760f9b66d429258e2ceca41a5df41f5df06cc7652de8d82e8679ff188ca095cad8eff2b6118d7d866af2b68400f74602fbcbce39c160a + languageName: node + linkType: hard + "normalize-path@npm:^3.0.0, normalize-path@npm:~3.0.0": version: 3.0.0 resolution: "normalize-path@npm:3.0.0" @@ -14367,6 +16954,17 @@ __metadata: languageName: node linkType: hard +"normalize-url@npm:2.0.1": + version: 2.0.1 + resolution: "normalize-url@npm:2.0.1" + dependencies: + prepend-http: ^2.0.0 + query-string: ^5.0.1 + sort-keys: ^2.0.0 + checksum: 30e337ee03fc7f360c7d2b966438657fabd2628925cc58bffc893982fe4d2c59b397ae664fa2c319cd83565af73eee88906e80bc5eec91bc32b601920e770d75 + languageName: node + linkType: hard + "normalize-url@npm:^6.0.1": version: 6.1.0 resolution: "normalize-url@npm:6.1.0" @@ -14374,6 +16972,13 @@ __metadata: languageName: node linkType: hard +"normalize-url@npm:^8.0.0": + version: 8.0.0 + resolution: "normalize-url@npm:8.0.0" + checksum: 24c20b75ebfd526d8453084692720b49d111c63c0911f1b7447427829597841eef5a8ba3f6bb93d6654007b991c1f5cd85da2c907800e439e2e2ec6c2abd0fc0 + languageName: node + linkType: hard + "npm-run-path@npm:^4.0.1": version: 4.0.1 resolution: "npm-run-path@npm:4.0.1" @@ -14432,14 +17037,21 @@ __metadata: languageName: node linkType: hard -"object-assign@npm:^4.1.1": +"oauth-sign@npm:~0.9.0": + version: 0.9.0 + resolution: "oauth-sign@npm:0.9.0" + checksum: 8f5497a127967866a3c67094c21efd295e46013a94e6e828573c62220e9af568cc1d2d04b16865ba583e430510fa168baf821ea78f355146d8ed7e350fc44c64 + languageName: node + linkType: hard + +"object-assign@npm:^4.0.1, object-assign@npm:^4.1.0, object-assign@npm:^4.1.1": version: 4.1.1 resolution: "object-assign@npm:4.1.1" checksum: fcc6e4ea8c7fe48abfbb552578b1c53e0d194086e2e6bbbf59e0a536381a292f39943c6e9628af05b5528aa5e3318bb30d6b2e53cadaf5b8fe9e12c4b69af23f languageName: node linkType: hard -"object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": +"object-inspect@npm:^1.12.0, object-inspect@npm:^1.12.3, object-inspect@npm:^1.9.0": version: 1.12.3 resolution: "object-inspect@npm:1.12.3" checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db @@ -14525,6 +17137,20 @@ __metadata: languageName: node linkType: hard +"obliterator@npm:^2.0.1": + version: 2.0.4 + resolution: "obliterator@npm:2.0.4" + checksum: f28ad35b6d812089315f375dc3e6e5f9bebf958ebe4b10ccd471c7115cbcf595e74bdac4783ae758e5b1f47e3096427fdb37cfa7bed566b132df92ff317b9a7c + languageName: node + linkType: hard + +"on-exit-leak-free@npm:^2.1.0": + version: 2.1.0 + resolution: "on-exit-leak-free@npm:2.1.0" + checksum: 7334d98b87b0c89c9b69c747760b21196ff35afdedc4eaf1a0a3a02964463d7f6802481b120e4c8298967c74773ca7b914ab2eb3d9b279010eb7f67ac4960eed + languageName: node + linkType: hard + "on-finished@npm:2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -14655,6 +17281,13 @@ __metadata: languageName: node linkType: hard +"p-cancelable@npm:^0.4.0": + version: 0.4.1 + resolution: "p-cancelable@npm:0.4.1" + checksum: d11144d72ee3a99f62fe595cb0e13b8585ea73c3807b4a9671744f1bf5d3ccddb049247a4ec3ceff05ca4adba9d0bb0f1862829daf20795bf528c86fa088509c + languageName: node + linkType: hard + "p-cancelable@npm:^2.0.0": version: 2.1.1 resolution: "p-cancelable@npm:2.1.1" @@ -14662,6 +17295,43 @@ __metadata: languageName: node linkType: hard +"p-cancelable@npm:^3.0.0": + version: 3.0.0 + resolution: "p-cancelable@npm:3.0.0" + checksum: 2b5ae34218f9c2cf7a7c18e5d9a726ef9b165ef07e6c959f6738371509e747334b5f78f3bcdeb03d8a12dcb978faf641fd87eb21486ed7d36fb823b8ddef3219 + languageName: node + linkType: hard + +"p-event@npm:^2.1.0": + version: 2.3.1 + resolution: "p-event@npm:2.3.1" + dependencies: + p-timeout: ^2.0.1 + checksum: 7f973c4c001045bcd561202fc1b2bdf9e148182bb28a7bafa8e7b2ebfaf71a4f9ba91554222040d364290e707e3ebbb049122b8eda9d2aac413b4cf8de0b79ff + languageName: node + linkType: hard + +"p-finally@npm:^1.0.0": + version: 1.0.0 + resolution: "p-finally@npm:1.0.0" + checksum: 93a654c53dc805dd5b5891bab16eb0ea46db8f66c4bfd99336ae929323b1af2b70a8b0654f8f1eae924b2b73d037031366d645f1fd18b3d30cbd15950cc4b1d4 + languageName: node + linkType: hard + +"p-is-promise@npm:^1.1.0": + version: 1.1.0 + resolution: "p-is-promise@npm:1.1.0" + checksum: 64d7c6cda18af2c91c04209e5856c54d1a9818662d2320b34153d446645f431307e04406969a1be00cad680288e86dcf97b9eb39edd5dc4d0b1bd714ee85e13b + languageName: node + linkType: hard + +"p-iteration@npm:^1.1.8": + version: 1.1.8 + resolution: "p-iteration@npm:1.1.8" + checksum: 3eb8d8affc2ef947c076807e5c57030949abad0ff81759ebc54fc43823e30ce918e69b035bf1884991c61b7885c77efaf32c0de7ac01110a2c874f6aa81e0d7f + languageName: node + linkType: hard + "p-limit@npm:^1.1.0": version: 1.3.0 resolution: "p-limit@npm:1.3.0" @@ -14689,6 +17359,15 @@ __metadata: languageName: node linkType: hard +"p-limit@npm:^4.0.0": + version: 4.0.0 + resolution: "p-limit@npm:4.0.0" + dependencies: + yocto-queue: ^1.0.0 + checksum: 01d9d70695187788f984226e16c903475ec6a947ee7b21948d6f597bed788e3112cc7ec2e171c1d37125057a5f45f3da21d8653e04a3a793589e12e9e80e756b + languageName: node + linkType: hard + "p-locate@npm:^2.0.0": version: 2.0.0 resolution: "p-locate@npm:2.0.0" @@ -14725,6 +17404,22 @@ __metadata: languageName: node linkType: hard +"p-locate@npm:^6.0.0": + version: 6.0.0 + resolution: "p-locate@npm:6.0.0" + dependencies: + p-limit: ^4.0.0 + checksum: 2bfe5234efa5e7a4e74b30a5479a193fdd9236f8f6b4d2f3f69e3d286d9a7d7ab0c118a2a50142efcf4e41625def635bd9332d6cbf9cc65d85eb0718c579ab38 + languageName: node + linkType: hard + +"p-map@npm:^2.0.0": + version: 2.1.0 + resolution: "p-map@npm:2.1.0" + checksum: 9e3ad3c9f6d75a5b5661bcad78c91f3a63849189737cd75e4f1225bf9ac205194e5c44aac2ef6f09562b1facdb9bd1425584d7ac375bfaa17b3f1a142dab936d + languageName: node + linkType: hard + "p-map@npm:^4.0.0": version: 4.0.0 resolution: "p-map@npm:4.0.0" @@ -14734,6 +17429,15 @@ __metadata: languageName: node linkType: hard +"p-timeout@npm:^2.0.1": + version: 2.0.1 + resolution: "p-timeout@npm:2.0.1" + dependencies: + p-finally: ^1.0.0 + checksum: 9205a661173f03adbeabda8e02826de876376b09c99768bdc33e5b25ae73230e3ac00e520acedbe3cf05fbd3352fb02efbd3811a9a021b148fb15eb07e7accac + languageName: node + linkType: hard + "p-try@npm:^1.0.0": version: 1.0.0 resolution: "p-try@npm:1.0.0" @@ -14783,6 +17487,13 @@ __metadata: languageName: node linkType: hard +"pako@npm:~1.0.2": + version: 1.0.11 + resolution: "pako@npm:1.0.11" + checksum: 1be2bfa1f807608c7538afa15d6f25baa523c30ec870a3228a89579e474a4d992f4293859524e46d5d87fd30fa17c5edf34dbef0671251d9749820b488660b16 + languageName: node + linkType: hard + "parent-module@npm:^1.0.0": version: 1.0.1 resolution: "parent-module@npm:1.0.1" @@ -14826,6 +17537,15 @@ __metadata: languageName: node linkType: hard +"parse-json@npm:^2.2.0": + version: 2.2.0 + resolution: "parse-json@npm:2.2.0" + dependencies: + error-ex: ^1.2.0 + checksum: dda78a63e57a47b713a038630868538f718a7ca0cd172a36887b0392ccf544ed0374902eb28f8bf3409e8b71d62b79d17062f8543afccf2745f9b0b2d2bb80ca + languageName: node + linkType: hard + "parse-json@npm:^4.0.0": version: 4.0.0 resolution: "parse-json@npm:4.0.0" @@ -14904,6 +17624,15 @@ __metadata: languageName: node linkType: hard +"path-exists@npm:^2.0.0": + version: 2.1.0 + resolution: "path-exists@npm:2.1.0" + dependencies: + pinkie-promise: ^2.0.0 + checksum: fdb734f1d00f225f7a0033ce6d73bff6a7f76ea08936abf0e5196fa6e54a645103538cd8aedcb90d6d8c3fa3705ded0c58a4da5948ae92aa8834892c1ab44a84 + languageName: node + linkType: hard + "path-exists@npm:^3.0.0": version: 3.0.0 resolution: "path-exists@npm:3.0.0" @@ -14918,6 +17647,13 @@ __metadata: languageName: node linkType: hard +"path-exists@npm:^5.0.0": + version: 5.0.0 + resolution: "path-exists@npm:5.0.0" + checksum: 8ca842868cab09423994596eb2c5ec2a971c17d1a3cb36dbf060592c730c725cd524b9067d7d2a1e031fef9ba7bd2ac6dc5ec9fb92aa693265f7be3987045254 + languageName: node + linkType: hard + "path-is-absolute@npm:^1.0.0": version: 1.0.1 resolution: "path-is-absolute@npm:1.0.1" @@ -14925,6 +17661,13 @@ __metadata: languageName: node linkType: hard +"path-is-inside@npm:^1.0.2": + version: 1.0.2 + resolution: "path-is-inside@npm:1.0.2" + checksum: 0b5b6c92d3018b82afb1f74fe6de6338c4c654de4a96123cb343f2b747d5606590ac0c890f956ed38220a4ab59baddfd7b713d78a62d240b20b14ab801fa02cb + languageName: node + linkType: hard + "path-key@npm:^3.0.0, path-key@npm:^3.1.0": version: 3.1.1 resolution: "path-key@npm:3.1.1" @@ -14946,7 +17689,7 @@ __metadata: languageName: node linkType: hard -"path-scurry@npm:^1.6.1": +"path-scurry@npm:^1.6.1, path-scurry@npm:^1.7.0": version: 1.7.0 resolution: "path-scurry@npm:1.7.0" dependencies: @@ -14977,6 +17720,17 @@ __metadata: languageName: node linkType: hard +"path-type@npm:^1.0.0": + version: 1.1.0 + resolution: "path-type@npm:1.1.0" + dependencies: + graceful-fs: ^4.1.2 + pify: ^2.0.0 + pinkie-promise: ^2.0.0 + checksum: 59a4b2c0e566baf4db3021a1ed4ec09a8b36fca960a490b54a6bcefdb9987dafe772852982b6011cd09579478a96e57960a01f75fa78a794192853c9d468fc79 + languageName: node + linkType: hard + "path-type@npm:^3.0.0": version: 3.0.0 resolution: "path-type@npm:3.0.0" @@ -15000,6 +17754,15 @@ __metadata: languageName: node linkType: hard +"pause-stream@npm:0.0.11": + version: 0.0.11 + resolution: "pause-stream@npm:0.0.11" + dependencies: + through: ~2.3 + checksum: 3c4a14052a638b92e0c96eb00c0d7977df7f79ea28395250c525d197f1fc02d34ce1165d5362e2e6ebbb251524b94a76f3f0d4abc39ab8b016d97449fe15583c + languageName: node + linkType: hard + "peek-stream@npm:^1.1.0": version: 1.1.3 resolution: "peek-stream@npm:1.1.3" @@ -15059,6 +17822,13 @@ __metadata: languageName: node linkType: hard +"pify@npm:^2.0.0, pify@npm:^2.3.0": + version: 2.3.0 + resolution: "pify@npm:2.3.0" + checksum: 9503aaeaf4577acc58642ad1d25c45c6d90288596238fb68f82811c08104c800e5a7870398e9f015d82b44ecbcbef3dc3d4251a1cbb582f6e5959fe09884b2ba + languageName: node + linkType: hard + "pify@npm:^3.0.0": version: 3.0.0 resolution: "pify@npm:3.0.0" @@ -15066,6 +17836,67 @@ __metadata: languageName: node linkType: hard +"pify@npm:^4.0.1": + version: 4.0.1 + resolution: "pify@npm:4.0.1" + checksum: 9c4e34278cb09987685fa5ef81499c82546c033713518f6441778fbec623fc708777fe8ac633097c72d88470d5963094076c7305cafc7ad340aae27cfacd856b + languageName: node + linkType: hard + +"pinkie-promise@npm:^2.0.0": + version: 2.0.1 + resolution: "pinkie-promise@npm:2.0.1" + dependencies: + pinkie: ^2.0.0 + checksum: b53a4a2e73bf56b6f421eef711e7bdcb693d6abb474d57c5c413b809f654ba5ee750c6a96dd7225052d4b96c4d053cdcb34b708a86fceed4663303abee52fcca + languageName: node + linkType: hard + +"pinkie@npm:^2.0.0": + version: 2.0.4 + resolution: "pinkie@npm:2.0.4" + checksum: b12b10afea1177595aab036fc220785488f67b4b0fc49e7a27979472592e971614fa1c728e63ad3e7eb748b4ec3c3dbd780819331dad6f7d635c77c10537b9db + languageName: node + linkType: hard + +"pino-abstract-transport@npm:v1.0.0": + version: 1.0.0 + resolution: "pino-abstract-transport@npm:1.0.0" + dependencies: + readable-stream: ^4.0.0 + split2: ^4.0.0 + checksum: 05dd0eda52dd99fd204b39fe7b62656744b63e863bc052cdd5105d25f226a236966d0a46e39a1ace4838f6e988c608837ff946d2d0bc92835ca7baa0a3bff8d8 + languageName: node + linkType: hard + +"pino-std-serializers@npm:^6.0.0": + version: 6.2.1 + resolution: "pino-std-serializers@npm:6.2.1" + checksum: 9f86579dea7939a5d63c8313b0e2c3ad778a92aa9011a64d170677552b7634025738df890d09679eeed8be334ea90d37ded4b7a8cef4e3fa4d9c4387d339f905 + languageName: node + linkType: hard + +"pino@npm:^8.5.0": + version: 8.12.1 + resolution: "pino@npm:8.12.1" + dependencies: + atomic-sleep: ^1.0.0 + fast-redact: ^3.1.1 + on-exit-leak-free: ^2.1.0 + pino-abstract-transport: v1.0.0 + pino-std-serializers: ^6.0.0 + process-warning: ^2.0.0 + quick-format-unescaped: ^4.0.3 + real-require: ^0.2.0 + safe-stable-stringify: ^2.3.1 + sonic-boom: ^3.1.0 + thread-stream: ^2.0.0 + bin: + pino: bin.js + checksum: 2e1689ea43dc42340f6316d311c0b41b8dc08ab8b75b3f182a7c0c14598d6501e96d26403bc3912b6b9478d37d272414e345e03c3aed1e2e42908aea317aa5fd + languageName: node + linkType: hard + "pirates@npm:^4.0.4": version: 4.0.5 resolution: "pirates@npm:4.0.5" @@ -15073,6 +17904,17 @@ __metadata: languageName: node linkType: hard +"pixelmatch@npm:^5.3.0": + version: 5.3.0 + resolution: "pixelmatch@npm:5.3.0" + dependencies: + pngjs: ^6.0.0 + bin: + pixelmatch: bin/pixelmatch + checksum: f542713d89536551181ad9ddb666a1792ba00a8632d831093232a075cb3ccac05856e7a453ed7d0a41aaef64dcb5962e8ae5cbe646dd2761790d8ee51b0a0743 + languageName: node + linkType: hard + "pkg-conf@npm:^2.1.0": version: 2.1.0 resolution: "pkg-conf@npm:2.1.0" @@ -15110,6 +17952,13 @@ __metadata: languageName: node linkType: hard +"pngjs@npm:^6.0.0": + version: 6.0.0 + resolution: "pngjs@npm:6.0.0" + checksum: ab6c285086060087097eab9fe6b5a528a24f9e79c03dea2b4fd6264ed4fdb5beff4a3257eeeaf2a9dc18249b539609c2a4e4013c567164a1f6b5ba2c974d5ecb + languageName: node + linkType: hard + "postcss-discard-duplicates@npm:^5.1.0": version: 5.1.0 resolution: "postcss-discard-duplicates@npm:5.1.0" @@ -15238,7 +18087,7 @@ __metadata: languageName: node linkType: hard -"prebuild-install@npm:^7.0.1": +"prebuild-install@npm:^7.0.1, prebuild-install@npm:^7.1.1": version: 7.1.1 resolution: "prebuild-install@npm:7.1.1" dependencies: @@ -15274,6 +18123,13 @@ __metadata: languageName: node linkType: hard +"prepend-http@npm:^2.0.0": + version: 2.0.0 + resolution: "prepend-http@npm:2.0.0" + checksum: 7694a9525405447662c1ffd352fcb41b6410c705b739b6f4e3a3e21cf5fdede8377890088e8934436b8b17ba55365a615f153960f30877bf0d0392f9e93503ea + languageName: node + linkType: hard + "prettier-plugin-organize-imports@npm:^3.2.2": version: 3.2.2 resolution: "prettier-plugin-organize-imports@npm:3.2.2" @@ -15366,6 +18222,13 @@ __metadata: languageName: node linkType: hard +"process-warning@npm:^2.0.0": + version: 2.2.0 + resolution: "process-warning@npm:2.2.0" + checksum: 394ae451c2622ee7d014a7196d36658fc1a5d5cc9f3bfeb54aadd5b77fcfecc89a30a25db259ae76ff49fde3f3f3dd7031dcdfb4da2e5445dac795549352e5d0 + languageName: node + linkType: hard + "process@npm:^0.11.10": version: 0.11.10 resolution: "process@npm:0.11.10" @@ -15373,6 +18236,13 @@ __metadata: languageName: node linkType: hard +"progress@npm:2.0.3": + version: 2.0.3 + resolution: "progress@npm:2.0.3" + checksum: f67403fe7b34912148d9252cb7481266a354bd99ce82c835f79070643bb3c6583d10dbcfda4d41e04bbc1d8437e9af0fb1e1f2135727878f5308682a579429b7 + languageName: node + linkType: hard + "promise-inflight@npm:^1.0.1": version: 1.0.1 resolution: "promise-inflight@npm:1.0.1" @@ -15418,7 +18288,7 @@ __metadata: languageName: node linkType: hard -"proxy-addr@npm:~2.0.7": +"proxy-addr@npm:^2.0.7, proxy-addr@npm:~2.0.7": version: 2.0.7 resolution: "proxy-addr@npm:2.0.7" dependencies: @@ -15444,14 +18314,32 @@ __metadata: languageName: node linkType: hard -"proxy-from-env@npm:^1.0.0": +"proxy-from-env@npm:1.1.0, proxy-from-env@npm:^1.0.0, proxy-from-env@npm:^1.1.0": version: 1.1.0 resolution: "proxy-from-env@npm:1.1.0" checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 languageName: node linkType: hard -"psl@npm:^1.1.33": +"ps-tree@npm:=1.2.0": + version: 1.2.0 + resolution: "ps-tree@npm:1.2.0" + dependencies: + event-stream: =3.3.4 + bin: + ps-tree: ./bin/ps-tree.js + checksum: e635dd00f53d30d31696cf5f95b3a8dbdf9b1aeb36d4391578ce8e8cd22949b7c5536c73b0dc18c78615ea3ddd4be96101166be59ca2e3e3cb1e2f79ba3c7f98 + languageName: node + linkType: hard + +"pseudomap@npm:^1.0.2": + version: 1.0.2 + resolution: "pseudomap@npm:1.0.2" + checksum: 856c0aae0ff2ad60881168334448e898ad7a0e45fe7386d114b150084254c01e200c957cf378378025df4e052c7890c5bd933939b0e0d2ecfcc1dc2f0b2991f5 + languageName: node + linkType: hard + +"psl@npm:^1.1.28, psl@npm:^1.1.33": version: 1.9.0 resolution: "psl@npm:1.9.0" checksum: 20c4277f640c93d393130673f392618e9a8044c6c7bf61c53917a0fddb4952790f5f362c6c730a9c32b124813e173733f9895add8d26f566ed0ea0654b2e711d @@ -15496,6 +18384,30 @@ __metadata: languageName: node linkType: hard +"puppeteer-core@npm:19.11.1": + version: 19.11.1 + resolution: "puppeteer-core@npm:19.11.1" + dependencies: + "@puppeteer/browsers": 0.5.0 + chromium-bidi: 0.4.7 + cross-fetch: 3.1.5 + debug: 4.3.4 + devtools-protocol: 0.0.1107588 + extract-zip: 2.0.1 + https-proxy-agent: 5.0.1 + proxy-from-env: 1.1.0 + tar-fs: 2.1.1 + unbzip2-stream: 1.4.3 + ws: 8.13.0 + peerDependencies: + typescript: ">= 4.7.4" + peerDependenciesMeta: + typescript: + optional: true + checksum: 06126e478b8b653e83b98b51cec35dceef8ab576abd1369afd45360c5bac3711443e58ebe3b852d40801a118e4cb7ddf5d3154518b5a9294ee93f7a42d9f22d4 + languageName: node + linkType: hard + "pvtsutils@npm:^1.3.2": version: 1.3.2 resolution: "pvtsutils@npm:1.3.2" @@ -15530,6 +18442,31 @@ __metadata: languageName: node linkType: hard +"qs@npm:~6.5.2": + version: 6.5.3 + resolution: "qs@npm:6.5.3" + checksum: 6f20bf08cabd90c458e50855559539a28d00b2f2e7dddcb66082b16a43188418cb3cb77cbd09268bcef6022935650f0534357b8af9eeb29bf0f27ccb17655692 + languageName: node + linkType: hard + +"query-selector-shadow-dom@npm:^1.0.0": + version: 1.0.1 + resolution: "query-selector-shadow-dom@npm:1.0.1" + checksum: 8ab1cdd5e1927b583503b590165d66770fb91c87ac28b50a43596b755db3792c0e506250f46d0af97f0064a5cc12a1de449fd5c2cfcadf18b0880a4d8aecebbd + languageName: node + linkType: hard + +"query-string@npm:^5.0.1": + version: 5.1.1 + resolution: "query-string@npm:5.1.1" + dependencies: + decode-uri-component: ^0.2.0 + object-assign: ^4.1.0 + strict-uri-encode: ^1.0.0 + checksum: 4ac760d9778d413ef5f94f030ed14b1a07a1708dd13fd3bc54f8b9ef7b425942c7577f30de0bf5a7d227ee65a9a0350dfa3a43d1d266880882fb7ce4c434a4dd + languageName: node + linkType: hard + "querystringify@npm:^2.1.1": version: 2.2.0 resolution: "querystringify@npm:2.2.0" @@ -15544,6 +18481,13 @@ __metadata: languageName: node linkType: hard +"quick-format-unescaped@npm:^4.0.3": + version: 4.0.4 + resolution: "quick-format-unescaped@npm:4.0.4" + checksum: 7bc32b99354a1aa46c089d2a82b63489961002bb1d654cee3e6d2d8778197b68c2d854fd23d8422436ee1fdfd0abaddc4d4da120afe700ade68bd357815b26fd + languageName: node + linkType: hard + "quick-lru@npm:^5.1.1": version: 5.1.1 resolution: "quick-lru@npm:5.1.1" @@ -15764,6 +18708,50 @@ __metadata: languageName: node linkType: hard +"read-pkg-up@npm:9.1.0, read-pkg-up@npm:^9.1.0": + version: 9.1.0 + resolution: "read-pkg-up@npm:9.1.0" + dependencies: + find-up: ^6.3.0 + read-pkg: ^7.1.0 + type-fest: ^2.5.0 + checksum: 41b8ba4bdb7c1e914aa6ce2d36a7c1651e9086938977fa12f058f6fca51ee15315634af648ca4ef70dd074e575e854616b39032ad0b376e9e97d61a9d0867afe + languageName: node + linkType: hard + +"read-pkg-up@npm:^1.0.1": + version: 1.0.1 + resolution: "read-pkg-up@npm:1.0.1" + dependencies: + find-up: ^1.0.0 + read-pkg: ^1.0.0 + checksum: d18399a0f46e2da32beb2f041edd0cda49d2f2cc30195a05c759ef3ed9b5e6e19ba1ad1bae2362bdec8c6a9f2c3d18f4d5e8c369e808b03d498d5781cb9122c7 + languageName: node + linkType: hard + +"read-pkg@npm:^1.0.0": + version: 1.1.0 + resolution: "read-pkg@npm:1.1.0" + dependencies: + load-json-file: ^1.0.0 + normalize-package-data: ^2.3.2 + path-type: ^1.0.0 + checksum: a0f5d5e32227ec8e6a028dd5c5134eab229768dcb7a5d9a41a284ed28ad4b9284fecc47383dc1593b5694f4de603a7ffaee84b738956b9b77e0999567485a366 + languageName: node + linkType: hard + +"read-pkg@npm:^7.1.0": + version: 7.1.0 + resolution: "read-pkg@npm:7.1.0" + dependencies: + "@types/normalize-package-data": ^2.4.1 + normalize-package-data: ^3.0.2 + parse-json: ^5.2.0 + type-fest: ^2.0.0 + checksum: 20d11c59be3ae1fc79d4b9c8594dabeaec58105f9dfd710570ef9690ec2ac929247006e79ca114257683228663199735d60f149948dbc5f34fcd2d28883ab5f7 + languageName: node + linkType: hard + "read@npm:^1.0.7": version: 1.0.7 resolution: "read@npm:1.0.7" @@ -15785,7 +18773,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.0.0, readable-stream@npm:~2.3.6": +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.3.0, readable-stream@npm:^2.3.5, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -15811,6 +18799,27 @@ __metadata: languageName: node linkType: hard +"readable-stream@npm:^4.0.0": + version: 4.3.0 + resolution: "readable-stream@npm:4.3.0" + dependencies: + abort-controller: ^3.0.0 + buffer: ^6.0.3 + events: ^3.3.0 + process: ^0.11.10 + checksum: 5f8d5fc1eb0c6eb47771ad4537881126d6280666e1f10ba1e2262a670a0352c36f59e6a04d17c9a6f7c888218984836dc67f55e95a77de8bfdf06fb75f00f670 + languageName: node + linkType: hard + +"readdir-glob@npm:^1.0.0": + version: 1.1.3 + resolution: "readdir-glob@npm:1.1.3" + dependencies: + minimatch: ^5.1.0 + checksum: 1dc0f7440ff5d9378b593abe9d42f34ebaf387516615e98ab410cf3a68f840abbf9ff1032d15e0a0dbffa78f9e2c46d4fafdbaac1ca435af2efe3264e3f21874 + languageName: node + linkType: hard + "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -15820,6 +18829,13 @@ __metadata: languageName: node linkType: hard +"real-require@npm:^0.2.0": + version: 0.2.0 + resolution: "real-require@npm:0.2.0" + checksum: fa060f19f2f447adf678d1376928c76379dce5f72bd334da301685ca6cdcb7b11356813332cc243c88470796bc2e2b1e2917fc10df9143dd93c2ea608694971d + languageName: node + linkType: hard + "realistic-structured-clone@npm:^3.0.0": version: 3.0.0 resolution: "realistic-structured-clone@npm:3.0.0" @@ -15856,6 +18872,15 @@ __metadata: languageName: node linkType: hard +"recursive-readdir@npm:^2.2.2": + version: 2.2.3 + resolution: "recursive-readdir@npm:2.2.3" + dependencies: + minimatch: ^3.0.5 + checksum: 88ec96e276237290607edc0872b4f9842837b95cfde0cdbb1e00ba9623dfdf3514d44cdd14496ab60a0c2dd180a6ef8a3f1c34599e6cf2273afac9b72a6fb2b5 + languageName: node + linkType: hard + "redent@npm:^3.0.0": version: 3.0.0 resolution: "redent@npm:3.0.0" @@ -16097,6 +19122,34 @@ __metadata: languageName: node linkType: hard +"request@npm:^2.88.0": + version: 2.88.2 + resolution: "request@npm:2.88.2" + dependencies: + aws-sign2: ~0.7.0 + aws4: ^1.8.0 + caseless: ~0.12.0 + combined-stream: ~1.0.6 + extend: ~3.0.2 + forever-agent: ~0.6.1 + form-data: ~2.3.2 + har-validator: ~5.1.3 + http-signature: ~1.2.0 + is-typedarray: ~1.0.0 + isstream: ~0.1.2 + json-stringify-safe: ~5.0.1 + mime-types: ~2.1.19 + oauth-sign: ~0.9.0 + performance-now: ^2.1.0 + qs: ~6.5.2 + safe-buffer: ^5.1.2 + tough-cookie: ~2.5.0 + tunnel-agent: ^0.6.0 + uuid: ^3.3.2 + checksum: 4e112c087f6eabe7327869da2417e9d28fcd0910419edd2eb17b6acfc4bfa1dad61954525949c228705805882d8a98a86a0ea12d7f739c01ee92af7062996983 + languageName: node + linkType: hard + "require-directory@npm:^2.1.1": version: 2.1.1 resolution: "require-directory@npm:2.1.1" @@ -16150,7 +19203,7 @@ __metadata: languageName: node linkType: hard -"resolve-alpn@npm:^1.0.0": +"resolve-alpn@npm:^1.0.0, resolve-alpn@npm:^1.2.0": version: 1.2.1 resolution: "resolve-alpn@npm:1.2.1" checksum: f558071fcb2c60b04054c99aebd572a2af97ef64128d59bef7ab73bd50d896a222a056de40ffc545b633d99b304c259ea9d0c06830d5c867c34f0bfa60b8eae0 @@ -16187,7 +19240,7 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.0.0, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:~1.22.1": +"resolve@npm:^1.0.0, resolve@npm:^1.10.0, resolve@npm:^1.14.2, resolve@npm:^1.20.0, resolve@npm:^1.22.1, resolve@npm:~1.22.1": version: 1.22.3 resolution: "resolve@npm:1.22.3" dependencies: @@ -16241,7 +19294,7 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.0.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@~1.22.1#~builtin": +"resolve@patch:resolve@^1.0.0#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.14.2#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@~1.22.1#~builtin": version: 1.22.3 resolution: "resolve@patch:resolve@npm%3A1.22.3#~builtin::version=1.22.3&hash=c3c19d" dependencies: @@ -16295,6 +19348,15 @@ __metadata: languageName: node linkType: hard +"responselike@npm:1.0.2": + version: 1.0.2 + resolution: "responselike@npm:1.0.2" + dependencies: + lowercase-keys: ^1.0.0 + checksum: 2e9e70f1dcca3da621a80ce71f2f9a9cad12c047145c6ece20df22f0743f051cf7c73505e109814915f23f9e34fb0d358e22827723ee3d56b623533cab8eafcd + languageName: node + linkType: hard + "responselike@npm:^2.0.0": version: 2.0.1 resolution: "responselike@npm:2.0.1" @@ -16304,6 +19366,24 @@ __metadata: languageName: node linkType: hard +"responselike@npm:^3.0.0": + version: 3.0.0 + resolution: "responselike@npm:3.0.0" + dependencies: + lowercase-keys: ^3.0.0 + checksum: e0cc9be30df4f415d6d83cdede3c5c887cd4a73e7cc1708bcaab1d50a28d15acb68460ac5b02bcc55a42f3d493729c8856427dcf6e57e6e128ad05cba4cfb95e + languageName: node + linkType: hard + +"resq@npm:^1.9.1": + version: 1.11.0 + resolution: "resq@npm:1.11.0" + dependencies: + fast-deep-equal: ^2.0.1 + checksum: a596c0125883246946cf6b9172557265d00334019327c09b84c9016b1e7e876e15c35c81d2f8ed315adf6b93ac035f3d993f9a8b323dcd80ffd6cf8f3eb5cc7e + languageName: node + linkType: hard + "restore-cursor@npm:^3.1.0": version: 3.1.0 resolution: "restore-cursor@npm:3.1.0" @@ -16324,6 +19404,13 @@ __metadata: languageName: node linkType: hard +"ret@npm:~0.2.0": + version: 0.2.2 + resolution: "ret@npm:0.2.2" + checksum: 774964bb413a3525e687bca92d81c1cd75555ec33147c32ecca22f3d06409e35df87952cfe3d57afff7650a0f7e42139cf60cb44e94c29dde390243bc1941f16 + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -16338,14 +19425,21 @@ __metadata: languageName: node linkType: hard -"rfdc@npm:^1.3.0": +"rfdc@npm:^1.2.0, rfdc@npm:^1.3.0": version: 1.3.0 resolution: "rfdc@npm:1.3.0" checksum: fb2ba8512e43519983b4c61bd3fa77c0f410eff6bae68b08614437bc3f35f91362215f7b4a73cbda6f67330b5746ce07db5dd9850ad3edc91271ad6deea0df32 languageName: node linkType: hard -"rimraf@npm:^2.6.1": +"rgb2hex@npm:0.2.5": + version: 0.2.5 + resolution: "rgb2hex@npm:0.2.5" + checksum: 2c36c878bd28b24112dbf5b8d6e898ddb03dcc14e5bd0ddb1a0cc48479aac426cc4f3d1c56d22358ea7ff06154ca4dbe26bca8af303145392afa2d139a8131c4 + languageName: node + linkType: hard + +"rimraf@npm:^2.6.1, rimraf@npm:^2.6.3": version: 2.7.1 resolution: "rimraf@npm:2.7.1" dependencies: @@ -16378,6 +19472,17 @@ __metadata: languageName: node linkType: hard +"rimraf@npm:~2.5.2": + version: 2.5.4 + resolution: "rimraf@npm:2.5.4" + dependencies: + glob: ^7.0.5 + bin: + rimraf: ./bin.js + checksum: 3d217997a312d520e0db3f89b148add6abfe1b03c3f82ab0fc6b0e1d43d0e9b63856bb0708dee18d4f397717af0d06cb52153c606ba145caa0dc567bc4f4ed4d + languageName: node + linkType: hard + "rollup-plugin-inject@npm:^3.0.0": version: 3.0.2 resolution: "rollup-plugin-inject@npm:3.0.2" @@ -16460,6 +19565,15 @@ __metadata: languageName: node linkType: hard +"rxjs@npm:^7.0.0": + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" + dependencies: + tslib: ^2.1.0 + checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 + languageName: node + linkType: hard + "rxjs@npm:^7.5.5, rxjs@npm:^7.8.0": version: 7.8.0 resolution: "rxjs@npm:7.8.0" @@ -16478,7 +19592,14 @@ __metadata: languageName: node linkType: hard -"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:~5.2.0": +"safaridriver@npm:^0.0.4": + version: 0.0.4 + resolution: "safaridriver@npm:0.0.4" + checksum: 1ea3d7213316b4124e460618ffd1c1b0e84b2b809f78da35ea42bb15e20ec0104a0ede49d96c4014b5da77f6e2b904896247d019f70ed82c10d15bf784469c8c + languageName: node + linkType: hard + +"safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491 @@ -16503,7 +19624,23 @@ __metadata: languageName: node linkType: hard -"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0": +"safe-regex2@npm:^2.0.0": + version: 2.0.0 + resolution: "safe-regex2@npm:2.0.0" + dependencies: + ret: ~0.2.0 + checksum: f5e182fca040dedd50ae052ea0eb035d9903b2db71243d5d8b43299735857288ef2ab52546a368d9c6fd1333b2a0d039297925e78ffc14845354f3f6158af7c2 + languageName: node + linkType: hard + +"safe-stable-stringify@npm:^2.3.1": + version: 2.4.3 + resolution: "safe-stable-stringify@npm:2.4.3" + checksum: 3aeb64449706ee1f5ad2459fc99648b131d48e7a1fbb608d7c628020177512dc9d94108a5cb61bbc953985d313d0afea6566d243237743e02870490afef04b43 + languageName: node + linkType: hard + +"safer-buffer@npm:>= 2.1.2 < 3, safer-buffer@npm:>= 2.1.2 < 3.0.0, safer-buffer@npm:^2.0.2, safer-buffer@npm:^2.1.0, safer-buffer@npm:~2.1.0": version: 2.1.2 resolution: "safer-buffer@npm:2.1.2" checksum: cab8f25ae6f1434abee8d80023d7e72b598cf1327164ddab31003c51215526801e40b66c5e65d658a0af1e9d6478cadcb4c745f4bd6751f97d8644786c0978b0 @@ -16554,6 +19691,34 @@ __metadata: languageName: node linkType: hard +"secure-json-parse@npm:^2.5.0": + version: 2.7.0 + resolution: "secure-json-parse@npm:2.7.0" + checksum: d9d7d5a01fc6db6115744ba23cf9e67ecfe8c524d771537c062ee05ad5c11b64c730bc58c7f33f60bd6877f96b86f0ceb9ea29644e4040cb757f6912d4dd6737 + languageName: node + linkType: hard + +"seek-bzip@npm:^1.0.5": + version: 1.0.6 + resolution: "seek-bzip@npm:1.0.6" + dependencies: + commander: ^2.8.1 + bin: + seek-bunzip: bin/seek-bunzip + seek-table: bin/seek-bzip-table + checksum: c2ab3291e7085558499efd4e99d1466ee6782f6c4a4e4c417aa859e1cd2f5117fb3b5444f3d27c38ec5908c0f0312e2a0bc69dff087751f97b3921b5bde4f9ed + languageName: node + linkType: hard + +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.1.0, semver@npm:^5.6.0": + version: 5.7.1 + resolution: "semver@npm:5.7.1" + bin: + semver: ./bin/semver + checksum: 57fd0acfd0bac382ee87cd52cd0aaa5af086a7dc8d60379dfe65fea491fb2489b6016400813930ecd61fd0952dae75c115287a1b16c234b1550887117744dfaf + languageName: node + linkType: hard + "semver@npm:6.1.1": version: 6.1.1 resolution: "semver@npm:6.1.1" @@ -16574,15 +19739,6 @@ __metadata: languageName: node linkType: hard -"semver@npm:^5.1.0": - version: 5.7.1 - resolution: "semver@npm:5.7.1" - bin: - semver: ./bin/semver - checksum: 57fd0acfd0bac382ee87cd52cd0aaa5af086a7dc8d60379dfe65fea491fb2489b6016400813930ecd61fd0952dae75c115287a1b16c234b1550887117744dfaf - languageName: node - linkType: hard - "semver@npm:^6.0.0, semver@npm:^6.1.1, semver@npm:^6.1.2, semver@npm:^6.3.0": version: 6.3.0 resolution: "semver@npm:6.3.0" @@ -16624,6 +19780,15 @@ __metadata: languageName: node linkType: hard +"serialize-error@npm:^8.0.0": + version: 8.1.0 + resolution: "serialize-error@npm:8.1.0" + dependencies: + type-fest: ^0.20.2 + checksum: 2eef236d50edd2d7926e602c14fb500dc3a125ee52e9f08f67033181b8e0be5d1122498bdf7c23c80683cddcad083a27974e9e7111ce23165f4d3bcdd6d65102 + languageName: node + linkType: hard + "serialize-javascript@npm:6.0.0": version: 6.0.0 resolution: "serialize-javascript@npm:6.0.0" @@ -16652,13 +19817,20 @@ __metadata: languageName: node linkType: hard -"set-cookie-parser@npm:^2.4.8": +"set-cookie-parser@npm:^2.4.1, set-cookie-parser@npm:^2.4.8": version: 2.6.0 resolution: "set-cookie-parser@npm:2.6.0" checksum: bf11ebc594c53d84588f1b4c04f1b8ce14e0498b1c011b3d76b5c6d5aac481bbc3f7c5260ec4ce99bdc1d9aed19f9fc315e73166a36ca74d0f12349a73f6bdc9 languageName: node linkType: hard +"setimmediate@npm:^1.0.5": + version: 1.0.5 + resolution: "setimmediate@npm:1.0.5" + checksum: c9a6f2c5b51a2dabdc0247db9c46460152ffc62ee139f3157440bd48e7c59425093f42719ac1d7931f054f153e2d26cf37dfeb8da17a794a58198a2705e527fd + languageName: node + linkType: hard + "setprototypeof@npm:1.2.0": version: 1.2.0 resolution: "setprototypeof@npm:1.2.0" @@ -16666,6 +19838,23 @@ __metadata: languageName: node linkType: hard +"sharp@npm:^0.31.2": + version: 0.31.3 + resolution: "sharp@npm:0.31.3" + dependencies: + color: ^4.2.3 + detect-libc: ^2.0.1 + node-addon-api: ^5.0.0 + node-gyp: latest + prebuild-install: ^7.1.1 + semver: ^7.3.8 + simple-get: ^4.0.1 + tar-fs: ^2.1.1 + tunnel-agent: ^0.6.0 + checksum: 29fd1dfbc616c6389f53f366cec342b4353d9f2a37e98952ca273db38dca57dfa0f336322d6d763f0fae876042ead22fd86ffe26d70c32ade2458d421db60d04 + languageName: node + linkType: hard + "shebang-command@npm:^2.0.0": version: 2.0.0 resolution: "shebang-command@npm:2.0.0" @@ -16682,6 +19871,13 @@ __metadata: languageName: node linkType: hard +"shell-quote@npm:^1.7.3": + version: 1.8.1 + resolution: "shell-quote@npm:1.8.1" + checksum: 5f01201f4ef504d4c6a9d0d283fa17075f6770bfbe4c5850b074974c68062f37929ca61700d95ad2ac8822e14e8c4b990ca0e6e9272e64befd74ce5e19f0736b + languageName: node + linkType: hard + "side-channel@npm:^1.0.4": version: 1.0.4 resolution: "side-channel@npm:1.0.4" @@ -16700,6 +19896,13 @@ __metadata: languageName: node linkType: hard +"signal-exit@npm:^4.0.1": + version: 4.0.1 + resolution: "signal-exit@npm:4.0.1" + checksum: 832043367dca23e61ab6033e8b41c595fc805119bfe4fee63dea201cdc809a8b086bc54597bbbc1b2cde1a63c7dd554d1295ed2cca92db598233834a0b59b281 + languageName: node + linkType: hard + "signale@npm:^1.4.0": version: 1.4.0 resolution: "signale@npm:1.4.0" @@ -16736,7 +19939,7 @@ __metadata: languageName: node linkType: hard -"simple-get@npm:^4.0.0": +"simple-get@npm:^4.0.0, simple-get@npm:^4.0.1": version: 4.0.1 resolution: "simple-get@npm:4.0.1" dependencies: @@ -16747,6 +19950,15 @@ __metadata: languageName: node linkType: hard +"simple-swizzle@npm:^0.2.2": + version: 0.2.2 + resolution: "simple-swizzle@npm:0.2.2" + dependencies: + is-arrayish: ^0.3.1 + checksum: a7f3f2ab5c76c4472d5c578df892e857323e452d9f392e1b5cf74b74db66e6294a1e1b8b390b519fa1b96b5b613f2a37db6cffef52c3f1f8f3c5ea64eb2d54c0 + languageName: node + linkType: hard + "sisteransi@npm:^1.0.5": version: 1.0.5 resolution: "sisteransi@npm:1.0.5" @@ -16775,6 +19987,13 @@ __metadata: languageName: node linkType: hard +"slash@npm:^5.0.1": + version: 5.0.1 + resolution: "slash@npm:5.0.1" + checksum: c0f5262d80730a22fa9eb06a4999fb382e6acb0fe045ed7894462e8fcd8bf487ee57377c7915be678847a5a1d01bacfb5b1b1f181aea7c0142f1e5e0386a14bb + languageName: node + linkType: hard + "slice-ansi@npm:^3.0.0": version: 3.0.0 resolution: "slice-ansi@npm:3.0.0" @@ -16856,6 +20075,42 @@ __metadata: languageName: node linkType: hard +"sonic-boom@npm:^3.1.0": + version: 3.3.0 + resolution: "sonic-boom@npm:3.3.0" + dependencies: + atomic-sleep: ^1.0.0 + checksum: 4a290dd0f3edf49894bb72c631ee304dc3f9be0752c43d516808a365f341821f5cf49997c80ee7c0e67167e0e5131dc71afe7c58812858eb965d6b9746c0cac7 + languageName: node + linkType: hard + +"sort-keys-length@npm:^1.0.0": + version: 1.0.1 + resolution: "sort-keys-length@npm:1.0.1" + dependencies: + sort-keys: ^1.0.0 + checksum: f9acac5fb31580a9e3d43b419dc86a1b75e85b79036a084d95dd4d1062b621c9589906588ac31e370a0dd381be46d8dbe900efa306d087ca9c912d7a59b5a590 + languageName: node + linkType: hard + +"sort-keys@npm:^1.0.0": + version: 1.1.2 + resolution: "sort-keys@npm:1.1.2" + dependencies: + is-plain-obj: ^1.0.0 + checksum: 5963fd191a2a185a5ec86f06e47721e8e04713eda43bb04ae60d2a8afb21241553dd5bc9d863ed2bd7c3d541b609b0c8d0e58836b1a3eb6764c09c094bcc8b00 + languageName: node + linkType: hard + +"sort-keys@npm:^2.0.0": + version: 2.0.0 + resolution: "sort-keys@npm:2.0.0" + dependencies: + is-plain-obj: ^1.0.0 + checksum: f0fd827fa9f8f866e98588d2a38c35209afbf1e9a05bb0e4ceeeb8bbf31d923c8902b0a7e0f561590ddb65e58eba6a74f74b991c85360bcc52e83a3f0d1cffd7 + languageName: node + linkType: hard + "sort-object-keys@npm:^1.1.3": version: 1.1.3 resolution: "sort-object-keys@npm:1.1.3" @@ -16941,6 +20196,56 @@ __metadata: languageName: node linkType: hard +"spdx-correct@npm:^3.0.0": + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" + dependencies: + spdx-expression-parse: ^3.0.0 + spdx-license-ids: ^3.0.0 + checksum: e9ae98d22f69c88e7aff5b8778dc01c361ef635580e82d29e5c60a6533cc8f4d820803e67d7432581af0cc4fb49973125076ee3b90df191d153e223c004193b2 + languageName: node + linkType: hard + +"spdx-exceptions@npm:^2.1.0": + version: 2.3.0 + resolution: "spdx-exceptions@npm:2.3.0" + checksum: cb69a26fa3b46305637123cd37c85f75610e8c477b6476fa7354eb67c08128d159f1d36715f19be6f9daf4b680337deb8c65acdcae7f2608ba51931540687ac0 + languageName: node + linkType: hard + +"spdx-expression-parse@npm:^3.0.0": + version: 3.0.1 + resolution: "spdx-expression-parse@npm:3.0.1" + dependencies: + spdx-exceptions: ^2.1.0 + spdx-license-ids: ^3.0.0 + checksum: a1c6e104a2cbada7a593eaa9f430bd5e148ef5290d4c0409899855ce8b1c39652bcc88a725259491a82601159d6dc790bedefc9016c7472f7de8de7361f8ccde + languageName: node + linkType: hard + +"spdx-license-ids@npm:^3.0.0": + version: 3.0.13 + resolution: "spdx-license-ids@npm:3.0.13" + checksum: 3469d85c65f3245a279fa11afc250c3dca96e9e847f2f79d57f466940c5bb8495da08a542646086d499b7f24a74b8d0b42f3fc0f95d50ff99af1f599f6360ad7 + languageName: node + linkType: hard + +"split2@npm:^4.0.0, split2@npm:^4.1.0, split2@npm:^4.2.0": + version: 4.2.0 + resolution: "split2@npm:4.2.0" + checksum: 05d54102546549fe4d2455900699056580cca006c0275c334611420f854da30ac999230857a85fdd9914dc2109ae50f80fda43d2a445f2aa86eccdc1dfce779d + languageName: node + linkType: hard + +"split@npm:0.3": + version: 0.3.3 + resolution: "split@npm:0.3.3" + dependencies: + through: 2 + checksum: 2e076634c9637cfdc54ab4387b6a243b8c33b360874a25adf6f327a5647f07cb3bf1c755d515248eb3afee4e382278d01f62c62d87263c118f28065b86f74f02 + languageName: node + linkType: hard + "sprintf-js@npm:~1.0.2": version: 1.0.3 resolution: "sprintf-js@npm:1.0.3" @@ -16948,6 +20253,27 @@ __metadata: languageName: node linkType: hard +"sshpk@npm:^1.7.0": + version: 1.17.0 + resolution: "sshpk@npm:1.17.0" + dependencies: + asn1: ~0.2.3 + assert-plus: ^1.0.0 + bcrypt-pbkdf: ^1.0.0 + dashdash: ^1.12.0 + ecc-jsbn: ~0.1.1 + getpass: ^0.1.1 + jsbn: ~0.1.0 + safer-buffer: ^2.0.2 + tweetnacl: ~0.14.0 + bin: + sshpk-conv: bin/sshpk-conv + sshpk-sign: bin/sshpk-sign + sshpk-verify: bin/sshpk-verify + checksum: ba109f65c8e6c35133b8e6ed5576abeff8aa8d614824b7275ec3ca308f081fef483607c28d97780c1e235818b0f93ed8c8b56d0a5968d5a23fd6af57718c7597 + languageName: node + linkType: hard + "ssri@npm:^8.0.1": version: 8.0.1 resolution: "ssri@npm:8.0.1" @@ -17000,6 +20326,22 @@ __metadata: languageName: node linkType: hard +"stream-buffers@npm:^3.0.2": + version: 3.0.2 + resolution: "stream-buffers@npm:3.0.2" + checksum: b09fdeea606e3113ebd0e07010ed0cf038608fa396130add9e45deaff5cc3ba845dc25c31ad24f8341f85907846344cb7c85f75ea52c6572e2ac646e9b6072d0 + languageName: node + linkType: hard + +"stream-combiner@npm:~0.0.4": + version: 0.0.4 + resolution: "stream-combiner@npm:0.0.4" + dependencies: + duplexer: ~0.1.1 + checksum: 844b622cfe8b9de45a6007404f613b60aaf85200ab9862299066204242f89a7c8033b1c356c998aa6cfc630f6cd9eba119ec1c6dc1f93e245982be4a847aee7d + languageName: node + linkType: hard + "stream-shift@npm:^1.0.0": version: 1.0.1 resolution: "stream-shift@npm:1.0.1" @@ -17007,6 +20349,20 @@ __metadata: languageName: node linkType: hard +"streamsearch@npm:^1.1.0": + version: 1.1.0 + resolution: "streamsearch@npm:1.1.0" + checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 + languageName: node + linkType: hard + +"strict-uri-encode@npm:^1.0.0": + version: 1.1.0 + resolution: "strict-uri-encode@npm:1.1.0" + checksum: 9466d371f7b36768d43f7803f26137657559e4c8b0161fb9e320efb8edba3ae22f8e99d4b0d91da023b05a13f62ec5412c3f4f764b5788fac11d1fea93720bb3 + languageName: node + linkType: hard + "string-argv@npm:^0.3.1, string-argv@npm:~0.3.1": version: 0.3.1 resolution: "string-argv@npm:0.3.1" @@ -17031,7 +20387,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": version: 4.2.3 resolution: "string-width@npm:4.2.3" dependencies: @@ -17148,6 +20504,24 @@ __metadata: languageName: node linkType: hard +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + +"strip-ansi@npm:^3.0.0": + version: 3.0.1 + resolution: "strip-ansi@npm:3.0.1" + dependencies: + ansi-regex: ^2.0.0 + checksum: 9b974de611ce5075c70629c00fa98c46144043db92ae17748fb780f706f7a789e9989fd10597b7c2053ae8d1513fd707816a91f1879b2f71e6ac0b6a863db465 + languageName: node + linkType: hard + "strip-ansi@npm:^5.0.0, strip-ansi@npm:^5.1.0, strip-ansi@npm:^5.2.0": version: 5.2.0 resolution: "strip-ansi@npm:5.2.0" @@ -17157,15 +20531,6 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" - dependencies: - ansi-regex: ^5.0.1 - checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c - languageName: node - linkType: hard - "strip-ansi@npm:^7.0.1": version: 7.0.1 resolution: "strip-ansi@npm:7.0.1" @@ -17182,6 +20547,15 @@ __metadata: languageName: node linkType: hard +"strip-bom@npm:^2.0.0": + version: 2.0.0 + resolution: "strip-bom@npm:2.0.0" + dependencies: + is-utf8: ^0.2.0 + checksum: 08efb746bc67b10814cd03d79eb31bac633393a782e3f35efbc1b61b5165d3806d03332a97f362822cf0d4dd14ba2e12707fcff44fe1c870c48a063a0c9e4944 + languageName: node + linkType: hard + "strip-bom@npm:^3.0.0": version: 3.0.0 resolution: "strip-bom@npm:3.0.0" @@ -17196,6 +20570,15 @@ __metadata: languageName: node linkType: hard +"strip-dirs@npm:^2.0.0": + version: 2.1.0 + resolution: "strip-dirs@npm:2.1.0" + dependencies: + is-natural-number: ^4.0.1 + checksum: 9465547d71d8819daa7a5c9d4d783289ed8eac72eb06bd687bed382ce62af8ab8e6ffbda229805f5d2e71acce2ca4915e781c94190d284994cbc0b7cdc8303cc + languageName: node + linkType: hard + "strip-final-newline@npm:^2.0.0": version: 2.0.0 resolution: "strip-final-newline@npm:2.0.0" @@ -17233,6 +20616,15 @@ __metadata: languageName: node linkType: hard +"strip-outer@npm:^1.0.0": + version: 1.0.1 + resolution: "strip-outer@npm:1.0.1" + dependencies: + escape-string-regexp: ^1.0.2 + checksum: f8d65d33ca2b49aabc66bb41d689dda7b8b9959d320e3a40a2ef4d7079ff2f67ffb72db43f179f48dbf9495c2e33742863feab7a584d180fa62505439162c191 + languageName: node + linkType: hard + "style-to-object@npm:^0.4.1": version: 0.4.1 resolution: "style-to-object@npm:0.4.1" @@ -17258,6 +20650,13 @@ __metadata: languageName: node linkType: hard +"suffix@npm:^0.1.0": + version: 0.1.1 + resolution: "suffix@npm:0.1.1" + checksum: 5e0eff027bac0ad1c6d42361ad19c48abdd4e86971afdb4e4f4aeb8c9a4149a0b55ea5f3a22d7e59cd09638cf64dd022baa552a1c0a2c6a6107520a657d563a2 + languageName: node + linkType: hard + "supports-color@npm:8.1.1, supports-color@npm:^8.0.0, supports-color@npm:^8.1.0": version: 8.1.1 resolution: "supports-color@npm:8.1.1" @@ -17267,6 +20666,20 @@ __metadata: languageName: node linkType: hard +"supports-color@npm:9.3.1": + version: 9.3.1 + resolution: "supports-color@npm:9.3.1" + checksum: 00c4d1082a7ba0ee21cba1d4e4a466642635412e40476777b530aa5110d035e99a420cd048e1fb6811f2254c0946095fbb87a1eccf1af1d1ca45ab0a4535db93 + languageName: node + linkType: hard + +"supports-color@npm:^2.0.0": + version: 2.0.0 + resolution: "supports-color@npm:2.0.0" + checksum: 602538c5812b9006404370b5a4b885d3e2a1f6567d314f8b4a41974ffe7d08e525bf92ae0f9c7030e3b4c78e4e34ace55d6a67a74f1571bc205959f5972f88f0 + languageName: node + linkType: hard + "supports-color@npm:^5.3.0": version: 5.5.0 resolution: "supports-color@npm:5.5.0" @@ -17354,7 +20767,7 @@ __metadata: languageName: node linkType: hard -"tar-fs@npm:^2.0.0, tar-fs@npm:^2.1.1": +"tar-fs@npm:2.1.1, tar-fs@npm:^2.0.0, tar-fs@npm:^2.1.1": version: 2.1.1 resolution: "tar-fs@npm:2.1.1" dependencies: @@ -17366,7 +20779,22 @@ __metadata: languageName: node linkType: hard -"tar-stream@npm:^2.1.4": +"tar-stream@npm:^1.5.2": + version: 1.6.2 + resolution: "tar-stream@npm:1.6.2" + dependencies: + bl: ^1.0.0 + buffer-alloc: ^1.2.0 + end-of-stream: ^1.0.0 + fs-constants: ^1.0.0 + readable-stream: ^2.3.0 + to-buffer: ^1.1.1 + xtend: ^4.0.0 + checksum: a5d49e232d3e33321bbd150381b6a4e5046bf12b1c2618acb95435b7871efde4d98bd1891eb2200478a7142ef7e304e033eb29bbcbc90451a2cdfa1890e05245 + languageName: node + linkType: hard + +"tar-stream@npm:^2.1.4, tar-stream@npm:^2.2.0": version: 2.2.0 resolution: "tar-stream@npm:2.2.0" dependencies: @@ -17379,6 +20807,20 @@ __metadata: languageName: node linkType: hard +"tar@npm:6.1.11": + version: 6.1.11 + resolution: "tar@npm:6.1.11" + dependencies: + chownr: ^2.0.0 + fs-minipass: ^2.0.0 + minipass: ^3.0.0 + minizlib: ^2.1.1 + mkdirp: ^1.0.3 + yallist: ^4.0.0 + checksum: a04c07bb9e2d8f46776517d4618f2406fb977a74d914ad98b264fc3db0fe8224da5bec11e5f8902c5b9bcb8ace22d95fbe3c7b36b8593b7dfc8391a25898f32f + languageName: node + linkType: hard + "tar@npm:^6.0.2, tar@npm:^6.1.11, tar@npm:^6.1.2": version: 6.1.13 resolution: "tar@npm:6.1.13" @@ -17393,6 +20835,25 @@ __metadata: languageName: node linkType: hard +"tcp-port-used@npm:^1.0.1, tcp-port-used@npm:^1.0.2": + version: 1.0.2 + resolution: "tcp-port-used@npm:1.0.2" + dependencies: + debug: 4.3.1 + is2: ^2.0.6 + checksum: ea1bd3f7789a79bb228382e7314167357cd2a2dc3e17521393739075b85e3df0009c53aab4aaa9d180a59791ab152fe87079adaf05242c411b1778a41e543863 + languageName: node + linkType: hard + +"temp-fs@npm:^0.9.9": + version: 0.9.9 + resolution: "temp-fs@npm:0.9.9" + dependencies: + rimraf: ~2.5.2 + checksum: 308a3fc881e4548cc8a55db6e8fdc5cf75d2a1421714450b66cdbbd77a27c2395c8f1236f5d56e2be3d561b81095d23c164a4519b544f725053f03e6d3cfaa80 + languageName: node + linkType: hard + "terminal-link@npm:^2.0.0, terminal-link@npm:^2.1.1": version: 2.1.1 resolution: "terminal-link@npm:2.1.1" @@ -17421,6 +20882,15 @@ __metadata: languageName: node linkType: hard +"thread-stream@npm:^2.0.0": + version: 2.3.0 + resolution: "thread-stream@npm:2.3.0" + dependencies: + real-require: ^0.2.0 + checksum: e9ea58f9f36320165b41c2aae5c439bf68bd3575eb533c458483d8b290e31d519979e351408c7d6e248711611434332c2a3aae2165650b028cc3eb9b1052ac16 + languageName: node + linkType: hard + "through2@npm:^2.0.3": version: 2.0.5 resolution: "through2@npm:2.0.5" @@ -17431,7 +20901,7 @@ __metadata: languageName: node linkType: hard -"through@npm:^2.3.6, through@npm:^2.3.8": +"through@npm:2, through@npm:^2.3.6, through@npm:^2.3.8, through@npm:~2.3, through@npm:~2.3.1": version: 2.3.8 resolution: "through@npm:2.3.8" checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd @@ -17447,6 +20917,13 @@ __metadata: languageName: node linkType: hard +"timed-out@npm:^4.0.1": + version: 4.0.1 + resolution: "timed-out@npm:4.0.1" + checksum: 98efc5d6fc0d2a329277bd4d34f65c1bf44d9ca2b14fd267495df92898f522e6f563c5e9e467c418e0836f5ca1f47a84ca3ee1de79b1cc6fe433834b7f02ec54 + languageName: node + linkType: hard + "tiny-glob@npm:^0.2.9": version: 0.2.9 resolution: "tiny-glob@npm:0.2.9" @@ -17457,6 +20934,13 @@ __metadata: languageName: node linkType: hard +"tiny-lru@npm:^11.0.1": + version: 11.0.1 + resolution: "tiny-lru@npm:11.0.1" + checksum: 709ab58a454028eae15dd249518a1e348520e22514e52fd625ef89ba04a42599522e9f6cc89f50f76d3809cc46cac352bd0b63f052d23562e7adafe3e728531a + languageName: node + linkType: hard + "tinycolor2@npm:^1.4.1": version: 1.6.0 resolution: "tinycolor2@npm:1.6.0" @@ -17492,6 +20976,15 @@ __metadata: languageName: unknown linkType: soft +"tmp-promise@npm:^3.0.3": + version: 3.0.3 + resolution: "tmp-promise@npm:3.0.3" + dependencies: + tmp: ^0.2.0 + checksum: f854f5307dcee6455927ec3da9398f139897faf715c5c6dcee6d9471ae85136983ea06662eba2edf2533bdcb0fca66d16648e79e14381e30c7fb20be9c1aa62c + languageName: node + linkType: hard + "tmp@npm:^0.0.33": version: 0.0.33 resolution: "tmp@npm:0.0.33" @@ -17501,7 +20994,7 @@ __metadata: languageName: node linkType: hard -"tmp@npm:^0.2.1": +"tmp@npm:^0.2.0, tmp@npm:^0.2.1": version: 0.2.1 resolution: "tmp@npm:0.2.1" dependencies: @@ -17517,6 +21010,13 @@ __metadata: languageName: node linkType: hard +"to-buffer@npm:^1.1.1": + version: 1.1.1 + resolution: "to-buffer@npm:1.1.1" + checksum: 6c897f58c2bdd8b8b1645ea515297732fec6dafb089bf36d12370c102ff5d64abf2be9410e0b1b7cfc707bada22d9a4084558010bfc78dd7023748dc5dd9a1ce + languageName: node + linkType: hard + "to-fast-properties@npm:^2.0.0": version: 2.0.0 resolution: "to-fast-properties@npm:2.0.0" @@ -17559,6 +21059,16 @@ __metadata: languageName: node linkType: hard +"tough-cookie@npm:~2.5.0": + version: 2.5.0 + resolution: "tough-cookie@npm:2.5.0" + dependencies: + psl: ^1.1.28 + punycode: ^2.1.1 + checksum: 16a8cd090224dd176eee23837cbe7573ca0fa297d7e468ab5e1c02d49a4e9a97bb05fef11320605eac516f91d54c57838a25864e8680e27b069a5231d8264977 + languageName: node + linkType: hard + "tr46@npm:^2.1.0": version: 2.1.0 resolution: "tr46@npm:2.1.0" @@ -17600,6 +21110,15 @@ __metadata: languageName: node linkType: hard +"trim-repeated@npm:^1.0.0": + version: 1.0.0 + resolution: "trim-repeated@npm:1.0.0" + dependencies: + escape-string-regexp: ^1.0.2 + checksum: e25c235305b82c43f1d64a67a71226c406b00281755e4c2c4f3b1d0b09c687a535dd3c4483327f949f28bb89dc400a0bc5e5b749054f4b99f49ebfe48ba36496 + languageName: node + linkType: hard + "trough@npm:^2.0.0": version: 2.1.0 resolution: "trough@npm:2.1.0" @@ -17666,7 +21185,34 @@ __metadata: languageName: node linkType: hard -"ts-node@npm:10.9.1, ts-node@npm:^10.9.1": +"ts-node-dev@npm:^2.0.0": + version: 2.0.0 + resolution: "ts-node-dev@npm:2.0.0" + dependencies: + chokidar: ^3.5.1 + dynamic-dedupe: ^0.3.0 + minimist: ^1.2.6 + mkdirp: ^1.0.4 + resolve: ^1.0.0 + rimraf: ^2.6.1 + source-map-support: ^0.5.12 + tree-kill: ^1.2.2 + ts-node: ^10.4.0 + tsconfig: ^7.0.0 + peerDependencies: + node-notifier: "*" + typescript: "*" + peerDependenciesMeta: + node-notifier: + optional: true + bin: + ts-node-dev: lib/bin.js + tsnd: lib/bin.js + checksum: d654b401de3d13c167981481be2a375229f6bfd2aeedf43bc0b6816e57676fcbfba3afdcf209c7a06fb6bd8768ca548c2eb0a0c9d38fa42246be3f50df1b28fb + languageName: node + linkType: hard + +"ts-node@npm:10.9.1, ts-node@npm:^10.4.0, ts-node@npm:^10.9.1": version: 10.9.1 resolution: "ts-node@npm:10.9.1" dependencies: @@ -17839,6 +21385,13 @@ __metadata: languageName: node linkType: hard +"tweetnacl@npm:^0.14.3, tweetnacl@npm:~0.14.0": + version: 0.14.5 + resolution: "tweetnacl@npm:0.14.5" + checksum: 6061daba1724f59473d99a7bb82e13f211cdf6e31315510ae9656fefd4779851cb927adad90f3b488c8ed77c106adc0421ea8055f6f976ff21b27c5c4e918487 + languageName: node + linkType: hard + "type-check@npm:^0.4.0, type-check@npm:~0.4.0": version: 0.4.0 resolution: "type-check@npm:0.4.0" @@ -17878,6 +21431,13 @@ __metadata: languageName: node linkType: hard +"type-fest@npm:^2.0.0, type-fest@npm:^2.5.0": + version: 2.19.0 + resolution: "type-fest@npm:2.19.0" + checksum: a4ef07ece297c9fba78fc1bd6d85dff4472fe043ede98bd4710d2615d15776902b595abf62bd78339ed6278f021235fb28a96361f8be86ed754f778973a0d278 + languageName: node + linkType: hard + "type-fest@npm:^3.0.0": version: 3.8.0 resolution: "type-fest@npm:3.8.0" @@ -17917,6 +21477,13 @@ __metadata: languageName: node linkType: hard +"typedarray@npm:^0.0.6": + version: 0.0.6 + resolution: "typedarray@npm:0.0.6" + checksum: 33b39f3d0e8463985eeaeeacc3cb2e28bc3dfaf2a5ed219628c0b629d5d7b810b0eb2165f9f607c34871d5daa92ba1dc69f49051cf7d578b4cbd26c340b9d1b1 + languageName: node + linkType: hard + "typescript-memoize@npm:^1.0.0-alpha.3": version: 1.1.1 resolution: "typescript-memoize@npm:1.1.1" @@ -18036,6 +21603,13 @@ __metadata: languageName: node linkType: hard +"ua-parser-js@npm:^1.0.1, ua-parser-js@npm:^1.0.33": + version: 1.0.35 + resolution: "ua-parser-js@npm:1.0.35" + checksum: 02370d38a0c8b586f2503d1c3bbba5cbc0b97d407282f9023201a99e4c03eae4357a2800fdf50cf80d73ec25c0b0cc5bfbaa03975b0add4043d6e4c86712c9c1 + languageName: node + linkType: hard + "uc.micro@npm:^1.0.1, uc.micro@npm:^1.0.5": version: 1.0.6 resolution: "uc.micro@npm:1.0.6" @@ -18062,6 +21636,16 @@ __metadata: languageName: node linkType: hard +"unbzip2-stream@npm:1.4.3, unbzip2-stream@npm:^1.0.9": + version: 1.4.3 + resolution: "unbzip2-stream@npm:1.4.3" + dependencies: + buffer: ^5.2.1 + through: ^2.3.8 + checksum: 0e67c4a91f4fa0fc7b4045f8b914d3498c2fc2e8c39c359977708ec85ac6d6029840e97f508675fdbdf21fcb8d276ca502043406f3682b70f075e69aae626d1d + languageName: node + linkType: hard + "underscore@npm:^1.12.1": version: 1.13.6 resolution: "underscore@npm:1.13.6" @@ -18069,6 +21653,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.22.0": + version: 5.22.0 + resolution: "undici@npm:5.22.0" + dependencies: + busboy: ^1.6.0 + checksum: 8dc55240a60ae7680798df344e8f46ad0f872ed0fa434fb94cc4fd2b5b2f8053bdf11994d15902999d3880f9bf7cd875a2e90883d2702bf0f366dacd9cbf3fc6 + languageName: node + linkType: hard + "unicode-canonical-property-names-ecmascript@npm:^2.0.0": version: 2.0.0 resolution: "unicode-canonical-property-names-ecmascript@npm:2.0.0" @@ -18309,6 +21902,15 @@ __metadata: languageName: node linkType: hard +"url-parse-lax@npm:^3.0.0": + version: 3.0.0 + resolution: "url-parse-lax@npm:3.0.0" + dependencies: + prepend-http: ^2.0.0 + checksum: 1040e357750451173132228036aff1fd04abbd43eac1fb3e4fca7495a078bcb8d33cb765fe71ad7e473d9c94d98fd67adca63bd2716c815a2da066198dd37217 + languageName: node + linkType: hard + "url-parse@npm:^1.5.3": version: 1.5.10 resolution: "url-parse@npm:1.5.10" @@ -18319,6 +21921,13 @@ __metadata: languageName: node linkType: hard +"url-to-options@npm:^1.0.1": + version: 1.0.1 + resolution: "url-to-options@npm:1.0.1" + checksum: 20e59f4578525fb0d30ffc22b13b5aa60bc9e57cefd4f5842720f5b57211b6dec54abeae2d675381ac4486fd1a2e987f1318725dea996e503ff89f8c8ce2c17e + languageName: node + linkType: hard + "use-callback-ref@npm:^1.3.0": version: 1.3.0 resolution: "use-callback-ref@npm:1.3.0" @@ -18398,6 +22007,33 @@ __metadata: languageName: node linkType: hard +"uuid@npm:^3.3.2": + version: 3.4.0 + resolution: "uuid@npm:3.4.0" + bin: + uuid: ./bin/uuid + checksum: 58de2feed61c59060b40f8203c0e4ed7fd6f99d42534a499f1741218a1dd0c129f4aa1de797bcf822c8ea5da7e4137aa3673431a96dae729047f7aca7b27866f + languageName: node + linkType: hard + +"uuid@npm:^8.3.2": + version: 8.3.2 + resolution: "uuid@npm:8.3.2" + bin: + uuid: dist/bin/uuid + checksum: 5575a8a75c13120e2f10e6ddc801b2c7ed7d8f3c8ac22c7ed0c7b2ba6383ec0abda88c905085d630e251719e0777045ae3236f04c812184b7c765f63a70e58df + languageName: node + linkType: hard + +"uuid@npm:^9.0.0": + version: 9.0.0 + resolution: "uuid@npm:9.0.0" + bin: + uuid: dist/bin/uuid + checksum: 8dd2c83c43ddc7e1c71e36b60aea40030a6505139af6bee0f382ebcd1a56f6cd3028f7f06ffb07f8cf6ced320b76aea275284b224b002b289f89fe89c389b028 + languageName: node + linkType: hard + "uvu@npm:^0.5.0": version: 0.5.6 resolution: "uvu@npm:0.5.6" @@ -18430,6 +22066,16 @@ __metadata: languageName: node linkType: hard +"validate-npm-package-license@npm:^3.0.1": + version: 3.0.4 + resolution: "validate-npm-package-license@npm:3.0.4" + dependencies: + spdx-correct: ^3.0.0 + spdx-expression-parse: ^3.0.0 + checksum: 35703ac889d419cf2aceef63daeadbe4e77227c39ab6287eeb6c1b36a746b364f50ba22e88591f5d017bc54685d8137bc2d328d0a896e4d3fd22093c0f32a9ad + languageName: node + linkType: hard + "validator@npm:^13.7.0": version: 13.9.0 resolution: "validator@npm:13.9.0" @@ -18465,6 +22111,17 @@ __metadata: languageName: node linkType: hard +"verror@npm:1.10.0": + version: 1.10.0 + resolution: "verror@npm:1.10.0" + dependencies: + assert-plus: ^1.0.0 + core-util-is: 1.0.2 + extsprintf: ^1.2.0 + checksum: c431df0bedf2088b227a4e051e0ff4ca54df2c114096b0c01e1cbaadb021c30a04d7dd5b41ab277bcd51246ca135bf931d4c4c796ecae7a4fef6d744ecef36ea + languageName: node + linkType: hard + "vfile-location@npm:^4.0.0": version: 4.1.0 resolution: "vfile-location@npm:4.1.0" @@ -18642,6 +22299,13 @@ __metadata: languageName: node linkType: hard +"vscode-uri@npm:^3.0.7": + version: 3.0.7 + resolution: "vscode-uri@npm:3.0.7" + checksum: c899a0334f9f6ba53021328e083f6307978c09b94407d7e5fe86fcd8fcb8f1da0cb344123a335e55769055007a46d51aff83f9ee1dfc0296ee54b78f34ef0e4f + languageName: node + linkType: hard + "w3c-hr-time@npm:^1.0.2": version: 1.0.2 resolution: "w3c-hr-time@npm:1.0.2" @@ -18687,6 +22351,127 @@ __metadata: languageName: node linkType: hard +"wdio-chromedriver-service@npm:^8.0.1, wdio-chromedriver-service@npm:^8.1.1": + version: 8.1.1 + resolution: "wdio-chromedriver-service@npm:8.1.1" + dependencies: + "@wdio/logger": ^8.1.0 + fs-extra: ^11.1.0 + split2: ^4.1.0 + tcp-port-used: ^1.0.2 + peerDependencies: + "@wdio/types": ^7.0.0 || ^8.0.0-alpha.219 + chromedriver: "*" + webdriverio: ^7.0.0 || ^8.0.0-alpha.219 + peerDependenciesMeta: + "@wdio/types": + optional: true + chromedriver: + optional: true + webdriverio: + optional: false + checksum: e9e797bf1c3366e2dd5cf6b2c362c1fc1a22a8d97c23afff31a670f42dd99a33cb73228011cef7e799613e70ecd30529472a1e00f42dcc28f8a0e88c0ce299e0 + languageName: node + linkType: hard + +"wdio-edgedriver-service@npm:^2.1.2": + version: 2.1.2 + resolution: "wdio-edgedriver-service@npm:2.1.2" + dependencies: + "@wdio/logger": ^8.1.0 + fs-extra: ^11.1.0 + get-port: ^6.1.2 + split2: ^4.1.0 + tcp-port-used: ^1.0.2 + peerDependencies: + "@wdio/types": ^7.0.0 || ^8.0.0 + msedgedriver: ^91.0.0 + peerDependenciesMeta: + "@wdio/types": + optional: true + msedgedriver: + optional: false + checksum: 242f14186ed84a9560bbe20cb1d467d5ded4b7354f3a1ceda25c84ca596f295b46d8e427fb45e1e5c584a3ad2f0c666d3f48d1bf8c0b51e4cfb8528fdc8ad3d1 + languageName: node + linkType: hard + +"wdio-geckodriver-service@npm:^4.1.1": + version: 4.1.1 + resolution: "wdio-geckodriver-service@npm:4.1.1" + dependencies: + "@wdio/logger": ^8.6.6 + fs-extra: ^11.1.1 + get-port: ^6.1.2 + split2: ^4.2.0 + tcp-port-used: ^1.0.2 + peerDependencies: + "@wdio/types": ^7.0.0 || ^8.0.0 + geckodriver: ^3.2.0 + peerDependenciesMeta: + "@wdio/types": + optional: true + geckodriver: + optional: true + checksum: 7b62c27f46587f1e27a5ba0e8cd516cbec27c6e4de6d15a1fa51d70d229845b871492d635770877e20bc543f3ba35b91f99a0b61c7db2c959b6c7e8663f7b227 + languageName: node + linkType: hard + +"wdio-safaridriver-service@npm:^2.1.0": + version: 2.1.0 + resolution: "wdio-safaridriver-service@npm:2.1.0" + dependencies: + "@types/split2": ^3.2.1 + "@types/tcp-port-used": ^1.0.1 + "@wdio/logger": ^8.1.0 + fs-extra: ^11.1.0 + safaridriver: ^0.0.4 + split2: ^4.1.0 + tcp-port-used: ^1.0.2 + peerDependencies: + "@wdio/types": ^7.0.0 || ^8.0.0-alpha.219 + webdriverio: ^7.0.0 || ^8.0.0-alpha.219 + peerDependenciesMeta: + "@wdio/types": + optional: true + webdriverio: + optional: false + checksum: de54c72728ee5322d9d932c77e8167c08e72f47724e27e874c3dd915e82a3ff06d5b9a305f39899ad7319e2ecbd12c6421aae6ff1d119824db9ad516794402bd + languageName: node + linkType: hard + +"wdio-vscode-service@npm:^5.0.0": + version: 5.1.0 + resolution: "wdio-vscode-service@npm:5.1.0" + dependencies: + "@fastify/cors": ^8.2.1 + "@fastify/static": ^6.10.1 + "@types/ws": ^8.5.4 + "@vscode/test-electron": ^2.3.0 + "@wdio/logger": ^8.6.6 + clipboardy: ^3.0.0 + decamelize: 6.0.0 + download: ^8.0.0 + fastify: ^4.16.3 + get-port: 6.1.2 + slash: ^5.0.1 + tmp-promise: ^3.0.3 + undici: ^5.22.0 + vscode-uri: ^3.0.7 + wdio-chromedriver-service: ^8.1.1 + ws: ^8.13.0 + yargs-parser: ^21.1.1 + peerDependencies: + chromedriver: "*" + webdriverio: ^8.0.0 + peerDependenciesMeta: + chromedriver: + optional: false + webdriverio: + optional: true + checksum: 49414c2b186030d27a6393b937d9463dfc5075dc0556a9e3000a580457c9bd940b045c57a9ce68d62f1f7ab368fc710fa991fdf571a5dd7cca867871f7186359 + languageName: node + linkType: hard + "web-vitals@npm:0.2.4": version: 0.2.4 resolution: "web-vitals@npm:0.2.4" @@ -18707,6 +22492,81 @@ __metadata: languageName: node linkType: hard +"webdriver@npm:8.10.0": + version: 8.10.0 + resolution: "webdriver@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + "@types/ws": ^8.5.3 + "@wdio/config": 8.10.0 + "@wdio/logger": 8.6.6 + "@wdio/protocols": 8.8.1 + "@wdio/types": 8.10.0 + "@wdio/utils": 8.10.0 + deepmerge-ts: ^5.0.0 + got: ^12.1.0 + ky: ^0.33.0 + ws: ^8.8.0 + checksum: f81ccc35f20fc3f57f0a8c1ae4563a3b41d46400a95b78182013612e22786134d3698901275278e6f252246aab2f38ed5a5aba60fdfe8f0feb709cbefc3d094c + languageName: node + linkType: hard + +"webdriver@workspace:apps/webdriver": + version: 0.0.0-use.local + resolution: "webdriver@workspace:apps/webdriver" + dependencies: + "@tldraw/assets": "workspace:*" + "@tldraw/tldraw": "workspace:*" + "@types/ip": ^1.1.0 + "@types/node-forge": ^1.3.1 + browserslist-to-esbuild: ^1.2.0 + concurrently: ^7.4.0 + esbuild: ^0.16.7 + ip: ^1.1.8 + kleur: ^4.1.5 + lazyrepo: 0.0.0-alpha.26 + node-forge: ^1.3.1 + react: ^18.2.0 + react-dom: ^18.2.0 + react-router-dom: ^6.9.0 + signia: 0.1.4 + signia-react: 0.1.4 + languageName: unknown + linkType: soft + +"webdriverio@npm:8.10.0, webdriverio@npm:^8.8.8": + version: 8.10.0 + resolution: "webdriverio@npm:8.10.0" + dependencies: + "@types/node": ^18.0.0 + "@wdio/config": 8.10.0 + "@wdio/logger": 8.6.6 + "@wdio/protocols": 8.8.1 + "@wdio/repl": 8.6.6 + "@wdio/types": 8.10.0 + "@wdio/utils": 8.10.0 + archiver: ^5.0.0 + aria-query: ^5.0.0 + css-shorthand-properties: ^1.1.1 + css-value: ^0.0.1 + devtools: 8.10.0 + devtools-protocol: ^0.0.1138159 + grapheme-splitter: ^1.0.2 + import-meta-resolve: ^3.0.0 + is-plain-obj: ^4.1.0 + lodash.clonedeep: ^4.5.0 + lodash.zip: ^4.2.0 + minimatch: ^9.0.0 + puppeteer-core: 19.11.1 + query-selector-shadow-dom: ^1.0.0 + resq: ^1.9.1 + rgb2hex: 0.2.5 + serialize-error: ^8.0.0 + webdriver: 8.10.0 + checksum: e910e27535255a062fe210d6e406766f04100e29859038fd5050a6a914b5ada5db9f7642555943f0716f411e9e1cf76e7dfc69e61598b22c16651059717cc107 + languageName: node + linkType: hard + "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -18849,6 +22709,28 @@ __metadata: languageName: node linkType: hard +"which@npm:^1.2.9": + version: 1.3.1 + resolution: "which@npm:1.3.1" + dependencies: + isexe: ^2.0.0 + bin: + which: ./bin/which + checksum: f2e185c6242244b8426c9df1510e86629192d93c1a986a7d2a591f2c24869e7ffd03d6dac07ca863b2e4c06f59a4cc9916c585b72ee9fa1aa609d0124df15e04 + languageName: node + linkType: hard + +"which@npm:^3.0.0": + version: 3.0.1 + resolution: "which@npm:3.0.1" + dependencies: + isexe: ^2.0.0 + bin: + node-which: bin/which.js + checksum: adf720fe9d84be2d9190458194f814b5e9015ae4b88711b150f30d0f4d0b646544794b86f02c7ebeec1db2029bc3e83a7ff156f542d7521447e5496543e26890 + languageName: node + linkType: hard + "wide-align@npm:^1.1.2, wide-align@npm:^1.1.5": version: 1.1.5 resolution: "wide-align@npm:1.1.5" @@ -18882,6 +22764,24 @@ __metadata: languageName: node linkType: hard +"workerpool@npm:6.2.1": + version: 6.2.1 + resolution: "workerpool@npm:6.2.1" + checksum: c2c6eebbc5225f10f758d599a5c016fa04798bcc44e4c1dffb34050cd361d7be2e97891aa44419e7afe647b1f767b1dc0b85a5e046c409d890163f655028b09d + languageName: node + linkType: hard + +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + languageName: node + linkType: hard + "wrap-ansi@npm:^5.1.0": version: 5.1.0 resolution: "wrap-ansi@npm:5.1.0" @@ -18904,17 +22804,6 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" - dependencies: - ansi-styles: ^4.0.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b - languageName: node - linkType: hard - "wrap-ansi@npm:^8.1.0": version: 8.1.0 resolution: "wrap-ansi@npm:8.1.0" @@ -18943,6 +22832,21 @@ __metadata: languageName: node linkType: hard +"ws@npm:8.13.0, ws@npm:^8.10.0, ws@npm:^8.11.0, ws@npm:^8.13.0, ws@npm:^8.2.3, ws@npm:^8.8.0": + version: 8.13.0 + resolution: "ws@npm:8.13.0" + peerDependencies: + bufferutil: ^4.0.1 + utf-8-validate: ">=5.0.2" + peerDependenciesMeta: + bufferutil: + optional: true + utf-8-validate: + optional: true + checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c + languageName: node + linkType: hard + "ws@npm:^7.4.5": version: 7.5.9 resolution: "ws@npm:7.5.9" @@ -18958,21 +22862,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^8.10.0, ws@npm:^8.11.0, ws@npm:^8.2.3": - version: 8.13.0 - resolution: "ws@npm:8.13.0" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ">=5.0.2" - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 53e991bbf928faf5dc6efac9b8eb9ab6497c69feeb94f963d648b7a3530a720b19ec2e0ec037344257e05a4f35bd9ad04d9de6f289615ffb133282031b18c61c - languageName: node - linkType: hard - "xdm@npm:^2.0.0": version: 2.1.0 resolution: "xdm@npm:2.1.0" @@ -19065,6 +22954,13 @@ __metadata: languageName: node linkType: hard +"yallist@npm:^2.1.2": + version: 2.1.2 + resolution: "yallist@npm:2.1.2" + checksum: 9ba99409209f485b6fcb970330908a6d41fa1c933f75e08250316cce19383179a6b70a7e0721b89672ebb6199cc377bf3e432f55100da6a7d6e11902b0a642cb + languageName: node + linkType: hard + "yallist@npm:^3.0.2": version: 3.1.1 resolution: "yallist@npm:3.1.1" @@ -19151,6 +23047,21 @@ __metadata: languageName: node linkType: hard +"yargs@npm:17.7.1, yargs@npm:^17.3.1": + version: 17.7.1 + resolution: "yargs@npm:17.7.1" + dependencies: + cliui: ^8.0.1 + escalade: ^3.1.1 + get-caller-file: ^2.0.5 + require-directory: ^2.1.1 + string-width: ^4.2.3 + y18n: ^5.0.5 + yargs-parser: ^21.1.1 + checksum: 3d8a43c336a4942bc68080768664aca85c7bd406f018bad362fd255c41c8f4e650277f42fd65d543fce99e084124ddafee7bbfc1a5c6a8fda4cec78609dcf8d4 + languageName: node + linkType: hard + "yargs@npm:^13.3.0": version: 13.3.2 resolution: "yargs@npm:13.3.2" @@ -19169,9 +23080,9 @@ __metadata: languageName: node linkType: hard -"yargs@npm:^17.3.1": - version: 17.7.1 - resolution: "yargs@npm:17.7.1" +"yargs@npm:^17.5.1": + version: 17.7.2 + resolution: "yargs@npm:17.7.2" dependencies: cliui: ^8.0.1 escalade: ^3.1.1 @@ -19180,11 +23091,25 @@ __metadata: string-width: ^4.2.3 y18n: ^5.0.5 yargs-parser: ^21.1.1 - checksum: 3d8a43c336a4942bc68080768664aca85c7bd406f018bad362fd255c41c8f4e650277f42fd65d543fce99e084124ddafee7bbfc1a5c6a8fda4cec78609dcf8d4 + checksum: 73b572e863aa4a8cbef323dd911d79d193b772defd5a51aab0aca2d446655216f5002c42c5306033968193bdbf892a7a4c110b0d77954a7fdf563e653967b56a languageName: node linkType: hard -"yauzl@npm:^2.3.1": +"yarn-install@npm:^1.0.0": + version: 1.0.0 + resolution: "yarn-install@npm:1.0.0" + dependencies: + cac: ^3.0.3 + chalk: ^1.1.3 + cross-spawn: ^4.0.2 + bin: + yarn-install: bin/yarn-install.js + yarn-remove: bin/yarn-remove.js + checksum: b9301c1db6d9025aabad1d995413a75559e3156b54e86d3cbf03313a163a793ca6f2419516ad1b2ae8677dbe39a97adb451b10123fe32bc2e2c39d3900e2b216 + languageName: node + linkType: hard + +"yauzl@npm:^2.10.0, yauzl@npm:^2.3.1, yauzl@npm:^2.4.2": version: 2.10.0 resolution: "yauzl@npm:2.10.0" dependencies: @@ -19217,6 +23142,13 @@ __metadata: languageName: node linkType: hard +"yocto-queue@npm:^1.0.0": + version: 1.0.0 + resolution: "yocto-queue@npm:1.0.0" + checksum: 2cac84540f65c64ccc1683c267edce396b26b1e931aa429660aefac8fbe0188167b7aee815a3c22fa59a28a58d898d1a2b1825048f834d8d629f4c2a5d443801 + languageName: node + linkType: hard + "z-schema@npm:~5.0.2": version: 5.0.5 resolution: "z-schema@npm:5.0.5" @@ -19234,6 +23166,17 @@ __metadata: languageName: node linkType: hard +"zip-stream@npm:^4.1.0": + version: 4.1.0 + resolution: "zip-stream@npm:4.1.0" + dependencies: + archiver-utils: ^2.1.0 + compress-commons: ^4.1.0 + readable-stream: ^3.6.0 + checksum: 4a73da856738b0634700b52f4ab3fe0bf0a532bea6820ad962d0bda0163d2d5525df4859f89a7238e204a378384e12551985049790c1894c3ac191866e85887f + languageName: node + linkType: hard + "zod-validation-error@npm:^1.3.0": version: 1.3.0 resolution: "zod-validation-error@npm:1.3.0" diff --git a/scripts/e2e-run-ci b/scripts/e2e-run-ci new file mode 100755 index 000000000..e8fa80283 --- /dev/null +++ b/scripts/e2e-run-ci @@ -0,0 +1,14 @@ +set -eux +export ENABLE_SSL=1 +export ENABLE_NETWORK_CACHING=1 +yarn workspace @tldraw/tldraw prebuild +SHELL=/bin/bash nohup yarn dev-webdriver & +PROCCESS_PID="$!" + +mode="${1:-local}" +sleep 5 +yarn workspace @tldraw/e2e "test:${mode}" +exit_code=$? + +kill $PROCCESS_PID +exit $exit_code \ No newline at end of file diff --git a/scripts/e2e-run-tests b/scripts/e2e-run-tests new file mode 100755 index 000000000..d94285d00 --- /dev/null +++ b/scripts/e2e-run-tests @@ -0,0 +1,9 @@ +#!/usr/bin/env bash +set -eux + +mode="${1:-local}" +export BROWSERS="${2:-chrome}" +# if [[ "$mode" == "remote" ]]; then +# export ENABLE_SSL=1 +# fi +yarn workspace @tldraw/e2e "test:${mode}" diff --git a/scripts/e2e-start-server b/scripts/e2e-start-server new file mode 100755 index 000000000..618cadcd7 --- /dev/null +++ b/scripts/e2e-start-server @@ -0,0 +1,3 @@ +#!/usr/bin/env bash +set -eux +yarn dev-webdriver