This commit is contained in:
Sagi Dayan 2020-04-12 10:25:42 -04:00
parent 1eaf0a6176
commit b02adbdb43
59 changed files with 2617 additions and 341 deletions

5
.babelrc Normal file
View file

@ -0,0 +1,5 @@
{
"plugins": [
"@babel/plugin-transform-regenerator"
]
}

View file

@ -1,7 +1,8 @@
'use strict'
const User = use('App/Models/User');
const Child = use('App/Models/Child')
const Link = use('App/Models/Link')
const Child = use('App/Models/Child');
const Link = use('App/Models/Link');
const IceServer = use('App/Models/IceServer');
class AdminApiController {
async getUsers({response}) {
console.log('API');
@ -12,6 +13,8 @@ class AdminApiController {
// });
return users;
}
async addStunServer({request, response}) {}
async addTurnServer({request, response}) {}
}
module.exports = AdminApiController

View file

@ -32,8 +32,8 @@ class AuthController {
try {
const token = await auth.attempt(email, password);
const user = auth.user;
user.last_logged_in = new Date();
await user.save();
// user.last_logged_in = new Date();
// await user.save();
console.log('logged in');
} catch (e) {
console.error(e);

View file

@ -3,44 +3,18 @@ const {validate, rule} = use('Validator');
const User = use('App/Models/User');
const Child = use('App/Models/Child')
const Link = use('App/Models/Link');
// import FileUtils from '../../Utils/FileUtils';
const Call = use('App/Models/Call');
const FileUtils = use('App/Utils/FileUtils');
const UserChildUtils = use('App/Utils/UserChildUtils');
const uuidv4 = require('uuid').v4;
class ClientApiController {
async getConnections({request, auth, response}) {
try {
const user = auth.user;
const userLinks = (await user.links().fetch()).rows;
// console.log(userLinks.toJSON());
let links = Promise.resolve({});
const result = await Promise.all(userLinks.map(async (l) => {
const child = await l.child().fetch();
const is_parent = !!l.is_parent;
const childLinks = (await child.links().fetch())
.rows.filter(l => l.user_id != user.id);
const linkedUsers = await Promise.all(childLinks.map(async l => {
return (await l.user().fetch()).toJSON();
}));
return {
...child.toJSON(), linkedUsers, is_parent
}
}));
return result;
} catch (err) {
console.error(err);
response.send(err.message);
}
}
async getUser({auth}) {
const user = auth.user.toJSON();
const children = await Promise.all((await auth.user.links().fetch())
.rows.filter(l => l.is_parent)
.map(async (l) => {
return await l.child().fetch();
}));
const connections = await UserChildUtils.getUserConnections(user.id);
return {
...user, children
...user, connections: {...connections}
}
}
@ -70,6 +44,145 @@ class ClientApiController {
{user_id: auth.user.id, child_id: child.id, is_parent: true});
return child;
}
async createCall({auth, request, response}) {
try {
const user = auth.user;
const rules = {
connection_id: 'number|required',
child_id: 'number|required',
};
const validation = await validate(request.all(), rules);
if (validation.fails()) {
response.status(400);
response.send(validation.messages());
return false;
}
const body = request.body;
if (!(await UserChildUtils.isParentOf(user.id, body.child_id))) {
response.status(403);
response.send({code: 403, message: 'Unauthorized'});
return false;
}
if (!(await UserChildUtils.isUserConnectedToChild(
body.connection_id, body.child_id))) {
response.status(403);
response.send({code: 403, message: 'Unauthorized'});
return false;
}
const call = await Call.create({
state: 'NEW',
user_1: user.id,
user_2: body.connection_id,
child_id: body.child_id
});
return {
code: 0, data: call
}
} catch (error) {
console.error(error);
return error;
}
}
async getChild({auth, request, response}) {
const userId = auth.user.id;
const childId = request.params.id;
console.log(`${userId} -> ${childId}`);
const hasPermission =
await UserChildUtils.isUserConnectedToChild(userId, childId);
if (!hasPermission) {
response.status(403);
response.send(
{code: 403, message: `You have no permission to connect with child`});
return false;
}
const child = await Child.find(childId);
const parents = await UserChildUtils.getChildParents(childId);
const connections = await UserChildUtils.getChildConnections(childId);
return {
code: 0, data: {...child.toJSON(), parents, connections}
}
}
async createConnection({request, auth, response}) {
try {
const user = auth.user;
const rules = {
email: 'string|email|required',
is_parent: 'boolean|required',
child_id: 'number|required'
};
const validation = await validate(request.all(), rules);
if (validation.fails()) {
response.status(400);
response.send(validation.messages());
return false;
}
const body = request.body;
if (!await UserChildUtils.isParentOf(user.id, body.child_id)) {
response.status(403);
response.send({
code: 403,
message: `You have no permission to add connection to child`
});
return false;
}
const usersWithEmail =
(await User.query().where({email: body.email}).fetch()).rows;
if (!usersWithEmail.length) {
return {code: 404, message: 'No user with that Email...'};
}
const targetUser = usersWithEmail[0];
if (await UserChildUtils.isUserConnectedToChild(
targetUser.id, body.child_id)) {
return {code: 409, message: 'User already connected'};
}
return {
code: 0,
data: await UserChildUtils.addConnection(
body.child_id, targetUser.id, body.is_parent)
};
} catch (error) {
console.error(error);
return error;
}
//
}
async setChildProfileCover({request, auth, response}) {
try {
const rules = {
profile_cover: [rule('regex', /^(data:image\/\w+;base64).+/)]
};
const validation = await validate(request.all(), rules);
if (validation.fails()) {
response.status(400);
response.send(validation.messages());
return false;
}
const body = request.body;
const userId = auth.user.id;
const childId = request.params.id;
const isParent = await UserChildUtils.isParentOf(userId, childId);
if (!isParent) {
response.status(403);
response.send(
{code: 403, message: `You have no permission to edit this child`});
return false;
}
const child = await Child.find(childId);
const file = await FileUtils.saveBase64File(body.profile_cover);
console.log(file);
child.profile_cover = `/u/images/${file.fileName}`;
await child.save();
return child.profile_cover;
} catch (error) {
console.error(error);
return error;
}
}
}
module.exports = ClientApiController

View file

@ -0,0 +1,120 @@
'use strict'
const User = use('App/Models/User');
const UserChildUtils = use('App/Utils/UserChildUtils');
const Call = use('App/Models/Call');
const IceServer = use('App/Models/IceServer');
const calls = {};
class SignalingController {
constructor({socket, request, auth, call}) {
this.callId = socket.topic.split(':')[1];
this.user = auth.user;
this.socket = socket;
this.request = request;
this.register(call);
console.log(`User #${this.user.id} connected to call ${this.callId}`);
}
register(callModel) {
if (!calls[this.callId])
calls[this.callId] = new CallSession(callModel);
else
console.log(`Call #${this.callId} Already Found`);
const callSession = calls[this.callId];
callSession.registerUser(this.user, this.socket)
.then(
success => {
})
.catch(
error => {
});
}
onClose() {
console.log(`User #${this.user.id} left call ${this.callId}`);
}
}
class CallSession {
// states: NEW -> STARTED -> IN_PROGRESS -> ENDED
constructor(callModel) {
this.callId = callModel.id;
this.callModel = callModel;
this.state = callModel.state;
this.user_1 = {id: callModel.user_1, socket: null, userModel: null};
this.user_2 = {id: callModel.user_2, socket: null, userModel: null};
this.startTime = Date.now();
this.heartbeat = setInterval(this.onHeartbeat.bind(this), 5000);
}
onHeartbeat() {
console.log(`We have ${Object.keys(calls).length} ongoing calls. Ids=${
Object.keys(calls)}`)
console.log(`Heartbeat for call #${this.callId} State: ${this.state}`);
}
async registerUser(user, socket) {
let userIndex = -1;
if (this.user_1.id === user.id)
userIndex = 1;
else if (this.user_2.id === user.id)
userIndex = 2;
if (userIndex < 0) return false;
this[`user_${userIndex}`].userModel = user;
this[`user_${userIndex}`].socket = socket;
socket.on('wrtc:sdp:offer', this.onSdpOffer.bind(this));
socket.on('wrtc:sdp:answer', this.onSdpAnswer.bind(this));
socket.on('wrtc:ice', this.onIceCandidate.bind(this));
await this.updateState();
if (this.state === 'STARTED') await this.sendStandby(socket, userIndex);
if (this.state === 'IN_PROGRESS') await this.sendStart(socket, userIndex);
return true;
}
async sendStandby(socket, userIndex) {
const iceServers = (await IceServer.all()).rows.map(i => i.toJSON());
socket.emit('call:standby', {iceServers, id: userIndex});
}
async sendStart(socket, userIndex) {
const iceServers = (await IceServer.all()).rows.map(i => i.toJSON());
socket.emit('call:start', {iceServers, id: userIndex});
}
async updateState() {
console.log(`Call #${this.callId} state=${this.state}`);
switch (this.state) {
case 'NEW':
if (this.areAllPartnersConnected())
this.state = 'IN_PROGRESS';
else
this.state = 'STARTED';
case 'STARTED':
if (this.areAllPartnersConnected()) this.state = 'IN_PROGRESS';
}
console.log(`Call #${this.callId} state=${this.state}`);
}
areAllPartnersConnected() {
return !!this.user_1.socket && !!this.user_2.socket;
}
async onIceCandidate(payload) {
const {from = id, ice} = payload;
const to = from === 1 ? 2 : 1;
this[`user_${to}`].socket.emit('wrtc:ice', {sdp});
console.log(`[Signal] [onIceCandidate] ${from} -> ${to}`)
return true;
}
async onSdpOffer(payload) {
const {from = payload.id, sdp} = payload;
const to = from === 1 ? 2 : 1;
this[`user_${to}`].socket.emit('wrtc:sdp:offer', {sdp});
console.log(`[Signal] [onSdpOffer] ${from} -> ${to}`)
return true;
}
async onSdpAnswer(payload) {
const {from = payload.id, sdp} = payload;
const to = from === 1 ? 2 : 1;
this[`user_${to}`].socket.emit('wrtc:sdp:answer', {sdp});
console.log(`[Signal] [onSdpAnswer] ${from} -> ${to}`)
return true;
}
}
module.exports = SignalingController

View file

@ -0,0 +1,73 @@
'use strict'
const UserChildUtils = use('App/Utils/UserChildUtils');
const connectedUsers = {
};
class UserChannelController {
constructor({socket, request, auth}) {
console.log(`User #${auth.user.id} connected`);
this.socket = socket;
this.request = request;
this.user = auth.user;
connectedUsers[this.user.id] = this;
this.notifyOnConnection(true).catch(console.error);
// socket.on('close', this.onClose.bind(this))
}
onClose() {
this.notifyOnConnection(false)
.then(_ => {
delete connectedUsers[this.user.id];
})
.catch(console.error);
}
emit(event, data) {
this.socket.emit(event, data);
}
static getUserChannel(userId) {
return connectedUsers[userId];
}
static isUserOnline(userId) {
return !!UserChannelController.getUserChannel(userId);
}
async notifyOnConnection(online) {
this.user.last_logged_in = new Date();
try {
await this.user.save();
} catch (e) {
console.error(e);
}
const userConnections =
await UserChildUtils.getUserConnections(this.user.id);
const notifiedIds = [];
for (const child of userConnections.children) {
for (const user of child.connections) {
const channel = UserChannelController.getUserChannel(user.id);
if (channel && notifiedIds.indexOf(user.id) < 0 &&
user.id != this.user.id) {
/// connection:online
channel.emit(
`connection:${online ? 'online' : 'offline'}`,
this.user.toJSON());
notifiedIds.push(user.id);
}
}
}
for (const child of userConnections.connections) {
for (const user of child.connections) {
const channel = UserChannelController.getUserChannel(user.id);
if (channel && notifiedIds.indexOf(user.id) < 0 &&
user.id != this.user.id) {
channel.emit(
`connection:${online ? 'online' : 'offline'}`,
this.user.toJSON());
notifiedIds.push(user.id);
}
}
}
return true;
}
}
module.exports = UserChannelController

View file

@ -0,0 +1,32 @@
'use strict'
/** @typedef {import('@adonisjs/framework/src/Request')} Request */
/** @typedef {import('@adonisjs/framework/src/Response')} Response */
/** @typedef {import('@adonisjs/framework/src/View')} View */
const Call = use('App/Models/Call');
class WsCallAuth {
/**
* @param {object} ctx
* @param {Request} ctx.request
* @param {Function} next
*/
async wsHandle(ctx, next) {
const {request, auth, socket} = ctx;
const callId = Number(socket.topic.split(':')[1]);
const user = auth.user;
const call = await Call.find(callId);
if (!call) {
throw new Error('Call not found');
}
if (user.id === call.user_1 || user.id === call.user_2) {
ctx.call = call;
await next()
}
// call next to advance the request
else
throw new Error('Not allowed');
}
}
module.exports = WsCallAuth

9
app/Models/Call.js Normal file
View file

@ -0,0 +1,9 @@
'use strict'
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
class Call extends Model {
}
module.exports = Call

33
app/Models/IceServer.js Normal file
View file

@ -0,0 +1,33 @@
'use strict'
/** @type {typeof import('@adonisjs/lucid/src/Lucid/Model')} */
const Model = use('Model')
const uuidv4 = require('uuid').v4;
const crypto = require('crypto')
class IceServer extends Model {
toJSON() {
const json = {};
if (this.type === 'STUN') {
json.urls = `stun:${this.url}:${this.port}`;
} else {
json.urls = '';
json.username = `${Math.ceil(Date.now() / 1000)}:${uuidv4()}`
json.credential = this.getCredentials(json.username);
if (this.protocol === 'UDP') {
json.urls = `turn:${this.url}:${this.port}?transport=udp`;
} else if (this.protocol === 'TCP') {
json.urls = `turn:${this.url}:${this.port}?transport=tcp`;
}
}
return json;
}
getCredentials(username) {
return crypto.createHmac('sha1', this.secret)
.update(username)
.digest('base64');
}
}
module.exports = IceServer

View file

@ -0,0 +1,69 @@
const Link = use('App/Models/Link');
class UserChildUtils {
static async isUserConnectedToChild(user_id, child_id) {
const links = await Link.query().where({user_id, child_id}).fetch();
return !!links.rows.length;
}
static async isParentOf(user_id, child_id) {
const links =
await Link.query().where({user_id, child_id, is_parent: true}).fetch();
return !!links.rows.length;
}
static async getChildParents(child_id) {
const links = await Link.query().where({child_id, is_parent: true}).fetch();
const parents = await Promise.all(links.rows.map(async l => {
return l.user().fetch();
}));
return parents;
}
static async getUserConnections(user_id) {
const links = await Link.query().where({user_id}).fetch();
const connections = await links.rows.reduce(async (_prev, link) => {
const prev = await _prev;
const is_parent = link.is_parent;
const child = await link.child().fetch();
if (is_parent) {
const parents = await UserChildUtils.getChildParents(child.id);
const nonSameUserParents = parents.filter(p => p.id != user_id);
prev.children.push({
...child.toJSON(),
connections: [
...await UserChildUtils.getChildConnections(child.id),
...nonSameUserParents
]
})
} else {
prev.connections.push({
...child.toJSON(),
connections: [...await UserChildUtils.getChildParents(child.id)]
})
}
return prev;
}, Promise.resolve({children: [], connections: []}));
return connections;
}
static async getChildConnections(child_id) {
const links =
await Link.query().where({child_id, is_parent: false}).fetch();
const connections = await Promise.all(links.rows.map(async l => {
return l.user().fetch();
}));
return connections;
}
static async addConnection(child_id, user_id, is_parent = false) {
const link = await Link.create({child_id, user_id, is_parent});
const user = await link.user().fetch();
const child = await link.child().fetch();
return {
user, child, is_parent
}
}
}
module.exports = UserChildUtils;

View file

@ -20,7 +20,7 @@ module.exports = {
| is over 1mb it will not be processed.
|
*/
limit: '1mb',
limit: '20mb',
/*
|--------------------------------------------------------------------------
@ -44,10 +44,8 @@ module.exports = {
|
*/
types: [
'application/json',
'application/json-patch+json',
'application/vnd.api+json',
'application/csp-report'
'application/json', 'application/json-patch+json',
'application/vnd.api+json', 'application/csp-report'
]
},
@ -59,11 +57,7 @@ module.exports = {
|
|
*/
raw: {
types: [
'text/*'
]
},
raw: {types: ['text/*']},
/*
|--------------------------------------------------------------------------
@ -73,11 +67,7 @@ module.exports = {
|
|
*/
form: {
types: [
'application/x-www-form-urlencoded'
]
},
form: {types: ['application/x-www-form-urlencoded']},
/*
|--------------------------------------------------------------------------
@ -88,9 +78,7 @@ module.exports = {
|
*/
files: {
types: [
'multipart/form-data'
],
types: ['multipart/form-data'],
/*
|--------------------------------------------------------------------------

66
config/socket.js Normal file
View file

@ -0,0 +1,66 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Websocket Config
|--------------------------------------------------------------------------
|
| Used by AdonisJs websocket server
|
*/
module.exports = {
/*
|--------------------------------------------------------------------------
| Path
|--------------------------------------------------------------------------
|
| The base path on which the websocket server will accept connections.
|
*/
path: '/connect',
/*
|--------------------------------------------------------------------------
| Server Interval
|--------------------------------------------------------------------------
|
| This interval is used to create a timer for identifying dead client
| connections.
|
*/
serverInterval: 30000,
/*
|--------------------------------------------------------------------------
| Server Attempts
|--------------------------------------------------------------------------
|
| Server attempts are used with serverInterval to identify dead client
| connections. A total of `serverAttempts` attmepts after `serverInterval`
| will be made before terminating the client connection.
|
*/
serverAttempts: 3,
/*
|--------------------------------------------------------------------------
| Client Interval
|--------------------------------------------------------------------------
|
| This interval is used by client to send ping frames to the server.
|
*/
clientInterval: 25000,
/*
|--------------------------------------------------------------------------
| Client Attempts
|--------------------------------------------------------------------------
|
| Clients attempts are number of times the client will attempt to send the
| ping, without receiving a pong from the server. After attempts have
| been elapsed, the client will consider server as dead.
|
*/
clientAttempts: 3
}

View file

@ -11,6 +11,7 @@ class UserSchema extends Schema {
table.string('name').notNullable();
table.string('password', 60).notNullable();
table.string('avatar');
table.string('profile_cover');
table.boolean('is_admin').defaultTo(false).notNullable();
table.datetime('last_logged_in').defaultTo(null).nullable();
table.timestamps();

View file

@ -10,6 +10,7 @@ class ChildSchema extends Schema {
table.string('name');
table.date('dob');
table.string('avatar');
table.string('profile_cover');
table.timestamps();
})
}

View file

@ -0,0 +1,26 @@
'use strict'
/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema');
class CallSchema extends Schema {
up() {
this.create('calls', (table) => {
table.increments();
table.string('state').notNullable();
table.bigInteger('user_1').notNullable();
table.bigInteger('user_2').notNullable();
table.bigInteger('child_id').notNullable();
table.timestamps();
table.index(['child_id']);
table.index(['user_1']);
table.index(['user_2']);
})
}
down() {
this.drop('calls');
}
}
module.exports = CallSchema;

View file

@ -0,0 +1,24 @@
'use strict'
/** @type {import('@adonisjs/lucid/src/Schema')} */
const Schema = use('Schema');
class IceServerSchema extends Schema {
up() {
this.create('ice_servers', (table) => {
table.increments();
table.string('type', 254).notNullable();
table.string('url', 254).notNullable();
table.integer('port').notNullable();
table.string('protocol', 254);
table.string('secret', 254);
table.timestamps();
})
}
down() {
this.drop('ice_servers');
}
}
module.exports = IceServerSchema;

View file

@ -34,17 +34,26 @@
"@adonisjs/session": "^1.0.27",
"@adonisjs/shield": "^1.0.8",
"@adonisjs/validator": "^5.0.6",
"@adonisjs/websocket": "^1.0.12",
"@adonisjs/websocket-client": "^1.0.9",
"adonis-vue-websocket": "^2.0.2",
"animate.css": "^3.7.2",
"bulma": "^0.8.0",
"fork-awesome": "^1.1.7",
"moment": "^2.24.0",
"regenerator-runtime": "^0.13.5",
"sqlite3": "^4.1.1",
"typescript": "^3.7.5",
"uuid": "^7.0.3",
"vue": "^2.6.11",
"vue-router": "^3.1.5",
"vuex": "^3.1.2"
},
"devDependencies": {
"@babel/core": "^7.8.3",
"@babel/plugin-transform-regenerator": "^7.8.7",
"@babel/plugin-transform-runtime": "^7.9.0",
"@types/node": "^13.11.0",
"babel-loader": "^8.0.6",
"babel-preset-env": "^1.7.0",
"babel-preset-stage-2": "^6.24.1",

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="36px" height="33px" viewBox="0 0 36 33" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><g transform="translate(0 .5)" fill="none" fill-rule="evenodd"><path d="M20 2.236L5.618 31h28.764L20 2.236z" stroke="#FFFFFF" stroke-width="2"/><path fill="#FFFFFF" d="M12 2l12 24H0"/></g></svg>

Before

Width:  |  Height:  |  Size: 363 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

View file

@ -6843,17 +6843,17 @@ label.panel-block {
transform: rotate(-90deg);
margin: -.8em auto; }
.columns.is-fullheight {
min-height: calc(100vh - ( 3.25rem - .75rem ));
max-height: calc(100vh - ( 3.25rem - .75rem ));
height: calc(100vh - ( 3.25rem - .75rem ));
.is-fullheight {
min-height: calc(100vh - ( 3.25rem ));
max-height: calc(100vh - ( 3.25rem ));
height: calc(100vh - ( 3.25rem ));
display: flex;
flex-direction: row;
justify-content: stretch; }
.columns.is-fullheight .column {
.is-fullheight .column {
overflow-y: auto; }
.child-avatar-image {
.avatar-badge-image {
display: table;
margin: auto; }
@ -6863,3 +6863,54 @@ label.panel-block {
.m-auto {
margin: auto; }
.css-loader {
display: flex;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
left: 50%; }
.css-loader .dot {
margin: 5px;
width: 2rem;
height: 2rem;
background: #8A4D76;
border-radius: 50%;
animation-name: loading-pulse;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out; }
.css-loader .dot.delay-1 {
animation-delay: .3s; }
.css-loader .dot.delay-2 {
animation-delay: .6s; }
@keyframes loading-pulse {
0% {
opacity: .1;
transform: scale(0.7); }
50% {
opacity: 1;
transform: scale(1); }
100% {
opacity: .1;
transform: scale(0.7); } }
.has-pointer {
cursor: pointer; }
.notifications {
width: 15vw;
z-index: 100;
position: fixed;
right: 25px;
top: calc(25px + ( 3.25rem )); }
.notification-fade {
animation: notification-fade .2s; }
@keyframes notification-fade {
0% {
opacity: 0; }
100% {
opacity: 1; } }

View file

@ -1 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?><svg width="186px" height="31px" viewBox="0 0 186 31" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M20.25 6.106V.1H.72v6.006h5.712v17.388H.72V29.5h19.53v-6.006h-5.712V6.106h5.712zm18.774 7.35v-5.46h-6.846V1.528h-7.182v3.108c0 2.226-1.218 3.36-3.444 3.36h-.084v5.46h3.528v8.904c0 4.578 3.528 7.686 8.82 7.686 1.932 0 3.276-.126 5.208-1.05v-5.964c-2.016.882-2.982.966-3.822.966-1.806 0-3.024-1.008-3.024-2.898v-7.644h6.846zm37.17-5.46l-3.612 13.692L68.76 7.996H63.3l-3.822 13.692-3.654-13.692h-7.308L55.068 29.5h7.266l3.696-12.516L69.684 29.5h7.308l6.552-21.504h-7.35zM95.43 7.45c6.846 0 11.424 4.746 11.424 11.256 0 6.552-4.578 11.34-11.424 11.34-6.888 0-11.466-4.788-11.466-11.34 0-6.51 4.578-11.256 11.466-11.256zm0 5.628c-2.898 0-4.62 2.352-4.62 5.628 0 3.318 1.722 5.712 4.62 5.712 2.856 0 4.578-2.394 4.578-5.712 0-3.276-1.722-5.628-4.578-5.628zm22.092.714V7.996h-7.182V29.5h7.182v-7.518c0-5.376 1.89-7.728 8.946-6.972V7.534c-4.788 0-7.686 1.806-8.946 6.258zM145.158 29.5h8.4l-7.812-11.886 7.14-9.618h-8.316l-4.284 6.552h-3.612V.1h-7.182v29.4h7.182v-8.442h3.612l4.872 8.442zm22.092-14.196h6.384c-.462-5.46-4.326-7.854-9.87-7.854-6.09 0-9.534 2.436-9.534 6.804 0 4.998 4.494 5.586 8.19 6.426 3.822.882 4.704 1.134 4.704 2.478 0 1.512-1.428 1.932-2.982 1.932-2.394 0-3.822-.966-4.074-3.486h-6.384c.336 5.88 4.536 8.442 10.542 8.442 6.132 0 9.66-2.688 9.66-7.14 0-4.998-4.41-5.628-8.736-6.594-3.234-.672-4.326-.882-4.326-2.31 0-1.134 1.176-1.848 2.856-1.848 2.268 0 3.276.882 3.57 3.15zm11.424 4.536h6.258L185.94.1h-8.316l1.05 19.74zm-.63 9.66h7.518v-6.51h-7.518v6.51z" fill="#FFFFFF" fill-rule="evenodd"/></svg>

Before

Width:  |  Height:  |  Size: 1.7 KiB

View file

@ -4,6 +4,7 @@
@import './variables.scss';
@import './mixins.scss';
@import "../../node_modules/bulma/bulma.sass";
// @import '../../node_modules/animate.css/source/_base.css';
.hero-bg-landing01 {
background-image: url('/images/landing-hero01.jpg');
@ -132,11 +133,10 @@ $positions: (
margin: -.8em auto;
}
.columns{
&.is-fullheight{
min-height: calc(100vh - ( #{$navbar-height} - .75rem ) );
max-height: calc(100vh - ( #{$navbar-height} - .75rem ) );
height: calc(100vh - ( #{$navbar-height} - .75rem ) );
.is-fullheight{
min-height: calc(100vh - ( #{$navbar-height} ) );
max-height: calc(100vh - ( #{$navbar-height} ) );
height: calc(100vh - ( #{$navbar-height} ) );
display: flex;
flex-direction: row;
justify-content: stretch;
@ -144,9 +144,8 @@ $positions: (
overflow-y: auto;
}
}
}
.child-avatar-image{
.avatar-badge-image{
display: table;
margin: auto;
}
@ -158,3 +157,69 @@ $positions: (
.m-auto{
margin: auto;
}
.css-loader{
display: flex;
position: absolute;
transform: translate(-50%, -50%);
top: 50%;
left: 50%;
.dot{
margin: 5px;
width: 2rem;
height: 2rem;
background: $primary;
border-radius: 50%;
animation-name: loading-pulse;
animation-duration: 2s;
animation-iteration-count: infinite;
animation-timing-function: ease-in-out;
&.delay-1{
animation-delay:.3s;
}
&.delay-2{
animation-delay:.6s;
}
}
}
@keyframes loading-pulse{
0%{
opacity: .1;
transform: scale(.7);
}
50%{
opacity: 1;
transform: scale(1);
}
100%{
opacity: .1;
transform: scale(.7);
}
}
.has-pointer{
cursor: pointer;
}
.notifications {
width: 15vw;
z-index: 100;
position: fixed;
right: 25px;
top: calc(25px + ( #{$navbar-height} ) );
}
.notification-fade{
animation: notification-fade .2s;
}
@keyframes notification-fade{
0%{
opacity: 0;
}
100%{
opacity: 1;
}
}

View file

@ -1,4 +1,5 @@
<script lang="ts">
import "regenerator-runtime/runtime.js";
import Vue from "vue";
import Vuex from "vuex";
import App from "./app.vue";

View file

@ -2,7 +2,6 @@
<div class="wrapper">
<div class="has-text-centered">
<h3 class="title">Settings</h3>
<h4 class="subtitle">UserName</h4>
</div>
<div class="columns">
<div class="column is-one-quarter">

View file

@ -1,6 +1,18 @@
<template>
<div class="app">
<Header :appName="appName" />
<!-- <Header :appName="appName" /> -->
<div class="loading" v-if="loading">
<Loading />
</div>
<div class v-else>
<div class="notifications">
<Notification
v-for="notification in notifications"
:key="notification.id"
:notification="notification"
@onClose="onNotificationClose(notification)"
/>
</div>
<div class="columns m-t-xs is-fullheight">
<div class="column sidebar">
<SideBar :title="appName" :menu="menu" :appName="appName" />
@ -12,11 +24,16 @@
</section>
</div>
</div>
</div>
</template>
<script lang="ts">
import Header from "./components/Header.vue";
import Services from "../services/index";
import Notification from "../shared/components/Notification.vue";
import { mapActions, mapGetters } from "vuex";
import Loading from "../shared/components/Loading/Loading.vue";
import WebsocketService from "./scripts/websocket.service";
// import AppRouter from "./router/router.vue";
import {
default as SideBar,
@ -29,12 +46,6 @@ const menu: IMenuItem[] = [
isRouterLink: true,
icon: "fa fa-home"
},
{
href: "/applications",
text: "Applications",
isRouterLink: true,
icon: "fa fa-puzzle-piece"
},
{
href: "/settings",
isRouterLink: true,
@ -55,13 +66,39 @@ export default {
// router: AppRouter,
components: {
SideBar,
Header
Header,
Notification,
Loading
},
async created() {
await this.getUser();
this.ws = await WebsocketService.getInstance();
this.ws.on(WebsocketService.Events.CONNECTION_ONLINE, user => {
console.log(`User Online: ${JSON.stringify(user, null, 2)}`);
this.notify({ message: `${user.name} is online!`, level: "success" });
});
this.ws.on(WebsocketService.Events.CONNECTION_OFFLINE, user => {
this.notify({ message: `${user.name} disconnected`, level: "warning" });
});
this.loading = false;
return true;
},
data() {
return {
appName: "Seepur",
menu
menu,
loading: true,
ws: null
};
},
computed: {
...mapGetters(["notifications"])
},
methods: {
onNotificationClose(notification) {
this.dismissNotification(notification.id);
},
...mapActions(["dismissNotification", "getUser", "notify"])
}
};
</script>

View file

@ -0,0 +1,71 @@
<template>
<Modal
:title="`Add Connection to ${childName}`"
:isActive="isActive"
acceptText="Add"
rejectText="Cancel"
@accept="addConnection()"
@close="close()"
>
<div class="field">
<label class="label">Connection Email</label>
<input class="input" type="email" placeholder="name@zmail.com" v-model="email" />
</div>
<div class="field">
<label>
<input class="checkbox" type="checkbox" v-model="isParent" aria-label="isParent" />
Is this a parent? {{isParent ? "Yes" : "No"}}
</label>
</div>
</Modal>
</template>
<script lang="ts">
import Modal from "../../shared/components/Modal/Modal.vue";
import { mapActions } from "vuex";
export default {
name: "AddConnectionModal",
props: ["isActive", "childName"],
components: {
Modal
},
methods: {
close() {
this.reset();
this.$emit("dismiss");
},
reset() {
this.email = "";
this.isParent = false;
},
addConnection() {
// validate email
if (!validateEmail(this.email)) {
this.notify({
message: "Please provide a valid email",
level: "warning"
});
} else {
this.$emit("createNewConnection", {
email: this.email,
is_parent: this.isParent
});
this.reset();
}
},
...mapActions(["notify"])
},
data() {
return {
email: "",
isParent: false
};
}
};
function validateEmail(email) {
var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
return re.test(email);
}
</script>

View file

@ -0,0 +1,28 @@
<template>
<div :class="['has-text-centered' ,'p-t-sm', {'has-pointer': !!isLink}]" @click="onClick()">
<div class="avatar-badge-image">
<figure class="image is-48x48">
<img :src="img" alt="Placeholder image" class="is-rounded is-avatar" />
</figure>
</div>
<div class>{{text}}</div>
</div>
</template>
<script lang="ts">
export interface IChildAvatar {
url: string;
name: string;
}
export default {
name: "AvatarBadge",
props: ["img", "text", "isLink"],
created() {},
methods: {
onClick() {
this.$emit("onClick");
}
}
};
</script>

View file

@ -1,6 +1,6 @@
<template>
<!-- <div class="card"> -->
<div class="card-content">
<div class="card-content has-pointer" @click="goToChild(child)">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
@ -16,11 +16,16 @@
<!-- </div> -->
</template>
<script lang="ts">
import * as Moment from "moment";
import Moment from "moment";
let childAge;
export default {
name: "ChildCard",
props: ["child"],
methods: {
goToChild(child) {
this.$router.push({ path: `/child/${child.id}` });
}
},
created() {
this.childAge = Moment().diff(this.child.dob, "years");
},

View file

@ -0,0 +1,112 @@
<template>
<Modal
:title="`New Call`"
:isActive="isActive"
acceptText="Call"
rejectText="Cancel"
@accept="createCall()"
@close="close()"
>
<div class="columns">
<div class="column">
Select Child:
<div class="card">
<div class="card-content">
<avatar-badge
v-for="child in user.connections.children"
:key="child.id"
:img="child.avatar"
:text="child.name"
@onClick="onChildSelected(child)"
:isLink="true"
:style="child_id===child.id ? `background-color:rgba(56, 181, 187, 0.19); border-radius:15px;` : ''"
/>
</div>
</div>
</div>
<div class="column">
Select Connection:
<div class="card">
<div class="card-content">
<avatar-badge
v-for="user in connection_options"
:key="user.id"
:img="user.avatar"
:text="user.name"
@onClick="onConnectionSelected(user)"
:isLink="true"
:style="connection_id===user.id ? `background-color:rgba(56, 181, 187, 0.19); border-radius:15px` : ''"
/>
</div>
</div>
</div>
</div>
</Modal>
</template>
<script lang="ts">
import Modal from "../../shared/components/Modal/Modal.vue";
import AvatarBadge from "./AvatarBadge.vue";
import { mapActions, mapGetters } from "vuex";
export default {
name: "ConfigureNewCallModal",
props: ["isActive"],
components: {
Modal,
AvatarBadge
},
computed: {
...mapGetters(["user"])
},
methods: {
close() {
this.reset();
this.$emit("dismiss");
},
onChildSelected(child) {
this.child_id = child.id;
this.connection_options = child.connections;
},
onConnectionSelected(user) {
this.connection_id = user.id;
},
reset() {
this.connection_id = "";
this.child_id = false;
},
validate() {
if (!this.child_id || !this.connection_id) return false;
const children = this.user.connections.children;
for (const child of children) {
for (const connection of child.connections) {
if (connection.id === this.connection_id) return true;
}
}
return false;
},
createCall() {
if (!this.validate()) {
this.notify({
message: "Please select a child and a user",
level: "warning"
});
} else {
this.$emit("newCall", {
connection_id: this.connection_id,
child_id: this.child_id
});
this.reset();
}
},
...mapActions(["notify"])
},
data() {
return {
connection_id: null,
child_id: null,
connection_options: []
};
}
};
</script>

View file

@ -0,0 +1,23 @@
<template>
<section class="hero is-small p-t-xl p-b-lg" :style="style">
<div class="hero-body">
<div class="container">
<h1 class="title has-text-light">{{title}}</h1>
</div>
</div>
</section>
</template>
<script lang="ts">
export default {
name: "ProfileHeader",
props: ["title", "background"],
computed: {
style() {
return `background-image: url('${this.background ||
"/images/landing-hero01.jpg"}');
background-size: cover;
background-position: center;`;
}
}
};
</script>

View file

@ -1,6 +1,6 @@
<template>
<div class="chiled-avatar has-text-centered">
<div class="child-avatar-image">
<div class="avatar-badge-image">
<figure class="image is-48x48">
<img :src="child.avatar" alt="Placeholder image" class="is-rounded is-avatar" />
</figure>

View file

@ -1,10 +1,14 @@
<script lang="ts">
import "regenerator-runtime/runtime.js";
// import Ws from "@adonisjs/websocket-client";
// import WsPlugin from "adonis-vue-websocket";
import Vue from "vue";
import Vuex from "vuex";
import App from "./app.vue";
import AppRouter from "./router/router.vue";
import store from "./state.vuex";
// Vue.use(WsPlugin, { adonisWS: Ws });
Vue.use(Vuex);
const app = new Vue({
router: AppRouter,

View file

@ -12,7 +12,8 @@ Vue.use(VueRouter);
// Views
import Home from "../views/home.vue";
import Settings from "../views/settings.vue";
import Applications from "../views/application.vue";
import Call from "../views/call.vue";
import ChildProfile from "../views/child_profile.vue";
const routes: RouteConfig[] = [
/** Define Application Routes */
@ -26,8 +27,12 @@ const routes: RouteConfig[] = [
component: Settings
},
{
path: "/applications",
component: Applications
path: "/call/:id",
component: Call
},
{
path: "/child/:id",
component: ChildProfile
},
{
path: "*",

View file

@ -0,0 +1,165 @@
export default class CallService {
private callId: string;
private inCall: boolean;
private peerId: number;
private subscription;
private localStream: MediaStream;
private remoteStream: MediaStream;
private needToAddStream: boolean = true;
private pc: RTCPeerConnection;
constructor(private ws) {
this.callId = null;
this.inCall = false;
this.peerId = -1;
this.subscription = null;
this.pc = null;
this.localStream = null;
this.remoteStream = null;
this.remoteStream = new MediaStream();
}
async connectToCall(callId: string): Promise<boolean> {
if (this.inCall) throw new Error('Already connected to call');
this.subscription = this.ws.subscribe(`call:${callId}`);
const subscription = this.subscription;
const self = this;
return new Promise((resolve, reject) => {
subscription.on('error', (e) => {
console.error(e);
resolve(false)
});
subscription.on('ready', () => {
this.callId = callId;
this.inCall = true;
resolve(true)
});
subscription.on('close', self.close.bind(this));
subscription.on('call:start', self.onCallStart.bind(this));
subscription.on('call:standby', self.onCallStandby.bind(this));
subscription.on('wrtc:sdp:offer', self.onRemoteOffer.bind(this));
subscription.on('wrtc:sdp:answer', self.onRemoteAnswer.bind(this));
subscription.on('wrtc:ice', self.onRemoteIce.bind(this));
});
}
private send(event: string, payload: { [key: string]: any }) {
this.subscription.emit(event, {
id: this.peerId,
...payload
})
}
async onCallStart(payload: { iceServers: RTCIceServer[], id: number }) {
console.log(payload);
this.peerId = payload.id;
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
console.log('Created PeerConnection');
this.setupPeerConnectionListeners();
const sdp = await this.pc.createOffer();
await this.pc.setLocalDescription(sdp);
console.log('Local description Set');
this.send('wrtc:sdp:offer', {
sdp
});
return true;
}
async onCallStandby(payload: { iceServers: RTCIceServer[], id: number }) {
console.log(payload);
this.peerId = payload.id;
this.pc = new RTCPeerConnection({ iceServers: payload.iceServers });
console.log('Created PeerConnection');
this.setupPeerConnectionListeners();
return true;
}
setupPeerConnectionListeners() {
this.pc.addEventListener("icecandidate", this.onLocalIce.bind(this));
this.pc.addEventListener('connectionstatechange', event => {
console.log(`PC Connection state: ${this.pc.connectionState}`);
// if (this.pc.connectionState === 'connected') {
// // Peers connected!
// }
});
this.pc.addEventListener('iceconnectionstatechange', event => {
console.log('iceconnectionstatechange');
console.log(this.pc.iceConnectionState);
})
this.pc.addEventListener('track', async (event) => {
console.log('On remote track!');
this.remoteStream.addTrack(event.track);
});
this.pc.addEventListener('icegatheringstatechange', event => {
console.log('icegatheringstatechange', this.pc.iceGatheringState);
});
if (this.needToAddStream && this.localStream) {
this.localStream.getTracks().forEach(t => {
console.log('adding track to pc - in the event list');
console.log(t);
this.pc.addTrack(t, this.localStream);
});
this.needToAddStream = false;
}
}
onLocalIce(event) {
if (event.candidate) {
console.log('Sending candidate');
this.send('wrtc:ice', {
ice: event.candidate
});
}
}
async onRemoteOffer(payload) {
const offer = new RTCSessionDescription(payload.sdp);
await this.pc.setRemoteDescription(offer);
console.log('Remote offer Set');
const sdp = await this.pc.createAnswer();
this.send('wrtc:sdp:answer', {
sdp
});
await this.pc.setLocalDescription(sdp);
console.log('Local answer Set');
return true;
}
async onRemoteAnswer(payload) {
const answer = new RTCSessionDescription(payload.sdp);
await this.pc.setRemoteDescription(answer);
console.log('Remote answer Set');
return true;
}
async onRemoteIce(payload) {
const ice = payload.ice;
await this.pc.addIceCandidate(ice);
return true;
}
async getUserMedia(constraints: MediaStreamConstraints = { video: false, audio: true }) {
if (this.localStream) return this.localStream;
this.localStream = await navigator.mediaDevices.getUserMedia(constraints);
if (this.pc) {
if (this.needToAddStream && this.localStream) {
this.localStream.getTracks().forEach(t => {
console.log('adding track to pc - in get user media');
this.pc.addTrack(t, this.localStream);
});
this.needToAddStream = false;
}
}
return this.localStream;
}
getRemoteStream() {
return this.remoteStream;
}
close() {
if (this.subscription) this.subscription.close();
this.subscription = null;
this.inCall = false;
}
}

View file

@ -0,0 +1,35 @@
let singleton: UserChannel = null;
export default class UserChannel {
private subscription;
private constructor(private ws) {
this.subscription = null;
}
async connect(): Promise<boolean> {
this.subscription = this.ws.subscribe(`user_channel`);
const subscription = this.subscription;
const self = this;
return new Promise((resolve, reject) => {
subscription.on('error', () => { resolve(false) });
subscription.on('ready', () => {
resolve(true)
});
subscription.on('close', self.close);
});
}
on(event: string, callback: (...args) => void) {
if (this.subscription) this.subscription.on(event, callback);
}
static async getInstance(ws) {
if (singleton) return singleton;
else return new UserChannel(ws);
}
close() {
this.subscription.close();
singleton = null;
}
}

View file

@ -0,0 +1,98 @@
import Ws from "@adonisjs/websocket-client";
import CallService from './call.service';
import UserChannelService from './user.channel.service';
import { EventEmitter } from 'events';
let singleton: WebSocketService = null;
enum EEvents {
NEW_CONNECTION,
CONNECTION_ONLINE,
CONNECTION_OFFLINE,
INCOMING_CALL,
SIGNALING_EVENTS,
CALL_ACTIONS
}
export default class WebSocketService {
static Events = EEvents;
private emitter;
private callService: CallService;
private constructor(private ws, private userChannelService: UserChannelService) {
this.emitter = new EventEmitter();
this.callService = new CallService(this.ws);
this.userChannelService.on('new:connection', this.onUserNewConnection.bind(this));
this.userChannelService.on('connection:online', this.onUserConnectionOnline.bind(this));
this.userChannelService.on('connection:offline', this.onUserConnectionOffline.bind(this));
}
on(event: EEvents, callback: Function) {
this.emitter.on(event, callback);
}
removeListener(event: EEvents, callback) {
this.emitter.removeListener(event, callback);
}
// onPublicChannelMessage(msg) {
// this.emitter
// }
private onUserNewConnection(data) {
this.emitter.emit(EEvents.NEW_CONNECTION, data);
}
private onUserConnectionOnline(data) {
this.emitter.emit(EEvents.CONNECTION_ONLINE, data);
}
private onUserConnectionOffline(data) {
this.emitter.emit(EEvents.CONNECTION_OFFLINE, data);
}
async getLocalMedia(constraints: MediaStreamConstraints = null) {
return this.callService.getUserMedia(constraints);
}
getRemoteStream() {
return this.callService.getRemoteStream();
}
static getInstance(): Promise<WebSocketService> {
return new Promise((resolve, reject) => {
// resolve();
// return;
if (singleton) return resolve(singleton);
const ws = Ws('', { path: 'connect' });
ws.connect();
ws.on('open', async () => {
const userChannelService = await UserChannelService.getInstance(ws);
const success = await userChannelService.connect();
console.log('Connected to user socket:', success);
singleton = new WebSocketService(ws, userChannelService);
resolve(singleton);
});
ws.on('error', (error) => {
console.log(error)
reject(new Error('Failed to connect'));
})
ws.on('close', _ => {
console.log('Socket Closed');
});
});
}
async connectToCall(callId: string) {
return this.callService.connectToCall(callId);
}
async leaveCall() {
this.callService.close();
}
onSignalingMsg(message) {
console.log(message);
}
}

View file

@ -6,15 +6,30 @@ const store = new Store({
strict: true,
state: {
user: null,
notifications: []
},
getters: {
user(state) {
return state.user;
},
notifications(state) {
return state.notifications;
}
},
mutations: {
setUser: (state, user) => {
setUser(state, user) {
state.user = user;
},
notify(state, notification) {
const id = Math.ceil(Math.random() * 1000);
state.notifications.push({ ...notification, id });
const dispatch = this.dispatch;
setTimeout(() => {
dispatch("dismissNotification", id);
}, 5000);
},
dismissNotification(state, noteId: number) {
state.notifications = state.notifications.filter(n => n.id != noteId);
}
},
actions: {
@ -22,6 +37,12 @@ const store = new Store({
const user = await Services.ApiService.getUser(userId);
ctx.commit('setUser', user);
},
notify(ctx, notification: { message: string, level?: "info" | "warning" | "success" | "danger" }) {
ctx.commit("notify", notification);
},
dismissNotification(ctx, id: number) {
ctx.commit("dismissNotification", id);
}
}
});
export default store;

View file

@ -1,15 +0,0 @@
<template>
<div class="wrapper">
<h1 class="is-1">Applications!!!</h1>
</div>
</template>
<script lang="ts">
export default {
name: "Applications",
beforeCreate: () => {
console.log("before create home vue");
}
};
</script>

View file

@ -0,0 +1,77 @@
<template>
<div class="wrapper">
<div v-if="loading">
<Loading />
</div>
<div v-else>
<video
:srcObject="localStream"
autoplay="true"
controls="false"
playsinline="true"
muted="true"
/>
<video :srcObject="remoteStream" autoplay="true" controls="false" />
</div>
</div>
</template>
<script lang="ts">
import Loading from "../../shared/components/Loading/Loading.vue";
import WebsocketService from "../scripts/websocket.service";
import Services from "../../services/index";
import { mapActions, mapGetters } from "vuex";
export default {
components: {
Loading
},
name: "Call",
async created() {
this.loading = true;
try {
const ws = await WebsocketService.getInstance();
const success = await ws.connectToCall(this.$route.params.id);
if (!success) {
this.notify({ message: "Can find this call...", level: "danger" });
this.$router.push({ path: `/` });
return false;
}
this.signalingChannel = this.localStream = await ws.getLocalMedia({
video: false,
audio: true
});
this.remoteStream = ws.getRemoteStream();
console.log(this.localStream);
this.notify({ message: "Connected!", level: "success" });
} catch (e) {
console.error(e);
this.notify({ message: e.message, level: "danger" });
}
this.loading = false;
},
async beforeDestroy() {
console.log("destroyed");
const ws = await WebsocketService.getInstance();
ws.leaveCall();
return true;
},
methods: {
...mapActions(["notify"])
},
computed: {
...mapGetters([])
},
data() {
return {
loading: true,
call: null,
localStream: null,
remoteStream: null,
signalingChannel: null
};
},
beforeCreate: () => {}
};
</script>

View file

@ -0,0 +1,258 @@
<template>
<div class="wrapper">
<div class="loading" v-if="loading">
<Loading />
</div>
<div class v-else>
<!-- Profile Cover Modal -->
<Modal
title="Change Cover"
:isActive="showCoverModal"
acceptText="Change"
rejectText="Cancel"
@accept="changeCover()"
@close="showCoverModal=false"
>
<ProfileHeader
:title="child.name"
:background="childCoverModalImage ? childCoverModalImage : child.profile_cover"
/>
<file-select v-model="childCoverModalImage" accept="image/*" lable="Select Cover:"></file-select>
</Modal>
<!-- Add Connection Modal -->
<AddConnectionModal
@createNewConnection="addConnection($event)"
@dismiss="showAddConnectionModal=false"
:isActive="showAddConnectionModal"
:childName="child.name"
/>
<!-- Profile -->
<ProfileHeader :title="child.name" :background="child.profile_cover" />
<div class="columns is-fullheight m-t-md">
<div class="column is-3">
<div class="card">
<div class="card-image">
<figure class="image is-4by4 p-md">
<img :src="child.avatar" class="is-rounded is-avatar" />
</figure>
</div>
<div class="card-content">
<div class="content">
<p>Hi!</p>
<p>Im {{age}}</p>
<br />
</div>
</div>
<footer v-if="isParent" class="card-footer">
<a
class="card-footer-item"
@click="togleEditMode()"
>{{inEditMode ? 'Cancel' : 'Edit'}}</a>
<a class="card-footer-item is-danger" @click="onDeleteClicked()">Delete</a>
</footer>
</div>
</div>
<div class="column">
<div class="card">
<div class="card-content">
<nav class="level">
<!-- Left side -->
<div class="level-left">
<div class="level-item">
<h1 class="subtitle">Parents</h1>
</div>
</div>
<!-- Right side -->
<div class="level-right">
<div class="level-item" v-if="isParent">
<button class="button" @click="showAddConnectionModal = true">
<i class="fa fa-fw fa-plus"></i> Add Connection
</button>
</div>
</div>
</nav>
<div class="parents">
<div class="columns">
<AvatarBadge
class="column"
v-for="parent in child.parents"
:key="parent.id"
:img="parent.avatar"
:text="parent.name"
:isLink="user.id === parent.id"
@onClick="goToUserProfile(parent)"
/>
</div>
</div>
<nav class="level">
<!-- Left side -->
<div class="level-left">
<div class="level-item">
<h1 class="subtitle">Connections</h1>
</div>
</div>
<!-- Right side -->
</nav>
<div class="columns">
<AvatarBadge
class="column"
v-for="connection in child.connections"
:key="connection.id"
:img="connection.avatar"
:text="connection.name"
:isLink="user.id === connection.id"
@onClick="goToUserProfile(connection)"
/>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts">
import { mapActions, mapGetters } from "vuex";
import ChildAvatar, { IChildAvatar } from "../components/child_avatar.vue";
import Services from "../../services/index";
import Loading from "../../shared/components/Loading/Loading.vue";
import ProfileHeader from "../components/ProfileHeader.vue";
import AddConnectionModal from "../components/AddConnectionModal.vue";
import FileSelect from "../../shared/components/FileSelect/FileSelect.vue";
import AvatarBadge from "../components/AvatarBadge.vue";
import Moment from "moment";
import Modal from "../../shared/components/Modal/Modal.vue";
export default {
name: "ChildProfile",
components: {
ChildAvatar,
Loading,
ProfileHeader,
Modal,
FileSelect,
AvatarBadge,
AddConnectionModal
},
beforeCreate() {},
async created() {
const response = await Services.ApiService.getChild(this.$route.params.id);
this.loading = false;
if (response.code === 0) {
console.log(response);
//Cool
this.child = response.data;
if (!this.user) await this.getUser();
this.isParent = this.child.parents.reduce((isParent, parent) => {
if (isParent) return true;
return parent.id === this.user.id;
}, this.isParent);
} else {
// notCool
this.notify({ message: "Sorry, Child not found!", level: "danger" });
this.$router.push({ path: `/` });
}
return true;
},
data() {
return {
loading: true,
child: null,
isParent: false,
inEditMode: false,
showCoverModal: false,
showAddConnectionModal: false,
childCoverModalImage: null
};
},
methods: {
onDeleteClicked() {
this.notify({ message: "Test" });
},
goToUserProfile(user) {
if (this.user.id === user.id) {
this.$router.push({ path: `/` });
} else {
this.$router.push({ path: `/user/${user.id}` });
}
},
async addConnection(_connection) {
try {
this.loading = true;
const connection = await Services.ApiService.createConnection({
..._connection,
child_id: this.child.id
});
if (connection.code === 409) {
this.loading = false;
this.showAddConnectionModal = false;
return this.notify({ message: connection.message, level: "warning" });
} else if (connection.code !== 0) {
this.loading = false;
this.showAddConnectionModal = false;
return this.notify({ message: connection.message, level: "danger" });
}
// debugger;
this.notify({
message: `Awesome!\n${connection.data.user.name} is connected to ${this.child.name}`,
level: "success"
});
if (connection.data.is_parent) {
this.child.parents.push(connection.data.user);
} else {
this.child.connections.push(connection.data.user);
}
console.log(connection);
} catch (e) {
console.error(e);
}
this.loading = false;
this.showAddConnectionModal = false;
return true;
},
async changeCover() {
if (this.childCoverModalImage) {
this.loading = true;
try {
this.child.profile_cover = await Services.ApiService.updateChildCover(
this.child.id,
this.childCoverModalImage
);
} catch (error) {
console.error(error);
}
this.loading = false;
}
this.showCoverModal = false;
this.this.childCoverModalImage = null;
},
togleEditMode() {
this.inEditMode = !this.inEditMode;
if (this.inEditMode) {
this.showCoverModal = true;
}
},
...mapActions(["getUser", "getConnections", "notify"])
},
computed: {
age() {
const years = Moment().diff(this.child.dob, "years");
const months = Moment().diff(this.child.dob, "months") % 12;
const isNewBorn = !years && !months;
let text = "a new boarn!";
if (!isNewBorn) {
text = "";
if (years) text += `${years} years`;
if (years && months) text += ` and`;
if (months) text += ` ${months} months`;
text += " old";
}
return `${text}`;
},
...mapGetters(["user", "connections"])
}
};
</script>

View file

@ -1,45 +1,116 @@
<template>
<div class="wrapper">
<div class="loading" v-if="loading">
<Loading />
</div>
<div class v-else>
<!-- Profile Cover Modal -->
<Modal
title="Change Cover"
:isActive="showCoverModal"
acceptText="Change"
rejectText="Cancel"
@accept="changeCover()"
@close="showCoverModal=false"
>
<ProfileHeader
:title="user.name"
:background="childCoverModalImage ? childCoverModalImage : user.profile_cover"
/>
<file-select v-model="childCoverModalImage" accept="image/*" lable="Select Cover:"></file-select>
</Modal>
<!-- Create Call Modal -->
<ConfigureNewCallModal
@newCall="makeCall($event)"
@dismiss="showCreateCallModal=false"
:isActive="showCreateCallModal"
/>
<!-- Profile -->
<ProfileHeader :title="user.name" :background="user.profile_cover" />
<div class="columns is-fullheight m-t-md">
<div class="column is-3">
<div class="card">
<div class="card-image">
<figure class="image is-4by4 p-md">
<img :src="user.avatar" class="is-rounded is-avatar" />
</figure>
</div>
<div class="card-content">
<div v-if="!![...user.connections.children, ...user.connections.connections].length">
<p class="card-header-title">Connections</p>
<ChildCard
v-for="child in [...user.connections.children, ...user.connections.connections]"
:key="child.id"
:child="child"
></ChildCard>
<br />
</div>
</div>
</div>
</div>
<div class="column">
<div class="card">
<div class="card-content">
<nav class="level">
<!-- Left side -->
<div class="level-left">
<div class="level-item">
<p class="subtitle">
<i class="fa fa-users"></i>&nbsp;My Connections
</p>
<h1 class="title">My Room</h1>
</div>
</div>
<!-- Right side -->
<div class="level-right">
<!-- <div class="level-item">
<a href="#" class="button is-success">
<i class="fa fa-plus"></i>&nbsp;Add a new Child
</a>
</div>-->
<div class="level-item">
<a href="#" class="button is-primary">
<i class="fa fa-plug"></i>&nbsp;Connect to a child
</a>
<button class="button">
<i class="fa fa-fw fa-plus"></i> Add
</button>
<button class="button is-success m-l-md" @click="showCreateCallModal=true">
<i class="fa fa-fw fa-phone"></i> Call
</button>
</div>
</div>
</nav>
<div class="card m-b-lg">
<div v-for="connection in connections" :key="connection.id">
<div class="card-content">
<div class="media">
<div class="media-left">
<figure class="image is-48x48">
<img :src="connection.avatar" alt="Placeholder image" class="is-rounded is-avatar" />
<div class="Games">
<h2 class="subtitle">
<i class="fa fa-fw fa-puzzle-piece"></i> My Games
</h2>
<div class="is-flex m-b-md">
<div class="game m-l-md" v-for="i of [1, 2, 3, 4]" :key="i">
<div class="game-cover">
<figure class="image is-2by3 m-a">
<img
src="https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fblog.springshare.com%2Fwp-content%2Fuploads%2F2010%2F02%2Fnc-md.gif&f=1&nofb=1"
/>
</figure>
</div>
<div class="media-content">
<p class="title is-4">{{connection.name}}</p>
<p class="subtitle is-6">@code</p>
<div class="game-text">
<div>Name</div>
<div>Type</div>
</div>
</div>
</div>
</div>
<div class="Books">
<h2 class="subtitle">
<i class="fa fa-fw fa-book"></i> My Books
</h2>
<div class="is-flex m-b-md">
<div class="book m-l-md" v-for="i of [1, 2, 3, 4]" :key="i">
<div class="book-cover">
<figure class="image is-2by3 m-a">
<img
src="https://external-content.duckduckgo.com/iu/?u=http%3A%2F%2Fwww.sylviaday.com%2FWP%2Fwp-content%2Fthemes%2Fsylviaday%2Fimages%2Fcovers%2Ftemp-covers%2Fplaceholder-cover.jpg&f=1&nofb=1"
/>
</figure>
</div>
<div class="book-text">
<div>book_name</div>
</div>
</div>
</div>
</div>
<div class="content">
<div class="columns">
<div class="column" v-for="user in connection.linkedUsers" :key="user.id">
<ChildAvatar :child="user" />
</div>
</div>
</div>
@ -49,24 +120,103 @@
</template>
<script lang="ts">
import ChildAvatar, { IChildAvatar } from "../components/child_avatar.vue";
import { mapActions, mapGetters } from "vuex";
import ChildCard from "../components/Child_Card.vue";
import Services from "../../services/index";
let connections = [];
import Loading from "../../shared/components/Loading/Loading.vue";
import ProfileHeader from "../components/ProfileHeader.vue";
import AddConnectionModal from "../components/AddConnectionModal.vue";
import ConfigureNewCallModal from "../components/ConfigureNewCallModal.vue";
import FileSelect from "../../shared/components/FileSelect/FileSelect.vue";
import AvatarBadge from "../components/AvatarBadge.vue";
import Moment from "moment";
import Modal from "../../shared/components/Modal/Modal.vue";
export default {
name: "Home",
components: {
ChildAvatar
Loading,
ProfileHeader,
Modal,
FileSelect,
AvatarBadge,
AddConnectionModal,
ConfigureNewCallModal,
ChildCard
},
beforeCreate() {},
async created() {
this.connections = await Services.ApiService.getConnections();
// console.dir(connections);
this.loading = false;
return true;
},
data() {
return {
connections
loading: true,
child: null,
isParent: false,
inEditMode: false,
showCoverModal: false,
showCreateCallModal: false,
showAddConnectionModal: false,
childCoverModalImage: null
};
},
methods: {
onDeleteClicked() {
this.notify({ message: "Test" });
},
goChildProfile(connection) {
this.$router.push({ path: `/child/${connection.id}` });
},
async makeCall(event) {
try {
const response = await Services.ApiService.createCall(event);
this.notify({ message: `Connectiong...` });
this.$router.push({ path: `/call/${response.data.id}` });
} catch (e) {
console.error(e);
}
return true;
},
async changeCover() {
if (this.childCoverModalImage) {
this.loading = true;
try {
this.child.profile_cover = await Services.ApiService.updateChildCover(
this.child.id,
this.childCoverModalImage
);
} catch (error) {
console.error(error);
}
this.loading = false;
}
this.showCoverModal = false;
this.this.childCoverModalImage = null;
},
togleEditMode() {
this.inEditMode = !this.inEditMode;
if (this.inEditMode) {
this.showCoverModal = true;
}
},
...mapActions(["getUser", "notify"])
},
computed: {
age() {
const years = Moment().diff(this.child.dob, "years");
const months = Moment().diff(this.child.dob, "months") % 12;
const isNewBorn = !years && !months;
let text = "a new boarn!";
if (!isNewBorn) {
text = "";
if (years) text += `${years} years`;
if (years && months) text += ` and`;
if (months) text += ` ${months} months`;
text += " old";
}
return `${text}`;
},
...mapGetters(["user"])
}
};
</script>

View file

@ -1,6 +1,8 @@
<template>
<div class="wrapper">
<div class="loading" v-if="loading">Loading...</div>
<div class="loading" v-if="loading">
<Loading />
</div>
<div class v-else>
<Modal
title="Add A child"
@ -64,7 +66,7 @@
<p class="card-header-title">My Children</p>
</header>
<div class="card-content">
<ChildCard v-for="child in user.children" :key="child.id" :child="child"></ChildCard>
<ChildCard v-for="child in user.connections.children" :key="child.id" :child="child"></ChildCard>
</div>
<footer class="card-footer">
<a
@ -108,11 +110,13 @@ import Modal from "../../shared/components/Modal/Modal.vue";
import ChildCard from "../components/Child_Card.vue";
import Services from "../../services";
import FileSelect from "../../shared/components/FileSelect/FileSelect.vue";
import Loading from "../../shared/components/Loading/Loading.vue";
export default {
components: {
Modal,
FileSelect,
ChildCard
ChildCard,
Loading
},
name: "Settings",
async beforeCreate() {
@ -153,10 +157,14 @@ export default {
this.enableChildModel = false;
await this.getUser();
this.notify({
message: `Yay!, ${child.name} was cretated`,
level: "success"
});
// console.log(child);
return true;
},
...mapActions(["getUser"])
...mapActions(["getUser", "notify"])
},
computed: {
...mapGetters(["user"])

View file

@ -0,0 +1,127 @@
export default class AdminApiService {
static async getAllUsers() {
return (await fetch('/api/v1/admin/users')).json();
}
static async addStunServer(payload: { port: number, url: string }) {
const options = {
method: 'POST',
body: JSON.stringify({
type: 'STUN',
...payload
}),
headers: {
'Content-Type': 'application/json'
}
}
try {
return (await fetch('/api/v1/admin/ice/stun', options)).json();
} catch (error) {
return error;
}
}
static async addTurnServer(payload: { port: number, url: string, secret: string, protocols: string }) {
const options = {
method: 'POST',
body: JSON.stringify({
type: 'TURN',
...payload
}),
headers: {
'Content-Type': 'application/json'
}
}
try {
return (await fetch('/api/v1/admin/ice/turn', options)).json();
} catch (error) {
return error;
}
}
static async createConnection(payload: { child_id: number, email: string, is_parent: boolean }) {
const options = {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
}
try {
return (await fetch('/api/v1/client/connections/create', options)).json();
} catch (error) {
return error;
}
}
static async updateChildCover(child_id: number, profile_cover: string) {
const options = {
method: 'POST',
body: JSON.stringify({
profile_cover
}),
headers: {
'Content-Type': 'application/json'
}
};
try {
const response = await fetch(`/api/v1/client/child/${child_id}/profile/cover`, options);
console.log(response);
return response.json();
} catch (e) {
console.error(e);
return false;
}
};
static async createCall(payload: { connection_id: number, child_id: number }) {
const options = {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
};
try {
const response = await fetch('/api/v1/client/call/create', options);
return response.json();
} catch (e) {
console.error(e);
return false;
}
}
static async createChild(name: string, dob: Date, avatar: string): Promise<any> {
const options = {
method: 'POST',
body: JSON.stringify({
name,
dob,
avatar
}),
headers: {
'Content-Type': 'application/json'
}
};
try {
const response = await fetch('/api/v1/client/child/', options);
console.log(response);
return response.json();
} catch (e) {
console.error(e);
return false;
}
}
}
interface IApiResponse {
code: number;
data?: any;
message?: string;
}

View file

@ -4,13 +4,69 @@ export default class ApiService {
return (await fetch('/api/v1/client/user/')).json();
}
static async getConnections() {
return (await fetch('/api/v1/client/connections')).json();
}
static async getAllUsers() {
return (await fetch('/api/v1/admin/users')).json();
}
static async getChild(id: number): Promise<IApiResponse> {
return (await fetch(`/api/v1/client/child/${id}`)).json();
}
static async createConnection(payload: { child_id: number, email: string, is_parent: boolean }) {
const options = {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
}
try {
return (await fetch('/api/v1/client/connections/create', options)).json();
} catch (error) {
return error;
}
}
static async updateChildCover(child_id: number, profile_cover: string) {
const options = {
method: 'POST',
body: JSON.stringify({
profile_cover
}),
headers: {
'Content-Type': 'application/json'
}
};
try {
const response = await fetch(`/api/v1/client/child/${child_id}/profile/cover`, options);
console.log(response);
return response.json();
} catch (e) {
console.error(e);
return false;
}
};
static async createCall(payload: { connection_id: number, child_id: number }) {
const options = {
method: 'POST',
body: JSON.stringify(payload),
headers: {
'Content-Type': 'application/json'
}
};
try {
const response = await fetch('/api/v1/client/call/create', options);
return response.json();
} catch (e) {
console.error(e);
return false;
}
}
static async createChild(name: string, dob: Date, avatar: string): Promise<any> {
@ -36,3 +92,9 @@ export default class ApiService {
}
}
}
interface IApiResponse {
code: number;
data?: any;
message?: string;
}

View file

@ -0,0 +1,15 @@
<template>
<div class="wrapper is-fullheight">
<div class="css-loader">
<div class="dot"></div>
<div class="dot delay-1"></div>
<div class="dot delay-2"></div>
</div>
</div>
</template>
<script lang="ts">
import Vue from "vue";
export default {};
</script>

View file

@ -0,0 +1,20 @@
<template>
<div
:class="['notification','notification-fade' ,'is-light', `is-${notification.level || 'info'}`]"
>
<button class="delete" @click="close()"></button>
{{notification.message}}
</div>
</template>
<script lang="ts">
export default {
name: "Notification",
props: ["notification"],
methods: {
close() {
this.$emit("onClose");
}
}
};
</script>

View file

@ -21,5 +21,6 @@ const { Ignitor } = require('@adonisjs/ignitor')
new Ignitor(require('@adonisjs/fold'))
.appRoot(__dirname)
.wsServer()
.fireHttpServer()
.catch(console.error)

View file

@ -22,6 +22,7 @@ const providers =
'@adonisjs/auth/providers/AuthProvider',
'@adonisjs/validator/providers/ValidatorProvider',
'@adonisjs/drive/providers/DriveProvider',
'@adonisjs/websocket/providers/WsProvider',
]
/*

View file

@ -32,9 +32,14 @@ Route.post('/login', 'AuthController.login').validator('Login');
Route
.group(() => {
Route.get('connections', 'ClientApiController.getConnections');
Route.post('connections/create', 'ClientApiController.createConnection');
Route.get('user', 'ClientApiController.getUser');
Route.post('child', 'ClientApiController.createChild');
Route.get('child/:id', 'ClientApiController.getChild');
Route.post(
'child/:id/profile/cover',
'ClientApiController.setChildProfileCover');
Route.post('call/create', 'ClientApiController.createCall');
})
.prefix('api/v1/client')
.middleware(['auth']);

22
start/socket.js Normal file
View file

@ -0,0 +1,22 @@
'use strict'
/*
|--------------------------------------------------------------------------
| Websocket
|--------------------------------------------------------------------------
|
| This file is used to register websocket channels and start the Ws server.
| Learn more about same in the official documentation.
| https://adonisjs.com/docs/websocket
|
| For middleware, do check `wsKernel.js` file.
|
*/
const Ws = use('Ws');
// const SignalingController = use('App/Controllers/Ws/SignalingController')
Ws.channel('call:*', 'SignalingController').middleware([
'auth', 'ws_call_auth'
]);
Ws.channel('user_channel', 'UserChannelController').middleware(['auth']);

39
start/wsKernel.js Normal file
View file

@ -0,0 +1,39 @@
'use strict'
const Ws = use('Ws');
/*
|--------------------------------------------------------------------------
| Global middleware
|--------------------------------------------------------------------------
|
| Global middleware are executed on each Websocket channel subscription.
|
*/
const globalMiddleware =
['Adonis/Middleware/Session', 'Adonis/Middleware/AuthInit'];
/*
|--------------------------------------------------------------------------
| Named middleware
|--------------------------------------------------------------------------
|
| Named middleware are defined as key/value pairs. Later you can use the
| keys to run selected middleware on a given channel.
|
| // define
| {
| auth: 'Adonis/Middleware/Auth'
| }
|
| // use
| Ws.channel('chat', 'ChatController').middleware(['auth'])
*/
const namedMiddleware = {
auth: 'Adonis/Middleware/Auth',
ws_call_auth: 'App/Middleware/WsCallAuth',
};
Ws.registerGlobal(globalMiddleware).registerNamed(namedMiddleware);

View file

@ -1,15 +1,30 @@
{
"compilerOptions": {
"module": "es2015",
"target": "esnext",
"module": "esnext",
// "strict": true,
"jsx": "preserve",
"importHelpers": true,
"moduleResolution": "node",
"target": "es5",
"experimentalDecorators": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"allowJs": true,
"lib": [
"dom",
"es2015"
"baseUrl": ".",
"types": [
"@types/node"
],
"experimentalDecorators": true
"paths": {
"@/*": [
"src/*"
]
},
"lib": [
"esnext",
"dom",
"dom.iterable",
"scripthost"
]
},
"include": [
"resources/scripts/main.ts"

View file

@ -1,21 +1,14 @@
const path = require('path');
const webpack = require('webpack');
// const HtmlWebpackPlugin = require('html-webpack-plugin');
// const CleanWebpackPlugin = require('clean-webpack-plugin');
// const CopyWebpackPlugin = require('copy-webpack-plugin');
const {VueLoaderPlugin} = require('vue-loader')
const {VueLoaderPlugin} = require('vue-loader');
require('babel-loader');
require('ts-loader');
const resolve = relativePath => path.resolve(__dirname, '..', relativePath);
module.exports = {
mode: 'development',
entry: {
'components/navbar': './resources/scripts/components/navbar.ts',
'views/register': './resources/scripts/views/register.ts',
// 'applications/story-time':
// './resources/scripts/applications/story-time/main.vue',
'applications/home': './resources/scripts/applications/home/main.vue',
'applications/admin': './resources/scripts/applications/admin/main.vue',
},
@ -75,8 +68,6 @@ module.exports = {
plugins: [
new webpack.NamedModulesPlugin(),
new VueLoaderPlugin(),
// Exchanges, adds, or removes modules while an application is running,
// without a full reload.
new webpack.HotModuleReplacementPlugin(),
],
resolve: {

490
yarn.lock
View file

@ -206,6 +206,34 @@
indicative "^5.0.8"
lodash "^4.17.11"
"@adonisjs/websocket-client@^1.0.9":
version "1.0.9"
resolved "https://registry.yarnpkg.com/@adonisjs/websocket-client/-/websocket-client-1.0.9.tgz#94f018489e68e05425046b053478d44ed003b827"
integrity sha512-GhHmGgyylwYUB+lBMt6KFPEQo1tck4Pn1n/aFs23CydcdChJfy4Yhtn+edqTtIYS+HTpbvXNvqn2z4EiS/xZOg==
dependencies:
"@adonisjs/websocket-packet" "^1.0.6"
emittery "^0.3.0"
query-string "^6.0.0"
"@adonisjs/websocket-packet@^1.0.6":
version "1.0.6"
resolved "https://registry.yarnpkg.com/@adonisjs/websocket-packet/-/websocket-packet-1.0.6.tgz#7b310c777fd66793149ff2de94c6b2be46302bdc"
integrity sha512-4MlcnwfJ5fy9EGBA0gNDAUThfrK4JeisQzHkRg0Z/Y3WGeciSuzgRB8hdwi2YEFeNOsEFcKme4ubEZXTAF020g==
"@adonisjs/websocket@^1.0.12":
version "1.0.12"
resolved "https://registry.yarnpkg.com/@adonisjs/websocket/-/websocket-1.0.12.tgz#ce48276be2dc287c6873d25bad0bfed5d16c35b8"
integrity sha512-03Y0KOLs9X+df7XJbVku/Izg3BsjG1tquJzw4fAs9HNjiiLhTKtsBuS6nA3tn6PST12Je9wlddzUeH92xitwZg==
dependencies:
"@adonisjs/generic-exceptions" "^2.0.1"
"@adonisjs/middleware-base" "^1.0.0"
"@adonisjs/websocket-packet" "^1.0.6"
cuid "^2.1.1"
debug "^3.1.0"
emittery "^0.4.1"
macroable "^1.0.0"
ws "^5.2.2"
"@babel/code-frame@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/code-frame/-/code-frame-7.8.3.tgz#33e25903d7481181534e12ec0a25f16b6fcf419e"
@ -214,44 +242,45 @@
"@babel/highlight" "^7.8.3"
"@babel/core@^7.8.3":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.8.4.tgz#d496799e5c12195b3602d0fddd77294e3e38e80e"
integrity sha512-0LiLrB2PwrVI+a2/IEskBopDYSd8BCb3rOvH7D5tzoWd696TBEduBvuLVm4Nx6rltrLZqvI3MCalB2K2aVzQjA==
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.9.0.tgz#ac977b538b77e132ff706f3b8a4dbad09c03c56e"
integrity sha512-kWc7L0fw1xwvI0zi8OKVBuxRVefwGOrKSQMvrQ3dW+bIIavBY3/NpXmpjMy7bQnLgwgzWQZ8TlM57YHpHNHz4w==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.8.4"
"@babel/helpers" "^7.8.4"
"@babel/parser" "^7.8.4"
"@babel/template" "^7.8.3"
"@babel/traverse" "^7.8.4"
"@babel/types" "^7.8.3"
"@babel/generator" "^7.9.0"
"@babel/helper-module-transforms" "^7.9.0"
"@babel/helpers" "^7.9.0"
"@babel/parser" "^7.9.0"
"@babel/template" "^7.8.6"
"@babel/traverse" "^7.9.0"
"@babel/types" "^7.9.0"
convert-source-map "^1.7.0"
debug "^4.1.0"
gensync "^1.0.0-beta.1"
json5 "^2.1.0"
json5 "^2.1.2"
lodash "^4.17.13"
resolve "^1.3.2"
semver "^5.4.1"
source-map "^0.5.0"
"@babel/generator@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.8.4.tgz#35bbc74486956fe4251829f9f6c48330e8d0985e"
integrity sha512-PwhclGdRpNAf3IxZb0YVuITPZmmrXz9zf6fH8lT4XbrmfQKr6ryBzhv593P5C6poJRciFCL/eHGW2NuGrgEyxA==
"@babel/generator@^7.9.0", "@babel/generator@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.9.5.tgz#27f0917741acc41e6eaaced6d68f96c3fa9afaf9"
integrity sha512-GbNIxVB3ZJe3tLeDm1HSn2AhuD/mVcyLDpgtLXa5tplmWrJdF/elxB56XNqCuD6szyNkDi6wuoKXln3QeBmCHQ==
dependencies:
"@babel/types" "^7.8.3"
"@babel/types" "^7.9.5"
jsesc "^2.5.1"
lodash "^4.17.13"
source-map "^0.5.0"
"@babel/helper-function-name@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.8.3.tgz#eeeb665a01b1f11068e9fb86ad56a1cb1a824cca"
integrity sha512-BCxgX1BC2hD/oBlIFUgOCQDOPV8nSINxCwM3o93xP4P9Fq6aV5sgv2cOOITDMtCfQ+3PvHp3l689XZvAM9QyOA==
"@babel/helper-function-name@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/helper-function-name/-/helper-function-name-7.9.5.tgz#2b53820d35275120e1874a82e5aabe1376920a5c"
integrity sha512-JVcQZeXM59Cd1qanDUxv9fgJpt3NeKUaqBqUEvfmQ+BCOKq2xUgaWZW2hr0dkbyJgezYuplEoh5knmrnS68efw==
dependencies:
"@babel/helper-get-function-arity" "^7.8.3"
"@babel/template" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/types" "^7.9.5"
"@babel/helper-get-function-arity@^7.8.3":
version "7.8.3"
@ -260,6 +289,63 @@
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-member-expression-to-functions@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.8.3.tgz#659b710498ea6c1d9907e0c73f206eee7dadc24c"
integrity sha512-fO4Egq88utkQFjbPrSHGmGLFqmrshs11d46WI+WZDESt7Wu7wN2G2Iu+NMMZJFDOVRHAMIkB5SNh30NtwCA7RA==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-module-imports@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-module-imports/-/helper-module-imports-7.8.3.tgz#7fe39589b39c016331b6b8c3f441e8f0b1419498"
integrity sha512-R0Bx3jippsbAEtzkpZ/6FIiuzOURPcMjHp+Z6xPe6DtApDJx+w7UYyOLanZqO8+wKR9G10s/FmHXvxaMd9s6Kg==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-module-transforms@^7.9.0":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/helper-module-transforms/-/helper-module-transforms-7.9.0.tgz#43b34dfe15961918707d247327431388e9fe96e5"
integrity sha512-0FvKyu0gpPfIQ8EkxlrAydOWROdHpBmiCiRwLkUiBGhCUPRRbVD2/tm3sFr/c/GWFrQ/ffutGUAnx7V0FzT2wA==
dependencies:
"@babel/helper-module-imports" "^7.8.3"
"@babel/helper-replace-supers" "^7.8.6"
"@babel/helper-simple-access" "^7.8.3"
"@babel/helper-split-export-declaration" "^7.8.3"
"@babel/template" "^7.8.6"
"@babel/types" "^7.9.0"
lodash "^4.17.13"
"@babel/helper-optimise-call-expression@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.8.3.tgz#7ed071813d09c75298ef4f208956006b6111ecb9"
integrity sha512-Kag20n86cbO2AvHca6EJsvqAd82gc6VMGule4HwebwMlwkpXuVqrNRj6CkCV2sKxgi9MyAUnZVnZ6lJ1/vKhHQ==
dependencies:
"@babel/types" "^7.8.3"
"@babel/helper-plugin-utils@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.8.3.tgz#9ea293be19babc0f52ff8ca88b34c3611b208670"
integrity sha512-j+fq49Xds2smCUNYmEHF9kGNkhbet6yVIBp4e6oeQpH1RUs/Ir06xUKzDjDkGcaaokPiTNs2JBWHjaE4csUkZQ==
"@babel/helper-replace-supers@^7.8.6":
version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/helper-replace-supers/-/helper-replace-supers-7.8.6.tgz#5ada744fd5ad73203bf1d67459a27dcba67effc8"
integrity sha512-PeMArdA4Sv/Wf4zXwBKPqVj7n9UF/xg6slNRtZW84FM7JpE1CbG8B612FyM4cxrf4fMAMGO0kR7voy1ForHHFA==
dependencies:
"@babel/helper-member-expression-to-functions" "^7.8.3"
"@babel/helper-optimise-call-expression" "^7.8.3"
"@babel/traverse" "^7.8.6"
"@babel/types" "^7.8.6"
"@babel/helper-simple-access@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-simple-access/-/helper-simple-access-7.8.3.tgz#7f8109928b4dab4654076986af575231deb639ae"
integrity sha512-VNGUDjx5cCWg4vvCTR8qQ7YJYZ+HBjxOgXEl7ounz+4Sn7+LMD3CFrCTEU6/qXKbA2nKg21CwhhBzO0RpRbdCw==
dependencies:
"@babel/template" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/helper-split-export-declaration@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.8.3.tgz#31a9f30070f91368a7182cf05f831781065fc7a9"
@ -267,59 +353,88 @@
dependencies:
"@babel/types" "^7.8.3"
"@babel/helpers@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.4.tgz#754eb3ee727c165e0a240d6c207de7c455f36f73"
integrity sha512-VPbe7wcQ4chu4TDQjimHv/5tj73qz88o12EPkO2ValS2QiQS/1F2SsjyIGNnAD0vF/nZS6Cf9i+vW6HIlnaR8w==
"@babel/helper-validator-identifier@^7.9.0", "@babel/helper-validator-identifier@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==
"@babel/helpers@^7.9.0":
version "7.9.2"
resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.9.2.tgz#b42a81a811f1e7313b88cba8adc66b3d9ae6c09f"
integrity sha512-JwLvzlXVPjO8eU9c/wF9/zOIN7X6h8DYf7mG4CiFRZRvZNKEF5dQ3H3V+ASkHoIB3mWhatgl5ONhyqHRI6MppA==
dependencies:
"@babel/template" "^7.8.3"
"@babel/traverse" "^7.8.4"
"@babel/types" "^7.8.3"
"@babel/traverse" "^7.9.0"
"@babel/types" "^7.9.0"
"@babel/highlight@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.8.3.tgz#28f173d04223eaaa59bc1d439a3836e6d1265797"
integrity sha512-PX4y5xQUvy0fnEVHrYOarRPXVWafSjTW9T0Hab8gVIawpl2Sj0ORyrygANq+KjcNlSSTw0YCLSNA8OyZ1I4yEg==
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/highlight/-/highlight-7.9.0.tgz#4e9b45ccb82b79607271b2979ad82c7b68163079"
integrity sha512-lJZPilxX7Op3Nv/2cvFdnlepPXDxi29wxteT57Q965oc5R9v86ztx0jfxVrTcBk8C2kcPkkDa2Z4T3ZsPPVWsQ==
dependencies:
"@babel/helper-validator-identifier" "^7.9.0"
chalk "^2.0.0"
esutils "^2.0.2"
js-tokens "^4.0.0"
"@babel/parser@^7.8.3", "@babel/parser@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.8.4.tgz#d1dbe64691d60358a974295fa53da074dd2ce8e8"
integrity sha512-0fKu/QqildpXmPVaRBoXOlyBb3MC+J0A66x97qEfLOMkn3u6nfY5esWogQwi/K0BjASYy4DbnsEWnpNL6qT5Mw==
"@babel/parser@^7.8.6", "@babel/parser@^7.9.0":
version "7.9.4"
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
"@babel/template@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8"
integrity sha512-04m87AcQgAFdvuoyiQ2kgELr2tV8B4fP/xJAVUL3Yb3bkNdMedD3d0rlSQr3PegP0cms3eHjl1F7PWlvWbU8FQ==
"@babel/plugin-transform-regenerator@^7.8.7":
version "7.8.7"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-regenerator/-/plugin-transform-regenerator-7.8.7.tgz#5e46a0dca2bee1ad8285eb0527e6abc9c37672f8"
integrity sha512-TIg+gAl4Z0a3WmD3mbYSk+J9ZUH6n/Yc57rtKRnlA/7rcCvpekHXe0CMZHP1gYp7/KLe9GHTuIba0vXmls6drA==
dependencies:
regenerator-transform "^0.14.2"
"@babel/plugin-transform-runtime@^7.9.0":
version "7.9.0"
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.9.0.tgz#45468c0ae74cc13204e1d3b1f4ce6ee83258af0b"
integrity sha512-pUu9VSf3kI1OqbWINQ7MaugnitRss1z533436waNXp+0N3ur3zfut37sXiQMxkuCF4VUjwZucen/quskCh7NHw==
dependencies:
"@babel/helper-module-imports" "^7.8.3"
"@babel/helper-plugin-utils" "^7.8.3"
resolve "^1.8.1"
semver "^5.5.1"
"@babel/runtime@^7.8.4":
version "7.9.2"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.9.2.tgz#d90df0583a3a252f09aaa619665367bae518db06"
integrity sha512-NE2DtOdufG7R5vnfQUTehdTfNycfUANEtCa9PssN9O/xmTzP4E08UI797ixaei6hBEVL9BI/PsdJS5x7mWoB9Q==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/template@^7.8.3", "@babel/template@^7.8.6":
version "7.8.6"
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/parser" "^7.8.3"
"@babel/types" "^7.8.3"
"@babel/parser" "^7.8.6"
"@babel/types" "^7.8.6"
"@babel/traverse@^7.8.4":
version "7.8.4"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.8.4.tgz#f0845822365f9d5b0e312ed3959d3f827f869e3c"
integrity sha512-NGLJPZwnVEyBPLI+bl9y9aSnxMhsKz42so7ApAv9D+b4vAFPpY013FTS9LdKxcABoIYFU52HcYga1pPlx454mg==
"@babel/traverse@^7.8.6", "@babel/traverse@^7.9.0":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.9.5.tgz#6e7c56b44e2ac7011a948c21e283ddd9d9db97a2"
integrity sha512-c4gH3jsvSuGUezlP6rzSJ6jf8fYjLj3hsMZRx/nX0h+fmHN0w+ekubRrHPqnMec0meycA2nwCsJ7dC8IPem2FQ==
dependencies:
"@babel/code-frame" "^7.8.3"
"@babel/generator" "^7.8.4"
"@babel/helper-function-name" "^7.8.3"
"@babel/generator" "^7.9.5"
"@babel/helper-function-name" "^7.9.5"
"@babel/helper-split-export-declaration" "^7.8.3"
"@babel/parser" "^7.8.4"
"@babel/types" "^7.8.3"
"@babel/parser" "^7.9.0"
"@babel/types" "^7.9.5"
debug "^4.1.0"
globals "^11.1.0"
lodash "^4.17.13"
"@babel/types@^7.8.3":
version "7.8.3"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.8.3.tgz#5a383dffa5416db1b73dedffd311ffd0788fb31c"
integrity sha512-jBD+G8+LWpMBBWvVcdr4QysjUE4mU/syrhN17o1u3gx0/WzJB1kwiVZAXRtWbsIPOwW8pF/YJV5+nmetPzepXg==
"@babel/types@^7.8.3", "@babel/types@^7.8.6", "@babel/types@^7.9.0", "@babel/types@^7.9.5":
version "7.9.5"
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.5.tgz#89231f82915a8a566a703b3b20133f73da6b9444"
integrity sha512-XjnvNqenk818r5zMaba+sLQjnbda31UfUURv3ei0qPQw4u+j2jMyJ5b11y8ZHYTRSI3NnInQkkkRT4fLqqPdHg==
dependencies:
esutils "^2.0.2"
"@babel/helper-validator-identifier" "^7.9.5"
lodash "^4.17.13"
to-fast-properties "^2.0.0"
@ -333,6 +448,11 @@
node-exceptions "^3.0.0"
resetable "^1.0.2"
"@types/node@^13.11.0":
version "13.11.0"
resolved "https://registry.yarnpkg.com/@types/node/-/node-13.11.0.tgz#390ea202539c61c8fa6ba4428b57e05bc36dc47b"
integrity sha512-uM4mnmsIIPK/yeO+42F2RQhGUIs39K2RFmugcJANppXe6J1nvH87PvzPZYpza7Xhhs8Yn9yIAVdLZ84z61+0xQ==
"@types/tinycolor2@^1.4.0":
version "1.4.2"
resolved "https://registry.yarnpkg.com/@types/tinycolor2/-/tinycolor2-1.4.2.tgz#721ca5c5d1a2988b4a886e35c2ffc5735b6afbdf"
@ -537,20 +657,27 @@ acorn-walk@^7.0.0:
integrity sha512-wdlPY2tm/9XBr7QkKlq0WQVgiuGTX6YWPyRyBviSoScBuLfTVQhvwg6wJ369GJ/1nPfTLMfnrFIfjqVg6d+jQQ==
acorn@^6.2.1:
version "6.4.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.0.tgz#b659d2ffbafa24baf5db1cdbb2c94a983ecd2784"
integrity sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==
version "6.4.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.4.1.tgz#531e58ba3f51b9dacb9a6646ca4debf5b14ca474"
integrity sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==
acorn@^7.0.0:
version "7.1.0"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.0.tgz#949d36f2c292535da602283586c2477c57eb2d6c"
integrity sha512-kL5CuoXA/dgxlBbVrflsflzQ3PAas7RYZB52NOm/6839iVYJgKMJ3cQJD+t2i5+qFa8h3MDpEOJiS64E8JLnSQ==
version "7.1.1"
resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.1.1.tgz#e35668de0b402f359de515c5482a1ab9f89a69bf"
integrity sha512-add7dgA5ppRPxCFJoAGfMDi7PIBXq1RtGo7BhbLaxwrXPOmw8gq48Y9ozT01hUKy9byMjlR20EJhu5zlkErEkg==
adonis-await-outside@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/adonis-await-outside/-/adonis-await-outside-1.0.0.tgz#56a11ad529e3bf4018a4ea1e804995c96cebc54c"
integrity sha512-YV4eqnlWaq5ptCrb/JTmJU/IZF1R/M8RJnKejT3+KCE8+mAOoHSxQwns8zWJTsJajvo2qcgKsKPQGtzDlENWfA==
adonis-vue-websocket@^2.0.2:
version "2.0.2"
resolved "https://registry.yarnpkg.com/adonis-vue-websocket/-/adonis-vue-websocket-2.0.2.tgz#1162de981d102d41c77e128b935909fb98b49ecf"
integrity sha512-W7VHk+mqCMpOmcfJIzzvOCpeUOIK0Fkuq7YyI+iBmWD3//HVOhZoQRjFM51HWlfofmz1PCYj9lzdVtwyFAjZ4g==
dependencies:
vue "^2.*"
ajv-errors@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/ajv-errors/-/ajv-errors-1.0.1.tgz#f35986aceb91afadec4102fbd85014950cefa64d"
@ -561,10 +688,10 @@ ajv-keywords@^3.1.0, ajv-keywords@^3.4.1:
resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-3.4.1.tgz#ef916e271c64ac12171fd8384eaae6b2345854da"
integrity sha512-RO1ibKvd27e6FEShVFfPALuHI3WjSVNeK5FIsmme/LYRNxjKuNj+Dt7bucLa6NdSv3JcVTyMlm9kGR84z1XpaQ==
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.5.5:
version "6.11.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.11.0.tgz#c3607cbc8ae392d8a5a536f25b21f8e5f3f87fe9"
integrity sha512-nCprB/0syFYy9fVYU1ox1l2KN8S9I+tziH8D4zdZuLT3N6RMlGSGt5FSTpAiHB/Whv8Qs1cWHma1aMKZyaHRKA==
ajv@^6.1.0, ajv@^6.10.2, ajv@^6.12.0, ajv@^6.5.5:
version "6.12.0"
resolved "https://registry.yarnpkg.com/ajv/-/ajv-6.12.0.tgz#06d60b96d87b8454a5adaba86e7854da629db4b7"
integrity sha512-D6gFiFA0RRLyUbvijN74DWAjXSFxWKaWP7mldxkVhyhAV3+SWA9HEJPHQ2c9soIeTFJqcSdFDGFgdqs1iUU2Hw==
dependencies:
fast-deep-equal "^3.1.1"
fast-json-stable-stringify "^2.0.0"
@ -576,6 +703,11 @@ amdefine@>=0.0.4:
resolved "https://registry.yarnpkg.com/amdefine/-/amdefine-1.0.1.tgz#4a5282ac164729e93619bcfd3ad151f817ce91f5"
integrity sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=
animate.css@^3.7.2:
version "3.7.2"
resolved "https://registry.yarnpkg.com/animate.css/-/animate.css-3.7.2.tgz#e73e0d50e92cb1cfef1597d9b38a9481020e08ea"
integrity sha512-0bE8zYo7C0KvgOYrSVfrzkbYk6IOTVPNqkiHg2cbyF4Pq/PXzilz4BRWA3hwEUBoMp5VBgrC29lQIZyhRWdBTw==
ansi-align@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-2.0.0.tgz#c36aeccba563b89ceb556f3690f0b1d9e3547f7f"
@ -954,6 +1086,11 @@ async-foreach@^0.1.3:
resolved "https://registry.yarnpkg.com/async-foreach/-/async-foreach-0.1.3.tgz#36121f845c0578172de419a97dbeb1d16ec34542"
integrity sha1-NhIfhFwFeBct5Bmpfb6x0W7DRUI=
async-limiter@~1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
integrity sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==
async@^2.6.1:
version "2.6.3"
resolved "https://registry.yarnpkg.com/async/-/async-2.6.3.tgz#d72625e2344a3656e3a3ad4fa749fa83299d82ff"
@ -1863,9 +2000,9 @@ camelcase@^5.0.0, camelcase@^5.3.1:
integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==
caniuse-lite@^1.0.30000844:
version "1.0.30001027"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001027.tgz#283e2ef17d94889cc216a22c6f85303d78ca852d"
integrity sha512-7xvKeErvXZFtUItTHgNtLgS9RJpVnwBlWX8jSo/BO8VsF6deszemZSkJJJA1KOKrXuzZH4WALpAJdq5EyfgMLg==
version "1.0.30001035"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001035.tgz#2bb53b8aa4716b2ed08e088d4dc816a5fe089a1e"
integrity sha512-C1ZxgkuA4/bUEdMbU5WrGY4+UhMFFiXrgNAfxiMIqWgFTWfv/xsZCS2xEHT2LMq7xAZfuAnu6mcqyDl0ZR6wLQ==
capture-stack-trace@^1.0.0:
version "1.0.1"
@ -2394,6 +2531,11 @@ cssesc@^3.0.0:
resolved "https://registry.yarnpkg.com/cssesc/-/cssesc-3.0.0.tgz#37741919903b868565e1c09ea747445cd18983ee"
integrity sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==
cuid@^2.1.1:
version "2.1.8"
resolved "https://registry.yarnpkg.com/cuid/-/cuid-2.1.8.tgz#cbb88f954171e0d5747606c0139fb65c5101eac0"
integrity sha512-xiEMER6E7TlTPnDxrM4eRiC6TRgjNX9xzEZ5U/Se2YJKr7Mq4pJn/2XEHjl3STcSh96GmkHPcBXLES8M29wyyg==
currently-unhandled@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea"
@ -2607,9 +2749,9 @@ ee-first@1.1.1:
integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
electron-to-chromium@^1.3.47:
version "1.3.353"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.353.tgz#c6f13f27d5212643979867a400c1a5e8a4ef042a"
integrity sha512-CkG24biyy9qQTQs8U2vGQaiyWSFDxAXP/UGHBveXZ1TGoWOAw+eYZXryrX0UeIMKnQjcaHx33hzYuydv98kqGQ==
version "1.3.377"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.377.tgz#b49d420b36ee6c48b0cd3137bfc7fec75f369b2e"
integrity sha512-cm2WzMKf/3dW5+hNANKm8GAW6SwIWOqLTJ6GPCD0Bbw1qJ9Wzm9nmx9M+byzSsgw8CdCv5fb/wzLFqVS5h6QrA==
elliptic@^6.0.0:
version "6.5.2"
@ -2624,6 +2766,16 @@ elliptic@^6.0.0:
minimalistic-assert "^1.0.0"
minimalistic-crypto-utils "^1.0.0"
emittery@^0.3.0:
version "0.3.0"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.3.0.tgz#e6dcedabae804b5478c760335ecbbaf159da645c"
integrity sha512-Bn/IFhx+BQIjTKn0vq7YWwo/yfTNeBZMqOGufY5FEV07tbwy5heDROFDCkMO2PcO5s7B9FDDXZc+JGgl6KzBOQ==
emittery@^0.4.1:
version "0.4.1"
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.4.1.tgz#abe9d3297389ba424ac87e53d1c701962ce7433d"
integrity sha512-r4eRSeStEGf6M5SKdrQhhLK5bOwOBxQhIE3YSTnZE3GpKiLfnnhE+tPtrJE79+eDJgm39BM6LSoI8SCx4HbwlQ==
emoji-regex@^7.0.1:
version "7.0.3"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-7.0.3.tgz#933a04052860c85e83c122479c4748a8e4c72156"
@ -2634,6 +2786,11 @@ emojis-list@^2.0.0:
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389"
integrity sha1-TapNnbAPmBmIDHn6RXrlsJof04k=
emojis-list@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-3.0.0.tgz#5570662046ad29e2e916e71aae260abdff4f6a78"
integrity sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==
enabled@1.0.x:
version "1.0.2"
resolved "https://registry.yarnpkg.com/enabled/-/enabled-1.0.2.tgz#965f6513d2c2d1c5f4652b64a2e3396467fc2f93"
@ -3398,9 +3555,9 @@ homedir-polyfill@^1.0.1:
parse-passwd "^1.0.0"
hosted-git-info@^2.1.4:
version "2.8.5"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.5.tgz#759cfcf2c4d156ade59b0b2dfabddc42a6b9c70c"
integrity sha512-kssjab8CvdXfcXMXVcvsXum4Hwdq9XGtRD3TteMEvEbq0LXyiNQr6AprqKqfeaDXze7SxWvRxdpwE6ku7ikLkg==
version "2.8.8"
resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488"
integrity sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==
http-errors@1.7.3, http-errors@~1.7.0, http-errors@~1.7.2:
version "1.7.3"
@ -3492,9 +3649,9 @@ imurmurhash@^0.1.4:
integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
in-publish@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.0.tgz#e20ff5e3a2afc2690320b6dc552682a9c7fadf51"
integrity sha1-4g/146KvwmkDILbcVSaCqcf631E=
version "2.0.1"
resolved "https://registry.yarnpkg.com/in-publish/-/in-publish-2.0.1.tgz#948b1a535c8030561cea522f73f78f4be357e00c"
integrity sha512-oDM0kUSNFC31ShNxHKUyfZKy8ZeXZBWMjMdZHKLOk13uvT27VTL/QzRGfRUcevJhpkZAvlhPYuXkF7eNWrtyxQ==
indent-string@^2.1.0:
version "2.1.0"
@ -3583,10 +3740,10 @@ invert-kv@^2.0.0:
resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-2.0.0.tgz#7393f5afa59ec9ff5f67a27620d11c226e3eec02"
integrity sha512-wPVv/y/QQ/Uiirj/vh3oP+1Ww+AWehmi1g5fFWGPF6IpCBCDVrhgHRMvrLfdYcwDh3QJbGXDW4JAuzxElLSqKA==
ipaddr.js@1.9.0:
version "1.9.0"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.0.tgz#37df74e430a0e47550fe54a2defe30d8acd95f65"
integrity sha512-M4Sjn6N/+O6/IXSJseKqHoFc+5FdGJ22sXqnjTpdZweHK64MzEPAyQZyEU3R/KRv2GLoa7nNtg/C2Ev6m7z+eA==
ipaddr.js@1.9.1:
version "1.9.1"
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-1.9.1.tgz#bff38543eeb8984825079ff3a2a8e6cbd46781b3"
integrity sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==
is-absolute@^1.0.0:
version "1.0.0"
@ -3918,12 +4075,12 @@ json5@^1.0.1:
dependencies:
minimist "^1.2.0"
json5@^2.1.0:
version "2.1.1"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.1.tgz#81b6cb04e9ba496f1c7005d07b4368a2638f90b6"
integrity sha512-l+3HXD0GEI3huGq1njuqtzYK8OYJyXMkOLtQ53pjWh89tvWS2h6l+1zMkYWqlb57+SiQodKZyvMEFb2X+KrFhQ==
json5@^2.1.2:
version "2.1.3"
resolved "https://registry.yarnpkg.com/json5/-/json5-2.1.3.tgz#c9b0f7fa9233bfe5807fe66fcf3a5617ed597d43"
integrity sha512-KXPvOm8K9IJKFM0bmdn8QXh7udDh1g/giieX0NLCaMnb4hEiVFqnop2ImTXCc5e0/oHz3LTqmHGtExn5hfMkOA==
dependencies:
minimist "^1.2.0"
minimist "^1.2.5"
jsonfile@^4.0.0:
version "4.0.0"
@ -4108,7 +4265,7 @@ loader-runner@^2.4.0:
resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-2.4.0.tgz#ed47066bfe534d7e84c4c7b9998c2a75607d9357"
integrity sha512-Jsmr89RcXGIwivFY21FcRrisYZfvLMTWx5kOLc+JTxtpBOG6xML0vzbc6SEQG2FO9/4Fc3wW4LVcB5DmGflaRw==
loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
loader-utils@1.2.3:
version "1.2.3"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.2.3.tgz#1ff5dc6911c9f0a062531a4c04b609406108c2c7"
integrity sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==
@ -4117,6 +4274,15 @@ loader-utils@1.2.3, loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.
emojis-list "^2.0.0"
json5 "^1.0.1"
loader-utils@^1.0.2, loader-utils@^1.1.0, loader-utils@^1.2.3:
version "1.4.0"
resolved "https://registry.yarnpkg.com/loader-utils/-/loader-utils-1.4.0.tgz#c579b5e34cb34b1a74edc6c1fb36bfa371d5a613"
integrity sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==
dependencies:
big.js "^5.2.2"
emojis-list "^3.0.0"
json5 "^1.0.1"
locate-path@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/locate-path/-/locate-path-3.0.0.tgz#dbec3b3ab759758071b58fe59fc41871af21400e"
@ -4441,16 +4607,16 @@ minimatch@^3.0.4, minimatch@~3.0.2:
dependencies:
brace-expansion "^1.1.7"
minimist@0.0.8:
version "0.0.8"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d"
integrity sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=
minimist@1.2.0, minimist@^1.1.3, minimist@^1.2.0:
minimist@1.2.0:
version "1.2.0"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284"
integrity sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=
minimist@^1.1.3, minimist@^1.2.0, minimist@^1.2.5:
version "1.2.5"
resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.5.tgz#67d66014b66a6a8aaa0c083c5fd58df4e4e97602"
integrity sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==
minipass@^2.6.0, minipass@^2.8.6, minipass@^2.9.0:
version "2.9.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-2.9.0.tgz#e713762e7d3e32fed803115cf93e04bca9fcc9a6"
@ -4499,11 +4665,11 @@ mixin-object@^2.0.1:
is-extendable "^0.1.1"
"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1:
version "0.5.1"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903"
integrity sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=
version "0.5.3"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.3.tgz#5a514b7179259287952881e94410ec5465659f8c"
integrity sha512-P+2gwrFqx8lhew375MQHHeTlY8AuOJSrGf0R5ddkEndUkmwpgUob/vQuBD1V22/Cw1/lJr4x+EjllSezBThzBg==
dependencies:
minimist "0.0.8"
minimist "^1.2.5"
moment@^2.22.2, moment@^2.24.0:
version "2.24.0"
@ -4580,9 +4746,9 @@ nanomatch@^1.2.9:
to-regex "^3.0.1"
needle@^2.2.1:
version "2.3.2"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.2.tgz#3342dea100b7160960a450dc8c22160ac712a528"
integrity sha512-DUzITvPVDUy6vczKKYTnWc/pBZ0EnjMJnQ3y+Jo5zfKFimJs7S3HFCxCRZYB9FUZcrzUQr3WsmvZgddMEIZv6w==
version "2.3.3"
resolved "https://registry.yarnpkg.com/needle/-/needle-2.3.3.tgz#a041ad1d04a871b0ebb666f40baaf1fb47867117"
integrity sha512-EkY0GeSq87rWp1hoq/sH/wnTWgFVhYlnIkbJ0YJFfRgEFlz2RraCjBpFQ+vrEgEdp0ThfyHADmkChEhcb7PKyw==
dependencies:
debug "^3.2.6"
iconv-lite "^0.4.4"
@ -4774,9 +4940,9 @@ nodemon@^1.18.7:
abbrev "1"
nopt@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d"
integrity sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=
version "4.0.3"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
integrity sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==
dependencies:
abbrev "1"
osenv "^0.1.4"
@ -5266,14 +5432,14 @@ postcss-selector-parser@^6.0.0, postcss-selector-parser@^6.0.2:
uniq "^1.0.1"
postcss-value-parser@^4.0.0, postcss-value-parser@^4.0.2:
version "4.0.2"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9"
integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ==
version "4.0.3"
resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.3.tgz#651ff4593aa9eda8d5d0d66593a2417aeaeb325d"
integrity sha512-N7h4pG+Nnu5BEIzyeaaIYWs0LI5XC40OrRh5L60z0QjFsqGWcHcbkBvpe1WYpcIS9yQ8sOi/vIPt1ejQCrMVrg==
postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.23, postcss@^7.0.5, postcss@^7.0.6:
version "7.0.26"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.26.tgz#5ed615cfcab35ba9bbb82414a4fa88ea10429587"
integrity sha512-IY4oRjpXWYshuTDFxMVkJDtWIk2LhsTlu8bZnbEJA4+bYT16Lvpo8Qv6EvDumhYRgzjZl489pmsY3qVgJQ08nA==
version "7.0.27"
resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.27.tgz#cc67cdc6b0daa375105b7c424a85567345fc54d9"
integrity sha512-WuQETPMcW9Uf1/22HWUWP9lgsIC+KEHg2kozMflKjbeUtw9ujvFX6QmIfozaErDkmLWS9WEnEdEe6Uo9/BNTdQ==
dependencies:
chalk "^2.4.2"
source-map "^0.6.1"
@ -5294,7 +5460,7 @@ pretty-hrtime@^1.0.3:
resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1"
integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE=
private@^0.1.6:
private@^0.1.6, private@^0.1.8:
version "0.1.8"
resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff"
integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==
@ -5494,12 +5660,12 @@ prompt-rawlist@^2.0.1:
prompt-list "^2.0.1"
proxy-addr@^2.0.4:
version "2.0.5"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.5.tgz#34cbd64a2d81f4b1fd21e76f9f06c8a45299ee34"
integrity sha512-t/7RxHXPH6cJtP0pRG6smSr9QJidhB+3kXu0KgXnbGYMgzEnUxRQ4/LDdfOwZEMyIh3/xHb8PX3t+lfL9z+YVQ==
version "2.0.6"
resolved "https://registry.yarnpkg.com/proxy-addr/-/proxy-addr-2.0.6.tgz#fdc2336505447d3f2f2c638ed272caf614bbb2bf"
integrity sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==
dependencies:
forwarded "~0.1.2"
ipaddr.js "1.9.0"
ipaddr.js "1.9.1"
prr@~1.0.1:
version "1.0.1"
@ -5583,6 +5749,15 @@ qs@~6.5.2:
resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36"
integrity sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==
query-string@^6.0.0:
version "6.12.0"
resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.12.0.tgz#fa0fe5b3ddf4d040d1236b80672949ab33d5cf80"
integrity sha512-aoiFW9ZU7jP8Itjqfpw80Qe7RoyCIhFrW522sdsp9LG92pat6CCG3d8qNZBaUi71FsEjIfLjx9Ky347FtVoqXA==
dependencies:
decode-uri-component "^0.2.0"
split-on-first "^1.0.0"
strict-uri-encode "^2.0.0"
querystring-es3@^0.2.0:
version "0.2.1"
resolved "https://registry.yarnpkg.com/querystring-es3/-/querystring-es3-0.2.1.tgz#9ec61f79049875707d69414596fd907a4d711e73"
@ -5752,6 +5927,11 @@ regenerator-runtime@^0.11.0:
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9"
integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==
regenerator-runtime@^0.13.4, regenerator-runtime@^0.13.5:
version "0.13.5"
resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.5.tgz#d878a1d094b4306d10b9096484b33ebd55e26697"
integrity sha512-ZS5w8CpKFinUzOwW3c83oPeVXoNsrLsaCoLtJvAClH135j/R77RuymhiSErhm2lKcwSCIpmvIWSbDkIfAqKQlA==
regenerator-transform@^0.10.0:
version "0.10.1"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.10.1.tgz#1e4996837231da8b7f3cf4114d71b5691a0680dd"
@ -5761,6 +5941,14 @@ regenerator-transform@^0.10.0:
babel-types "^6.19.0"
private "^0.1.6"
regenerator-transform@^0.14.2:
version "0.14.4"
resolved "https://registry.yarnpkg.com/regenerator-transform/-/regenerator-transform-0.14.4.tgz#5266857896518d1616a78a0479337a30ea974cc7"
integrity sha512-EaJaKPBI9GvKpvUz2mz4fhx7WPgvwRLY9v3hlNHWmAuJHI13T4nwKnNvm5RWJzEdnI5g5UwtOww+S8IdoUC2bw==
dependencies:
"@babel/runtime" "^7.8.4"
private "^0.1.8"
regex-not@^1.0.0, regex-not@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c"
@ -5925,7 +6113,7 @@ resolve-url@^0.2.1:
resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a"
integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2:
resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.3.2, resolve@^1.8.1:
version "1.15.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.15.1.tgz#27bdcdeffeaf2d6244b95bb0f9f4b4653451f3e8"
integrity sha512-84oo6ZTtoTUpjgNEr5SJyzQhzL72gaRodsSfyxC/AXRvwu0Yse9H8eF9IpGo7b8YetZhlI6v7ZQ6bKBFV/6S7w==
@ -6011,11 +6199,11 @@ schema-utils@^1.0.0:
ajv-keywords "^3.1.0"
schema-utils@^2.6.0:
version "2.6.4"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.4.tgz#a27efbf6e4e78689d91872ee3ccfa57d7bdd0f53"
integrity sha512-VNjcaUxVnEeun6B2fiiUDjXXBtD4ZSH7pdbfIu1pOFwgptDPLMo/z9jr4sUfsjFVPqDCEin/F7IYlq7/E6yDbQ==
version "2.6.5"
resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.6.5.tgz#c758f0a7e624263073d396e29cd40aa101152d8a"
integrity sha512-5KXuwKziQrTVHh8j/Uxz+QUbxkaLW9X/86NBlx/gnKgtsZA2GIVMUn17qWhRFwF8jdYb3Dig5hRO/W5mZqy6SQ==
dependencies:
ajv "^6.10.2"
ajv "^6.12.0"
ajv-keywords "^3.4.1"
scmp@2.0.0:
@ -6038,7 +6226,7 @@ semver-diff@^2.0.0:
dependencies:
semver "^5.0.3"
"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.6.0, semver@^5.7.1:
"semver@2 || 3 || 4 || 5", semver@^5.0.3, semver@^5.1.0, semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@ -6334,6 +6522,11 @@ spdx-license-ids@^3.0.0:
resolved "https://registry.yarnpkg.com/spdx-license-ids/-/spdx-license-ids-3.0.5.tgz#3694b5804567a458d3c8045842a6358632f62654"
integrity sha512-J+FWzZoynJEXGphVIS+XEh3kFSjZX/1i9gFBaWQcB+/tmpe2qUsSBABpcxqxnAxFdiUFEgAX1bjYGQvIZmoz9Q==
split-on-first@^1.0.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/split-on-first/-/split-on-first-1.1.0.tgz#f610afeee3b12bce1d0c30425e76398b78249a5f"
integrity sha512-43ZssAJaMusuKWL8sKUBQXHWOpq8d6CfN/u1p4gUzfJkM05C8rxTmYrkIPTXapZpORA6LkkzcUulJ8FqA7Uudw==
split-string@^3.0.1, split-string@^3.0.2:
version "3.1.0"
resolved "https://registry.yarnpkg.com/split-string/-/split-string-3.1.0.tgz#7cb09dda3a86585705c64b39a6466038682e8fe2"
@ -6434,6 +6627,11 @@ stream-shift@^1.0.0:
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
strict-uri-encode@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz#b9c7330c7042862f6b142dc274bbcc5866ce3546"
integrity sha1-ucczDHBChi9rFC3CdLvMWGbONUY=
string-width@^1.0.1, string-width@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
@ -6619,9 +6817,9 @@ terser-webpack-plugin@^1.4.3:
worker-farm "^1.7.0"
terser@^4.1.2:
version "4.6.3"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.3.tgz#e33aa42461ced5238d352d2df2a67f21921f8d87"
integrity sha512-Lw+ieAXmY69d09IIc/yqeBqXpEQIpDGZqT34ui1QWXIUpR2RjbqEkT8X7Lgex19hslSqcWM5iMN2kM11eMsESQ==
version "4.6.7"
resolved "https://registry.yarnpkg.com/terser/-/terser-4.6.7.tgz#478d7f9394ec1907f0e488c5f6a6a9a2bad55e72"
integrity sha512-fmr7M1f7DBly5cX2+rFDvmGBAaaZyPrHYK4mMdHEDAdNTqXSZgSOfqsfGq2HqPGT/1V0foZZuCZFx8CHKgAk3g==
dependencies:
commander "^2.20.0"
source-map "~0.6.1"
@ -6787,9 +6985,9 @@ ts-loader@^6.2.1:
semver "^6.0.0"
tslib@^1.9.0:
version "1.10.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.10.0.tgz#c3c19f95973fb0a62973fb09d90d961ee43e5c8a"
integrity sha512-qOebF53frne81cf0S9B41ByenJ3/IuH8yJKngAX35CmiZySA0khhkovshKK+jGCaMnVomla7gVlIcc3EvKPbTQ==
version "1.11.1"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.11.1.tgz#eb15d128827fbee2841549e171f45ed338ac7e35"
integrity sha512-aZW88SY8kQbU7gpV19lN24LtXh/yD4ZZg6qieAJDDg+YBsJcSmLGK9QpnUjAKVG/xefmvJGd1WUmfpT/g6AJGA==
tsscmp@1.0.6:
version "1.0.6"
@ -6832,9 +7030,9 @@ typedarray@^0.0.6:
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
typescript@^3.7.5:
version "3.7.5"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.5.tgz#0692e21f65fd4108b9330238aac11dd2e177a1ae"
integrity sha512-/P5lkRXkWHNAbcJIiHPfRoKqyd7bsyCma1hZNUGfn20qm64T6ZBlrzprymeu918H+mB/0rIg2gGK/BXkhhYgBw==
version "3.8.3"
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.8.3.tgz#409eb8544ea0335711205869ec458ab109ee1061"
integrity sha512-MYlEfn5VrLNsgudQTVJeNaQFUAI7DkhnOjdpAp4T+ku1TfQClewlbSuTVHiA+8skNBgaf02TL/kLOvig4y3G8w==
uid-safe@2.1.5:
version "2.1.5"
@ -7006,6 +7204,11 @@ uuid@^3.3.2:
resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee"
integrity sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==
uuid@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
v8-compile-cache@2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe"
@ -7062,9 +7265,9 @@ vue-loader@^15.8.3:
vue-style-loader "^4.1.0"
vue-router@^3.1.5:
version "3.1.5"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.5.tgz#ff29b8a1e1306c526b52d4dc0532109f16c41231"
integrity sha512-BszkPvhl7I9h334GjckCh7sVFyjTPMMJFJ4Bsrem/Ik+B/9gt5tgrk8k4gGLO4ZpdvciVdg7O41gW4DisQWurg==
version "3.1.6"
resolved "https://registry.yarnpkg.com/vue-router/-/vue-router-3.1.6.tgz#45f5a3a3843e31702c061dd829393554e4328f89"
integrity sha512-GYhn2ynaZlysZMkFE5oCHRUTqE8BWs/a9YbKpNLi0i7xD6KG1EzDqpHQmv1F5gXjr8kL5iIVS8EOtRaVUEXTqA==
vue-style-loader@^4.1.0, vue-style-loader@^4.1.2:
version "4.1.2"
@ -7087,15 +7290,15 @@ vue-template-es2015-compiler@^1.9.0:
resolved "https://registry.yarnpkg.com/vue-template-es2015-compiler/-/vue-template-es2015-compiler-1.9.1.tgz#1ee3bc9a16ecbf5118be334bb15f9c46f82f5825"
integrity sha512-4gDntzrifFnCEvyoO8PqyJDmguXgVPxKiIxrBKjIowvL9l+N66196+72XVYR8BBf1Uv1Fgt3bGevJ+sEmxfZzw==
vue@^2.6.11:
vue@^2.*, vue@^2.6.11:
version "2.6.11"
resolved "https://registry.yarnpkg.com/vue/-/vue-2.6.11.tgz#76594d877d4b12234406e84e35275c6d514125c5"
integrity sha512-VfPwgcGABbGAue9+sfrD4PuwFar7gPb1yl1UK1MwXoQPAw0BKSqWfoYCT/ThFrdEVWoI51dBuyCoiNU9bZDZxQ==
vuex@^3.1.2:
version "3.1.2"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.1.2.tgz#a2863f4005aa73f2587e55c3fadf3f01f69c7d4d"
integrity sha512-ha3jNLJqNhhrAemDXcmMJMKf1Zu4sybMPr9KxJIuOpVcsDQlTBYLLladav2U+g1AvdYDG5Gs0xBTb0M5pXXYFQ==
version "3.1.3"
resolved "https://registry.yarnpkg.com/vuex/-/vuex-3.1.3.tgz#f2ad73e3fb73691698b38c93f66e58e267947180"
integrity sha512-k8vZqNMSNMgKelVZAPYw5MNb2xWSmVgCKtYKAptvm9YtZiOXnRXFWu//Y9zQNORTrm3dNj1n/WaZZI26tIX6Mw==
warning-symbol@^0.1.0:
version "0.1.0"
@ -7137,9 +7340,9 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1:
source-map "~0.6.1"
webpack@^4.41.5:
version "4.41.6"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.6.tgz#12f2f804bf6542ef166755050d4afbc8f66ba7e1"
integrity sha512-yxXfV0Zv9WMGRD+QexkZzmGIh54bsvEs+9aRWxnN8erLWEOehAKUTeNBoUbA6HPEZPlRo7KDi2ZcNveoZgK9MA==
version "4.42.0"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.42.0.tgz#b901635dd6179391d90740a63c93f76f39883eb8"
integrity sha512-EzJRHvwQyBiYrYqhyjW9AqM90dE4+s1/XtCfn7uWg6cS72zH+2VPFAlsnW0+W0cDi0XRjNKUMoJtpSi50+Ph6w==
dependencies:
"@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5"
@ -7265,6 +7468,13 @@ write-file-atomic@^2.0.0:
imurmurhash "^0.1.4"
signal-exit "^3.0.2"
ws@^5.2.2:
version "5.2.2"
resolved "https://registry.yarnpkg.com/ws/-/ws-5.2.2.tgz#dffef14866b8e8dc9133582514d1befaf96e980f"
integrity sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==
dependencies:
async-limiter "~1.0.0"
xdg-basedir@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/xdg-basedir/-/xdg-basedir-3.0.0.tgz#496b2cc109eca8dbacfe2dc72b603c17c5870ad4"
@ -7296,9 +7506,9 @@ yallist@^3.0.0, yallist@^3.0.2, yallist@^3.0.3:
integrity sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==
yargs-parser@^13.1.0:
version "13.1.1"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.1.tgz#d26058532aa06d365fe091f6a1fc06b2f7e5eca0"
integrity sha512-oVAVsHz6uFrg3XQheFII8ESO2ssAf9luWuAd6Wexsu4F3OtIW0o8IribPXYrD4WC24LWtPrJlGy87y5udK+dxQ==
version "13.1.2"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-13.1.2.tgz#130f09702ebaeef2650d54ce6e3e5706f7a4fb38"
integrity sha512-3lbsNRf/j+A4QuSZfDRA7HRSfWrzO0YjqTJd5kjAq37Zep1CEgaYmrH9Q3GwPiB9cHyd1Y1UwggGhJGoxipbzg==
dependencies:
camelcase "^5.0.0"
decamelize "^1.2.0"