commit 34511496917da69ca7066e891cf6a74fbb1e124d Author: Bryce Thorup Date: Mon Sep 8 20:55:29 2025 -0600 init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6515f99 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode +node_modules +dist diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..6c13085 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Petr Vavrin + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..b57d62f --- /dev/null +++ b/README.md @@ -0,0 +1,69 @@ +## WebSerial TERMINAL + +is a terminal emulator that allows you to connect to a serial device from your web browser. + +Based on the WebSerial API and xterm.js. + +### Online version +[terminal.vavrin.eu](https://terminal.vavrin.eu/) + +### Features + +- Connect to a serial device from your web browser + + - Predefined baud rates - 9600, 19200, 38400, 57600, 115200, 230400, 460800, 921600 + - Default baud rate is 115200 + - The last used baud rate is saved in the local storage + +- Adjustable font size + + - From 10 to 40 + - Default is 15 + - The last used font size is saved in the local storage + - Font size can be changed using these methods: + - `Ctrl + Mouse Wheel` inside terminal to decrease or increase font size + - `Mouse Wheen` inside font size input to decrease or increase font size + - Clicking on minus or plus button to decrease or increase font size + +- Send keystrokes to the connected device and receive responses + +- Button `Ctrl+C` to send the break signal + +- Button `Ctrl+D` to send the end of transmission signal + +- Button `Scroll to bottom` to scroll to the bottom of the terminal + +- Button `Show Buffer` to show the buffer of the terminal + + - The buffer is limited to 512 bytes + - The buffer is circular and the oldest data is overwritten by the newest data + - The buffer shows hexadecimal and ASCII representation of the data + - Values with yellow text color or background are the control characters + - Values with black background are read bytes + - Values with colored background and black text are written bytes + - When mouse hovers over the buffer cell, the hexadecimal and ASCII representation is highlighted and value is shown in the top bar + - When mouse double clicks on the buffer cell, then value is freezed + - When mouse clicks on the buffer cell, then value is unfreezed + +- Button `Reset stats` to reset the statistics about read and written bytes + +### Build + +- Clone the repository +- Run `yarn install` or `npm install` +- Run `yarn build` or `npm run build` +- In `dist` folder, you can find these folders: + - `default` - separated local and remote assets + - `single-file` - contains all local and remote assets in one file + - `single-file-with-remote-assets` - contains all local in one file, remote assets are loaded from the web + +### Run + +- You can run the terminal by opening the `index.html` file in your browser from the `dist` folders. +- Or you can run it from a HTTPS server/webhosting. +- You can copy `index.html` from `dist/single-file` folder to your microcontroller and run the terminal from there even without internet connection. + +### Resources + +- [WebSerial API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Serial_API) +- [xterm.js](https://xtermjs.org/) diff --git a/build.js b/build.js new file mode 100644 index 0000000..24def75 --- /dev/null +++ b/build.js @@ -0,0 +1,152 @@ +const fs = require('fs'); +const path = require('path'); +const axios = require('axios'); +const { minify } = require('html-minifier-terser'); + +async function inlineAsset(html, params) { + let asset; + const filePath = params.path.startsWith('http') ? params.path : path.join(params.sourceFolder, params.path); + + if (fs.existsSync(filePath)) { + if (!params.inlineLocalAssets) { + if (filePath.includes('assets')) { + const assetPath = path.join(filePath.split('assets')[1]); + const targetPath = path.join(params.targetFolder, assetPath); + const targetFolder = path.dirname(targetPath); + const htmlAssetPath = `.${assetPath.replace(/\\/g, '/')}`; + + if (!fs.existsSync(targetFolder)) { + fs.mkdirSync(targetFolder, { recursive: true }); + } + + fs.copyFileSync(filePath, targetPath); + + if (params.originalTag.toLowerCase() === 'link') { + html = html.replace(params.regex, ``); + } else if (params.originalTag.toLowerCase() === 'script') { + html = html.replace(params.regex, ``); + } + } + + return html; + } + asset = fs.readFileSync(filePath, 'utf8'); + } else { + if (!params.inlineRemoteAssets) { + return html; + } + asset = (await axios.get(filePath)).data; + } + + return html.replace(params.regex, `<${params.tag}>\n${asset}\n`); +} + +function cleanTargetPath(targetDir) { + if (!fs.existsSync(targetDir)) { + return; + } + + fs.readdirSync(targetDir).forEach((file) => { + const entryPath = path.join(targetDir, file); + const stat = fs.lstatSync(entryPath); + if (stat.isFile()) { + fs.unlinkSync(entryPath); + } else if (stat.isDirectory()) { + cleanTargetPath(entryPath); + } + }); + fs.rmdirSync(targetDir); +} + +async function inlineAssets(params) { + const sourceFolder = params.sourceFolder; + const targetFolder = params.targetFolder; + + console.log(`Inlining assets from ${sourceFolder} to ${targetFolder}`); + + const inputFile = path.join(sourceFolder, params.inputFile); + const outputFile = path.join(targetFolder, 'index.html'); + + let html = fs.readFileSync(inputFile, 'utf8'); + + const inlineLocalAssets = params.inlineLocalAssets || false; + const inlineRemoteAssets = params.inlineRemoteAssets || false; + + cleanTargetPath(targetFolder); + + if (!fs.existsSync(targetFolder)) { + fs.mkdirSync(targetFolder, { recursive: true }); + } + + // Match and inline CSS links + const cssLinks = [...html.matchAll(/]*?\s+href=["'](.*?)["'][^>]*?>/gs)]; + for (const match of cssLinks) { + html = await inlineAsset(html, { + path: match[1], + regex: match[0], + originalTag: 'link', + tag: 'style', + sourceFolder, + targetFolder, + inlineLocalAssets, + inlineRemoteAssets, + }); + } + + // Match and inline JavaScript src + const scriptLinks = [...html.matchAll(/]*?\s+src=["'](.*?)["'][^>]*?>\s*<\/script>/gs)]; + for (const match of scriptLinks) { + html = await inlineAsset(html, { + path: match[1], + regex: match[0], + originalTag: 'script', + tag: 'script', + sourceFolder, + targetFolder, + inlineLocalAssets, + inlineRemoteAssets, + }); + } + + // Minify the HTML + const minifiedHtml = await minify(html, { + collapseWhitespace: true, + removeComments: true, + removeRedundantAttributes: true, + removeEmptyAttributes: true, + minifyCSS: true, + minifyJS: true, + }); + + // Save the minified HTML file + fs.writeFileSync(outputFile, minifiedHtml, 'utf8'); + console.log(`Saved minified HTML to ${outputFile}`); +} + +const defaultBuild = { + sourceFolder: './src', + inputFile: './index.html', + targetFolder: './dist/default', + inlineLocalAssets: false, + inlineRemoteAssets: false, +}; + +const singleFileWithRemoteAssets = { + sourceFolder: './src', + inputFile: './index.html', + targetFolder: './dist/single-file-with-remote-assets', + inlineLocalAssets: true, + inlineRemoteAssets: false, +}; + +const singleFile = { + sourceFolder: './src', + inputFile: './index.html', + targetFolder: './dist/single-file', + inlineLocalAssets: true, + inlineRemoteAssets: true, +}; + +inlineAssets(defaultBuild); +inlineAssets(singleFileWithRemoteAssets); +inlineAssets(singleFile); diff --git a/eslint.config.js b/eslint.config.js new file mode 100644 index 0000000..c557807 --- /dev/null +++ b/eslint.config.js @@ -0,0 +1 @@ +module.exports = [{}]; diff --git a/package.json b/package.json new file mode 100644 index 0000000..74361e0 --- /dev/null +++ b/package.json @@ -0,0 +1,14 @@ +{ + "name": "webserial_term", + "version": "1.0.0", + "main": "index.js", + "license": "MIT", + "dependencies": { + "axios": "^1.11.0", + "eslint": "^9.24.0", + "html-minifier-terser": "^7.2.0" + }, + "scripts": { + "build": "node build.js" + } +} diff --git a/src/assets/css/styles.css b/src/assets/css/styles.css new file mode 100644 index 0000000..14804e7 --- /dev/null +++ b/src/assets/css/styles.css @@ -0,0 +1,316 @@ +html { + overflow: hidden; +} +* { + font-family: 'Ubuntu Mono', Menlo, Consolas, 'DejaVu Sans Mono', 'Courier New', Courier, monospace; + outline: none; +} +body { + background-color: #000000; + color: #ffffff; + margin: 0; + padding: 0; +} +#app { + visibility: hidden; + display: flex; + flex-direction: column; + height: 100vh; + margin: 0; + overflow: hidden; +} +a { + color: #d9cb0a; + text-decoration: none; +} +#top-bar, +#bottom-bar { + display: flex; + justify-content: flex-start; + align-items: center; + flex-direction: row; + padding: 5px 10px; + background-color: #272727; + height: 25px !important; + white-space: nowrap; + color: #aaaaaa; +} +#top-bar { + user-select: none; + border-bottom: 1px solid #1a1a1a; +} +#bottom-bar { + border-top: 1px solid #1a1a1a; +} +.corner::before { + content: ''; + position: absolute; + top: 0; + left: 0; + border-bottom: 7px solid transparent; + border-left: 7px solid #272727; + width: 0; +} +.toolbar-button, +.toolbar-input, +.toolbar-select { + align-items: center; + background-color: #181818; + border-bottom: 1px solid #353535; + border-right: 1px solid #353535; + border: 0px solid #333333; + color: #aaaaaa; + display: flex; + height: 100%; + justify-content: center; + line-height: 100%; + padding: 0 10px; + position: relative; + text-align: center; +} +.toolbar-button { + font-size: 14px; + cursor: pointer; + user-select: none; +} +.toolbar-button:hover, +.toolbar-button.active { + color: #000000; + background-color: #d9cb0a; +} +.toolbar-space { + margin-right: 15px; +} +.toolbar-spacer { + flex-grow: 1; +} +.toolbar-input-label { + font-size: 14px; + color: #aaaaaa; + margin-right: 10px; +} +.toolbar-input { + width: 25px !important; + font-size: 15px; +} +.toolbar-input::-webkit-outer-spin-button, +.toolbar-input::-webkit-inner-spin-button { + -webkit-appearance: none; + margin: 0; +} +.toolbar-select { + font-size: 15px; +} +.toolbar-icons { + width: 60px; +} +.toolbar-svg-icon { + width: 30px !important; + height: 30px !important; + margin-top: 2px; +} +#main { + flex-grow: 1; + margin: 10px; + display: flex; + flex-direction: column; + height: 100vh; + margin: 0; + overflow: hidden; +} +#terminal-container { + flex-grow: 1; + margin: 10px; + overflow: hidden; +} +.terminal-background { + position: absolute; + width: 100%; + height: calc(100% - 100px); + pointer-events: none; + font-family: 'Ubuntu Mono', Menlo, Consolas, 'DejaVu Sans Mono', 'Courier New', Courier, monospace; +} +.xterm .xterm-viewport { + overflow-y: scroll; + cursor: default; + position: absolute; + right: 0; + left: 0; + top: 0; + bottom: 0; + scrollbar-color: #333333 #111111; + scrollbar-width: thin; + background-color: transparent !important; +} +.xterm-viewport::-webkit-scrollbar { + background-color: #333; + width: 8px; +} +.xterm-viewport::-webkit-scrollbar-thumb { + background: #00ccff; +} +#button-connect { + width: 150px; +} +#transfer-stats { + overflow: hidden; + margin-left: 10px; + white-space: nowrap; + text-overflow: ellipsis; +} +#data-table-info { + display: none; + position: absolute; + top: 45px; + right: 30px; + height: 40px; + flex-direction: row; + justify-content: start; + overflow: hidden; + display: inline-block; + border: 1px solid #202020; + background-color: #0a0a0a; +} +.data-table-info-title { + font-size: 14px; + color: #aaaaaa; + margin-right: 10px; +} +.data-table-info-cell { + margin-left: 20px; +} +.data-table-info-label { + height: 14px; + font-size: 10px; + text-align: center; + opacity: 0.5; +} +.data-table-info-value { + text-align: center; + font-size: 20px; +} +.data-table-info-value svg { + display: block; + margin: auto; +} +#data-table-info-dec, +#data-table-info-hex, +#data-table-info-ascii { + width: 45px; +} +#data-table-info-detail { + width: 285px; +} +#data-table-info-bin { + width: 80px; +} +#data-tables { + position: absolute; + display: none; + top: 90px; + right: 30px; + flex-direction: row; + justify-content: start; + overflow-y: auto; + overflow-x: hidden; + scrollbar-color: #333333 #111111; + scrollbar-width: thin; + pointer-events: none; +} +#data-table-hex, +#data-table-ascii { + display: grid; + grid-template-columns: repeat(16, 1fr); + font-size: 14px; + border-collapse: collapse; + border: 1px solid #202020; + gap: 0; + box-sizing: border-box; + height: max-content; + pointer-events: auto; +} +#data-table-ascii { + margin-left: 10px; +} +#data-table-hex div, +#data-table-ascii div { + display: table-cell; + white-space: pre; + overflow: hidden; + text-align: center; + cursor: default; + height: 18px !important; + line-height: 18px !important; + background-color: #0a0a0a; +} +#data-table-hex div { + width: 22px !important; + border: 1px solid #000000; +} +#data-table-ascii div { + width: 18px !important; + border: 1px solid #000000; +} +.cell, +.cell-in { + font-weight: 400; + background-color: #0a0a0a; +} +.cell { + color: #313131; +} +.cell-in { + color: #ababab; +} +.cell-in-escape { + color: #d9cb0a; +} +#data-table-ascii .cell-in-escape, +#data-table-ascii .cell-out-escape, +#data-table-ascii .cell-highlight-escape { + font-size: 10px; +} +.cell-out, +.cell-out-escape { + font-weight: 700; + color: #0a0a0a; +} +.cell-out { + background-color: #898989 !important; +} +.cell-out-escape { + background-color: #b7ab00 !important; +} +.cell-highlight, +.cell-highlight-escape { + font-weight: 700; + color: #c8c8c8; + background-color: #05198b !important; +} +#about { + display: none; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + width: 460px; + padding: 20px; + background-color: #0a0a0a; + border: 1px solid #202020; + box-shadow: 0 0 10px #000000; + z-index: 100; +} +#about pre { + position: relative; + text-align: left; + display: inline-block; + white-space: pre-wrap; + word-wrap: break-word; + padding: 0px; + margin: 0px; +} +@media (max-width: 880px) { + .toolbar-input-label { + display: none; + } +} diff --git a/src/assets/js/config.js b/src/assets/js/config.js new file mode 100644 index 0000000..a07f2a6 --- /dev/null +++ b/src/assets/js/config.js @@ -0,0 +1,53 @@ +class Config { + constructor(defaults) { + this.config = {}; + this.defaults = defaults; + this.init(); + } + + init() { + if (window.localStorage) { + const storedConfig = localStorage.getItem('config'); + if (storedConfig) { + try { + const storedConfigJSON = JSON.parse(storedConfig); + for (let key of Object.keys(storedConfigJSON)) { + if (storedConfigJSON[key] === null) { + continue; + } + this.config[key] = storedConfigJSON[key]; + } + } catch (error) { + console.error('Error parsing stored config'); + } + } + } + + for (const key in this.defaults) { + if (!(key in this.config)) { + this.config[key] = this.defaults[key]; + } + } + } + + saveConfig() { + if (window.localStorage) { + localStorage.setItem('config', JSON.stringify(this.config)); + } + } + + get(key) { + if (key in this.config) { + return this.config[key]; + } + if (key in this.defaults) { + return this.defaults[key]; + } + return null; + } + + set(key, value) { + this.config[key] = value; + this.saveConfig(); + } +} diff --git a/src/assets/js/io-buffer.js b/src/assets/js/io-buffer.js new file mode 100644 index 0000000..22bff6e --- /dev/null +++ b/src/assets/js/io-buffer.js @@ -0,0 +1,228 @@ +class IOBuffer { + constructor(options) { + this.dataTables = options.dataTables; + this.dataTableHex = options.dataTableHex; + this.dataTableInfoLock = options.dataTableInfoLock; + this.dataTableInfoDirection = options.dataTableInfoDirection; + this.dataTableAscii = options.dataTableAscii; + this.dataTableInfoHex = options.dataTableInfoHex; + this.dataTableInfoDec = options.dataTableInfoDec; + this.dataTableInfoBin = options.dataTableInfoBin; + this.dataTableInfoAscii = options.dataTableInfoAscii; + this.dataTableInfoDetail = options.dataTableInfoDetail; + this.freezedCellInfo = false; + + this.size = options.bufferSize; + this.rowLength = options.rowLength; + + this.cellsSize = this.size + this.rowLength; + this.buffer = new Array(this.size); + this.cells = new Array(this.cellsSize); + this.head = 0; + this.tail = 0; + this.rowOffset = 0; + this.releaseNodes = []; + this.classes = { + in: 'cell-in', + out: 'cell-out', + init: 'cell', + }; + + this.escapeChars = [ + { value: 'NUL', label: 'NULL character' }, + { value: 'SOH', label: 'Start of Heading' }, + { value: 'STX', label: 'Start of Text' }, + { value: 'ETX', label: 'End of Text' }, + { value: 'EOT', label: 'End of Transmission' }, + { value: 'ENQ', label: 'Enquiry' }, + { value: 'ACK', label: 'Acknowledge' }, + { value: 'BEL', label: 'Bell' }, + { value: 'BS', label: 'Backspace' }, + { value: 'HT', label: 'Horizontal Tab' }, + { value: 'LF', label: 'Line feed' }, + { value: 'VT', label: 'Vertical Tab' }, + { value: 'FF', label: 'Form Feed' }, + { value: 'CR', label: 'Carriage return' }, + { value: 'SO', label: 'Shift Out' }, + { value: 'SI', label: 'Shift In' }, + { value: 'DLE', label: 'Data Link Escape' }, + { value: 'DC1', label: 'Device Control (XOn)' }, + { value: 'DC2', label: 'Device Control' }, + { value: 'DC3', label: 'Device Control (XOff)' }, + { value: 'DC4', label: 'Device Control' }, + { value: 'NAK', label: 'Negative Acknowledge' }, + { value: 'SYN', label: 'Synchronous Idle' }, + { value: 'ETB', label: 'End of Transmission Block' }, + { value: 'CAN', label: 'Cancel' }, + { value: 'EM', label: 'End of Medium' }, + { value: 'SUB', label: 'Substitute' }, + { value: 'ESC', label: 'Escape' }, + { value: 'FS', label: 'File Separator' }, + { value: 'GS', label: 'Group Separator' }, + { value: 'RS', label: 'Record Separator' }, + { value: 'US', label: 'Unit Separator' }, + ]; + this.prepareTableCells(); + } + + prepareTableCells() { + for (let index = 0; index < this.cellsSize; index++) { + const hexCell = document.createElement('div'); + const asciiCell = document.createElement('div'); + this.dataTableHex.appendChild(hexCell); + this.dataTableAscii.appendChild(asciiCell); + this.cells[index] = { + nodeHex: hexCell, + nodeAscii: asciiCell, + }; + } + this.reset(); + } + + reset() { + this.head = 0; + this.tail = 0; + this.rowOffset = 0; + this.cells.forEach((cell, index) => { + cell.dir = 'init'; + cell.nodeHex.dataset.index = index; + cell.nodeHex.className = 'cell'; + cell.nodeHex.textContent = '  '; + cell.nodeAscii.dataset.index = index; + cell.nodeAscii.className = 'cell'; + cell.nodeAscii.textContent = ' '; + cell.value = null; + }); + } + + push(data, dir) { + const now = Date.now(); + for (let i = 0; i < data.length; i++) { + this.buffer[this.tail] = { + data: data[i], + ts: now, + dir, + }; + this.tail = (this.tail + 1) % this.size; + if (this.tail === this.head) { + this.head = (this.head + 1) % this.size; + } + } + this.rowOffset = (this.rowOffset + data.length) % this.rowLength; + } + + getCellClassName(cell) { + let className = cell.highlight ? 'cell-highlight' : this.classes[cell.dir]; + if (cell.value !== null && cell.value < 32) { + className = `${className}-escape`; + } + return className; + } + + updateCell(index, dir, value) { + const cell = this.cells[index]; + + if (cell.value !== value) { + cell.nodeHex.textContent = value === null ? '  ' : value.toString(16).toUpperCase().padStart(2, '0'); + + if (value === null) { + cell.nodeAscii.textContent = ' '; + } else if (value < 32) { + cell.nodeAscii.innerHTML = this.escapeChars[value].value; + } else { + cell.nodeAscii.textContent = String.fromCharCode(value); + } + cell.value = value; + } + + if (cell.dir !== dir) { + cell.dir = dir; + } + + const className = this.getCellClassName(cell); + if (cell.nodeHex.className !== className) { + cell.nodeHex.className = className; + cell.nodeAscii.className = className; + } + } + + showInfo(value, dir) { + this.dataTableInfoDirection.textContent = dir === 'init' ? '' : dir.toUpperCase(); + this.dataTableInfoHex.textContent = value.toString(16).toUpperCase().padStart(2, '0'); + this.dataTableInfoDec.textContent = value.toString(10); + this.dataTableInfoBin.textContent = value.toString(2).padStart(8, '0'); + this.dataTableInfoAscii.textContent = value < 32 ? this.escapeChars[value].value : String.fromCharCode(value); + this.dataTableInfoDetail.textContent = value < 32 ? this.escapeChars[value].label : ''; + this.dataTableInfoLock.style.display = this.freezedCellInfo ? 'block' : 'none'; + } + + cleanInfo() { + this.dataTableInfoDirection.textContent = ''; + this.dataTableInfoHex.textContent = ''; + this.dataTableInfoDec.textContent = ''; + this.dataTableInfoBin.textContent = ''; + this.dataTableInfoAscii.textContent = ''; + this.dataTableInfoDetail.textContent = ''; + this.dataTableInfoLock.style.display = 'none'; + } + + highlightCell(index, state) { + const cell = this.cells[index]; + cell.highlight = state; + const className = this.getCellClassName(cell); + cell.nodeHex.className = className; + cell.nodeAscii.className = className; + + if (this.freezedCellInfo) { + return; + } + + if (state && cell.value !== null) { + this.showInfo(cell.value, cell.dir); + } else { + this.cleanInfo(); + } + } + + freezeCellInfo(index) { + const cell = this.cells[index]; + this.freezedCellInfo = true; + this.showInfo(cell.value, cell.dir); + this.dataTableInfoLock.style.display = 'block'; + } + + unfreezeCellInfo() { + this.freezedCellInfo = false; + this.cleanInfo(); + this.dataTableInfoLock.style.display = 'none'; + } + + redraw() { + let index = this.head; + let cellIndex = 0; + if (this.head !== 0) { + for (let i = 0; i < this.rowOffset; i++) { + this.updateCell(cellIndex, 'init', null); + cellIndex++; + } + } + + while (index !== this.tail) { + const item = this.buffer[index]; + this.updateCell(cellIndex, item.dir, item.data); + index = (index + 1) % this.size; + cellIndex++; + } + + const cellsLeft = this.rowLength - (cellIndex % this.rowLength); + for (let i = 0; i < cellsLeft; i++) { + this.updateCell(cellIndex, 'init', null); + cellIndex++; + } + } + + resize(newHeight) { + this.dataTables.style.height = `${newHeight - 60}px`; + this.dataTables.scrollTop = this.dataTables.scrollHeight; + } +} diff --git a/src/assets/js/main.js b/src/assets/js/main.js new file mode 100644 index 0000000..effd247 --- /dev/null +++ b/src/assets/js/main.js @@ -0,0 +1,453 @@ +document.addEventListener('DOMContentLoaded', () => { + const [ + app, + terminalContainer, + connectButton, + selectSerialPortBaudrate, + buttonCtrlC, + buttonCtrlD, + buttonScrollToBottom, + buttonShowAbout, + buttonShowBuffer, + buttonResetStats, + inputFontSize, + buttonFontSizeMinus, + buttonFontSizePlus, + svgDevice, + svgToDevice, + svgFromDevice, + transferStats, + dataTableInfo, + dataTables, + dataTableHex, + dataTableAscii, + dataTableInfoLock, + dataTableInfoDirection, + dataTableInfoHex, + dataTableInfoDec, + dataTableInfoBin, + dataTableInfoAscii, + dataTableInfoDetail, + terminalBackgroundText, + ] = [ + 'app', + 'terminal-container', + 'button-connect', + 'select-serial-port-baudrate', + 'button-ctrl-c', + 'button-ctrl-d', + 'button-scroll-to-bottom', + 'button-show-about', + 'button-show-buffer', + 'button-reset-stats', + 'input-font-size', + 'button-font-size-minus', + 'button-font-size-plus', + 'svg-device', + 'svg-to-device', + 'svg-from-device', + 'transfer-stats', + 'data-table-info', + 'data-tables', + 'data-table-hex', + 'data-table-ascii', + 'data-table-info-lock', + 'data-table-info-direction', + 'data-table-info-hex', + 'data-table-info-dec', + 'data-table-info-bin', + 'data-table-info-ascii', + 'data-table-info-detail', + 'terminal-background-text', + ].map((id) => document.getElementById(id)); + + // small animation on terminal background just for fun + const backgroundDot = ['•', '•', '•']; + const backgroundText = ['T', 'E', 'R', 'M', 'I', 'N', 'A', 'L']; + + function animationPhase(text, stepDelay, endDelay, callback) { + terminalBackgroundText.textContent = ''; + const interval = setInterval(() => { + terminalBackgroundText.textContent = terminalBackgroundText.textContent + text.shift(); + if (text.length) return; + clearInterval(interval); + if (endDelay && callback) { + setTimeout(callback, endDelay); + } + }, stepDelay); + } + + animationPhase(backgroundDot, 200, 400, () => { + animationPhase(backgroundText, 70); + }); + + let port; + let reader; + let portReadedChars = 0; + let portWritedChars = 0; + let portLastReadedChars = 0; + let portLastWritedChars = 0; + let showAbout = false; + let showDataBuffer = false; + let lastHighlightIndex = null; + + const ioBufferOptions = { + dataTables, + dataTableHex, + dataTableAscii, + dataTableInfoLock, + dataTableInfoDirection, + dataTableInfoHex, + dataTableInfoDec, + dataTableInfoBin, + dataTableInfoAscii, + dataTableInfoDetail, + bufferSize: 512, + rowLength: 16, + }; + + const defaultConfig = { + fontSize: 12, + baudRate: 115200, + }; + + const ioBuffer = new IOBuffer(ioBufferOptions); + const config = new Config(defaultConfig); + + inputFontSize.value = parseInt(config.get('fontSize')); + selectSerialPortBaudrate.value = parseInt(config.get('baudRate')); + + const { Terminal } = window; + const { FitAddon } = window.FitAddon; + const terminal = new Terminal({ + scrollback: 1000, + cursorBlink: true, + fontFamily: '"Ubuntu Mono", Menlo, Consolas, "DejaVu Sans Mono", "Courier New", Courier, monospace', + theme: { + background: 'transparent', + allowTransparency: true, + // selectionBackground: '#bbbbbb', + // selectionForeground: '#000000', + }, + }); + const fitAddon = new FitAddon(); + terminal.loadAddon(fitAddon); + + terminal.onTitleChange((title) => { + document.title = title; + }); + + terminal.attachCustomKeyEventHandler((event) => { + if (event.ctrlKey && (event.type === 'keydown' || event.type === 'keyup')) { + let button = null; + if (event.code === 'KeyC') { + button = buttonCtrlC; + } else if (event.code === 'KeyD') { + button = buttonCtrlD; + } else { + return; + } + + if (event.type === 'keydown') { + button.classList.add('active'); + } else if (event.type === 'keyup') { + button.classList.remove('active'); + } + } + }); + + terminal.open(terminalContainer); + fitAddon.fit(); + + terminal.onResize(function () { + ioBuffer.resize(terminalContainer.clientHeight); + }); + + terminal.onData(async (data) => { + if (port && port.writable) { + const writer = port.writable.getWriter(); + const chars = new TextEncoder().encode(data); + portWritedChars += chars.length; + ioBuffer.push(chars, 'out'); + await writer.write(chars); + writer.releaseLock(); + } + }); + + if (navigator && 'serial' in navigator) { + terminal.writeln('WebSerial Terminal - Click on "Connect to Serial" to start.'); + } else { + terminal.writeln('Terminal error: WebSerial not supported'); + terminal.writeln('Please use a browser that supports it.'); + terminal.writeln( + 'Supported browsers: Chrome 89+, Edge 89+, Opera 75+ and other browsers based on Chromium 89+.' + ); + } + + function updateToolbar() { + if (!navigator || !('serial' in navigator)) { + connectButton.setAttribute('disabled', 'disabled'); + } + const connected = !!port; + + connectButton.textContent = connected ? 'Disconnect' : 'Connect to Serial'; + buttonCtrlC.style.display = connected ? 'flex' : 'none'; + buttonCtrlD.style.display = connected ? 'flex' : 'none'; + buttonScrollToBottom.style.display = connected ? 'flex' : 'none'; + buttonShowBuffer.textContent = showDataBuffer ? 'Hide Buffer' : 'Show Buffer'; + + if (connected) { + selectSerialPortBaudrate.setAttribute('disabled', 'disabled'); + } else { + selectSerialPortBaudrate.removeAttribute('disabled'); + } + } + + async function sendToPort(data) { + if (port && port.writable) { + const writer = port.writable.getWriter(); + const chars = new TextEncoder().encode(data); + portWritedChars += chars.length; + ioBuffer.push(chars, 'out'); + await writer.write(chars); + writer.releaseLock(); + } + } + + function chageFontSize(delta) { + let fontSize = parseInt(inputFontSize.value, 10); + if (isNaN(fontSize)) { + fontSize = config.get('fontSize'); + } + if (delta !== undefined) { + fontSize = delta < 0 ? fontSize + 1 : fontSize - 1; + } + fontSize = Math.max(10, Math.min(fontSize, 40)); + inputFontSize.value = fontSize; + terminal.options.fontSize = fontSize; + config.set('fontSize', fontSize); + fitAddon.fit(); + } + + function changePortBaudrate() { + const baudRate = parseInt(selectSerialPortBaudrate.value, 10); + if (baudRate) { + config.set('baudRate', baudRate); + } + } + + function highlightCell(event, state) { + const target = event.target; + if (lastHighlightIndex !== null) { + ioBuffer.highlightCell(lastHighlightIndex, false); + lastHighlightIndex = null; + } + if (target.tagName === 'DIV' && 'index' in target.dataset) { + const index = parseInt(target.dataset.index, 10); + ioBuffer.highlightCell(index, state); + lastHighlightIndex = state ? index : null; + } + } + + function freeceCell(event) { + const target = event.target; + if (target.tagName === 'DIV' && 'index' in target.dataset) { + const index = parseInt(target.dataset.index, 10); + ioBuffer.freezeCellInfo(index); + } + } + + function unfreeceCell(event) { + ioBuffer.unfreezeCellInfo(); + highlightCell(event, true); + } + + async function sendCtrlC() { + await sendToPort('\x03'); + terminal.focus(); + } + + async function sendCtrlD() { + await sendToPort('\x04'); + terminal.focus(); + } + + async function scrollToBottom() { + terminal.scrollToBottom(); + terminal.focus(); + } + + async function resetStats() { + portReadedChars = 0; + portWritedChars = 0; + portLastReadedChars = 0; + portLastWritedChars = 0; + } + + function updateDataBufferVisibility() { + dataTables.style.display = showDataBuffer ? 'flex' : 'none'; + dataTableInfo.style.display = showDataBuffer ? 'flex' : 'none'; + if (showDataBuffer) { + ioBuffer.redraw(); + } + updateToolbar(); + } + + function toggleAboutVisibility() { + showAbout = !showAbout; + about.style.display = showAbout ? 'block' : 'none'; + } + + function toggleDataBufferVisibility() { + showDataBuffer = !showDataBuffer; + updateDataBufferVisibility(); + } + + function onWindowClick(event) { + if (showAbout && !about.contains(event.target) && !buttonShowAbout.contains(event.target)) { + toggleAboutVisibility(); + } + } + + function confirmExit() { + return "You have attempted to leave this page. Are you sure?"; + } + + async function readFromPort() { + const decoder = new TextDecoder('utf-8'); + try { + reader = port.readable.getReader(); + while (true) { + const { value, done } = await reader.read(); + if (done) { + break; + } + if (value) { + ioBuffer.push(value, 'in'); + portReadedChars += value.length; + const chars = decoder.decode(value, { stream: true }); + terminal.write(chars); + } + } + } catch (err) { + console.error(err); + terminal.writeln(`Terminal error: reading from serial port.`); + } finally { + if (reader) { + reader.releaseLock(); + } + disconnect(); + } + } + + async function connect() { + if (!port) { + try { + const baudRate = parseInt(selectSerialPortBaudrate.value, 10); + if (!baudRate) { + terminal.writeln('Terminal error: Invalid baudrate'); + return; + } + port = await navigator.serial.requestPort(); + await port.open({ baudRate }); + terminal.reset(); + terminal.focus(); + portReadedChars = 0; + portWritedChars = 0; + updateToolbar(); + readFromPort(); + window.onbeforeunload = confirmExit; + + port.addEventListener('disconnect', () => { + terminal.writeln('Terminal error: Serial port disconnected.'); + disconnect(); + }); + } catch (err) { + console.error(err); + terminal.writeln(`Terminal error: Unable to connect to serial port.`); + } + } else { + disconnect(); + } + } + + async function disconnect() { + if (reader) { + try { + await reader.cancel(); + } catch (error) { + console.error('Error canceling reader:', error); + } + reader = null; + } + if (port) { + try { + await port.close(); + } catch (error) { + console.error('Error closing port:', error); + } + port = null; + } + portReadedChars = 0; + portWritedChars = 0; + document.title = 'WebSerial Terminal'; + updateToolbar(); + ioBuffer.reset(); + window.onbeforeunload = null; + } + + window.addEventListener('resize', () => fitAddon.fit()); + window.addEventListener('click', onWindowClick); + terminalContainer.addEventListener('click', () => terminal.focus()); + connectButton.addEventListener('click', () => connect()); + buttonCtrlC.addEventListener('click', () => sendCtrlC()); + buttonCtrlD.addEventListener('click', () => sendCtrlD()); + buttonScrollToBottom.addEventListener('click', () => scrollToBottom()); + buttonShowAbout.addEventListener('click', () => toggleAboutVisibility()); + buttonShowBuffer.addEventListener('click', () => toggleDataBufferVisibility()); + buttonResetStats.addEventListener('click', () => resetStats()); + inputFontSize.addEventListener('change', () => chageFontSize()); + selectSerialPortBaudrate.addEventListener('change', () => changePortBaudrate()); + buttonFontSizeMinus.addEventListener('click', () => chageFontSize(1)); + buttonFontSizePlus.addEventListener('click', () => chageFontSize(-1)); + dataTableHex.addEventListener('mouseover', (event) => highlightCell(event, true)); + dataTableHex.addEventListener('mouseout', (event) => highlightCell(event, false)); + dataTableHex.addEventListener('dblclick', freeceCell); + dataTableHex.addEventListener('click', unfreeceCell); + dataTableAscii.addEventListener('mouseover', (event) => highlightCell(event, true)); + dataTableAscii.addEventListener('mouseout', (event) => highlightCell(event, false)); + inputFontSize.addEventListener('wheel', (event) => chageFontSize(event.deltaY), { + passive: false, + }); + + terminalContainer.addEventListener( + 'wheel', + (event) => { + if (event.ctrlKey) { + event.preventDefault(); + chageFontSize(event.deltaY); + } + }, + { passive: false } + ); + + updateDataBufferVisibility(); + updateToolbar(); + chageFontSize(); + ioBuffer.cleanInfo(); + ioBuffer.resize(terminalContainer.clientHeight); + app.style.visibility = 'visible'; + + setInterval(() => { + const connected = !!port; + svgToDevice.style.stroke = portWritedChars - portLastWritedChars > 0 ? '#aa1b1b' : '#313131'; + svgFromDevice.style.stroke = portReadedChars - portLastReadedChars > 0 ? '#15aa00' : '#313131'; + portLastReadedChars = portReadedChars; + portLastWritedChars = portWritedChars; + svgDevice.style.stroke = connected ? '#15aa00' : '#aa1b1b'; + transferStats.textContent = `Read: ${portReadedChars} bytes, Written: ${portWritedChars} bytes`; + if (showDataBuffer) { + ioBuffer.redraw(); + } + }, 250); +}); diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..e6601b0 --- /dev/null +++ b/src/index.html @@ -0,0 +1,287 @@ + + + + + + + + WebSerial TERMINAL + + +
+
+ + + + +
+ − +
+ +
+ + +
+
+ + + +
+
+ + + +
+
+
+
+
Locked
+
+ + + +
+
+
+
Direction
+
+
+
+
HEX
+
+
+ +
+
DEC
+
+
+
+
BIN
+
+
+
+
ASCII
+
+
+
+
Info
+
+
+
+
+
+
+
+
+ WebSerial TERMINAL - version 1.0.0

