Compare commits

...
41 changed files with 2760 additions and 13036 deletions

29
.gitignore vendored
View file

@ -1,5 +1,30 @@
.DS_Store
/node_modules
/out
/dist
/crate/target/*
/.log
*.sublime-workspace
crash.log
# local env files
.env.local
.env.*.local
.env
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
# Editor directories and files
.idea
.vscode
*.suo
*.ntvs*
*.njsproj
*.sln
*.sw?
#Electron-builder output
/dist_electron

63
.gitlab-ci.yml Normal file
View file

@ -0,0 +1,63 @@
stages:
- Build
- Upload
- Release
build:
stage: Build
tags:
# Use a macos runner to build.
- darwin
before_script:
- yarn
script:
- export VERSION=$(node -e "console.log(require('./package.json').version)")
- echo "VERSION=$VERSION" >> variables.env
- export APPNAME=$(node -e "console.log(require('./package.json').productName)")
- echo "APPNAME=$APPNAME" >> variables.env
- yarn build
artifacts:
reports:
dotenv: variables.env
name: $CI_COMMIT_REF_SLUG
paths:
- dist_electron/*.dmg
- dist_electron/*.zip
- dist_electron/*.yml
when: on_success
only:
- main
variables:
PACKAGE: '${APPNAME}-${VERSION}.dmg'
PACKAGE_REGISTRY_URL: '${CI_API_V4_URL}/projects/${CI_PROJECT_ID}/packages/generic/${APPNAME}/${VERSION}'
# Upload package to gitlab registry.
upload:
stage: Upload
needs:
- job: build
artifacts: true
rules:
- if: $CI_COMMIT_TAG
when: never # Do not run this job when a tag is created manually
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when when commits are pushed to the default branch
script:
# Take the package we built and place it in the package registry.
- 'curl --header "JOB-TOKEN: $CI_JOB_TOKEN" --upload-file "dist_electron/${PACKAGE}" "${PACKAGE_REGISTRY_URL}/${PACKAGE}"'
auto-release-master:
image: registry.gitlab.com/gitlab-org/release-cli
needs:
- job: build
artifacts: true
- job: upload
artifacts: true
stage: Release
rules:
- if: $CI_COMMIT_TAG
when: never # Do not run this job when a tag is created manually
- if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH # Run this job when when commits are pushed to the default branch
script:
- echo "Release $VERSION"
- release-cli create --name "Release $VERSION" --tag-name v$VERSION --description "Release $CI_COMMIT_TITLE" --ref $CI_COMMIT_SHA --assets-link "{\"name\":\"${APPNAME}\",\"url\":\"${PACKAGE_REGISTRY_URL}/${PACKAGE}\"}"

2
.npmrc
View file

@ -1,2 +1,2 @@
@crimata:registry=https://gitlab.com/api/v4/projects/28849281/packages/npm/
//gitlab.com/api/v4/projects/28849281/packages/npm
//gitlab.com/api/v4/projects/28849281/packages/npm/:_authToken=${NPM_TOKEN}

29
README.md Normal file
View file

@ -0,0 +1,29 @@
# crimata-electron
## Project setup
```
yarn install
```
### Compiles and hot-reloads for development
```
yarn electron:serve
```
### Compiles and minifies for production
```
yarn electron:build
```
<!-- ### Run your unit tests
```
yarn test:unit
```
### Lints and fixes files
```
yarn lint
``` -->
### Customize configuration
See [Configuration Reference](https://cli.vuejs.org/config/).

View file

@ -1,8 +0,0 @@
{
"folders":
[
{
"path": "."
}
]
}

View file

@ -1,14 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.cs.allow-jit</key>
<true/>
<key>com.apple.security.cs.debugger</key>
<true/>
<key>com.apple.security.device.audio-input</key>
<true/>
</dict>
</plist>
<!--<key>com.apple.security.cs.disable-library-validation</key>
<true/>-->

BIN
icon.png

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

11756
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,54 +1,24 @@
{
"name": "crimata",
"name": "Crimata",
"productName": "crimata-messenger",
"version": "1.0.0",
"description": "Cross-platform messenger app (Electron, Vue3)",
"private": true,
"description": "Cross-platform messenger app (Electron, Vue3, and TS)",
"author": {
"name": "Andrew Gundersen"
},
"main": "src/main.js",
"scripts": {
"start": "electron-forge start",
"test": "echo \"Error: no test specified\" && exit 1",
"package": "electron-forge package",
"make": "electron-forge make"
},
"author": "Andrew Gundersen",
"license": "ISC",
"devDependencies": {
"@electron-forge/cli": "^6.0.0-beta.63",
"@electron-forge/maker-deb": "^6.0.0-beta.63",
"@electron-forge/maker-rpm": "^6.0.0-beta.63",
"@electron-forge/maker-squirrel": "^6.0.0-beta.63",
"@electron-forge/maker-zip": "^6.0.0-beta.63",
"electron": "^18.2.3"
"start": "electron ."
},
"dependencies": {
"@crimata/nodeaudio": "^0.0.0",
"axios": "^0.21.1",
"base64-arraybuffer": "^1.0.1",
"electron-store": "^8.0.0",
"ws": "^7.3.1",
"electron-squirrel-startup": "^1.0.0"
"ws": "^7.3.1"
},
"config": {
"forge": {
"packagerConfig": {},
"makers": [
{
"name": "@electron-forge/maker-squirrel",
"config": {}
},
{
"name": "@electron-forge/maker-zip",
"platforms": [
"darwin"
]
},
{
"name": "@electron-forge/maker-deb",
"config": {}
},
{
"name": "@electron-forge/maker-rpm",
"config": {}
}
]
}
"devDependencies": {
"electron": "^13.2.3"
}
}

View file

@ -1,43 +1,44 @@
const { ipcMain } = require("electron");
const { post } = require("./api");
const store = require("./utils/store");
const { postLogin } = require("./api");
const { ipcEmit } = require("./utils/emitter");
const { launchSession, endSession } = require("./session");
const { backgroundMitt, ipcEmit } = require("./utils/emitter");
store.set("account", process.env.ACCOUNT);
// store.set("account", "smith12@gmail.com");
store.set("account", "adgundersen@gmail.com");
async function auth(_e, creds)
async function login(_e, email, password)
{
const { error, data } = await post("/auth", creds);
if (error)
const res = await postLogin(email, password);
if (res)
{
return data;
const account = res.crimataId;
store.set("account", account);
ipcEmit("set-account", account);
launchSession(account);
return true;
}
else
{
return false;
}
store.set("account", data);
ipcEmit("account", data);
launchSession(data);
};
function logout(_e, reason)
function logout()
{
store.delete("account");
ipcEmit("account", false, reason);
ipcEmit("set-account", false);
endSession();
};
function initAccount()
{
ipcMain.handle("auth", auth);
ipcMain.handle("login", login);
ipcMain.on("logout", logout);
const account = store.get("account");
if (account) launchSession(account);
}
backgroundMitt.on("logout", (reason) => logout(null, reason));
exports.initAccount = initAccount;

View file

@ -2,37 +2,18 @@ const axios = require('axios').default;
const config = require("./config");
const baseUrl = config.BUSINESS_URL + config.BUSINESS_PREFIX;
async function post(route, body)
async function postLogin(email, password)
{
let error;
let data;
try
{
const res = await axios.post(config.API + route, body);
data = res.data;
error = false;
try {
const res = await axios.post(baseUrl + "/account/login", {
email: email, password: password
});
return res.data;
} catch (e) {
return false;
}
catch (e)
{
if (e.response)
{
data = e.response.data;
}
else if (e.request)
{
data = "Can't connect to the server."
}
else
{
data = "Unknown error occured."
}
error = true;
}
return { error, data };
}
exports.post = post;
exports.postLogin = postLogin;

View file

@ -26,9 +26,7 @@ backgroundMitt.on("data", (int16Arr) => {
backgroundMitt.on("write", (int16Arr) => {
clearTimeout(setWriteId);
setWriteId = setTimeout(() => {
setPlaybackStatus(false);
}, 500);
setWriteId = setTimeout(setPlaybackStatus.prototype.bind(null, false), 500);
});
function setStreams()
@ -132,3 +130,8 @@ exports.initAudio = initAudio;
exports.terminateAudio = terminateAudio;
exports.playback = playback;
exports.streamState = streamState;
// setWriteId = setTimeout(() => {
// setPlaybackStatus(false);
// }, 500);

View file

@ -1,11 +1,16 @@
const { app } = require("electron");
const DOMAIN = "crimata.com";
const env = process.env;
/* If PROD env variable exists */
const prod = process.env.PROD;
const PLATFORM_PORT = env.PLATFORM_PORT || 8760;
const PLATFORM_IP = env.PLATFORM_IP || 'http://127.0.0.1';
const BUSINESS_PORT = env.BUSINESS_PORT || 3000;
const BUSINESS_IP = env.BUSINESS_IP || 'http://127.0.0.1';
module.exports = {
PLATFORM: prod ? `https://app.${DOMAIN}` : `http://localhost:5000`,
API: prod ? `https://${DOMAIN}/api` : `http://localhost:5001`
}
PLATFORM_URL: `${PLATFORM_IP}:${PLATFORM_PORT}`,
BUSINESS_URL: `${BUSINESS_IP}:${BUSINESS_PORT}`,
BUSINESS_PREFIX: '/api',
configPath: app.getPath('userData'),
}

