Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Web sdk #5

Merged
merged 16 commits into from
Jan 15, 2024
Merged
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
demo
albho committed Jan 9, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
commit 5a3f98aa7fc4872f36d07ff260afaac18532a34c
7 changes: 7 additions & 0 deletions demo/web/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
node_modules
.rts2_cache/
.idea/
dist/
*.log
.DS_Store
models/*
35 changes: 35 additions & 0 deletions demo/web/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# falcon-web-demo

This is a basic demo to show how to use Falcon for web browsers, using the IIFE version of the library (i.e. an HTML script tag). It instantiates a Falcon worker engine and uses it with the [@picovoice/web-voice-processor](https://www.npmjs.com/package/@picovoice/web-voice-processor) to access (and automatically downsample) microphone audio.

## AccessKey

Falcon requires a valid Picovoice `AccessKey` at initialization. `AccessKey` acts as your credentials when using Falcon SDKs.
You can get your `AccessKey` for free. Make sure to keep your `AccessKey` secret.
Signup or Login to [Picovoice Console](https://console.picovoice.ai/) to get your `AccessKey`.

## Install & run

Use `yarn` or `npm` to install the dependencies, and the `start` script to start a local web server hosting the demo.

```console
yarn
yarn start
```

(or)

```console
npm install
npm run start
```

Open `localhost:5000` in your web browser, as hinted at in the output:

```console
Available on:
http://localhost:5000
Hit CTRL-C to stop the server
```

Wait until Falcon and the WebVoiceProcessor have initialized. Choose an audio file or record audio to diarize.
192 changes: 192 additions & 0 deletions demo/web/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,192 @@
<!DOCTYPE html>
<html lang="en">
<head>
<script src="node_modules/@picovoice/falcon-web/dist/iife/index.js"></script>
<script src="node_modules/@picovoice/web-voice-processor/dist/iife/index.js"></script>
<script src="models/falconModel.js"></script>
<script type="application/javascript">
let falcon = null;

window.onload = function () {
const audioContext = new (window.AudioContext ||
window.webKitAudioContext)({ sampleRate: 16000 });

function readAudioFile(selectedFile, callback) {
let reader = new FileReader();
reader.onload = function (ev) {
let wavBytes = reader.result;
audioContext.decodeAudioData(wavBytes, callback);
};
reader.readAsArrayBuffer(selectedFile);
}

const fileSelector = document.getElementById("audioFile");
fileSelector.addEventListener("change", (event) => {
writeMessage("Loading audio file...");
const fileList = event.target.files;
readAudioFile(fileList[0], async (audioBuffer) => {
const f32PCM = audioBuffer.getChannelData(0);
const i16PCM = new Int16Array(f32PCM.length);

const INT16_MAX = 32767;
const INT16_MIN = -32768;
i16PCM.set(
f32PCM.map((f) => {
let i = Math.trunc(f * INT16_MAX);
if (f > INT16_MAX) i = INT16_MAX;
if (f < INT16_MIN) i = INT16_MIN;
return i;
})
);

writeMessage("Diarizing audio file...");
const { segments } = await falcon.process(i16PCM, {
transfer: true,
});
setWordTable(segments);
writeMessage("Diarizing audio file... done!");
});
});

const displayTimer = document.getElementById("displayTimer");
const recordButton = document.getElementById("recordAudio");
const stopRecord = document.getElementById("stopRecord");

let timer = null;
let currentTimer = 0.0;
let audioData = [];
const recorderEngine = {
onmessage: (event) => {
switch (event.data.command) {
case "process":
audioData.push(event.data.inputFrame);
break;
}
},
};
recordButton.addEventListener("click", async () => {
displayTimer.style.display = "inline";
stopRecord.style.display = "inline";
recordButton.style.display = "none";

currentTimer = 0.0;
audioData = [];
try {
writeMessage("Recording audio...");
await window.WebVoiceProcessor.WebVoiceProcessor.subscribe(
recorderEngine
);
timer = setInterval(() => {
currentTimer += 0.1;
displayTimer.innerText = `${currentTimer.toFixed(1)} / 120`;
if (currentTimer === 120) {
stopRecord.click();
}
}, 100);
} catch (e) {
writeMessage(e);
}
});

stopRecord.addEventListener("click", async () => {
displayTimer.style.display = "none";
stopRecord.style.display = "none";
recordButton.style.display = "inline";

await window.WebVoiceProcessor.WebVoiceProcessor.unsubscribe(
recorderEngine
);
clearInterval(timer);

const frames = new Int16Array(audioData.length * 512);
for (let i = 0; i < audioData.length; i++) {
frames.set(audioData[i], i * 512);
}

writeMessage("Diarizing audio file...");
const { segments } = await falcon.process(frames, {
transfer: true,
});
setWordTable(segments);
writeMessage("Diarizing audio file... done!");
});
};

function writeMessage(message) {
console.log(message);
document.getElementById("status").innerHTML = message;
}

function setWordTable(segments) {
let html = `
<tr>
<th>startSec</th>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
<th>startSec</th>
<th>Start time (s)</th>

For showing the output, can we have a table similar to octopus web demo: https://github.com/Picovoice/octopus/blob/main/demo/web/index.html

<th>endSec</th>
<th>speaker tag</th>
</tr>
`;
segments.forEach((obj) => {
html += `
<tr>
<td>${obj.startSec.toFixed(3)}</td>
<td>${obj.endSec.toFixed(3)}</td>
<td>${obj.speakerTag}</td>
</tr>
`;
});
document.getElementById("segments").innerHTML = html;
}

async function startFalcon(accessKey) {
writeMessage("Falcon is loading. Please wait...");
try {
falcon = await FalconWeb.FalconWorker.create(accessKey, falconModel);
document.getElementById("control").style.display = "block";
writeMessage("Falcon worker ready!");
} catch (err) {
writeMessage(err);
}
}
</script>
</head>
<body>
<h1>Falcon Web Demo</h1>
<p>This demo uses Falcon for Web to:</p>
<ol>
<li>Create an instance of Falcon with the default language model.</li>
<li>
Process/diarize an audio file or a recorded audio stream in browser: the
data does not leave the browser while Falcon processes the audio data.
</li>
</ol>
After entering the AccessKey, click the "Start Falcon" button.
<hr />
<label for="accessKey"
>AccessKey obtained from
<a href="https://console.picovoice.ai/">Picovoice Console</a>:</label
>
<input type="text" id="accessKey" name="accessKey" />
<input
type="button"
id="submit"
value="Start Falcon"
onclick="startFalcon(document.getElementById('accessKey').value)"
/>
<hr />
<div id="control" style="display: none">
<label for="audioFile">Choose audio file to diarize:</label>
<input type="file" id="audioFile" name="audioFile" />
<p><b>OR</b></p>
<label for="recordAudio"
>Record audio to diarize (up to 2 minutes):</label
>
<button id="recordAudio">Record Audio</button>
<span id="displayTimer" style="display: none"></span>
<button id="stopRecord" style="display: none">Stop Recording</button>
<hr />
</div>
<div id="status" style="white-space: pre"></div>
<br />
<table id="segments"></table>
</body>
</html>
26 changes: 26 additions & 0 deletions demo/web/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
{
"name": "falcon-web-demo",
"version": "1.0.0",
"description": "A basic demo to show how to use Falcon for web browsers, using the IIFE version of the library",
"main": "index.js",
"private": true,
"scripts": {
"start": "node scripts/run_demo.js"
},
"keywords": [
"Picovoice",
"Falcon",
"browser",
"voice ai",
"speech recognition"
],
"author": "Picovoice Inc",
"license": "Apache-2.0",
"dependencies": {
"@picovoice/falcon-web": "file:../../binding/web",
"@picovoice/web-voice-processor": "~4.0.8"
},
"devDependencies": {
"http-server": "^14.0.0"
}
}
40 changes: 40 additions & 0 deletions demo/web/scripts/run_demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
const child_process = require("child_process");
const fs = require("fs");
const path = require("path");

const rootDir = path.join(__dirname, "..", "..", "..");

let outputDirectory = path.join(__dirname, "..", "models");
if (fs.existsSync(outputDirectory)) {
fs.readdirSync(outputDirectory).forEach((f) => {
fs.unlinkSync(path.join(outputDirectory, f));
});
} else {
fs.mkdirSync(outputDirectory, { recursive: true });
}

const modelDir = path.join(rootDir, "lib", "common");
const modelName = "falcon_params.pv";
fs.copyFileSync(
path.join(modelDir, modelName),
path.join(outputDirectory, modelName)
);

fs.writeFileSync(
path.join(outputDirectory, "falconModel.js"),
`const falconModel = {
publicPath: "models/${modelName}",
forceWrite: true,
};
(function () {
if (typeof module !== "undefined" && typeof module.exports !== "undefined")
module.exports = falconModel;
})();`
);

const command = (process.platform === "win32") ? "npx.cmd" : "npx";

child_process.fork("http-server", ["-a", "localhost", "-p", "5000"], {
execPath: command,
});
324 changes: 324 additions & 0 deletions demo/web/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,324 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1


"@picovoice/falcon-web@file:../../binding/web":
version "1.0.0"
dependencies:
"@picovoice/web-utils" "=1.3.1"

"@picovoice/web-utils@=1.3.1":
version "1.3.1"
resolved "https://registry.yarnpkg.com/@picovoice/web-utils/-/web-utils-1.3.1.tgz#d417e98604a650b54a8e03669015ecf98c2383ec"
integrity sha512-jcDqdULtTm+yJrnHDjg64hARup+Z4wNkYuXHNx6EM8+qZkweBq9UA6XJrHAlUkPnlkso4JWjaIKhz3x8vZcd3g==
dependencies:
commander "^9.2.0"

"@picovoice/web-voice-processor@~4.0.8":
version "4.0.8"
resolved "https://registry.yarnpkg.com/@picovoice/web-voice-processor/-/web-voice-processor-4.0.8.tgz#95247a5393cac4d16490a53feb0f413c902ee5fa"
integrity sha512-/OSHn8YKniMo0jP5EwGimLOxvLQl/Yx4Hs+LydNmoSu4hfBrDdzhhfhB79118uDiK4aUUKx2A/RAD9TG0mQ/ng==
dependencies:
"@picovoice/web-utils" "=1.3.1"

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"

async@^2.6.4:
version "2.6.4"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.4.tgz#706b7ff6084664cd7eae713f6f965433b5504221"
integrity sha512-mzo5dfJYwAn29PeiJ0zvwTo04zj8HDJj0Mn8TD7sno7q12prdbnasKJHhkm2c1LgrhlJ0teaea8860oxi51mGA==
dependencies:
lodash "^4.17.14"

basic-auth@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/basic-auth/-/basic-auth-2.0.1.tgz#b998279bf47ce38344b4f3cf916d4679bbf51e3a"
integrity sha512-NF+epuEdnUYVlGuhaxbbq+dvJttwLnGY+YixlXlME5KpQ5W3CnXA5cVTneY3SPbPDRkcjMbifrwmFYcClgOZeg==
dependencies:
safe-buffer "5.1.2"

call-bind@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"
integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==
dependencies:
function-bind "^1.1.2"
get-intrinsic "^1.2.1"
set-function-length "^1.1.1"

chalk@^4.1.2:
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"

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==

commander@^9.2.0:
version "9.5.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-9.5.0.tgz#bc08d1eb5cedf7ccb797a96199d41c7bc3e60d30"
integrity sha512-KRs7WVDKg86PWiuAqhDrAQnTXZKraVcCc6vFdL14qrZ/DcWwuRo7VoiYXalXO7S5GKpqYiVEwCbgFDfxNHKJBQ==

corser@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/corser/-/corser-2.0.1.tgz#8eda252ecaab5840dcd975ceb90d9370c819ff87"
integrity sha512-utCYNzRSQIZNPIcGZdQc92UVJYAhtGAteCFg0yRaFm8f0P+CPtyGyHXJcGXnffjCybUCEx3FQ2G7U3/o9eIkVQ==

debug@^3.2.7:
version "3.2.7"
resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a"
integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==
dependencies:
ms "^2.1.1"

define-data-property@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"
integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==
dependencies:
get-intrinsic "^1.2.1"
gopd "^1.0.1"
has-property-descriptors "^1.0.0"

eventemitter3@^4.0.0:
version "4.0.7"
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==

follow-redirects@^1.0.0:
version "1.15.4"
resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.4.tgz#cdc7d308bf6493126b17ea2191ea0ccf3e535adf"
integrity sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==

function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==

get-intrinsic@^1.0.2, get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2:
version "1.2.2"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b"
integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==
dependencies:
function-bind "^1.1.2"
has-proto "^1.0.1"
has-symbols "^1.0.3"
hasown "^2.0.0"

gopd@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
dependencies:
get-intrinsic "^1.1.3"

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==

has-property-descriptors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340"
integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==
dependencies:
get-intrinsic "^1.2.2"

has-proto@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0"
integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==

has-symbols@^1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8"
integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==

hasown@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
integrity sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==
dependencies:
function-bind "^1.1.2"

he@^1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f"
integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==

html-encoding-sniffer@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz#2cb1a8cf0db52414776e5b2a7a04d5dd98158de9"
integrity sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==
dependencies:
whatwg-encoding "^2.0.0"

http-proxy@^1.18.1:
version "1.18.1"
resolved "https://registry.yarnpkg.com/http-proxy/-/http-proxy-1.18.1.tgz#401541f0534884bbf95260334e72f88ee3976549"
integrity sha512-7mz/721AbnJwIVbnaSv1Cz3Am0ZLT/UBwkC92VlxhXv/k/BBQfM2fXElQNC27BVGr0uwUpplYPQM9LnaBMR5NQ==
dependencies:
eventemitter3 "^4.0.0"
follow-redirects "^1.0.0"
requires-port "^1.0.0"

http-server@^14.0.0:
version "14.1.1"
resolved "https://registry.yarnpkg.com/http-server/-/http-server-14.1.1.tgz#d60fbb37d7c2fdff0f0fbff0d0ee6670bd285e2e"
integrity sha512-+cbxadF40UXd9T01zUHgA+rlo2Bg1Srer4+B4NwIHdaGxAGGv59nYRnGGDJ9LBk7alpS0US+J+bLLdQOOkJq4A==
dependencies:
basic-auth "^2.0.1"
chalk "^4.1.2"
corser "^2.0.1"
he "^1.2.0"
html-encoding-sniffer "^3.0.0"
http-proxy "^1.18.1"
mime "^1.6.0"
minimist "^1.2.6"
opener "^1.5.1"
portfinder "^1.0.28"
secure-compare "3.0.1"
union "~0.5.0"
url-join "^4.0.1"

iconv-lite@0.6.3:
version "0.6.3"
resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.6.3.tgz#a52f80bf38da1952eb5c681790719871a1a72501"
integrity sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==
dependencies:
safer-buffer ">= 2.1.2 < 3.0.0"

lodash@^4.17.14:
version "4.17.21"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==

mime@^1.6.0:
version "1.6.0"
resolved "https://registry.yarnpkg.com/mime/-/mime-1.6.0.tgz#32cd9e5c64553bd58d19a568af452acff04981b1"
integrity sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==

minimist@^1.2.6:
version "1.2.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c"
integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==

mkdirp@^0.5.6:
version "0.5.6"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.6.tgz#7def03d2432dcae4ba1d611445c48396062255f6"
integrity sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==
dependencies:
minimist "^1.2.6"

ms@^2.1.1:
version "2.1.3"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==

object-inspect@^1.9.0:
version "1.13.1"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==

opener@^1.5.1:
version "1.5.2"
resolved "https://registry.yarnpkg.com/opener/-/opener-1.5.2.tgz#5d37e1f35077b9dcac4301372271afdeb2a13598"
integrity sha512-ur5UIdyw5Y7yEj9wLzhqXiy6GZ3Mwx0yGI+5sMn2r0N0v3cKJvUmFH5yPP+WXh9e0xfyzyJX95D8l088DNFj7A==

portfinder@^1.0.28:
version "1.0.32"
resolved "https://registry.yarnpkg.com/portfinder/-/portfinder-1.0.32.tgz#2fe1b9e58389712429dc2bea5beb2146146c7f81"
integrity sha512-on2ZJVVDXRADWE6jnQaX0ioEylzgBpQk8r55NE4wjXW1ZxO+BgDlY6DXwj20i0V8eB4SenDQ00WEaxfiIQPcxg==
dependencies:
async "^2.6.4"
debug "^3.2.7"
mkdirp "^0.5.6"

qs@^6.4.0:
version "6.11.2"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.11.2.tgz#64bea51f12c1f5da1bc01496f48ffcff7c69d7d9"
integrity sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==
dependencies:
side-channel "^1.0.4"

requires-port@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/requires-port/-/requires-port-1.0.0.tgz#925d2601d39ac485e091cf0da5c6e694dc3dcaff"
integrity sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==

safe-buffer@5.1.2:
version "5.1.2"
resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d"
integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==

"safer-buffer@>= 2.1.2 < 3.0.0":
version "2.1.2"
resolved "https://registry.yarnpkg.com/safer-buffer/-/safer-buffer-2.1.2.tgz#44fa161b0187b9549dd84bb91802f9bd8385cd6a"
integrity sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==

secure-compare@3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/secure-compare/-/secure-compare-3.0.1.tgz#f1a0329b308b221fae37b9974f3d578d0ca999e3"
integrity sha512-AckIIV90rPDcBcglUwXPF3kg0P0qmPsPXAj6BBEENQE1p5yA1xfmDJzfi1Tappj37Pv2mVbKpL3Z1T+Nn7k1Qw==

set-function-length@^1.1.1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==
dependencies:
define-data-property "^1.1.1"
get-intrinsic "^1.2.1"
gopd "^1.0.1"
has-property-descriptors "^1.0.0"

side-channel@^1.0.4:
version "1.0.4"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.4.tgz#efce5c8fdc104ee751b25c58d4290011fa5ea2cf"
integrity sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==
dependencies:
call-bind "^1.0.0"
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"

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"

union@~0.5.0:
version "0.5.0"
resolved "https://registry.yarnpkg.com/union/-/union-0.5.0.tgz#b2c11be84f60538537b846edb9ba266ba0090075"
integrity sha512-N6uOhuW6zO95P3Mel2I2zMsbsanvvtgn6jVqJv4vbVcz/JN0OkL9suomjQGmWtxJQXOCqUJvquc1sMeNz/IwlA==
dependencies:
qs "^6.4.0"

url-join@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/url-join/-/url-join-4.0.1.tgz#b642e21a2646808ffa178c4c5fda39844e12cde7"
integrity sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==

whatwg-encoding@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz#e7635f597fd87020858626805a2729fa7698ac53"
integrity sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==
dependencies:
iconv-lite "0.6.3"