+ + is a terminal emulator that allows you to connect to a serial device from your web browser.

+ + Based on the + WebSerial API + and + xterm.js

+ + Author: + Peter Bay

+ + Licence: MIT

+ + Repository: Github - + peterbay/webserial-terminal +
+
+
+ + + + + + + + + + + + +
+ +
+ + + +
+
+ + + + + + + diff --git a/yarn.lock b/yarn.lock new file mode 100644 index 0000000..3ef8385 --- /dev/null +++ b/yarn.lock @@ -0,0 +1,822 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@eslint-community/eslint-utils@^4.2.0": + version "4.4.1" + resolved "https://registry.yarnpkg.com/@eslint-community/eslint-utils/-/eslint-utils-4.4.1.tgz#d1145bf2c20132d6400495d6df4bf59362fd9d56" + integrity sha512-s3O3waFUrMV8P/XaF/+ZTp1X9XBZW1a4B97ZnjQF2KYWaFD2A8KyFBsrsfSjEmjn3RGWAIuvlneuZm3CUK3jbA== + dependencies: + eslint-visitor-keys "^3.4.3" + +"@eslint-community/regexpp@^4.12.1": + version "4.12.1" + resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz#cfc6cffe39df390a3841cde2abccf92eaa7ae0e0" + integrity sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ== + +"@eslint/config-array@^0.20.0": + version "0.20.0" + resolved "https://registry.yarnpkg.com/@eslint/config-array/-/config-array-0.20.0.tgz#7a1232e82376712d3340012a2f561a2764d1988f" + integrity sha512-fxlS1kkIjx8+vy2SjuCB94q3htSNrufYTXubwiBFeaQHbH6Ipi43gFJq2zCMt6PHhImH3Xmr0NksKDvchWlpQQ== + dependencies: + "@eslint/object-schema" "^2.1.6" + debug "^4.3.1" + minimatch "^3.1.2" + +"@eslint/config-helpers@^0.2.0": + version "0.2.1" + resolved "https://registry.yarnpkg.com/@eslint/config-helpers/-/config-helpers-0.2.1.tgz#26042c028d1beee5ce2235a7929b91c52651646d" + integrity sha512-RI17tsD2frtDu/3dmI7QRrD4bedNKPM08ziRYaC5AhkGrzIAJelm9kJU1TznK+apx6V+cqRz8tfpEeG3oIyjxw== + +"@eslint/core@^0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.12.0.tgz#5f960c3d57728be9f6c65bd84aa6aa613078798e" + integrity sha512-cmrR6pytBuSMTaBweKoGMwu3EiHiEC+DoyupPmlZ0HxBJBtIxwe+j/E4XPIKNx+Q74c8lXKPwYawBf5glsTkHg== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/core@^0.13.0": + version "0.13.0" + resolved "https://registry.yarnpkg.com/@eslint/core/-/core-0.13.0.tgz#bf02f209846d3bf996f9e8009db62df2739b458c" + integrity sha512-yfkgDw1KR66rkT5A8ci4irzDysN7FRpq3ttJolR88OqQikAWqwA8j5VZyas+vjyBNFIJ7MfybJ9plMILI2UrCw== + dependencies: + "@types/json-schema" "^7.0.15" + +"@eslint/eslintrc@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz#e55f7f1dd400600dd066dbba349c4c0bac916964" + integrity sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ== + dependencies: + ajv "^6.12.4" + debug "^4.3.2" + espree "^10.0.1" + globals "^14.0.0" + ignore "^5.2.0" + import-fresh "^3.2.1" + js-yaml "^4.1.0" + minimatch "^3.1.2" + strip-json-comments "^3.1.1" + +"@eslint/js@9.24.0": + version "9.24.0" + resolved "https://registry.yarnpkg.com/@eslint/js/-/js-9.24.0.tgz#685277980bb7bf84ecc8e4e133ccdda7545a691e" + integrity sha512-uIY/y3z0uvOGX8cp1C2fiC4+ZmBhp6yZWkojtHL1YEMnRt1Y63HB9TM17proGEmeG7HeUY+UP36F0aknKYTpYA== + +"@eslint/object-schema@^2.1.6": + version "2.1.6" + resolved "https://registry.yarnpkg.com/@eslint/object-schema/-/object-schema-2.1.6.tgz#58369ab5b5b3ca117880c0f6c0b0f32f6950f24f" + integrity sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA== + +"@eslint/plugin-kit@^0.2.7": + version "0.2.8" + resolved "https://registry.yarnpkg.com/@eslint/plugin-kit/-/plugin-kit-0.2.8.tgz#47488d8f8171b5d4613e833313f3ce708e3525f8" + integrity sha512-ZAoA40rNMPwSm+AeHpCq8STiNAwzWLJuP8Xv4CHIc9wv/PSuExjMrmjfYNj682vW0OOiZ1HKxzvjQr9XZIisQA== + dependencies: + "@eslint/core" "^0.13.0" + levn "^0.4.1" + +"@humanfs/core@^0.19.1": + version "0.19.1" + resolved "https://registry.yarnpkg.com/@humanfs/core/-/core-0.19.1.tgz#17c55ca7d426733fe3c561906b8173c336b40a77" + integrity sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA== + +"@humanfs/node@^0.16.6": + version "0.16.6" + resolved "https://registry.yarnpkg.com/@humanfs/node/-/node-0.16.6.tgz#ee2a10eaabd1131987bf0488fd9b820174cd765e" + integrity sha512-YuI2ZHQL78Q5HbhDiBA1X4LmYdXCKCMQIfw0pw7piHJwyREFebJUvrQN4cMssyES6x+vfUbx1CIpaQUKYdQZOw== + dependencies: + "@humanfs/core" "^0.19.1" + "@humanwhocodes/retry" "^0.3.0" + +"@humanwhocodes/module-importer@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz#af5b2691a22b44be847b0ca81641c5fb6ad0172c" + integrity sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA== + +"@humanwhocodes/retry@^0.3.0": + version "0.3.1" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.3.1.tgz#c72a5c76a9fbaf3488e231b13dc52c0da7bab42a" + integrity sha512-JBxkERygn7Bv/GbN5Rv8Ul6LVknS+5Bp6RgDC/O8gEBU/yeH5Ui5C/OlWrTb6qct7LjjfT6Re2NxB0ln0yYybA== + +"@humanwhocodes/retry@^0.4.2": + version "0.4.2" + resolved "https://registry.yarnpkg.com/@humanwhocodes/retry/-/retry-0.4.2.tgz#1860473de7dfa1546767448f333db80cb0ff2161" + integrity sha512-xeO57FpIu4p1Ri3Jq/EXq4ClRm86dVF2z/+kvFnyqVYRavTZmaFaUBbWCOuuTh0o/g7DSsk6kc2vrS4Vl5oPOQ== + +"@jridgewell/gen-mapping@^0.3.5": + version "0.3.5" + resolved "https://registry.yarnpkg.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz#dcce6aff74bdf6dad1a95802b69b04a2fcb1fb36" + integrity sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg== + dependencies: + "@jridgewell/set-array" "^1.2.1" + "@jridgewell/sourcemap-codec" "^1.4.10" + "@jridgewell/trace-mapping" "^0.3.24" + +"@jridgewell/resolve-uri@^3.1.0": + version "3.1.2" + resolved "https://registry.yarnpkg.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz#7a0ee601f60f99a20c7c7c5ff0c80388c1189bd6" + integrity sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw== + +"@jridgewell/set-array@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@jridgewell/set-array/-/set-array-1.2.1.tgz#558fb6472ed16a4c850b889530e6b36438c49280" + integrity sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A== + +"@jridgewell/source-map@^0.3.3": + version "0.3.6" + resolved "https://registry.yarnpkg.com/@jridgewell/source-map/-/source-map-0.3.6.tgz#9d71ca886e32502eb9362c9a74a46787c36df81a" + integrity sha512-1ZJTZebgqllO79ue2bm3rIGud/bOe0pP5BjSRCRxxYkEZS8STV7zN84UBbiYu7jy+eCKSnVIUgoWWE/tt+shMQ== + dependencies: + "@jridgewell/gen-mapping" "^0.3.5" + "@jridgewell/trace-mapping" "^0.3.25" + +"@jridgewell/sourcemap-codec@^1.4.10", "@jridgewell/sourcemap-codec@^1.4.14": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.0.tgz#3188bcb273a414b0d215fd22a58540b989b9409a" + integrity sha512-gv3ZRaISU3fjPAgNsriBRqGWQL6quFx04YMPW/zD8XMLsU32mhCCbfbO6KZFLjvYpCZ8zyDEgqsgf+PwPaM7GQ== + +"@jridgewell/trace-mapping@^0.3.24", "@jridgewell/trace-mapping@^0.3.25": + version "0.3.25" + resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz#15f190e98895f3fc23276ee14bc76b675c2e50f0" + integrity sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ== + dependencies: + "@jridgewell/resolve-uri" "^3.1.0" + "@jridgewell/sourcemap-codec" "^1.4.14" + +"@types/estree@^1.0.6": + version "1.0.6" + resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.6.tgz#628effeeae2064a1b4e79f78e81d87b7e5fc7b50" + integrity sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw== + +"@types/json-schema@^7.0.15": + version "7.0.15" + resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841" + integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA== + +acorn-jsx@^5.3.2: + version "5.3.2" + resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz#7ed5bb55908b3b2f1bc55c6af1653bada7f07937" + integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ== + +acorn@^8.12.0: + version "8.14.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.0.tgz#063e2c70cac5fb4f6467f0b11152e04c682795b0" + integrity sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA== + +acorn@^8.14.0: + version "8.14.1" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.14.1.tgz#721d5dc10f7d5b5609a891773d47731796935dfb" + integrity sha512-OvQ/2pUDKmgfCg++xsTX1wGxfTaszcHVcTctW4UJB4hibJx2HXxxO5UmVgyjMa+ZDsiaf5wWLXYpRWMmBI0QHg== + +acorn@^8.8.2: + version "8.13.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.13.0.tgz#2a30d670818ad16ddd6a35d3842dacec9e5d7ca3" + integrity sha512-8zSiw54Oxrdym50NlZ9sUusyO1Z1ZchgRLWRaK6c86XJFClyCgFKetdowBg5bKxyp/u+CDBJG4Mpp0m3HLZl9w== + +ajv@^6.12.4: + version "6.12.6" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.6.tgz#baf5a62e802b07d977034586f8c3baf5adf26df4" + integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== + dependencies: + fast-deep-equal "^3.1.1" + fast-json-stable-stringify "^2.0.0" + json-schema-traverse "^0.4.1" + uri-js "^4.2.2" + +ansi-styles@^4.1.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.3.0.tgz#edd803628ae71c04c85ae7a0906edad34b648937" + integrity sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg== + dependencies: + color-convert "^2.0.1" + +argparse@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/argparse/-/argparse-2.0.1.tgz#246f50f3ca78a3240f6c997e8a9bd1eac49e4b38" + integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== + +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + +axios@^1.8.4: + version "1.8.4" + resolved "https://registry.yarnpkg.com/axios/-/axios-1.8.4.tgz#78990bb4bc63d2cae072952d374835950a82f447" + integrity sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw== + dependencies: + follow-redirects "^1.15.6" + form-data "^4.0.0" + proxy-from-env "^1.1.0" + +balanced-match@^1.0.0: + version "1.0.2" + resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" + integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== + +brace-expansion@^1.1.7: + version "1.1.11" + resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" + integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA== + dependencies: + balanced-match "^1.0.0" + concat-map "0.0.1" + +buffer-from@^1.0.0: + version "1.1.2" + resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.2.tgz#2b146a6fd72e80b4f55d255f35ed59a3a9a41bd5" + integrity sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ== + +callsites@^3.0.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" + integrity sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ== + +camel-case@^4.1.2: + version "4.1.2" + resolved "https://registry.yarnpkg.com/camel-case/-/camel-case-4.1.2.tgz#9728072a954f805228225a6deea6b38461e1bd5a" + integrity sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw== + dependencies: + pascal-case "^3.1.2" + tslib "^2.0.3" + +chalk@^4.0.0: + version "4.1.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" + integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== + dependencies: + ansi-styles "^4.1.0" + supports-color "^7.1.0" + +clean-css@~5.3.2: + version "5.3.3" + resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-5.3.3.tgz#b330653cd3bd6b75009cc25c714cae7b93351ccd" + integrity sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg== + dependencies: + source-map "~0.6.0" + +color-convert@^2.0.1: + version "2.0.1" + resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3" + integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ== + dependencies: + color-name "~1.1.4" + +color-name@~1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2" + integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== + +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + +commander@^10.0.0: + version "10.0.1" + resolved "https://registry.yarnpkg.com/commander/-/commander-10.0.1.tgz#881ee46b4f77d1c1dccc5823433aa39b022cbe06" + integrity sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug== + +commander@^2.20.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + +concat-map@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" + integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg== + +cross-spawn@^7.0.6: + version "7.0.6" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.6.tgz#8a58fe78f00dcd70c370451759dfbfaf03e8ee9f" + integrity sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA== + dependencies: + path-key "^3.1.0" + shebang-command "^2.0.0" + which "^2.0.1" + +debug@^4.3.1, debug@^4.3.2: + version "4.3.7" + resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.7.tgz#87945b4151a011d76d95a198d7111c865c360a52" + integrity sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ== + dependencies: + ms "^2.1.3" + +deep-is@^0.1.3: + version "0.1.4" + resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831" + integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ== + +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + +dot-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/dot-case/-/dot-case-3.0.4.tgz#9b2b670d00a431667a8a75ba29cd1b98809ce751" + integrity sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +entities@^4.4.0: + version "4.5.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-4.5.0.tgz#5d268ea5e7113ec74c4d033b79ea5a35a488fb48" + integrity sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw== + +escape-string-regexp@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" + integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== + +eslint-scope@^8.3.0: + version "8.3.0" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-8.3.0.tgz#10cd3a918ffdd722f5f3f7b5b83db9b23c87340d" + integrity sha512-pUNxi75F8MJ/GdeKtVLSbYg4ZI34J6C0C7sbL4YOp2exGwen7ZsuBqKzUhXd0qMQ362yET3z+uPwKeg/0C2XCQ== + dependencies: + esrecurse "^4.3.0" + estraverse "^5.2.0" + +eslint-visitor-keys@^3.4.3: + version "3.4.3" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz#0cd72fe8550e3c2eae156a96a4dddcd1c8ac5800" + integrity sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag== + +eslint-visitor-keys@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz#1f785cc5e81eb7534523d85922248232077d2f8c" + integrity sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg== + +eslint-visitor-keys@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.0.tgz#687bacb2af884fcdda8a6e7d65c606f46a14cd45" + integrity sha512-UyLnSehNt62FFhSwjZlHmeokpRK59rcz29j+F1/aDgbkbRTk7wIc9XzdoasMUbRNKDM0qQt/+BJ4BrpFeABemw== + +eslint@^9.24.0: + version "9.24.0" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-9.24.0.tgz#9a7f2e6cb2de81c405ab244b02f4584c79dc6bee" + integrity sha512-eh/jxIEJyZrvbWRe4XuVclLPDYSYYYgLy5zXGGxD6j8zjSAxFEzI2fL/8xNq6O2yKqVt+eF2YhV+hxjV6UKXwQ== + dependencies: + "@eslint-community/eslint-utils" "^4.2.0" + "@eslint-community/regexpp" "^4.12.1" + "@eslint/config-array" "^0.20.0" + "@eslint/config-helpers" "^0.2.0" + "@eslint/core" "^0.12.0" + "@eslint/eslintrc" "^3.3.1" + "@eslint/js" "9.24.0" + "@eslint/plugin-kit" "^0.2.7" + "@humanfs/node" "^0.16.6" + "@humanwhocodes/module-importer" "^1.0.1" + "@humanwhocodes/retry" "^0.4.2" + "@types/estree" "^1.0.6" + "@types/json-schema" "^7.0.15" + ajv "^6.12.4" + chalk "^4.0.0" + cross-spawn "^7.0.6" + debug "^4.3.2" + escape-string-regexp "^4.0.0" + eslint-scope "^8.3.0" + eslint-visitor-keys "^4.2.0" + espree "^10.3.0" + esquery "^1.5.0" + esutils "^2.0.2" + fast-deep-equal "^3.1.3" + file-entry-cache "^8.0.0" + find-up "^5.0.0" + glob-parent "^6.0.2" + ignore "^5.2.0" + imurmurhash "^0.1.4" + is-glob "^4.0.0" + json-stable-stringify-without-jsonify "^1.0.1" + lodash.merge "^4.6.2" + minimatch "^3.1.2" + natural-compare "^1.4.0" + optionator "^0.9.3" + +espree@^10.0.1: + version "10.2.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.2.0.tgz#f4bcead9e05b0615c968e85f83816bc386a45df6" + integrity sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g== + dependencies: + acorn "^8.12.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.1.0" + +espree@^10.3.0: + version "10.3.0" + resolved "https://registry.yarnpkg.com/espree/-/espree-10.3.0.tgz#29267cf5b0cb98735b65e64ba07e0ed49d1eed8a" + integrity sha512-0QYC8b24HWY8zjRnDTL6RiHfDbAWn63qb4LMj1Z4b076A4une81+z03Kg7l7mn/48PUTqoLptSXez8oknU8Clg== + dependencies: + acorn "^8.14.0" + acorn-jsx "^5.3.2" + eslint-visitor-keys "^4.2.0" + +esquery@^1.5.0: + version "1.6.0" + resolved "https://registry.yarnpkg.com/esquery/-/esquery-1.6.0.tgz#91419234f804d852a82dceec3e16cdc22cf9dae7" + integrity sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg== + dependencies: + estraverse "^5.1.0" + +esrecurse@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/esrecurse/-/esrecurse-4.3.0.tgz#7ad7964d679abb28bee72cec63758b1c5d2c9921" + integrity sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag== + dependencies: + estraverse "^5.2.0" + +estraverse@^5.1.0, estraverse@^5.2.0: + version "5.3.0" + resolved "https://registry.yarnpkg.com/estraverse/-/estraverse-5.3.0.tgz#2eea5290702f26ab8fe5370370ff86c965d21123" + integrity sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA== + +esutils@^2.0.2: + version "2.0.3" + resolved "https://registry.yarnpkg.com/esutils/-/esutils-2.0.3.tgz#74d2eb4de0b8da1293711910d50775b9b710ef64" + integrity sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g== + +fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: + version "3.1.3" + resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" + integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q== + +fast-json-stable-stringify@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" + integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== + +fast-levenshtein@^2.0.6: + version "2.0.6" + resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" + integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw== + +file-entry-cache@^8.0.0: + version "8.0.0" + resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz#7787bddcf1131bffb92636c69457bbc0edd6d81f" + integrity sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ== + dependencies: + flat-cache "^4.0.0" + +find-up@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/find-up/-/find-up-5.0.0.tgz#4c92819ecb7083561e4f4a240a86be5198f536fc" + integrity sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng== + dependencies: + locate-path "^6.0.0" + path-exists "^4.0.0" + +flat-cache@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-4.0.1.tgz#0ece39fcb14ee012f4b0410bd33dd9c1f011127c" + integrity sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw== + dependencies: + flatted "^3.2.9" + keyv "^4.5.4" + +flatted@^3.2.9: + version "3.3.1" + resolved "https://registry.yarnpkg.com/flatted/-/flatted-3.3.1.tgz#21db470729a6734d4997002f439cb308987f567a" + integrity sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw== + +follow-redirects@^1.15.6: + version "1.15.9" + resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.9.tgz#a604fa10e443bf98ca94228d9eebcc2e8a2c8ee1" + integrity sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ== + +form-data@^4.0.0: + version "4.0.1" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.1.tgz#ba1076daaaa5bfd7e99c1a6cb02aa0a5cff90d48" + integrity sha512-tzN8e4TX8+kkxGPK8D5u0FNmjPUjw3lwC9lSLxxoB/+GtsJG91CO8bSWy73APlgAZzZbXEYZJuxjkHH2w+Ezhw== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + mime-types "^2.1.12" + +glob-parent@^6.0.2: + version "6.0.2" + resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-6.0.2.tgz#6d237d99083950c79290f24c7642a3de9a28f9e3" + integrity sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A== + dependencies: + is-glob "^4.0.3" + +globals@^14.0.0: + version "14.0.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-14.0.0.tgz#898d7413c29babcf6bafe56fcadded858ada724e" + integrity sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ== + +has-flag@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b" + integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ== + +html-minifier-terser@^7.2.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/html-minifier-terser/-/html-minifier-terser-7.2.0.tgz#18752e23a2f0ed4b0f550f217bb41693e975b942" + integrity sha512-tXgn3QfqPIpGl9o+K5tpcj3/MN4SfLtsx2GWwBC3SSd0tXQGyF3gsSqad8loJgKZGM3ZxbYDd5yhiBIdWpmvLA== + dependencies: + camel-case "^4.1.2" + clean-css "~5.3.2" + commander "^10.0.0" + entities "^4.4.0" + param-case "^3.0.4" + relateurl "^0.2.7" + terser "^5.15.1" + +ignore@^5.2.0: + version "5.3.2" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-5.3.2.tgz#3cd40e729f3643fd87cb04e50bf0eb722bc596f5" + integrity sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g== + +import-fresh@^3.2.1: + version "3.3.0" + resolved "https://registry.yarnpkg.com/import-fresh/-/import-fresh-3.3.0.tgz#37162c25fcb9ebaa2e6e53d5b4d88ce17d9e0c2b" + integrity sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw== + dependencies: + parent-module "^1.0.0" + resolve-from "^4.0.0" + +imurmurhash@^0.1.4: + version "0.1.4" + resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea" + integrity sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA== + +is-extglob@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2" + integrity sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ== + +is-glob@^4.0.0, is-glob@^4.0.3: + version "4.0.3" + resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.3.tgz#64f61e42cbbb2eec2071a9dac0b28ba1e65d5084" + integrity sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg== + dependencies: + is-extglob "^2.1.1" + +isexe@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" + integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw== + +js-yaml@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" + integrity sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA== + dependencies: + argparse "^2.0.1" + +json-buffer@3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" + integrity sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ== + +json-schema-traverse@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz#69f6a87d9513ab8bb8fe63bdb0979c448e684660" + integrity sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg== + +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw== + +keyv@^4.5.4: + version "4.5.4" + resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.4.tgz#a879a99e29452f942439f2a405e3af8b31d4de93" + integrity sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw== + dependencies: + json-buffer "3.0.1" + +levn@^0.4.1: + version "0.4.1" + resolved "https://registry.yarnpkg.com/levn/-/levn-0.4.1.tgz#ae4562c007473b932a6200d403268dd2fffc6ade" + integrity sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ== + dependencies: + prelude-ls "^1.2.1" + type-check "~0.4.0" + +locate-path@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-6.0.0.tgz#55321eb309febbc59c4801d931a72452a681d286" + integrity sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw== + dependencies: + p-locate "^5.0.0" + +lodash.merge@^4.6.2: + version "4.6.2" + resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" + integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== + +lower-case@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-2.0.2.tgz#6fa237c63dbdc4a82ca0fd882e4722dc5e634e28" + integrity sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg== + dependencies: + tslib "^2.0.3" + +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + +minimatch@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b" + integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw== + dependencies: + brace-expansion "^1.1.7" + +ms@^2.1.3: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +natural-compare@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" + integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw== + +no-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/no-case/-/no-case-3.0.4.tgz#d361fd5c9800f558551a8369fc0dcd4662b6124d" + integrity sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg== + dependencies: + lower-case "^2.0.2" + tslib "^2.0.3" + +optionator@^0.9.3: + version "0.9.4" + resolved "https://registry.yarnpkg.com/optionator/-/optionator-0.9.4.tgz#7ea1c1a5d91d764fb282139c88fe11e182a3a734" + integrity sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g== + dependencies: + deep-is "^0.1.3" + fast-levenshtein "^2.0.6" + levn "^0.4.1" + prelude-ls "^1.2.1" + type-check "^0.4.0" + word-wrap "^1.2.5" + +p-limit@^3.0.2: + version "3.1.0" + resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-3.1.0.tgz#e1daccbe78d0d1388ca18c64fea38e3e57e3706b" + integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ== + dependencies: + yocto-queue "^0.1.0" + +p-locate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/p-locate/-/p-locate-5.0.0.tgz#83c8315c6785005e3bd021839411c9e110e6d834" + integrity sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw== + dependencies: + p-limit "^3.0.2" + +param-case@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/param-case/-/param-case-3.0.4.tgz#7d17fe4aa12bde34d4a77d91acfb6219caad01c5" + integrity sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A== + dependencies: + dot-case "^3.0.4" + tslib "^2.0.3" + +parent-module@^1.0.0: + version "1.0.1" + resolved "https://registry.yarnpkg.com/parent-module/-/parent-module-1.0.1.tgz#691d2709e78c79fae3a156622452d00762caaaa2" + integrity sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g== + dependencies: + callsites "^3.0.0" + +pascal-case@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/pascal-case/-/pascal-case-3.1.2.tgz#b48e0ef2b98e205e7c1dae747d0b1508237660eb" + integrity sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g== + dependencies: + no-case "^3.0.4" + tslib "^2.0.3" + +path-exists@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" + integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== + +path-key@^3.1.0: + version "3.1.1" + resolved "https://registry.yarnpkg.com/path-key/-/path-key-3.1.1.tgz#581f6ade658cbba65a0d3380de7753295054f375" + integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q== + +prelude-ls@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" + integrity sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g== + +proxy-from-env@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz#e102f16ca355424865755d2c9e8ea4f24d58c3e2" + integrity sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg== + +punycode@^2.1.0: + version "2.3.1" + resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5" + integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg== + +relateurl@^0.2.7: + version "0.2.7" + resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" + integrity sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog== + +resolve-from@^4.0.0: + version "4.0.0" + resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-4.0.0.tgz#4abcd852ad32dd7baabfe9b40e00a36db5f392e6" + integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g== + +shebang-command@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" + integrity sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA== + dependencies: + shebang-regex "^3.0.0" + +shebang-regex@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/shebang-regex/-/shebang-regex-3.0.0.tgz#ae16f1644d873ecad843b0307b143362d4c42172" + integrity sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A== + +source-map-support@~0.5.20: + version "0.5.21" + resolved "https://registry.yarnpkg.com/source-map-support/-/source-map-support-0.5.21.tgz#04fe7c7f9e1ed2d662233c28cb2b35b9f63f6e4f" + integrity sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w== + dependencies: + buffer-from "^1.0.0" + source-map "^0.6.0" + +source-map@^0.6.0, source-map@~0.6.0: + version "0.6.1" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" + integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g== + +strip-json-comments@^3.1.1: + version "3.1.1" + resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz#31f1281b3832630434831c310c01cccda8cbe006" + integrity sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig== + +supports-color@^7.1.0: + version "7.2.0" + resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.2.0.tgz#1b7dcdcb32b8138801b3e478ba6a51caa89648da" + integrity sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw== + dependencies: + has-flag "^4.0.0" + +terser@^5.15.1: + version "5.36.0" + resolved "https://registry.yarnpkg.com/terser/-/terser-5.36.0.tgz#8b0dbed459ac40ff7b4c9fd5a3a2029de105180e" + integrity sha512-IYV9eNMuFAV4THUspIRXkLakHnV6XO7FEdtKjf/mDyrnqUg9LnlOn6/RwRvM9SZjR4GUq8Nk8zj67FzVARr74w== + dependencies: + "@jridgewell/source-map" "^0.3.3" + acorn "^8.8.2" + commander "^2.20.0" + source-map-support "~0.5.20" + +tslib@^2.0.3: + version "2.8.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.8.0.tgz#d124c86c3c05a40a91e6fdea4021bd31d377971b" + integrity sha512-jWVzBLplnCmoaTr13V9dYbiQ99wvZRd0vNWaDRg+aVYRcjDF3nDksxFDE/+fkXnKhpnUUkmx5pK/v8mCtLVqZA== + +type-check@^0.4.0, type-check@~0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/type-check/-/type-check-0.4.0.tgz#07b8203bfa7056c0657050e3ccd2c37730bab8f1" + integrity sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew== + dependencies: + prelude-ls "^1.2.1" + +uri-js@^4.2.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" + integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== + dependencies: + punycode "^2.1.0" + +which@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" + integrity sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA== + dependencies: + isexe "^2.0.0" + +word-wrap@^1.2.5: + version "1.2.5" + resolved "https://registry.yarnpkg.com/word-wrap/-/word-wrap-1.2.5.tgz#d2c45c6dd4fbce621a66f136cbe328afd0410b34" + integrity sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA== + +yocto-queue@^0.1.0: + version "0.1.0" + resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" + integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==