View file

@ -14,7 +14,7 @@ function connectToPlatform(account)
{
if (pingId) clearInterval(pingId);
connection = new WebSocket(`${config.PLATFORM}/${account}`)
connection = new WebSocket(`${config.PLATFORM_URL}/${account}`)
.on("open", () => pingId = setInterval(() => connection.ping(null, true), 1000))
@ -24,22 +24,11 @@ function connectToPlatform(account)
.on("message", (payload) => backgroundMitt.emit("message", JSON.parse(payload)))
.on("close", (_code, reason) => {
.on("close", (code, reason) => {
setConnectionStatus(1);
if (reason)
{
if (reason == "unauthorized")
{
backgroundMitt.emit("logout", reason);
}
return;
if (reason !== "logout") {
reconnectId = setTimeout(() => connectToPlatform(account), 500);
}
reconnectId = setTimeout(() => connectToPlatform(account), 500);
});
}
@ -53,13 +42,13 @@ function sendMessage(message)
function disconnectFromPlatform()
{
clearTimeout(reconnectId);
if (connection.open) connection.close(1000, "logout");
connection.close(1000, "logout");
}
function setConnectionStatus(status)
{
updateTray("disconnect", status);
ipcEmit("ws", status);
ipcEmit("ws-status", status);
}
ipcMain.on("message", (_e, message) => sendMessage(message));

View file

@ -3,9 +3,12 @@ const { app, protocol, ipcMain, nativeTheme } = require("electron");
const store = require("./utils/store");
const { initTray } = require("./tray");
const { postLogin } = require("./api");
const { createWin } = require("./window");
const { initAccount } = require("./account");
// const { terminateAudio } = require("./audio");
const { terminateAudio } = require("./audio");
app.commandLine.appendSwitch("enable-transparent-visuals");
// Assert a light theme
nativeTheme.themeSource = "light";
@ -25,4 +28,4 @@ app.on("ready", () =>
app.on("activate", createWin);
// Prevents app from quitting on window close event
app.on('window-all-closed', (e) => e.preventDefault());
app.on('window-all-closed', (e) => e.preventDefault());

52
src/preview.html Normal file
View file

@ -0,0 +1,52 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<style>
html, body {
margin: 0;
padding: 0;
font-family: "SF Pro Text";
font-size: 14px;
}
#titlebar {
position: fixed;
width: 100vw;
height: 54px;
opacity: 0;
-webkit-app-region: drag;
border: none;
outline: none;
z-index: 1;
}
.building {
width: 250px;
height: 150px;
}
.email {
padding: 15px;
}
.email-subject {
font-size: 16px;
margin-top: 10px;
}
</style>
</head>
<body>
<button id="titlebar"></button>
<div id="content"></div>
<script>
window.mainApi.on("preview-init", (content) => {
document.getElementById("content").innerHTML = content;
});
</script>
</body>
</html>

View file

@ -20,25 +20,21 @@ const App =
},
template: `
<div id="app" v-if="account !== null">
<main id="app" v-if="account !== null">
<Header />
<Messenger v-if="account"/>
<Login v-else />
</div>
</main>
<Splash v-else />`
}
window.mainApi.on("account", (value, reason) => {
if (reason) alert(reason);
account.value = value;
window.mainApi.on("set-account", (account_) => {
account.value = account_;
});
export default App;
export { account };
// Is called when window is about to close or reload
window.onbeforeunload = () => console.log("beforeunload");

View file

@ -9,27 +9,15 @@ const Bubble =
}
Vue.onMounted(() => {
const el = document.getElementById("messenger");
setTimeout(() => {
el.dispatchEvent(new CustomEvent('adjust-scroll'));
}, 100);
document.dispatchEvent(new CustomEvent("scroll"));
});
if (!props.text) props.text = "...";
return { getUrl };
},
template: `
<div :class="'bubble ' + modifier + '-bubble ' + modifier +'-'+ child">
<small class="bubble-notify" v-if="modifier=='ai'"></small>
<p v-if="category=='text'">{{ text }}</p>
<p v-else-if="category=='audio'">{{ text }}</p>
@ -38,11 +26,11 @@ const Bubble =
<img v-else-if="category == 'image'" :src="getUrl()" />
<a v-else :download="text" :href="getUrl()">{{ text }}</a>
<a v-else :href="getUrl()">{{ text }}</a>
</div>`
};
export default Bubble;
// TODO: should include other styling/actions for audio bubbles
// TODO: should include other styling/actions for audio bubbles

View file

@ -1,5 +1,5 @@
const saveLocation = "input_item_position";
const defaultPosition = { x: 15, y: 400 };
const defaultPosition = { x: 15, y: window.innerHeight - 300 };
//---Dragabble Helper Funcs--------------------------------------
@ -33,10 +33,9 @@ function calcPosition (elementPosition, elementLength, percent, short, win) {
return elementPosition
}
function draggify(elementId, parentId, margin) {
function draggify(elementId, margin) {
let element;
let parent;
/* only compatible with elements having equal width and height */
let elementLength;
@ -53,20 +52,23 @@ function draggify(elementId, parentId, margin) {
let xShort;
let yShort;
// Keep track of window size;
let winW = window.innerWidth;
let winH = window.innerHeight;
//---Reposition Anime-----------------------------------------------
// Move element to target smoothly.
const repositionAnime = (xChange, yChange) => {
const xStep = xChange / 6000;
const yStep = yChange / 6000;
const xStep = xChange / 1000;
const yStep = yChange / 1000;
for (let i = 1; i <= 1000; i++) {
for (let i = 1; i <= 6000; i++) {
setTimeout(() => {
elementX.value += xStep;
elementY.value += yStep;
}, 30) // 60fps
}, 16) // 60fps
}
}
@ -77,8 +79,9 @@ function draggify(elementId, parentId, margin) {
const onMouseMove = (e) => {
e.preventDefault();
elementX.value = element.offsetLeft + e.movementX;
elementY.value = element.offsetTop + e.movementY;
const inputItemRect = element.getBoundingClientRect();
elementX.value = inputItemRect.left + e.movementX;
elementY.value = inputItemRect.top + e.movementY;
}
// Add an event listener for dragging.
@ -99,9 +102,6 @@ function draggify(elementId, parentId, margin) {
let x = 0; // vector change
let y = 0;
const winW = parent.clientWidth;
const winH = parent.clientHeight;
if (elementX.value < 0) {
x = (elementX.value - margin)*-1
}
@ -138,8 +138,12 @@ function draggify(elementId, parentId, margin) {
// Update position of targetEl on windowResize.
const onWindowResize = (_e) => {
elementX.value = calcPosition(elementX.value, elementLength, percentX, xShort, parent.clientWidth);
elementY.value = calcPosition(elementY.value, elementLength, percentY, yShort, parent.clientHeight);
// Update window dimensions.
winW = window.innerWidth;
winH = window.innerHeight;
elementX.value = calcPosition(elementX.value, elementLength, percentX, xShort, winW);
elementY.value = calcPosition(elementY.value, elementLength, percentY, yShort, winH);
savePosition();
}
@ -156,28 +160,27 @@ function draggify(elementId, parentId, margin) {
Vue.onMounted(() => {
element = document.getElementById(elementId);
parent = document.getElementById(parentId);
elementLength = element.offsetWidth;
// Initialize the positional references.
percentX = elementX.value / parent.clientWidth;
percentY = elementY.value / parent.clientHeight;
percentX = elementX.value / window.innerWidth;
percentY = elementY.value / window.innerHeight;
xShort = calcSideProximity(elementX.value, elementLength, parent.clientWidth);
yShort = calcSideProximity(elementX.value, elementLength, parent.clientHeight);
xShort = calcSideProximity(elementX.value, elementLength, window.innerWidth);
yShort = calcSideProximity(elementX.value, elementLength, window.innerHeight);
// Then, we can listen for window resize (and mousedown).
element.addEventListener("mousedown", onMouseDown);
parent.addEventListener('resize', onWindowResize)
window.addEventListener('resize', onWindowResize)
});
// remove event listeners on component dismount.
Vue.onUnmounted(() => {
element.removeEventListener('mousedown', onMouseDown);
parent.removeEventListener('resize', onWindowResize)
parent.removeEventListener('mouseup', onMouseUp)
parent.removeEventListener('mousemove', onMouseMove);
window.removeEventListener('resize', onWindowResize)
window.removeEventListener('mouseup', onMouseUp)
window.removeEventListener('mousemove', onMouseMove);
});
// Try loading initPosition, otherwise set default values

View file

@ -1,11 +1,6 @@
const onDrop = async (e) => {
const file = e.dataTransfer.items[0].getAsFile();
if (file.size >= 1 * 1000 * 1000) {
alert("File must be under 1MB.");
return;
}
const buffer = await file.arrayBuffer();
const b64String = await window.mainApi.invoke("encode", buffer);
@ -21,4 +16,4 @@ const onDrop = async (e) => {
});
}
export default onDrop;
export default onDrop;

View file

@ -1,20 +1,22 @@
let el;
const scroll = () => {
if (!el) {
el = document.getElementById("messenger");
window.addEventListener('resize', scroll);
}
const initScroll = (elementId) => {
el = document.getElementById(elementId);
el.addEventListener('adjust-scroll', adjustScroll);
window.addEventListener('resize', adjustScroll);
}
const isBottom = () => {
return el.scrollHeight - el.clientHeight <= el.scrollTop + 1;
}
const adjustScroll = () => {
if (isBottom()) {
el.scrollTo({
top: el.scrollHeight - el.clientHeight,
behavior: 'smooth'
});
}
}
export default scroll;
// const isBottom = () => {
// if (el) {
// return el.scrollHeight - el.clientHeight <= el.scrollTop + 1;
// }
// }
export default initScroll;

View file

@ -17,23 +17,21 @@ const metaKeys = [
"Escape"
];
function useText(elementId, parentId, left)
function useText(elementId, left, typing)
{
let el;
let parent;
const show = Vue.ref(false);
const leftSide = Vue.ref(false);
const calcSide = () => {
parent.clientWidth - left.value < inputLength ? leftSide.value = true : leftSide.value = false;
window.innerWidth - left.value < inputLength ? leftSide.value = true : leftSide.value = false;
}
/* handle user typing */
Vue.onMounted(() => {
el = document.getElementById(elementId);
parent = document.getElementById(parentId);
window.addEventListener("keydown", (e) => {
@ -48,23 +46,15 @@ function useText(elementId, parentId, left)
if (e.key === "Enter" && el.value)
{
if (el.value.length >= 250) {
alert("250 character limit")
return;
}
window.mainApi.send("message", {
category: "text",
text: el.value,
blob: null
});
show.value = false;
}
});
calcSide();
});
Vue.watch(left, calcSide);
@ -78,6 +68,8 @@ function useText(elementId, parentId, left)
}
});
calcSide();
return { leftSide, show };
}

View file

@ -1,23 +0,0 @@
export const isEmail = (value) => {
console.log(value);
if (!/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/.test(value)) {
return "Not a valid email."
}
}
export const isPassword = (value) => {
if (!/^(?=.*[0-9])(?=.*[!@#$%^&*])[a-zA-Z0-9!@#$%^&*]{6,16}$/.test(value)) {
return "Password must have symbol, number, uppercase and be 6-16 length.";
}
}
/**
* Name is optional and therefore doesn't return error if no value.
*/
export const isName = (value) => {
if (value) {
if (!/^([a-zA-Z ]){5,30}$/.test(value)) {
return "Name must be at least 5 long.";
}
}
}

View file

@ -1,86 +0,0 @@
// Basic debounce implentation
const debounce = (fn, delay) => {
let timeoutId;
return (...args) => {
if (timeoutId) clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn(...args);
}, delay);
}
}
/**
* Dynamic form component with auto submission and validation.
*
* props
* .fields => List of field objects
* .channel => e.g. "/login"
* .modifier => Form description
*/
const SmartForm =
{
props: ["fields", "channel", "modifier"],
setup(props)
{
// Error log below form
const log = Vue.ref(props.modifier);
/**
* Valid and submit form data to channel.
*/
const submit = async () => {
// Validate each field
for (let field of props.fields) {
const error = field.valid(field.value);
if (error) return error;
}
// Reduce fields into the form
const form = props.fields.reduce((v, n) => {
v[n.name] = n.value;
return v;
}, {});
console.log("Submitting =>", form);
return await window.mainApi.invoke(props.channel, form);
}
/**
* Wrapper for submit that is debounced and updates UI elements
*/
const preSub = debounce(async () => {
const error = await submit();
log.value = error ? error : props.modifier
}, 2000);
return { preSub, log };
},
template: `
<form id="smart-form" class="smart-form" v-on:input="preSub">
<div v-for="field in fields" style="display:flex">
<input type="text" class="text-field"
v-model="field.value"
:name="field.name"
:placeholder="field.placeholder"
/>
</div>
<div class="smart-form-logs">{{ log }}</div>
</form>`
}
export default SmartForm;

View file

@ -1,7 +1,7 @@
const Header =
{
setup() {
const onNavBar = (command) => window.mainApi.send("nav", command);
const onNavBar = (command) => window.mainApi.send('nav-bar', command);
return { onNavBar };
},

View file

@ -11,9 +11,9 @@ const Input =
const recording = Vue.ref(false);
const { elementX, elementY } = draggify("input", "app", 15);
const { elementX, elementY } = draggify("input", 15);
const { leftSide, show } = useText("input-text", "app", elementX);
const { leftSide, show } = useText("input-text", elementX);
window.mainApi.on("record", (status) => recording.value = status);

View file

@ -1,44 +1,58 @@
import SmartForm from "./form.js";
import { isEmail, isPassword, isName } from "./control/valid.js";
const Login =
{
components: {
SmartForm
},
setup()
{
return { isEmail, isPassword, isName };
const usr = Vue.ref("");
const pwd = Vue.ref("");
const submitForm = async () => {
const success = await window.mainApi.invoke("login", usr.value, pwd.value);
if (!success) shake("login-form");
}
return { usr, pwd, submitForm };
},
template: `
<div id="login">
<form id="login" @submit.prevent="submitForm">
<SmartForm
:fields="[{
name: 'email',
value: null,
placeholder: 'Email',
valid: isEmail
},
{
name: 'password',
value: null,
placeholder: 'Password',
valid: isPassword
},
{
name: 'name',
value: null,
placeholder: 'Name',
valid: isName
}]"
:channel="'auth'"
:modifier="'Login or enter name to register.'"
/>
<!-- username and password forms -->
<div id="login-form">
<!-- username -->
<div class="login-input-box">
<input class="login-input" type="text" v-model="usr" />
<div class="login-input-modifier">Email</div>
</div>
</div>`
<!-- password -->
<div class="login-input-box">
<input class="login-input" type="password" v-model="pwd" />
<div class="login-input-modifier">Password</div>
</div>
</div>
<!-- submit button; position: fixed -->
<button
class="login-submit-button button"
type="submit"
>
Login
</button>
</form> `
}
export default Login;
/** Shake a div to indicate incorrect credentials */
const shake = (elementId) =>
{
const el = document.getElementById(elementId);
if (el)
el.classList.remove("shake");
setTimeout(() => {
if (el) el.classList.add("shake");
}, 250);
}
export default Login;

View file

@ -8,7 +8,7 @@ const Message =
Context
},
props: ["modifier", "content", "context", "avatar", "id", "seen"],
props: ["modifier", "content", "context", "avatar", "id"],
setup(props)
{
@ -18,9 +18,8 @@ const Message =
template: `
<div :id="id" :class="'message ' + modifier + '-message'">
<Bubble
<Bubble
v-for="(c, index) in content"
:id="'bubble-' + id + '-' + index"
:category="c.category"
:text="c.text"
:html="c.html"

View file

@ -1,5 +1,5 @@
import onDrop from "./control/drop.js";
import scroll from "./control/scroll.js";
import adjustScroll from "./control/scroll.js";
import WS from "./ws.js";
import Input from "./input.js";
@ -20,45 +20,26 @@ const Messenger = {
const person = Vue.ref("");
const messages = Vue.ref([]);
let current = Vue.ref();
Vue.onMounted(() => {
window.mainApi.on("message", (update) => {
window.mainApi.on("update", (update) => {
if (update.person)
if (update.name)
{
person.value = update.person;
messages.value = update.messages;
current.value = messages.value.at(-1);
person.value = update;
}
else if (update.name)
else if (message.content)
{
person.value = update.name;
}
else if (update.modifier)
{
messages.value.push(update);
current.value = update;
}
else if (update.category)
{
current.value.content.push(update);
}
else if (update.context)
{
current.value.context = update.context;
messages.value.push(update);
}
else
{
current.value.content.at(-1).text = update.text;
messages.value.at(-1).content.push(update);
}
setTimeout(scroll, 10);
});
window.mainApi.send("messenger");
window.mainApi.send("message", {route: "window", open: true});
});
return { person, messages, onDrop };
@ -74,13 +55,18 @@ const Messenger = {
@dragover.prevent
@dragenter.prevent
>
<Message
v-for="message in messages"
v-bind="message"
<Message
v-for="message in messages"
:modifier="message.modifier"
:content="message.content"
:context="message.context"
:avatar="message.avatar"
:id="message.id"
/>
</div>`
</div> `
}
export default Messenger;
// Is called when window is about to close or reload
window.onbeforeunload = () => window.mainApi.send("message", {route: "window", open: true});

View file

@ -20,11 +20,11 @@ const Settings =
await window.mainApi.send("logout");
}
return { showSettings, active, logout, window };
return { showSettings, active, logout };
},
template: `
<div v-if="active" id="settings">
<div v-if="active" id="settings" >
<button class="settings-logout-button button" @click="logout">
Logout
@ -33,7 +33,7 @@ const Settings =
</div>
<button v-else class="settings-icon" @click="showSettings">
<img :src="window.path + 'assets/settingsIcon.svg'">
<img src="./assets/settingsIcon.svg">
</button>`
}

View file

@ -7,19 +7,19 @@ const WS =
Vue.onMounted(() => {
window.mainApi.on("ws", (val) => {
window.mainApi.on("ws-status", (val) => {
status.value = val;
});
});
return { status, window }
return { status }
},
template: `
<div id="ws">
<img id="ws-logo" :src="window.path + 'assets/connectLogo.svg'"
<img id="ws-logo" src="./assets/connectLogo.svg"
:class="{ move: status }"
>

View file

@ -1,5 +1,2 @@
import App from "./components/app.js";
window.path = "./";
Vue.createApp(App).mount("#app");

View file

@ -1,48 +1,30 @@
@font-face {
font-family: "Default";
src: url("assets/fonts/SF-Pro-Text-Regular.otf");
}
@font-face {
font-family: "Compact";
src: url("assets/fonts/SF-Compact-Display-Bold.otf");
}
@font-face {
font-family: "Rounded";
src: url("assets/fonts/SF-Compact-Rounded-Bold.otf");
}
html, body {
margin: 0;
padding: 0;
/*background-color: rgba(235, 235, 235, 0.75);*/
background-color: rgba(235, 235, 235, 0.75);
}
/* The first word of the class is the component that it modifys. */
#splash {
width: 100%;
height: 100%;
width: 100vw;
height: 100vh;
display: flex;
align-items: center;
justify-content: center;
}
#app {
position: relative; /* must explicitly be declared */
font-family: "Default";
font-family: "SF Pro Text";
-webkit-font-smoothing: antialiased;
height: 100vh; /* 100% for website */
width: 100vw; /* 100% for website */
/* Does not work on Linux */
height: 100vh;
width: 100vw;
border-radius: 15px;
}
#header-titlebar {
position: absolute;
width: 100%;
position: fixed;
width: 100vw;
height: 54px;
opacity: 0.75;
background-color: #EBEBEB;
@ -50,8 +32,6 @@ html, body {
border: none;
outline: none;
z-index: 1;
border-top-left-radius: 15px;
border-top-right-radius: 15px;
}
.contacts {
@ -81,26 +61,22 @@ html, body {
}
.contacts .email {
font-family: "Compact";
font-family: SF Compact Display;
font-size: 12px;
color: #898989;
overflow: w;
word-wrap: break-word;
}
#header-menu {
position: absolute;
position: fixed;
margin-left: 20px;
margin-top: 20px;
z-index: 2;
/* Needed for Linux even with higher Z index */
-webkit-app-region: no-drag;
}
.header-menu-button {
min-width: 14px;
min-height: 14px;
border-radius: 50%;
border-radius: 7px;
}
.header-exit-button {
@ -129,9 +105,87 @@ html, body {
justify-content: center;
}
#login-form {
font-family: "SF Compact Display";
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
padding-bottom: 20px;
}
.login-input-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
margin-bottom: 10px;
}
.login-input {
background-color: #B9B9B9;
border: none;
outline: none;
padding: 16px;
text-decoration: none;
margin: 4px 2px;
cursor: pointer;
border-radius: 10px;
}
.login-input-modifier {
min-width: 181px;
font-size: 12px;
font-weight: bold;
text-align: left;
margin-left: 15px;
}
.login-submit-button {
font-family: "SF Compact Display";
position: fixed;
margin-top: 260px;
background-color: #58C4FD;
color: white;
padding: 10px 30px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
}
.login-submit-button:active {
background-color: #4296C3;
}
/* generic shaking functionality */
.shake {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
#messenger {
width: 100%;
height: 100%;
width: 100vw;
height: 100vh;
overflow: auto;
}
@ -141,8 +195,8 @@ html, body {
}
#ws {
position: absolute;
width: 100%;
position: fixed;
width: 100vw;
height: 54px;
display: flex;
align-items: center;
@ -230,6 +284,7 @@ html, body {
border-radius: 50%;
/* Set opacity here to not affect child. */
background-color: rgba(235, 235, 235, 0.75);
cursor: pointer;
box-shadow: 0px 0px 10px 0px rgba(0,0,0,0.15);
}
@ -241,7 +296,6 @@ html, body {
padding: 10px;
outline: none;
border: none;
font-size: 14px;
pointer-events: none;
background-color: white;
z-index: -1;
@ -261,9 +315,9 @@ html, body {
}
#settings {
position: absolute;
width: 100%;
height: 100%;
position: fixed;
width: 100vw;
height: 100vh;
background-color: rgba(235, 235, 235, 0.75);
z-index: 4;
display: flex;
@ -272,7 +326,6 @@ html, body {
align-items: center;
animation-name: settings-appear;
animation-duration: 0.5s;
border-radius: inherit;
}
@keyframes settings-appear {
@ -285,7 +338,7 @@ html, body {
}
.settings-icon {
position: absolute;
position: fixed;
right: 0;
border: none;
outline: none;
@ -296,8 +349,10 @@ html, body {
margin-top: 19px;
z-index: 2;
background-color: Transparent;
/* Needed for Linux */
-webkit-app-region: no-drag;
}
.settings-icon:hover {
cursor: pointer;
}
.settings-account {
@ -307,11 +362,16 @@ html, body {
}
.settings-logout-button {
font-family: "Compact";
font-family: "SF Compact Display";
background-color: #B7B7B7;
padding: 10px 20px;
padding: 10px 30px;
border-radius: 20px;
font-size: 14px;
font-weight: bold;
}
.settings-logout-button:hover {
cursor: pointer;
}
.settings-version {
@ -325,7 +385,7 @@ html, body {
}
.message {
width: inherit;
width: 100vw;
display: flex;
flex-direction: column;
padding-top: 9px;
@ -350,17 +410,6 @@ html, body {
margin-bottom: 6px;
}
.message-session {
width: 100vw;
display: flex;
justify-content: center;
align-items: center;
font-family: "Compact";
font-size: 12px;
color: #9B9B9B;
margin-bottom: 18px;
}
.client-message {
align-items: flex-end;
}
@ -369,40 +418,13 @@ html, body {
align-items: flex-start;
}
.admin-message {
align-items: center;
}
.bubble {
position: relative;
max-width: 66%;
max-width: 66vw;
font-family: "SF Pro Text";
font-size: 14px;
border-radius: 18px;
margin-bottom: 4px;
overflow-wrap: break-word;
}
.bubble-notify {
position: absolute;
width: 12px;
height: 12px;
border-radius: 50%;
background-color: #58D9FF;
top: -5px;
left: -5px;
border: 2px solid #EBEBEB;
transform: scale(0);
animation-name: bubble-notify-anim;
animation-duration: 5s;
}
@keyframes bubble-notify-anim {
0%, 90% {
transform: scale(1);
}
100% {
transform: scale(0);
}
}
.ai-bubble {
@ -454,15 +476,15 @@ html, body {
.bubble > img {
border-radius: inherit;
display: block;
max-width: 100%;
max-width: inherit;
}
.bubble a {
display: inline-block;
font-family: "Rounded";
display:inline-block;
text-decoration: none;
border-radius: inherit;
background-color: #D9D9D9;
font-weight: bold;
padding: 10px;
color: #727272;
}
@ -472,8 +494,9 @@ html, body {
min-height: 14px;
display: flex;
align-items: center;
font-family: "Compact";
font-family: "SF Compact Display";
font-size: 12px;
font-weight: bold;
margin-top: 5px;
}
@ -503,32 +526,6 @@ html, body {
cursor: pointer;
}*/
/* shake a div to signal error*/
.shake {
animation: shake 0.82s cubic-bezier(.36,.07,.19,.97) both;
transform: translate3d(0, 0, 0);
backface-visibility: hidden;
perspective: 1000px;
}
@keyframes shake {
10%, 90% {
transform: translate3d(-1px, 0, 0);
}
20%, 80% {
transform: translate3d(2px, 0, 0);
}
30%, 50%, 70% {
transform: translate3d(-4px, 0, 0);
}
40%, 60% {
transform: translate3d(4px, 0, 0);
}
}
.audio {
animation-name: audio-anim;
animation-duration: 2s;
@ -550,29 +547,3 @@ html, body {
box-shadow: 0 0 0 0.25em rgba(195, 195, 195, 0.25), 0 0 0 0.55em rgba(195, 195, 195, 0.45);
}
}
.smart-form {
width: 225px;
display: flex;
flex-direction: column;
justify-content: flex-start;
}
.text-field {
width: 100%;
padding: 16px;
background: none;
border: none;
margin: 4px;
font-size: 14px;
border-radius: 10px;
background-color: #d1d1d1;
outline: none;
}
.smart-form-logs {
font-family: "Compact";
font-size: 12px;
text-align: left;
margin-left: 10px;
}

View file

@ -3,20 +3,21 @@ const { contextBridge, ipcRenderer } = require("electron");
/** The main <-> render interface */
const validFromMainChannels = [
"playback",
"account",
"message",
"set-account",
"update",
"ws-status",
"record",
"ws"
"playback"
];
const validToMain = [
"messenger",
"message",
"login",
"logout",
"encode",
"auth",
"nav"
"decode",
"nav-bar"
]
ipcRenderer.setMaxListeners(250);

View file

@ -3,90 +3,40 @@ const { Notification, ipcMain } = require("electron");
const store = require("./utils/store");
const { backgroundMitt, ipcEmit } = require("./utils/emitter");
const { connectToPlatform, disconnectFromPlatform } = require("./io");
// const { initAudio, terminateAudio, playback, streamState } = require("./audio");
const util = require('util');
let ui;
let current;
const { initAudio, terminateAudio, playback, streamState } = require("./audio");
const messageQueue = [];
let processContentId;
/* Possible messages:
* - New UI (set ui)
* - New message (add message to ui.messages)
* - New content (add content to existing message)
* - Update person
* - Notification
*/
backgroundMitt.on("message", (message) => messageQueue.push(message));
function handleMessageQueue()
function handleUpdateQueue()
{
// if (!streamState.pb && !streamState.rec)
// {
if (!streamState.pb && !streamState.rec)
{
const update = messageQueue.shift();
console.log(update);
if (update)
{
if (update.person)
{
if (update.title)
{
ui = update;
current = update.messages.at(-1);
new Notification(update).show();
}
else if (update.name)
else if (update.audio)
{
ui.person = update;
playback(update);
}
else if (update.modifier)
{
ui.messages.push(update);
current = update;
}
else if (update.category)
{
current.content.push(update);
}
else if (update.context)
{
current.context = update.context;
}
else if (update.text)
{
current.content.at(-1).text = update.text;
}
else
{
const content = current.content.at(-1);
new Notification({
title: current.context,
body: content.text
}).show();
/* Ideally, we want a more native solution than this */
// if (current.modifier == "ai" && content.category == "audio")
// {
// playback(content.blob, current.id);
// }
return;
}
ipcEmit("message", update);
else { ipcEmit("update", update); }
}
// }
}
}
function launchSession(account)
{
connectToPlatform(account);
// initAudio();
processContentId = setInterval(handleMessageQueue, 100);
processContentId = setInterval(handleUpdateQueue, 100);
}
function endSession()
@ -94,14 +44,7 @@ function endSession()
clearInterval(processContentId);
// terminateAudio();
disconnectFromPlatform();
ui = null;
}
ipcMain.on("messenger", () => {
if (ui) {
ipcEmit("message", ui);
}
});
exports.launchSession = launchSession;
exports.endSession = endSession;

View file

@ -5,7 +5,7 @@ const { Tray } = require("electron");
const { backgroundMitt } = require("./utils/emitter");
let tray;
const TRAY_PATH = path.join(__dirname, "assets/tray");
const TRAY_PATH = "./src/assets/tray/";
/* Current state of recording, playback, and ws connection */
const state = {
@ -19,24 +19,25 @@ const state = {
* @param {"recording" | "playback" | "disconnect"} attribute
* @param {boolean} newState
*/
function updateTray(attribute, newState)
{
function updateTray(attribute, newState) {
state[attribute] = newState;
// convert boolean to number
const bi = (b) => b ? 1 : 0;
// Make sure we have the icon we're looking for.
tray.setImage(path.join(
TRAY_PATH,
tray.setImage(
TRAY_PATH +
`${bi(state.recording)}${bi(state.playback)}${bi(state.disconnect)}` +
".png"
));
);
}
function initTray()
function initTray()
{
tray = new Tray(path.join(TRAY_PATH, "000.png"));
tray = new Tray(TRAY_PATH + "000" + ".png");
}
exports.initTray = initTray;

View file

@ -21,7 +21,7 @@ function updateWindowPosition()
}
function onNav(_e, cmd)
function onNavBar(_e, cmd)
{
cmd === "close" ? win.close() : win.minimize();
}
@ -42,8 +42,7 @@ function createWin()
frame: false,
minWidth: 350,
minHeight: 500,
backgroundColor: "#EBEBEB",
webPreferences: { preload: path.join(__dirname, "render/preload.js") }
webPreferences: { preload: path.join(__dirname, "/render/preload.js") }
})
// TODO: find a simpler way to notify platform of user events
@ -71,10 +70,10 @@ function createWin()
}
});
win.webContents.send("account", store.get("account"));
win.webContents.send("set-account", store.get("account"));
});
}
ipcMain.on("nav", onNav);
ipcMain.on("nav-bar", onNavBar);
exports.createWin = createWin;

785
yarn.lock Normal file
View file

@ -0,0 +1,785 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@crimata/nodeaudio@^0.0.0":
"integrity" "sha1-KTpDGuL8j3x72BLNb4Iyg8zgARo="
"resolved" "https://gitlab.com/api/v4/projects/28849281/packages/npm/@crimata/nodeaudio/-/@crimata/nodeaudio-0.0.0.tgz"
"version" "0.0.0"
dependencies:
"bindings" "~1.2.1"
"sleep" "^6.3.0"
"@electron/get@^1.0.1":
"integrity" "sha512-+SjZhRuRo+STTO1Fdhzqnv9D2ZhjxXP6egsJ9kiO8dtP68cDx7dFCwWi64dlMQV7sWcfW1OYCW4wviEBzmRsfQ=="
"resolved" "https://registry.npmjs.org/@electron/get/-/get-1.13.0.tgz"
"version" "1.13.0"
dependencies:
"debug" "^4.1.1"
"env-paths" "^2.2.0"
"fs-extra" "^8.1.0"
"got" "^9.6.0"
"progress" "^2.0.3"
"semver" "^6.2.0"
"sumchecker" "^3.0.1"
optionalDependencies:
"global-agent" "^2.0.2"
"global-tunnel-ng" "^2.7.1"
"@sindresorhus/is@^0.14.0":
"integrity" "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ=="
"resolved" "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz"
"version" "0.14.0"
"@szmarczak/http-timer@^1.1.2":
"integrity" "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA=="
"resolved" "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz"
"version" "1.1.2"
dependencies:
"defer-to-connect" "^1.0.1"
"@types/node@^14.6.2":
"integrity" "sha512-vhUqgjJR1qxwTWV5Ps5txuy2XMdf7Fw+OrdChRboy8BmWUPkckOhphaohzFG6b8DW7CrxaBMdrdJ47SYFq1okw=="
"resolved" "https://registry.npmjs.org/@types/node/-/node-14.17.12.tgz"
"version" "14.17.12"
"ajv-formats@^2.0.2":
"integrity" "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA=="
"resolved" "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz"
"version" "2.1.1"
dependencies:
"ajv" "^8.0.0"
"ajv@^8.0.0", "ajv@^8.1.0":
"integrity" "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w=="
"resolved" "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz"
"version" "8.6.2"
dependencies:
"fast-deep-equal" "^3.1.1"
"json-schema-traverse" "^1.0.0"
"require-from-string" "^2.0.2"
"uri-js" "^4.2.2"
"atomically@^1.7.0":
"integrity" "sha512-Xcz9l0z7y9yQ9rdDaxlmaI4uJHf/T8g9hOEzJcsEqX2SjCj4J20uK7+ldkDHMbpJDK76wF7xEIgxc/vSlsfw5w=="
"resolved" "https://registry.npmjs.org/atomically/-/atomically-1.7.0.tgz"
"version" "1.7.0"
"axios@^0.21.1":
"integrity" "sha512-dKQiRHxGD9PPRIUNIWvZhPTPpl1rf/OxTYKsqKUDjBwYylTvV7SjSHJb9ratfyzM6wCdLCOYLzs73qpg5c4iGA=="
"resolved" "https://registry.npmjs.org/axios/-/axios-0.21.1.tgz"
"version" "0.21.1"
dependencies:
"follow-redirects" "^1.10.0"
"base64-arraybuffer@^1.0.1":
"integrity" "sha512-vFIUq7FdLtjZMhATwDul5RZWv2jpXQ09Pd6jcVEOvIsqCWTRFD/ONHNfyOS8dA/Ippi5dsIgpyKWKZaAKZltbA=="
"resolved" "https://registry.npmjs.org/base64-arraybuffer/-/base64-arraybuffer-1.0.1.tgz"
"version" "1.0.1"
"bindings@~1.2.1":
"integrity" "sha1-FK1hE4EtLTfXLme0ystLtyZQXxE="
"resolved" "https://registry.npmjs.org/bindings/-/bindings-1.2.1.tgz"
"version" "1.2.1"
"boolean@^3.0.1":
"integrity" "sha512-3hx0kwU3uzG6ReQ3pnaFQPSktpBw6RHN3/ivDKEuU8g1XSfafowyvDnadjv1xp8IZqhtSukxlwv9bF6FhX8m0w=="
"resolved" "https://registry.npmjs.org/boolean/-/boolean-3.1.4.tgz"
"version" "3.1.4"
"buffer-crc32@~0.2.3":
"integrity" "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI="
"resolved" "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz"
"version" "0.2.13"
"buffer-from@^1.0.0":
"integrity" "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ=="
"resolved" "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz"
"version" "1.1.2"
"cacheable-request@^6.0.0":
"integrity" "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg=="
"resolved" "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz"
"version" "6.1.0"
dependencies:
"clone-response" "^1.0.2"
"get-stream" "^5.1.0"
"http-cache-semantics" "^4.0.0"
"keyv" "^3.0.0"
"lowercase-keys" "^2.0.0"
"normalize-url" "^4.1.0"
"responselike" "^1.0.2"
"clone-response@^1.0.2":
"integrity" "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws="
"resolved" "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz"
"version" "1.0.2"
dependencies:
"mimic-response" "^1.0.0"
"concat-stream@^1.6.2":
"integrity" "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw=="
"resolved" "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz"
"version" "1.6.2"
dependencies:
"buffer-from" "^1.0.0"
"inherits" "^2.0.3"
"readable-stream" "^2.2.2"
"typedarray" "^0.0.6"
"conf@^10.0.0":
"integrity" "sha512-iyy4ArqyQ/yrzNASNBN+jaylu53JRuq0ztvL6KAWYHj4iN56BVuhy2SrzEEHBodNbacZr2Pd/4nWhoAwc66T1g=="
"resolved" "https://registry.npmjs.org/conf/-/conf-10.0.2.tgz"
"version" "10.0.2"
dependencies:
"ajv" "^8.1.0"
"ajv-formats" "^2.0.2"
"atomically" "^1.7.0"
"debounce-fn" "^4.0.0"
"dot-prop" "^6.0.1"
"env-paths" "^2.2.1"
"json-schema-typed" "^7.0.3"
"onetime" "^5.1.2"
"pkg-up" "^3.1.0"
"semver" "^7.3.5"
"config-chain@^1.1.11":
"integrity" "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ=="
"resolved" "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz"
"version" "1.1.13"
dependencies:
"ini" "^1.3.4"
"proto-list" "~1.2.1"
"core-js@^3.6.5":
"integrity" "sha512-lM3GftxzHNtPNUJg0v4pC2RC6puwMd6VZA7vXUczi+SKmCWSf4JwO89VJGMqbzmB7jlK7B5hr3S64PqwFL49cA=="
"resolved" "https://registry.npmjs.org/core-js/-/core-js-3.16.3.tgz"
"version" "3.16.3"
"core-util-is@~1.0.0":
"integrity" "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
"resolved" "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz"
"version" "1.0.2"
"debounce-fn@^4.0.0":
"integrity" "sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ=="
"resolved" "https://registry.npmjs.org/debounce-fn/-/debounce-fn-4.0.0.tgz"
"version" "4.0.0"
dependencies:
"mimic-fn" "^3.0.0"
"debug@^2.6.9":
"integrity" "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA=="
"resolved" "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz"
"version" "2.6.9"
dependencies:
"ms" "2.0.0"
"debug@^4.1.0", "debug@^4.1.1":
"integrity" "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw=="
"resolved" "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz"
"version" "4.3.2"
dependencies:
"ms" "2.1.2"
"decompress-response@^3.3.0":
"integrity" "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M="
"resolved" "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz"
"version" "3.3.0"
dependencies:
"mimic-response" "^1.0.0"
"defer-to-connect@^1.0.1":
"integrity" "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ=="
"resolved" "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz"
"version" "1.1.3"
"define-properties@^1.1.3":
"integrity" "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ=="
"resolved" "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz"
"version" "1.1.3"
dependencies:
"object-keys" "^1.0.12"
"detect-node@^2.0.4":
"integrity" "sha512-T0NIuQpnTvFDATNuHN5roPwSBG83rFsuO+MXXH9/3N1eFbn4wcPjttvjMLEPWJ0RGUYgQE7cGgS3tNxbqCGM7g=="
"resolved" "https://registry.npmjs.org/detect-node/-/detect-node-2.1.0.tgz"
"version" "2.1.0"
"dot-prop@^6.0.1":
"integrity" "sha512-tE7ztYzXHIeyvc7N+hR3oi7FIbf/NIjVP9hmAt3yMXzrQ072/fpjGLx2GxNxGxUl5V73MEqYzioOMoVhGMJ5cA=="
"resolved" "https://registry.npmjs.org/dot-prop/-/dot-prop-6.0.1.tgz"
"version" "6.0.1"
dependencies:
"is-obj" "^2.0.0"
"duplexer3@^0.1.4":
"integrity" "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI="
"resolved" "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz"
"version" "0.1.4"
"electron-store@^8.0.0":
"integrity" "sha512-ZgRPUZkfrrjWSqxZeaxu7lEvmYf6tgl49dLMqxXGnEmliSiwv3u4rJPG+mH3fBQP9PBqgSh4TCuxHZImMMUgWg=="
"resolved" "https://registry.npmjs.org/electron-store/-/electron-store-8.0.0.tgz"
"version" "8.0.0"
dependencies:
"conf" "^10.0.0"
"type-fest" "^1.0.2"
"electron@^13.2.3":
"integrity" "sha512-FzWhbKHjq7ZTpPQFaYiLPL64erC8/BOsu5NlNN9nQ6f7rIP8ygKlNAlQit3vbOcksQAwItDUCIw4sW0mcaRpCA=="
"resolved" "https://registry.npmjs.org/electron/-/electron-13.2.3.tgz"
"version" "13.2.3"
dependencies:
"@electron/get" "^1.0.1"
"@types/node" "^14.6.2"
"extract-zip" "^1.0.3"
"encodeurl@^1.0.2":
"integrity" "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k="
"resolved" "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
"version" "1.0.2"
"end-of-stream@^1.1.0":
"integrity" "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q=="
"resolved" "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz"
"version" "1.4.4"
dependencies:
"once" "^1.4.0"
"env-paths@^2.2.0", "env-paths@^2.2.1":
"integrity" "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="
"resolved" "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz"
"version" "2.2.1"
"es6-error@^4.1.1":
"integrity" "sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg=="
"resolved" "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz"
"version" "4.1.1"
"escape-string-regexp@^4.0.0":
"integrity" "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="
"resolved" "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz"
"version" "4.0.0"
"extract-zip@^1.0.3":
"integrity" "sha512-xoh5G1W/PB0/27lXgMQyIhP5DSY/LhoCsOyZgb+6iMmRtCwVBo55uKaMoEYrDCKQhWvqEip5ZPKAc6eFNyf/MA=="
"resolved" "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz"
"version" "1.7.0"
dependencies:
"concat-stream" "^1.6.2"
"debug" "^2.6.9"
"mkdirp" "^0.5.4"
"yauzl" "^2.10.0"
"fast-deep-equal@^3.1.1":
"integrity" "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
"resolved" "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
"version" "3.1.3"
"fd-slicer@~1.1.0":
"integrity" "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4="
"resolved" "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz"
"version" "1.1.0"
dependencies:
"pend" "~1.2.0"
"find-up@^3.0.0":
"integrity" "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg=="
"resolved" "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"locate-path" "^3.0.0"
"follow-redirects@^1.10.0":
"integrity" "sha512-yLR6WaE2lbF0x4K2qE2p9PEXKLDjUjnR/xmjS3wHAYxtlsI9MLLBJUZirAHKzUZDGLxje7w/cXR49WOUo4rbsA=="
"resolved" "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.14.2.tgz"
"version" "1.14.2"
"fs-extra@^8.1.0":
"integrity" "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g=="
"resolved" "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz"
"version" "8.1.0"
dependencies:
"graceful-fs" "^4.2.0"
"jsonfile" "^4.0.0"
"universalify" "^0.1.0"
"get-stream@^4.1.0":
"integrity" "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w=="
"resolved" "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz"
"version" "4.1.0"
dependencies:
"pump" "^3.0.0"
"get-stream@^5.1.0":
"integrity" "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA=="
"resolved" "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz"
"version" "5.2.0"
dependencies:
"pump" "^3.0.0"
"global-agent@^2.0.2":
"integrity" "sha512-+20KpaW6DDLqhG7JDiJpD1JvNvb8ts+TNl7BPOYcURqCrXqnN1Vf+XVOrkKJAFPqfX+oEhsdzOj1hLWkBTdNJg=="
"resolved" "https://registry.npmjs.org/global-agent/-/global-agent-2.2.0.tgz"
"version" "2.2.0"
dependencies:
"boolean" "^3.0.1"
"core-js" "^3.6.5"
"es6-error" "^4.1.1"
"matcher" "^3.0.0"
"roarr" "^2.15.3"
"semver" "^7.3.2"
"serialize-error" "^7.0.1"
"global-tunnel-ng@^2.7.1":
"integrity" "sha512-4s+DyciWBV0eK148wqXxcmVAbFVPqtc3sEtUE/GTQfuU80rySLcMhUmHKSHI7/LDj8q0gDYI1lIhRRB7ieRAqg=="
"resolved" "https://registry.npmjs.org/global-tunnel-ng/-/global-tunnel-ng-2.7.1.tgz"
"version" "2.7.1"
dependencies:
"encodeurl" "^1.0.2"
"lodash" "^4.17.10"
"npm-conf" "^1.1.3"
"tunnel" "^0.0.6"
"globalthis@^1.0.1":
"integrity" "sha512-ZQnSFO1la8P7auIOQECnm0sSuoMeaSq0EEdXMBFF2QJO4uNcwbyhSgG3MruWNbFTqCLmxVwGOl7LZ9kASvHdeQ=="
"resolved" "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz"
"version" "1.0.2"
dependencies:
"define-properties" "^1.1.3"
"got@^9.6.0":
"integrity" "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q=="
"resolved" "https://registry.npmjs.org/got/-/got-9.6.0.tgz"
"version" "9.6.0"
dependencies:
"@sindresorhus/is" "^0.14.0"
"@szmarczak/http-timer" "^1.1.2"
"cacheable-request" "^6.0.0"
"decompress-response" "^3.3.0"
"duplexer3" "^0.1.4"
"get-stream" "^4.1.0"
"lowercase-keys" "^1.0.1"
"mimic-response" "^1.0.1"
"p-cancelable" "^1.0.0"
"to-readable-stream" "^1.0.0"
"url-parse-lax" "^3.0.0"
"graceful-fs@^4.1.6", "graceful-fs@^4.2.0":
"integrity" "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg=="
"resolved" "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz"
"version" "4.2.8"
"http-cache-semantics@^4.0.0":
"integrity" "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ=="
"resolved" "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz"
"version" "4.1.0"
"inherits@^2.0.3", "inherits@~2.0.3":
"integrity" "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
"resolved" "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz"
"version" "2.0.4"
"ini@^1.3.4":
"integrity" "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew=="
"resolved" "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz"
"version" "1.3.8"
"is-obj@^2.0.0":
"integrity" "sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w=="
"resolved" "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz"
"version" "2.0.0"
"isarray@~1.0.0":
"integrity" "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE="
"resolved" "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz"
"version" "1.0.0"
"json-buffer@3.0.0":
"integrity" "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg="
"resolved" "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz"
"version" "3.0.0"
"json-schema-traverse@^1.0.0":
"integrity" "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug=="
"resolved" "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz"
"version" "1.0.0"
"json-schema-typed@^7.0.3":
"integrity" "sha512-7DE8mpG+/fVw+dTpjbxnx47TaMnDfOI1jwft9g1VybltZCduyRQPJPvc+zzKY9WPHxhPWczyFuYa6I8Mw4iU5A=="
"resolved" "https://registry.npmjs.org/json-schema-typed/-/json-schema-typed-7.0.3.tgz"
"version" "7.0.3"
"json-stringify-safe@^5.0.1":
"integrity" "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
"resolved" "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz"
"version" "5.0.1"
"jsonfile@^4.0.0":
"integrity" "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss="
"resolved" "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz"
"version" "4.0.0"
optionalDependencies:
"graceful-fs" "^4.1.6"
"keyv@^3.0.0":
"integrity" "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA=="
"resolved" "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz"
"version" "3.1.0"
dependencies:
"json-buffer" "3.0.0"
"locate-path@^3.0.0":
"integrity" "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A=="
"resolved" "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"p-locate" "^3.0.0"
"path-exists" "^3.0.0"
"lodash@^4.17.10":
"integrity" "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
"resolved" "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz"
"version" "4.17.21"
"lowercase-keys@^1.0.0", "lowercase-keys@^1.0.1":
"integrity" "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA=="
"resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz"
"version" "1.0.1"
"lowercase-keys@^2.0.0":
"integrity" "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA=="
"resolved" "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz"
"version" "2.0.0"
"lru-cache@^6.0.0":
"integrity" "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA=="
"resolved" "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz"
"version" "6.0.0"
dependencies:
"yallist" "^4.0.0"
"matcher@^3.0.0":
"integrity" "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="
"resolved" "https://registry.npmjs.org/matcher/-/matcher-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"escape-string-regexp" "^4.0.0"
"mimic-fn@^2.1.0":
"integrity" "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg=="
"resolved" "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz"
"version" "2.1.0"
"mimic-fn@^3.0.0":
"integrity" "sha512-Ysbi9uYW9hFyfrThdDEQuykN4Ey6BuwPD2kpI5ES/nFTDn/98yxYNLZJcgUAKPT/mcrLLKaGzJR9YVxJrIdASQ=="
"resolved" "https://registry.npmjs.org/mimic-fn/-/mimic-fn-3.1.0.tgz"
"version" "3.1.0"
"mimic-response@^1.0.0", "mimic-response@^1.0.1":
"integrity" "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="
"resolved" "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz"
"version" "1.0.1"
"minimist@^1.2.5":
"integrity" "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
"resolved" "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz"
"version" "1.2.5"
"mkdirp@^0.5.4":
"integrity" "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ=="
"resolved" "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz"
"version" "0.5.5"
dependencies:
"minimist" "^1.2.5"
"ms@2.0.0":
"integrity" "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
"resolved" "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz"
"version" "2.0.0"
"ms@2.1.2":
"integrity" "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
"resolved" "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz"
"version" "2.1.2"
"nan@^2.14.1":
"integrity" "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ=="
"resolved" "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz"
"version" "2.15.0"
"normalize-url@^4.1.0":
"integrity" "sha512-9UZCFRHQdNrfTpGg8+1INIg93B6zE0aXMVFkw1WFwvO4SlZywU6aLg5Of0Ap/PgcbSw4LNxvMWXMeugwMCX0AA=="
"resolved" "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.1.tgz"
"version" "4.5.1"
"npm-conf@^1.1.3":
"integrity" "sha512-Yic4bZHJOt9RCFbRP3GgpqhScOY4HH3V2P8yBj6CeYq118Qr+BLXqT2JvpJ00mryLESpgOxf5XlFv4ZjXxLScw=="
"resolved" "https://registry.npmjs.org/npm-conf/-/npm-conf-1.1.3.tgz"
"version" "1.1.3"
dependencies:
"config-chain" "^1.1.11"
"pify" "^3.0.0"
"object-keys@^1.0.12":
"integrity" "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
"resolved" "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz"
"version" "1.1.1"
"once@^1.3.1", "once@^1.4.0":
"integrity" "sha1-WDsap3WWHUsROsF9nFC6753Xa9E="
"resolved" "https://registry.npmjs.org/once/-/once-1.4.0.tgz"
"version" "1.4.0"
dependencies:
"wrappy" "1"
"onetime@^5.1.2":
"integrity" "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg=="
"resolved" "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz"
"version" "5.1.2"
dependencies:
"mimic-fn" "^2.1.0"
"p-cancelable@^1.0.0":
"integrity" "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw=="
"resolved" "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz"
"version" "1.1.0"
"p-limit@^2.0.0":
"integrity" "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w=="
"resolved" "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz"
"version" "2.3.0"
dependencies:
"p-try" "^2.0.0"
"p-locate@^3.0.0":
"integrity" "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ=="
"resolved" "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"p-limit" "^2.0.0"
"p-try@^2.0.0":
"integrity" "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ=="
"resolved" "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz"
"version" "2.2.0"
"path-exists@^3.0.0":
"integrity" "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU="
"resolved" "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz"
"version" "3.0.0"
"pend@~1.2.0":
"integrity" "sha1-elfrVQpng/kRUzH89GY9XI4AelA="
"resolved" "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz"
"version" "1.2.0"
"pify@^3.0.0":
"integrity" "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY="
"resolved" "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz"
"version" "3.0.0"
"pkg-up@^3.1.0":
"integrity" "sha512-nDywThFk1i4BQK4twPQ6TA4RT8bDY96yeuCVBWL3ePARCiEKDRSrNGbFIgUJpLp+XeIR65v8ra7WuJOFUBtkMA=="
"resolved" "https://registry.npmjs.org/pkg-up/-/pkg-up-3.1.0.tgz"
"version" "3.1.0"
dependencies:
"find-up" "^3.0.0"
"prepend-http@^2.0.0":
"integrity" "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc="
"resolved" "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz"
"version" "2.0.0"
"process-nextick-args@~2.0.0":
"integrity" "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
"resolved" "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz"
"version" "2.0.1"
"progress@^2.0.3":
"integrity" "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
"resolved" "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz"
"version" "2.0.3"
"proto-list@~1.2.1":
"integrity" "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk="
"resolved" "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz"
"version" "1.2.4"
"pump@^3.0.0":
"integrity" "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww=="
"resolved" "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"end-of-stream" "^1.1.0"
"once" "^1.3.1"
"punycode@^2.1.0":
"integrity" "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
"resolved" "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz"
"version" "2.1.1"
"readable-stream@^2.2.2":
"integrity" "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw=="
"resolved" "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz"
"version" "2.3.7"
dependencies:
"core-util-is" "~1.0.0"
"inherits" "~2.0.3"
"isarray" "~1.0.0"
"process-nextick-args" "~2.0.0"
"safe-buffer" "~5.1.1"
"string_decoder" "~1.1.1"
"util-deprecate" "~1.0.1"
"require-from-string@^2.0.2":
"integrity" "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw=="
"resolved" "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz"
"version" "2.0.2"
"responselike@^1.0.2":
"integrity" "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec="
"resolved" "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz"
"version" "1.0.2"
dependencies:
"lowercase-keys" "^1.0.0"
"roarr@^2.15.3":
"integrity" "sha512-CHhPh+UNHD2GTXNYhPWLnU8ONHdI+5DI+4EYIAOaiD63rHeYlZvyh8P+in5999TTSFgUYuKUAjzRI4mdh/p+2A=="
"resolved" "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz"
"version" "2.15.4"
dependencies:
"boolean" "^3.0.1"
"detect-node" "^2.0.4"
"globalthis" "^1.0.1"
"json-stringify-safe" "^5.0.1"
"semver-compare" "^1.0.0"
"sprintf-js" "^1.1.2"
"safe-buffer@~5.1.0", "safe-buffer@~5.1.1":
"integrity" "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
"resolved" "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz"
"version" "5.1.2"
"semver-compare@^1.0.0":
"integrity" "sha1-De4hahyUGrN+nvsXiPavxf9VN/w="
"resolved" "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz"
"version" "1.0.0"
"semver@^6.2.0":
"integrity" "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
"resolved" "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz"
"version" "6.3.0"
"semver@^7.3.2", "semver@^7.3.5":
"integrity" "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ=="
"resolved" "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz"
"version" "7.3.5"
dependencies:
"lru-cache" "^6.0.0"
"serialize-error@^7.0.1":
"integrity" "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="
"resolved" "https://registry.npmjs.org/serialize-error/-/serialize-error-7.0.1.tgz"
"version" "7.0.1"
dependencies:
"type-fest" "^0.13.1"
"sleep@^6.3.0":
"integrity" "sha512-+WgYl951qdUlb1iS97UvQ01pkauoBK9ML9I/CMPg41v0Ze4EyMlTgFTDDo32iYj98IYqxIjDMRd+L71lawFfpQ=="
"resolved" "https://registry.npmjs.org/sleep/-/sleep-6.3.0.tgz"
"version" "6.3.0"
dependencies:
"nan" "^2.14.1"
"sprintf-js@^1.1.2":
"integrity" "sha512-VE0SOVEHCk7Qc8ulkWw3ntAzXuqf7S2lvwQaDLRnUeIEaKNQJzV6BwmLKhOqT61aGhfUMrXeaBk+oDGCzvhcug=="
"resolved" "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz"
"version" "1.1.2"
"string_decoder@~1.1.1":
"integrity" "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg=="
"resolved" "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz"
"version" "1.1.1"
dependencies:
"safe-buffer" "~5.1.0"
"sumchecker@^3.0.1":
"integrity" "sha512-MvjXzkz/BOfyVDkG0oFOtBxHX2u3gKbMHIF/dXblZsgD3BWOFLmHovIpZY7BykJdAjcqRCBi1WYBNdEC9yI7vg=="
"resolved" "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz"
"version" "3.0.1"
dependencies:
"debug" "^4.1.0"
"to-readable-stream@^1.0.0":
"integrity" "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q=="
"resolved" "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz"
"version" "1.0.0"
"tunnel@^0.0.6":
"integrity" "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg=="
"resolved" "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz"
"version" "0.0.6"
"type-fest@^0.13.1":
"integrity" "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="
"resolved" "https://registry.npmjs.org/type-fest/-/type-fest-0.13.1.tgz"
"version" "0.13.1"
"type-fest@^1.0.2":
"integrity" "sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA=="
"resolved" "https://registry.npmjs.org/type-fest/-/type-fest-1.4.0.tgz"
"version" "1.4.0"
"typedarray@^0.0.6":
"integrity" "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c="
"resolved" "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz"
"version" "0.0.6"
"universalify@^0.1.0":
"integrity" "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg=="
"resolved" "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz"
"version" "0.1.2"
"uri-js@^4.2.2":
"integrity" "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg=="
"resolved" "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz"
"version" "4.4.1"
dependencies:
"punycode" "^2.1.0"
"url-parse-lax@^3.0.0":
"integrity" "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww="
"resolved" "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz"
"version" "3.0.0"
dependencies:
"prepend-http" "^2.0.0"
"util-deprecate@~1.0.1":
"integrity" "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8="
"resolved" "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
"version" "1.0.2"
"wrappy@1":
"integrity" "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
"resolved" "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz"
"version" "1.0.2"
"ws@^7.3.1":
"integrity" "sha512-zP9z6GXm6zC27YtspwH99T3qTG7bBFv2VIkeHstMLrLlDJuzA7tQ5ls3OJ1hOGGCzTQPniNJoHXIAOS0Jljohg=="
"resolved" "https://registry.npmjs.org/ws/-/ws-7.5.4.tgz"
"version" "7.5.4"
"yallist@^4.0.0":
"integrity" "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
"resolved" "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz"
"version" "4.0.0"
"yauzl@^2.10.0":
"integrity" "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk="
"resolved" "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz"
"version" "2.10.0"
dependencies:
"buffer-crc32" "~0.2.3"
"fd-slicer" "~1.1.0"