Merge pull request #2760 from overleaf/em-faster-uploads

Make a single Mongo update when uploading projects

GitOrigin-RevId: de102d3e112c9014ca5885f963e35971e4db6cee
This commit is contained in:
Eric Mc Sween 2020-04-23 07:51:06 -04:00 committed by Copybot
parent 674afe400d
commit 27941dc8af
12 changed files with 804 additions and 581 deletions

View file

@ -7,6 +7,7 @@ const Async = require('async')
const FileHashManager = require('./FileHashManager') const FileHashManager = require('./FileHashManager')
const { File } = require('../../models/File') const { File } = require('../../models/File')
const Errors = require('../Errors/Errors') const Errors = require('../Errors/Errors')
const { promisifyAll } = require('../../util/promises')
const ONE_MIN_IN_MS = 60 * 1000 const ONE_MIN_IN_MS = 60 * 1000
const FIVE_MINS_IN_MS = ONE_MIN_IN_MS * 5 const FIVE_MINS_IN_MS = ONE_MIN_IN_MS * 5
@ -227,3 +228,8 @@ const FileStoreHandler = {
} }
module.exports = FileStoreHandler module.exports = FileStoreHandler
module.exports.promises = promisifyAll(FileStoreHandler, {
multiResult: {
uploadFileFromDisk: ['url', 'fileRef']
}
})

View file

@ -4,13 +4,13 @@ const { ObjectId } = require('mongodb')
module.exports = { buildFolderStructure } module.exports = { buildFolderStructure }
function buildFolderStructure(docUploads, fileUploads) { function buildFolderStructure(docEntries, fileEntries) {
const builder = new FolderStructureBuilder() const builder = new FolderStructureBuilder()
for (const docUpload of docUploads) { for (const docEntry of docEntries) {
builder.addDocUpload(docUpload) builder.addDocEntry(docEntry)
} }
for (const fileUpload of fileUploads) { for (const fileEntry of fileEntries) {
builder.addFileUpload(fileUpload) builder.addFileEntry(fileEntry)
} }
return builder.rootFolder return builder.rootFolder
} }
@ -24,18 +24,18 @@ class FolderStructureBuilder {
this.entityPaths.add('/') this.entityPaths.add('/')
} }
addDocUpload(docUpload) { addDocEntry(docEntry) {
this.recordEntityPath(Path.join(docUpload.dirname, docUpload.doc.name)) this.recordEntityPath(docEntry.path)
const folder = this.mkdirp(docUpload.dirname) const folderPath = Path.dirname(docEntry.path)
folder.docs.push(docUpload.doc) const folder = this.mkdirp(folderPath)
folder.docs.push(docEntry.doc)
} }
addFileUpload(fileUpload) { addFileEntry(fileEntry) {
this.recordEntityPath( this.recordEntityPath(fileEntry.path)
Path.join(fileUpload.dirname, fileUpload.fileRef.name) const folderPath = Path.dirname(fileEntry.path)
) const folder = this.mkdirp(folderPath)
const folder = this.mkdirp(fileUpload.dirname) folder.fileRefs.push(fileEntry.file)
folder.fileRefs.push(fileUpload.fileRef)
} }
mkdirp(path) { mkdirp(path) {

View file

@ -1,8 +1,3 @@
/* NOTE: this file is an async/await version of
* ProjectEntityMongoUpdateHandler.js. It's temporarily separate from the
* callback-style version so that we can test it in production for some code
* paths only.
*/
const { callbackify } = require('util') const { callbackify } = require('util')
const { callbackifyMultiResult } = require('../../util/promises') const { callbackifyMultiResult } = require('../../util/promises')
const _ = require('underscore') const _ = require('underscore')
@ -635,13 +630,24 @@ async function _checkValidMove(
} }
} }
async function createNewFolderStructure(projectId, docUploads, fileUploads) { /**
* Create an initial file tree out of a list of doc and file entries
*
* Each entry specifies a path to the doc or file. Folders are automatically
* created.
*
* @param {ObjectId} projectId - id of the project
* @param {DocEntry[]} docEntries - list of docs to add
* @param {FileEntry[]} fileEntries - list of files to add
* @return {Promise<string>} the project version after the operation
*/
async function createNewFolderStructure(projectId, docEntries, fileEntries) {
try { try {
const rootFolder = FolderStructureBuilder.buildFolderStructure( const rootFolder = FolderStructureBuilder.buildFolderStructure(
docUploads, docEntries,
fileUploads fileEntries
) )
const result = await Project.updateOne( const project = await Project.findOneAndUpdate(
{ {
_id: projectId, _id: projectId,
'rootFolder.0.folders.0': { $exists: false }, 'rootFolder.0.folders.0': { $exists: false },
@ -651,14 +657,20 @@ async function createNewFolderStructure(projectId, docUploads, fileUploads) {
{ {
$set: { rootFolder: [rootFolder] }, $set: { rootFolder: [rootFolder] },
$inc: { version: 1 } $inc: { version: 1 }
},
{
new: true,
lean: true,
fields: { version: 1 }
} }
).exec() ).exec()
if (result.n !== 1) { if (project == null) {
throw new OError({ throw new OError({
message: 'project not found or folder structure already exists', message: 'project not found or folder structure already exists',
info: { projectId } info: { projectId }
}) })
} }
return project.version
} catch (err) { } catch (err) {
throw new OError({ throw new OError({
message: 'failed to create folder structure', message: 'failed to create folder structure',

View file

@ -1,37 +1,22 @@
const fs = require('fs') const fs = require('fs')
const Path = require('path')
const { callbackify } = require('util') const { callbackify } = require('util')
const FileTypeManager = require('./FileTypeManager')
const EditorController = require('../Editor/EditorController') const EditorController = require('../Editor/EditorController')
const Errors = require('../Errors/Errors')
const FileTypeManager = require('./FileTypeManager')
const SafePath = require('../Project/SafePath')
const logger = require('logger-sharelatex') const logger = require('logger-sharelatex')
module.exports = { module.exports = {
addFolderContents: callbackify(addFolderContents),
addEntity: callbackify(addEntity), addEntity: callbackify(addEntity),
importDir: callbackify(importDir),
promises: { promises: {
addFolderContents, addEntity,
addEntity importDir
} }
} }
async function addDoc( async function addDoc(userId, projectId, folderId, name, lines, replace) {
userId,
projectId,
folderId,
name,
path,
encoding,
replace
) {
if (!(await _isSafeOnFileSystem(path))) {
logger.log(
{ userId, projectId, folderId, name, path },
'add doc is from symlink, stopping process'
)
throw new Error('path is symlink')
}
let content = await fs.promises.readFile(path, encoding)
content = content.replace(/\r\n?/g, '\n') // convert Windows line endings to unix. very old macs also created \r-separated lines
const lines = content.split('\n')
if (replace) { if (replace) {
const doc = await EditorController.promises.upsertDoc( const doc = await EditorController.promises.upsertDoc(
projectId, projectId,
@ -56,14 +41,6 @@ async function addDoc(
} }
async function addFile(userId, projectId, folderId, name, path, replace) { async function addFile(userId, projectId, folderId, name, path, replace) {
if (!(await _isSafeOnFileSystem(path))) {
logger.log(
{ userId, projectId, folderId, name, path },
'add file is from symlink, stopping insert'
)
throw new Error('path is symlink')
}
if (replace) { if (replace) {
const file = await EditorController.promises.upsertFile( const file = await EditorController.promises.upsertFile(
projectId, projectId,
@ -90,13 +67,6 @@ async function addFile(userId, projectId, folderId, name, path, replace) {
} }
async function addFolder(userId, projectId, folderId, name, path, replace) { async function addFolder(userId, projectId, folderId, name, path, replace) {
if (!(await _isSafeOnFileSystem(path))) {
logger.log(
{ userId, projectId, folderId, path },
'add folder is from symlink, stopping insert'
)
throw new Error('path is symlink')
}
const newFolder = await EditorController.promises.addFolder( const newFolder = await EditorController.promises.addFolder(
projectId, projectId,
folderId, folderId,
@ -137,51 +107,56 @@ async function addFolderContents(
} }
} }
async function addEntity(userId, projectId, folderId, name, path, replace) { async function addEntity(userId, projectId, folderId, name, fsPath, replace) {
if (!(await _isSafeOnFileSystem(path))) { if (!(await _isSafeOnFileSystem(fsPath))) {
logger.log( logger.log(
{ userId, projectId, folderId, path }, { userId, projectId, folderId, fsPath },
'add entry is from symlink, stopping insert' 'add entry is from symlink, stopping insert'
) )
throw new Error('path is symlink') throw new Error('path is symlink')
} }
if (await FileTypeManager.promises.isDirectory(path)) { if (await FileTypeManager.promises.isDirectory(fsPath)) {
const newFolder = await addFolder( const newFolder = await addFolder(
userId, userId,
projectId, projectId,
folderId, folderId,
name, name,
path, fsPath,
replace replace
) )
return newFolder return newFolder
} }
const { binary, encoding } = await FileTypeManager.promises.getType(
name, // Here, we cheat a little bit and provide the project path relative to the
path // folder, not the root of the project. This is because we don't know for sure
) // at this point what the final path of the folder will be. The project path
if (binary) { // is still important for importFile() to be able to figure out if the file is
// a binary file or an editable document.
const projectPath = Path.join('/', name)
const importInfo = await importFile(fsPath, projectPath)
switch (importInfo.type) {
case 'file': {
const entity = await addFile( const entity = await addFile(
userId, userId,
projectId, projectId,
folderId, folderId,
name, name,
path, importInfo.fsPath,
replace replace
) )
if (entity != null) { if (entity != null) {
entity.type = 'file' entity.type = 'file'
} }
return entity return entity
} else { }
case 'doc': {
const entity = await addDoc( const entity = await addDoc(
userId, userId,
projectId, projectId,
folderId, folderId,
name, name,
path, importInfo.lines,
encoding,
replace replace
) )
if (entity != null) { if (entity != null) {
@ -189,9 +164,92 @@ async function addEntity(userId, projectId, folderId, name, path, replace) {
} }
return entity return entity
} }
default: {
throw new Error(`unknown import type: ${importInfo.type}`)
}
}
} }
async function _isSafeOnFileSystem(path) { async function _isSafeOnFileSystem(path) {
// Use lstat() to ensure we don't follow symlinks. Symlinks from an
// untrusted source are dangerous.
const stat = await fs.promises.lstat(path) const stat = await fs.promises.lstat(path)
return stat.isFile() || stat.isDirectory() return stat.isFile() || stat.isDirectory()
} }
async function importFile(fsPath, projectPath) {
const stat = await fs.promises.lstat(fsPath)
if (!stat.isFile()) {
throw new Error(`can't import ${fsPath}: not a regular file`)
}
_validateProjectPath(projectPath)
const filename = Path.basename(projectPath)
const { binary, encoding } = await FileTypeManager.promises.getType(
filename,
fsPath
)
if (binary) {
return new FileImport(projectPath, fsPath)
} else {
const content = await fs.promises.readFile(fsPath, encoding)
// Handle Unix, DOS and classic Mac newlines
const lines = content.split(/\r\n|\n|\r/)
return new DocImport(projectPath, lines)
}
}
async function importDir(dirPath) {
const stat = await fs.promises.lstat(dirPath)
if (!stat.isDirectory()) {
throw new Error(`can't import ${dirPath}: not a directory`)
}
const entries = []
for await (const filePath of _walkDir(dirPath)) {
const projectPath = Path.join('/', Path.relative(dirPath, filePath))
const importInfo = await importFile(filePath, projectPath)
entries.push(importInfo)
}
return entries
}
function _validateProjectPath(path) {
if (!SafePath.isAllowedLength(path) || !SafePath.isCleanPath(path)) {
throw new Errors.InvalidNameError(`Invalid path: ${path}`)
}
}
async function* _walkDir(dirPath) {
const entries = await fs.promises.readdir(dirPath)
for (const entry of entries) {
const entryPath = Path.join(dirPath, entry)
if (await FileTypeManager.promises.shouldIgnore(entryPath)) {
continue
}
// Use lstat() to ensure we don't follow symlinks. Symlinks from an
// untrusted source are dangerous.
const stat = await fs.promises.lstat(entryPath)
if (stat.isFile()) {
yield entryPath
} else if (stat.isDirectory()) {
yield* _walkDir(entryPath)
}
}
}
class FileImport {
constructor(projectPath, fsPath) {
this.type = 'file'
this.projectPath = projectPath
this.fsPath = fsPath
}
}
class DocImport {
constructor(projectPath, lines) {
this.type = 'doc'
this.projectPath = projectPath
this.lines = lines
}
}

View file

@ -1,13 +1,19 @@
const path = require('path') const Path = require('path')
const fs = require('fs-extra') const fs = require('fs-extra')
const { callbackify } = require('util') const { callbackify } = require('util')
const ArchiveManager = require('./ArchiveManager') const ArchiveManager = require('./ArchiveManager')
const { Doc } = require('../../models/Doc')
const DocstoreManager = require('../Docstore/DocstoreManager')
const DocumentHelper = require('../Documents/DocumentHelper')
const DocumentUpdaterHandler = require('../DocumentUpdater/DocumentUpdaterHandler')
const FileStoreHandler = require('../FileStore/FileStoreHandler')
const FileSystemImportManager = require('./FileSystemImportManager') const FileSystemImportManager = require('./FileSystemImportManager')
const ProjectCreationHandler = require('../Project/ProjectCreationHandler') const ProjectCreationHandler = require('../Project/ProjectCreationHandler')
const ProjectEntityMongoUpdateHandler = require('../Project/ProjectEntityMongoUpdateHandler')
const ProjectRootDocManager = require('../Project/ProjectRootDocManager') const ProjectRootDocManager = require('../Project/ProjectRootDocManager')
const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler') const ProjectDetailsHandler = require('../Project/ProjectDetailsHandler')
const ProjectDeleter = require('../Project/ProjectDeleter') const ProjectDeleter = require('../Project/ProjectDeleter')
const DocumentHelper = require('../Documents/DocumentHelper') const TpdsProjectFlusher = require('../ThirdPartyDataStore/TpdsProjectFlusher')
const logger = require('logger-sharelatex') const logger = require('logger-sharelatex')
module.exports = { module.exports = {
@ -22,12 +28,12 @@ module.exports = {
} }
async function createProjectFromZipArchive(ownerId, defaultName, zipPath) { async function createProjectFromZipArchive(ownerId, defaultName, zipPath) {
const extractionPath = await _extractZip(zipPath) const contentsPath = await _extractZip(zipPath)
const { const {
path, path,
content content
} = await ProjectRootDocManager.promises.findRootDocFileFromDirectory( } = await ProjectRootDocManager.promises.findRootDocFileFromDirectory(
extractionPath contentsPath
) )
const projectName = const projectName =
@ -38,12 +44,7 @@ async function createProjectFromZipArchive(ownerId, defaultName, zipPath) {
uniqueName uniqueName
) )
try { try {
await _insertZipContentsIntoFolder( await _initializeProjectWithZipContents(ownerId, project, contentsPath)
ownerId,
project._id,
project.rootFolder[0]._id,
extractionPath
)
if (path) { if (path) {
await ProjectRootDocManager.promises.setRootDocFromName(project._id, path) await ProjectRootDocManager.promises.setRootDocFromName(project._id, path)
@ -60,6 +61,7 @@ async function createProjectFromZipArchive(ownerId, defaultName, zipPath) {
) )
throw err throw err
} }
await fs.remove(contentsPath)
return project return project
} }
@ -69,7 +71,7 @@ async function createProjectFromZipArchiveWithName(
zipPath, zipPath,
attributes = {} attributes = {}
) { ) {
const extractionPath = await _extractZip(zipPath) const contentsPath = await _extractZip(zipPath)
const uniqueName = await _generateUniqueName(ownerId, proposedName) const uniqueName = await _generateUniqueName(ownerId, proposedName)
const project = await ProjectCreationHandler.promises.createBlankProject( const project = await ProjectCreationHandler.promises.createBlankProject(
ownerId, ownerId,
@ -78,12 +80,7 @@ async function createProjectFromZipArchiveWithName(
) )
try { try {
await _insertZipContentsIntoFolder( await _initializeProjectWithZipContents(ownerId, project, contentsPath)
ownerId,
project._id,
project.rootFolder[0]._id,
extractionPath
)
await ProjectRootDocManager.promises.setRootDocAutomatically(project._id) await ProjectRootDocManager.promises.setRootDocAutomatically(project._id)
} catch (err) { } catch (err) {
// no need to wait for the cleanup here // no need to wait for the cleanup here
@ -97,33 +94,14 @@ async function createProjectFromZipArchiveWithName(
) )
throw err throw err
} }
await fs.remove(contentsPath)
return project return project
} }
async function _insertZipContentsIntoFolder(
ownerId,
projectId,
folderId,
destination
) {
const topLevelDestination = await ArchiveManager.promises.findTopLevelDirectory(
destination
)
await FileSystemImportManager.promises.addFolderContents(
ownerId,
projectId,
folderId,
topLevelDestination,
false
)
await fs.remove(destination)
}
async function _extractZip(zipPath) { async function _extractZip(zipPath) {
const destination = path.join( const destination = Path.join(
path.dirname(zipPath), Path.dirname(zipPath),
`${path.basename(zipPath, '.zip')}-${Date.now()}` `${Path.basename(zipPath, '.zip')}-${Date.now()}`
) )
await ArchiveManager.promises.extractZipArchive(zipPath, destination) await ArchiveManager.promises.extractZipArchive(zipPath, destination)
return destination return destination
@ -137,3 +115,96 @@ async function _generateUniqueName(ownerId, originalName) {
) )
return uniqueName return uniqueName
} }
async function _initializeProjectWithZipContents(
ownerId,
project,
contentsPath
) {
const topLevelDir = await ArchiveManager.promises.findTopLevelDirectory(
contentsPath
)
const importEntries = await FileSystemImportManager.promises.importDir(
topLevelDir
)
const { fileEntries, docEntries } = await _createEntriesFromImports(
project._id,
importEntries
)
const projectVersion = await ProjectEntityMongoUpdateHandler.promises.createNewFolderStructure(
project._id,
docEntries,
fileEntries
)
await _notifyDocumentUpdater(project, ownerId, {
newFiles: fileEntries,
newDocs: docEntries,
newProject: { version: projectVersion }
})
await TpdsProjectFlusher.promises.flushProjectToTpds(project._id)
}
async function _createEntriesFromImports(projectId, importEntries) {
const fileEntries = []
const docEntries = []
for (const importEntry of importEntries) {
switch (importEntry.type) {
case 'doc': {
const docEntry = await _createDoc(
projectId,
importEntry.projectPath,
importEntry.lines
)
docEntries.push(docEntry)
break
}
case 'file': {
const fileEntry = await _createFile(
projectId,
importEntry.projectPath,
importEntry.fsPath
)
fileEntries.push(fileEntry)
break
}
default: {
throw new Error(`Invalid import type: ${importEntry.type}`)
}
}
}
return { fileEntries, docEntries }
}
async function _createDoc(projectId, projectPath, docLines) {
const docName = Path.basename(projectPath)
const doc = new Doc({ name: docName })
await DocstoreManager.promises.updateDoc(
projectId.toString(),
doc._id.toString(),
docLines,
0,
{}
)
return { doc, path: projectPath, docLines: docLines.join('\n') }
}
async function _createFile(projectId, projectPath, fsPath) {
const fileName = Path.basename(projectPath)
const { fileRef, url } = await FileStoreHandler.promises.uploadFileFromDisk(
projectId,
{ name: fileName },
fsPath
)
return { file: fileRef, path: projectPath, url }
}
async function _notifyDocumentUpdater(project, userId, changes) {
const projectHistoryId =
project.overleaf && project.overleaf.history && project.overleaf.history.id
await DocumentUpdaterHandler.promises.updateProjectStructure(
project._id,
projectHistoryId,
userId,
changes
)
}

View file

@ -7,7 +7,7 @@
"@auth0/thumbprint": { "@auth0/thumbprint": {
"version": "0.0.6", "version": "0.0.6",
"resolved": "https://registry.npmjs.org/@auth0/thumbprint/-/thumbprint-0.0.6.tgz", "resolved": "https://registry.npmjs.org/@auth0/thumbprint/-/thumbprint-0.0.6.tgz",
"integrity": "sha1-yrEGLGwEZizmxZLUgVfsQmiuhRg=", "integrity": "sha512-+YciWHxNUOE78T+xoXI1fMI6G1WdyyAay8ioaMZhvGOJ+lReYzj0b7mpfNr5WtjGrmtWPvPOOxh0TO+5Y2M/Hw==",
"dev": true "dev": true
}, },
"@babel/cli": { "@babel/cli": {
@ -2597,7 +2597,7 @@
"@protobufjs/aspromise": { "@protobufjs/aspromise": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
"integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ=="
}, },
"@protobufjs/base64": { "@protobufjs/base64": {
"version": "1.1.2", "version": "1.1.2",
@ -2612,12 +2612,12 @@
"@protobufjs/eventemitter": { "@protobufjs/eventemitter": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
"integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q=="
}, },
"@protobufjs/fetch": { "@protobufjs/fetch": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
"integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==",
"requires": { "requires": {
"@protobufjs/aspromise": "^1.1.1", "@protobufjs/aspromise": "^1.1.1",
"@protobufjs/inquire": "^1.1.0" "@protobufjs/inquire": "^1.1.0"
@ -2626,27 +2626,27 @@
"@protobufjs/float": { "@protobufjs/float": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
"integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ=="
}, },
"@protobufjs/inquire": { "@protobufjs/inquire": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
"integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q=="
}, },
"@protobufjs/path": { "@protobufjs/path": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
"integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA=="
}, },
"@protobufjs/pool": { "@protobufjs/pool": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
"integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw=="
}, },
"@protobufjs/utf8": { "@protobufjs/utf8": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw=="
}, },
"@sentry/browser": { "@sentry/browser": {
"version": "5.15.4", "version": "5.15.4",
@ -3613,7 +3613,7 @@
"ansi-html": { "ansi-html": {
"version": "0.0.7", "version": "0.0.7",
"resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz", "resolved": "https://registry.npmjs.org/ansi-html/-/ansi-html-0.0.7.tgz",
"integrity": "sha1-gTWEAhliqenm/QOflA0S9WynhZ4=", "integrity": "sha512-JoAxEa1DfP9m2xfB/y2r/aKcwXNlltr4+0QSBC4TrLfcxyvepX2Pv0t/xpgGV5bGsDzCYV8SzjWgyCW0T9yYbA==",
"dev": true "dev": true
}, },
"ansi-regex": { "ansi-regex": {
@ -3908,7 +3908,7 @@
"aria-query": { "aria-query": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz", "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-3.0.0.tgz",
"integrity": "sha1-ZbP8wcoRVajJrmTW7uKX8V1RM8w=", "integrity": "sha512-majUxHgLehQTeSA+hClx+DY09OVUqG3GtezWkF1krgLGNdlDu9l9V8DaqNMWbq4Eddc8wsyDA0hpDUtnYxQEXw==",
"dev": true, "dev": true,
"requires": { "requires": {
"ast-types-flow": "0.0.7", "ast-types-flow": "0.0.7",
@ -4294,7 +4294,7 @@
"ast-types-flow": { "ast-types-flow": {
"version": "0.0.7", "version": "0.0.7",
"resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz", "resolved": "https://registry.npmjs.org/ast-types-flow/-/ast-types-flow-0.0.7.tgz",
"integrity": "sha1-9wtzXGvKGlycItmCw+Oef+ujva0=", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
"dev": true "dev": true
}, },
"async": { "async": {
@ -4989,7 +4989,7 @@
"batch": { "batch": {
"version": "0.6.1", "version": "0.6.1",
"resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz",
"integrity": "sha1-3DQxT05nkxgJP8dgJyUl+UvyXBY=", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==",
"dev": true "dev": true
}, },
"bcrypt": { "bcrypt": {
@ -5330,7 +5330,7 @@
"bintrees": { "bintrees": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz", "resolved": "https://registry.npmjs.org/bintrees/-/bintrees-1.0.1.tgz",
"integrity": "sha1-DmVcm5wkNeqraL9AJyJtK1WjRSQ=" "integrity": "sha512-tbaUB1QpTIj4cKY8c1rvNAvEQXA+ekzHmbe4jzNfW3QWsF9GnnP/BRWyl6/qqS53heoYJ93naaFcm/jooONH8g=="
}, },
"bitsyntax": { "bitsyntax": {
"version": "0.0.4", "version": "0.0.4",
@ -5470,7 +5470,7 @@
"bonjour": { "bonjour": {
"version": "3.5.0", "version": "3.5.0",
"resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz", "resolved": "https://registry.npmjs.org/bonjour/-/bonjour-3.5.0.tgz",
"integrity": "sha1-jokKGD2O6aI5OzhExpGkK897yfU=", "integrity": "sha512-RaVTblr+OnEli0r/ud8InrU7D+G0y6aJhlxaLa6Pwty4+xoxboF1BsUI45tujvRpbj9dQVoglChqonGAsjEBYg==",
"dev": true, "dev": true,
"requires": { "requires": {
"array-flatten": "^2.1.0", "array-flatten": "^2.1.0",
@ -5644,7 +5644,7 @@
"brorand": { "brorand": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=", "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==",
"dev": true "dev": true
}, },
"browser-stdout": { "browser-stdout": {
@ -5700,7 +5700,7 @@
"browserify-rsa": { "browserify-rsa": {
"version": "4.0.1", "version": "4.0.1",
"resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz",
"integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", "integrity": "sha512-+YpEyaLDDvvdzIxQ+cCx73r5YEhS3ANGOkiHdyWqW4t3gdeoNEYjSiQwntbU4Uo2/9yRkpYX3SRFeH+7jc2Duw==",
"dev": true, "dev": true,
"requires": { "requires": {
"bn.js": "^4.1.0", "bn.js": "^4.1.0",
@ -5710,7 +5710,7 @@
"browserify-sign": { "browserify-sign": {
"version": "4.0.4", "version": "4.0.4",
"resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz", "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.0.4.tgz",
"integrity": "sha1-qk62jl17ZYuqa/alfmMMvXqT0pg=", "integrity": "sha512-D2ItxCwNtLcHRrOCuEDZQlIezlFyUV/N5IYz6TY1svu1noyThFuthoEjzT8ChZe3UEctqnwmykcPhet3Eiz58A==",
"dev": true, "dev": true,
"requires": { "requires": {
"bn.js": "^4.1.1", "bn.js": "^4.1.1",
@ -5819,7 +5819,7 @@
"buffer-xor": { "buffer-xor": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz",
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=", "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==",
"dev": true "dev": true
}, },
"bufferedstream": { "bufferedstream": {
@ -5850,7 +5850,7 @@
"builtin-status-codes": { "builtin-status-codes": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz", "resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
"integrity": "sha1-hZgoeOIbmOHGZCXgPQF0eI9Wnug=", "integrity": "sha512-HpGFw18DgFWlncDfjTa2rcQ4W88O1mC8e8yZ2AvQY5KDaktSTwo+KRf6nHK6FRI5FyRyb/5T6+TSxfP7QyGsmQ==",
"dev": true "dev": true
}, },
"bunyan": { "bunyan": {
@ -6499,7 +6499,7 @@
"commondir": { "commondir": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/commondir/-/commondir-1.0.1.tgz",
"integrity": "sha1-3dgA2gxmEnOTzKWVDqloo6rxJTs=", "integrity": "sha512-W9pAhw0ja1Edb5GVdIF1mjZw/ASI0AlShXM83UUGe2DVr5TdAPEA1OA8m/g8zWp9x6On7gqufY+FatDbC3MDQg==",
"dev": true "dev": true
}, },
"component-bind": { "component-bind": {
@ -6606,7 +6606,7 @@
"vary": { "vary": {
"version": "1.1.2", "version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
"integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=", "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==",
"dev": true "dev": true
} }
} }
@ -6786,13 +6786,13 @@
"constants-browserify": { "constants-browserify": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz", "resolved": "https://registry.npmjs.org/constants-browserify/-/constants-browserify-1.0.0.tgz",
"integrity": "sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U=", "integrity": "sha512-xFxOwqIzR/e1k1gLiWEophSCMqXcwVHIH7akf7b/vxcUeGunlj3hvZaaqxwHsTgn+IndtkQJgSztIDWeumWJDQ==",
"dev": true "dev": true
}, },
"contains-path": { "contains-path": {
"version": "0.1.0", "version": "0.1.0",
"resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz", "resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
"integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=", "integrity": "sha512-OKZnPGeMQy2RPaUIBPFFd71iNf4791H12MCRuVQDnzGRwCYNYmTDy5pdafo2SLAcEMKzTOQnLWG4QdcjeJUMEg==",
"dev": true "dev": true
}, },
"content-disposition": { "content-disposition": {
@ -8106,7 +8106,7 @@
"detect-file": { "detect-file": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz",
"integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc=", "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==",
"dev": true "dev": true
}, },
"detect-node": { "detect-node": {
@ -8193,7 +8193,7 @@
"dns-equal": { "dns-equal": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz", "resolved": "https://registry.npmjs.org/dns-equal/-/dns-equal-1.0.0.tgz",
"integrity": "sha1-s55/HabrCnW6nBcySzR1PEfgZU0=", "integrity": "sha512-z+paD6YUQsk+AbGCEM4PrOXSss5gd66QfcVBFTKR/HpFL9jCqikS94HYwKww6fQyO7IxrIIyUu+g0Ka9tUS2Cg==",
"dev": true "dev": true
}, },
"dns-packet": { "dns-packet": {
@ -8214,7 +8214,7 @@
"dns-txt": { "dns-txt": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz", "resolved": "https://registry.npmjs.org/dns-txt/-/dns-txt-2.0.2.tgz",
"integrity": "sha1-uR2Ab10nGI5Ks+fRB9iBocxGQrY=", "integrity": "sha512-Ix5PrWjphuSoUXV/Zv5gaFHjnaJtb02F2+Si3Ht9dyJ87+Z/lMmy+dpNHtTGraNK958ndXq2i+GLkWsWHcKaBQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"buffer-indexof": "^1.0.0" "buffer-indexof": "^1.0.0"
@ -9727,7 +9727,7 @@
"expand-tilde": { "expand-tilde": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz",
"integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI=", "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==",
"dev": true, "dev": true,
"requires": { "requires": {
"homedir-polyfill": "^1.0.1" "homedir-polyfill": "^1.0.1"
@ -10062,7 +10062,7 @@
"faye-websocket": { "faye-websocket": {
"version": "0.10.0", "version": "0.10.0",
"resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz", "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.10.0.tgz",
"integrity": "sha1-TkkvjQTftviQA1B/btvy1QHnxvQ=", "integrity": "sha512-Xhj93RXbMSq8urNCUq4p9l0P6hnySJ/7YNRhYNug0bLOuii7pKO7xQFb5mx9xZXWCar88pLPb805PvUkwrLZpQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"websocket-driver": ">=0.5.1" "websocket-driver": ">=0.5.1"
@ -10366,7 +10366,7 @@
"findit2": { "findit2": {
"version": "2.2.3", "version": "2.2.3",
"resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz", "resolved": "https://registry.npmjs.org/findit2/-/findit2-2.2.3.tgz",
"integrity": "sha1-WKRmaX34piBc39vzlVNri9d3pfY=" "integrity": "sha512-lg/Moejf4qXovVutL0Lz4IsaPoNYMuxt4PA0nGqFxnJ1CTTGGlEO2wKgoDpwknhvZ8k4Q2F+eesgkLbG2Mxfog=="
}, },
"findup-sync": { "findup-sync": {
"version": "0.1.3", "version": "0.1.3",
@ -10432,7 +10432,7 @@
"flowstate": { "flowstate": {
"version": "0.4.1", "version": "0.4.1",
"resolved": "https://registry.npmjs.org/flowstate/-/flowstate-0.4.1.tgz", "resolved": "https://registry.npmjs.org/flowstate/-/flowstate-0.4.1.tgz",
"integrity": "sha1-tfu4t/wte9xbVL5GyYMJ73NvTsA=", "integrity": "sha512-U67AgveyMwXFIiDgs6Yz/PrUNrZGLJUUMDwJ9Q0fDFTQSzyDg8Jj9YDyZIUnFZKggQZONVueK9+grp/Gxa/scw==",
"dev": true, "dev": true,
"requires": { "requires": {
"clone": "^1.0.2", "clone": "^1.0.2",
@ -10453,7 +10453,7 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true "dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
@ -10574,7 +10574,7 @@
"from2": { "from2": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz", "resolved": "https://registry.npmjs.org/from2/-/from2-2.3.0.tgz",
"integrity": "sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=", "integrity": "sha512-OMcX/4IC/uqEPVgGeyfN22LJk6AZrMkRZHxcHBMBvHScDGgwTm2GT2Wkgtocyd3JfZffjj2kYUDXXII0Fk9W0g==",
"dev": true, "dev": true,
"requires": { "requires": {
"inherits": "^2.0.1", "inherits": "^2.0.1",
@ -10584,7 +10584,7 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true "dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
@ -10660,7 +10660,7 @@
"fs-write-stream-atomic": { "fs-write-stream-atomic": {
"version": "1.0.10", "version": "1.0.10",
"resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz", "resolved": "https://registry.npmjs.org/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz",
"integrity": "sha1-tH31NJPvkR33VzHnCp3tAYnbQMk=", "integrity": "sha512-gehEzmPn2nAwr39eay+x3X34Ra+M2QlVUTLhkXPjWdeO8RF9kszk116avgBJM3ZyNHgHXBNx+VmPaFC36k0PzA==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
@ -11504,7 +11504,7 @@
"global-prefix": { "global-prefix": {
"version": "1.0.2", "version": "1.0.2",
"resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz",
"integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4=", "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==",
"dev": true, "dev": true,
"requires": { "requires": {
"expand-tilde": "^2.0.2", "expand-tilde": "^2.0.2",
@ -12136,7 +12136,7 @@
"hash-base": { "hash-base": {
"version": "3.0.4", "version": "3.0.4",
"resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz",
"integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", "integrity": "sha512-EeeoJKjTyt868liAlVmcv2ZsUfGHlE3Q+BICOXcZiwN3osr5Q/zFGYmTJpoIzuaSTAwndFy+GqhEwlU4L3j4Ow==",
"dev": true, "dev": true,
"requires": { "requires": {
"inherits": "^2.0.1", "inherits": "^2.0.1",
@ -12233,7 +12233,7 @@
"hmac-drbg": { "hmac-drbg": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==",
"dev": true, "dev": true,
"requires": { "requires": {
"hash.js": "^1.0.3", "hash.js": "^1.0.3",
@ -12264,7 +12264,7 @@
"hpack.js": { "hpack.js": {
"version": "2.1.6", "version": "2.1.6",
"resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz",
"integrity": "sha1-h3dMCUnlE/QuhFdbPEVoH63ioLI=", "integrity": "sha512-zJxVehUdMGIKsRaNt7apO2Gqp0BdqW5yaiGHXXmbpvxgBYVZnAql+BJb4RO5ad2MgpbZKn5G6nMnegrH1FcNYQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"inherits": "^2.0.1", "inherits": "^2.0.1",
@ -12276,7 +12276,7 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true "dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
@ -12394,7 +12394,7 @@
"http-deceiver": { "http-deceiver": {
"version": "1.2.7", "version": "1.2.7",
"resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz", "resolved": "https://registry.npmjs.org/http-deceiver/-/http-deceiver-1.2.7.tgz",
"integrity": "sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc=", "integrity": "sha512-LmpOGxTfbpgtGVxJrj5k7asXHCgNZp5nLfp+hWc8QQRqtb7fUy6kRY3BO1h9ddF6yIPYUARgxGOwB42DnxIaNw==",
"dev": true "dev": true
}, },
"http-errors": { "http-errors": {
@ -12418,7 +12418,7 @@
"http-parser-js": { "http-parser-js": {
"version": "0.4.10", "version": "0.4.10",
"resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz", "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.4.10.tgz",
"integrity": "sha1-ksnBN0w1CF912zWexWzCV8u5P6Q=", "integrity": "sha512-ln7+HeZl3lL3PNRX9Y6ub4i8xcgQ0mO2J//ic97dR7tEXB+6IKAjx8JCCmEkwKiMcR2jidU9xNolz1fEyyf/Jg==",
"dev": true "dev": true
}, },
"http-proxy": { "http-proxy": {
@ -12469,13 +12469,13 @@
"arr-diff": { "arr-diff": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz",
"integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA=", "integrity": "sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==",
"dev": true "dev": true
}, },
"array-unique": { "array-unique": {
"version": "0.3.2", "version": "0.3.2",
"resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
"integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=", "integrity": "sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==",
"dev": true "dev": true
}, },
"debug": { "debug": {
@ -12496,7 +12496,7 @@
"expand-brackets": { "expand-brackets": {
"version": "2.1.4", "version": "2.1.4",
"resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
"integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=", "integrity": "sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==",
"dev": true, "dev": true,
"requires": { "requires": {
"debug": "^2.3.3", "debug": "^2.3.3",
@ -12511,7 +12511,7 @@
"define-property": { "define-property": {
"version": "0.2.5", "version": "0.2.5",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
"integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=", "integrity": "sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-descriptor": "^0.1.0" "is-descriptor": "^0.1.0"
@ -12520,7 +12520,7 @@
"extend-shallow": { "extend-shallow": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-extendable": "^0.1.0" "is-extendable": "^0.1.0"
@ -12529,7 +12529,7 @@
"is-accessor-descriptor": { "is-accessor-descriptor": {
"version": "0.1.6", "version": "0.1.6",
"resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz",
"integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY=", "integrity": "sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==",
"dev": true, "dev": true,
"requires": { "requires": {
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
@ -12538,7 +12538,7 @@
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-buffer": "^1.1.5" "is-buffer": "^1.1.5"
@ -12549,7 +12549,7 @@
"is-data-descriptor": { "is-data-descriptor": {
"version": "0.1.4", "version": "0.1.4",
"resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz",
"integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y=", "integrity": "sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==",
"dev": true, "dev": true,
"requires": { "requires": {
"kind-of": "^3.0.2" "kind-of": "^3.0.2"
@ -12558,7 +12558,7 @@
"kind-of": { "kind-of": {
"version": "3.2.2", "version": "3.2.2",
"resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz",
"integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ=", "integrity": "sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-buffer": "^1.1.5" "is-buffer": "^1.1.5"
@ -12604,7 +12604,7 @@
"define-property": { "define-property": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
"integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=", "integrity": "sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-descriptor": "^1.0.0" "is-descriptor": "^1.0.0"
@ -12613,7 +12613,7 @@
"extend-shallow": { "extend-shallow": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
"integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=", "integrity": "sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==",
"dev": true, "dev": true,
"requires": { "requires": {
"is-extendable": "^0.1.0" "is-extendable": "^0.1.0"
@ -12736,7 +12736,7 @@
"https-browserify": { "https-browserify": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz", "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
"integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM=", "integrity": "sha512-J+FkSdyD+0mA0N+81tMotaRMfSL9SGi+xpD3T6YApKsc3bGSXJlfXri3VyFOeYkfLRQisDk1W+jIFFKBeUBbBg==",
"dev": true "dev": true
}, },
"https-proxy-agent": { "https-proxy-agent": {
@ -12810,7 +12810,7 @@
"iferr": { "iferr": {
"version": "0.1.5", "version": "0.1.5",
"resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz", "resolved": "https://registry.npmjs.org/iferr/-/iferr-0.1.5.tgz",
"integrity": "sha1-xg7taebY/bazEEofy8ocGS3FtQE=", "integrity": "sha512-DUNFN5j7Tln0D+TxzloUjKB+CtVu6myn0JEFak6dG18mNt9YkQ6lzGCdafwofISZ1lLF3xRHJ98VKy9ynkcFaA==",
"dev": true "dev": true
}, },
"ignore": { "ignore": {
@ -13261,7 +13261,7 @@
"ip-regex": { "ip-regex": {
"version": "2.1.0", "version": "2.1.0",
"resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz",
"integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", "integrity": "sha512-58yWmlHpp7VYfcdTwMTvwMmqx/Elfxjd9RXTDyMsbL7lLWmhMylLEqiYVLKuLzOZqVgiWXD9MfR62Vv89VRxkw==",
"dev": true "dev": true
}, },
"ipaddr.js": { "ipaddr.js": {
@ -13643,7 +13643,7 @@
"is-wsl": { "is-wsl": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-1.1.0.tgz",
"integrity": "sha1-HxbkqiKwTRM2tmGIpmrzxgDDpm0=", "integrity": "sha512-gfygJYZ2gLTDlmbWMI0CE2MwnFzSN/2SZfkMlItC4K/JBlsWVDB0bO6XhqcY13YXE7iMcAJnzTCJjPiTeJJ0Mw==",
"dev": true "dev": true
}, },
"isarray": { "isarray": {
@ -13722,7 +13722,7 @@
"jmespath": { "jmespath": {
"version": "0.15.0", "version": "0.15.0",
"resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz", "resolved": "https://registry.npmjs.org/jmespath/-/jmespath-0.15.0.tgz",
"integrity": "sha1-o/Iiqarp+Wb10nx5ZRDigJF2Qhc=" "integrity": "sha512-+kHj8HXArPfpPEKGLZ+kB5ONRTCiGQXo8RQYL0hH8t6pWXUBBK5KkkQmTNOwKK4LEsd0yTsgtjJVm4UBSZea4w=="
}, },
"joi-mongodb-objectid": { "joi-mongodb-objectid": {
"version": "0.1.0", "version": "0.1.0",
@ -13790,7 +13790,7 @@
"json-bigint": { "json-bigint": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz", "resolved": "https://registry.npmjs.org/json-bigint/-/json-bigint-0.3.0.tgz",
"integrity": "sha1-DM2RLEuCcNBfBW+9E4FLU9OCWx4=", "integrity": "sha512-u+c/u/F+JNPUekHCFyGVycRPyh9UHD5iUhSyIAn10kxbDTJxijwAbT6XHaONEOXuGGfmWUSroheXgHcml4gLgg==",
"requires": { "requires": {
"bignumber.js": "^7.0.0" "bignumber.js": "^7.0.0"
} }
@ -14757,7 +14757,7 @@
"load-json-file": { "load-json-file": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz", "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=", "integrity": "sha512-3p6ZOGNbiX4CdvEd1VcE6yi78UrGNpjHO33noGwHCnT/o2fyllJDepsm8+mFFv/DvtwFHht5HIHSyOy5a+ChVQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"graceful-fs": "^4.1.2", "graceful-fs": "^4.1.2",
@ -14769,7 +14769,7 @@
"pify": { "pify": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"dev": true "dev": true
} }
} }
@ -14914,7 +14914,7 @@
"lodash.pickby": { "lodash.pickby": {
"version": "4.6.0", "version": "4.6.0",
"resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz", "resolved": "https://registry.npmjs.org/lodash.pickby/-/lodash.pickby-4.6.0.tgz",
"integrity": "sha1-feoh2MGNdwOifHBMFdO4SmfjOv8=" "integrity": "sha512-AZV+GsS/6ckvPOVQPXSiFFacKvKB4kOQu6ynt9wz0F3LO4R9Ij4K1ddYsIytDpSgLz88JHd9P+oaLeej5/Sl7Q=="
}, },
"lodash.set": { "lodash.set": {
"version": "4.3.2", "version": "4.3.2",
@ -15556,7 +15556,7 @@
"lynx": { "lynx": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz", "resolved": "https://registry.npmjs.org/lynx/-/lynx-0.1.1.tgz",
"integrity": "sha1-Mxjc7xaQi4KG6Bisz9sxzXQkj50=", "integrity": "sha512-JI52N0NwK2b/Md0TFPdPtUBI46kjyJXF7+q08l2yvQ56q6QA8s7ZjZQQRoxFpS2jDXNf/B0p8ID+OIKcTsZwzw==",
"requires": { "requires": {
"mersenne": "~0.0.3", "mersenne": "~0.0.3",
"statsd-parser": "~0.0.4" "statsd-parser": "~0.0.4"
@ -15833,7 +15833,7 @@
"mersenne": { "mersenne": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz", "resolved": "https://registry.npmjs.org/mersenne/-/mersenne-0.0.4.tgz",
"integrity": "sha1-QB/ex+whzbngPNPTAhOY2iGycIU=" "integrity": "sha512-XoSUL+nF8hMTKGQxUs8r3Btdsf1yuKKBdCCGbh3YXgCXuVKishpZv1CNc385w9s8t4Ynwc5h61BwW/FCVulkbg=="
}, },
"messageformat": { "messageformat": {
"version": "1.1.1", "version": "1.1.1",
@ -16093,7 +16093,7 @@
"minimalistic-crypto-utils": { "minimalistic-crypto-utils": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=", "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==",
"dev": true "dev": true
}, },
"minimatch": { "minimatch": {
@ -16247,7 +16247,7 @@
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true, "dev": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
@ -16407,10 +16407,16 @@
} }
} }
}, },
"mock-fs": {
"version": "4.11.0",
"resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.11.0.tgz",
"integrity": "sha512-Yp4o3/ZA15wsXqJTT+R+9w2AYIkD1i80Lds47wDbuUhOvQvm+O2EfjFZSz0pMgZZSPHRhGxgcd2+GL4+jZMtdw==",
"dev": true
},
"module-details-from-path": { "module-details-from-path": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz", "resolved": "https://registry.npmjs.org/module-details-from-path/-/module-details-from-path-1.0.3.tgz",
"integrity": "sha1-EUyUlnPiqKNenTV4hSeqN7Z52is=" "integrity": "sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A=="
}, },
"moment": { "moment": {
"version": "2.24.0", "version": "2.24.0",
@ -16572,7 +16578,7 @@
"move-concurrently": { "move-concurrently": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz", "resolved": "https://registry.npmjs.org/move-concurrently/-/move-concurrently-1.0.1.tgz",
"integrity": "sha1-viwAX9oy4LKa8fBdfEszIUxwH5I=", "integrity": "sha512-hdrFxZOycD/g6A6SoI2bB5NA/5NEqD0569+S47WZhPvm46sD50ZHdYaFmnua5lndde9rCHGjmfK7Z8BuCt/PcQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"aproba": "^1.1.1", "aproba": "^1.1.1",
@ -16703,7 +16709,7 @@
"multicast-dns-service-types": { "multicast-dns-service-types": {
"version": "1.1.0", "version": "1.1.0",
"resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz", "resolved": "https://registry.npmjs.org/multicast-dns-service-types/-/multicast-dns-service-types-1.1.0.tgz",
"integrity": "sha1-iZ8R2WhuXgXLkbNdXw5jt3PPyQE=", "integrity": "sha512-cnAsSVxIDsYt0v7HmC0hWZFwwXSh+E6PgCrREDuN/EsjgLwA5XRmlMHhSiDPrt6HxY1gTivEa/Zh7GtODoLevQ==",
"dev": true "dev": true
}, },
"muri": { "muri": {
@ -16990,7 +16996,7 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true "dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
@ -17045,7 +17051,7 @@
"url": { "url": {
"version": "0.11.0", "version": "0.11.0",
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz", "resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=", "integrity": "sha512-kbailJa29QrtXnxgq+DdCEGlbTeYM2eJUxsz6vjZavrCYPMIFHMKQmSKYAIuUK2i7hgPm28a8piX5NTUtM/LKQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"punycode": "1.3.2", "punycode": "1.3.2",
@ -17055,7 +17061,7 @@
"punycode": { "punycode": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=", "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw==",
"dev": true "dev": true
} }
} }
@ -18541,7 +18547,7 @@
"os-browserify": { "os-browserify": {
"version": "0.3.0", "version": "0.3.0",
"resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz", "resolved": "https://registry.npmjs.org/os-browserify/-/os-browserify-0.3.0.tgz",
"integrity": "sha1-hUNzx/XCMVkU/Jv8a9gjj92h7Cc=", "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==",
"dev": true "dev": true
}, },
"os-homedir": { "os-homedir": {
@ -18587,7 +18593,7 @@
"p-defer": { "p-defer": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz", "resolved": "https://registry.npmjs.org/p-defer/-/p-defer-1.0.0.tgz",
"integrity": "sha1-n26xgvbJqozXQwBKfU+WsZaw+ww=", "integrity": "sha512-wB3wfAxZpk2AzOfUMJNL+d36xothRSyj8EXOa4f6GMqYDN9BJaaSISbsk+wS9abmnebVw95C2Kb5t85UmpCxuw==",
"dev": true "dev": true
}, },
"p-each-series": { "p-each-series": {
@ -18852,7 +18858,7 @@
"parse-json": { "parse-json": {
"version": "2.2.0", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=", "integrity": "sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"error-ex": "^1.2.0" "error-ex": "^1.2.0"
@ -18871,7 +18877,7 @@
"parse-passwd": { "parse-passwd": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz",
"integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY=", "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==",
"dev": true "dev": true
}, },
"parse5": { "parse5": {
@ -19096,7 +19102,7 @@
"path-type": { "path-type": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz", "resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
"integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=", "integrity": "sha512-dUnb5dXUf+kzhC/W/F4e5/SkluXIFf5VUHolW1Eg1irn1hGWjPGdsRcvYJ1nD6lhk8Ir7VM0bHJKsYTx8Jx9OQ==",
"dev": true, "dev": true,
"requires": { "requires": {
"pify": "^2.0.0" "pify": "^2.0.0"
@ -19105,7 +19111,7 @@
"pify": { "pify": {
"version": "2.3.0", "version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=", "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==",
"dev": true "dev": true
} }
} }
@ -20174,7 +20180,7 @@
"promise-inflight": { "promise-inflight": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz", "resolved": "https://registry.npmjs.org/promise-inflight/-/promise-inflight-1.0.1.tgz",
"integrity": "sha1-mEcocL8igTL8vdhoEputEsPAKeM=", "integrity": "sha512-6zWPyEOFaQBJYcGMHBKTKJ3u6TBsnMFOIZSa6ce1e/ZrrsOlnHRHbabMjLiBYKp+n44X9eUI6VUPaukCXHuG4g==",
"dev": true "dev": true
}, },
"promisify-any": { "promisify-any": {
@ -20863,7 +20869,7 @@
"read-pkg": { "read-pkg": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
"integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=", "integrity": "sha512-eFIBOPW7FGjzBuk3hdXEuNSiTZS/xEMlH49HxMyzb0hyPfu4EhVjT2DH32K1hSSmVq4sebAWnZuuY5auISUTGA==",
"dev": true, "dev": true,
"requires": { "requires": {
"load-json-file": "^2.0.0", "load-json-file": "^2.0.0",
@ -20874,7 +20880,7 @@
"read-pkg-up": { "read-pkg-up": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz", "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
"integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=", "integrity": "sha512-1orxQfbWGUiTn9XsPlChs6rLie/AV9jwZTGmu2NZw/CUDJQchXJFYE0Fq5j7+n558T1JhDWLdhyd1Zj+wLY//w==",
"dev": true, "dev": true,
"requires": { "requires": {
"find-up": "^2.0.0", "find-up": "^2.0.0",
@ -21818,7 +21824,7 @@
"resolve-cwd": { "resolve-cwd": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-2.0.0.tgz",
"integrity": "sha1-AKn3OHVW4nA46uIyyqNypqWbZlo=", "integrity": "sha512-ccu8zQTrzVr954472aUVPLEcB3YpKSYR3cg/3lo1okzobPBM+1INXBbBZlDbnI/hbEocnf8j0QVo43hQKrbchg==",
"dev": true, "dev": true,
"requires": { "requires": {
"resolve-from": "^3.0.0" "resolve-from": "^3.0.0"
@ -21827,7 +21833,7 @@
"resolve-from": { "resolve-from": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
"integrity": "sha1-six699nWiBvItuZTM17rywoYh0g=", "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==",
"dev": true "dev": true
} }
} }
@ -21835,7 +21841,7 @@
"resolve-dir": { "resolve-dir": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz",
"integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M=", "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==",
"dev": true, "dev": true,
"requires": { "requires": {
"expand-tilde": "^2.0.0", "expand-tilde": "^2.0.0",
@ -21883,7 +21889,7 @@
"retry": { "retry": {
"version": "0.12.0", "version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=", "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"dev": true "dev": true
}, },
"retry-axios": { "retry-axios": {
@ -22013,7 +22019,7 @@
"run-queue": { "run-queue": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz", "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
"integrity": "sha1-6Eg5bwV9Ij8kOGkkYY4laUFh7Ec=", "integrity": "sha512-ntymy489o0/QQplUDnpYAYUsO50K9SBrIVaKCWDOJzYJts0f9WH9RFJkyagebkw5+y1oi00R7ynNW/d12GBumg==",
"dev": true, "dev": true,
"requires": { "requires": {
"aproba": "^1.1.1" "aproba": "^1.1.1"
@ -22070,7 +22076,7 @@
"saml": { "saml": {
"version": "0.12.5", "version": "0.12.5",
"resolved": "https://registry.npmjs.org/saml/-/saml-0.12.5.tgz", "resolved": "https://registry.npmjs.org/saml/-/saml-0.12.5.tgz",
"integrity": "sha1-pDyQhUifefceg94uxato3sxrjME=", "integrity": "sha512-723DD6x293D01zvQP4D6Otu207VZXF1t6t15MCvR3SM5vj+DycuoO5mePnD1VexUjbgVeycCiwwK6PQzfSKVnA==",
"dev": true, "dev": true,
"requires": { "requires": {
"async": "~0.2.9", "async": "~0.2.9",
@ -22086,19 +22092,19 @@
"async": { "async": {
"version": "0.2.10", "version": "0.2.10",
"resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz",
"integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", "integrity": "sha512-eAkdoKxU6/LkKDBzLpT+t6Ff5EtfSF4wx1WfJiPEEV7WNLnDaRXk0oVysiEPm262roaachGexwUv94WhSgN5TQ==",
"dev": true "dev": true
}, },
"moment": { "moment": {
"version": "2.15.2", "version": "2.15.2",
"resolved": "https://registry.npmjs.org/moment/-/moment-2.15.2.tgz", "resolved": "https://registry.npmjs.org/moment/-/moment-2.15.2.tgz",
"integrity": "sha1-G/3t9qbjRfMi/pVtXfW9CKjOhNw=", "integrity": "sha512-dv9NAmbJRSckFY2Dt3EcgoUGg85U4AaUvtJQ56k0QFumwqpOK3Huf0pYutSVgCFfN+DekvF4pW45PP9rf6ts7g==",
"dev": true "dev": true
}, },
"xml-crypto": { "xml-crypto": {
"version": "0.10.1", "version": "0.10.1",
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz", "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz",
"integrity": "sha1-+DL3TM9W8kr8rhFjofyrRNlndKg=", "integrity": "sha512-w64qUhByslUJ9D9nwfCyRUCXfVWA5WdzHevHT3BwAig2KOsDNYcuvE2soGUGUs0qp9cy+vGG6B/Ap8qCXPLN/g==",
"dev": true, "dev": true,
"requires": { "requires": {
"xmldom": "=0.1.19", "xmldom": "=0.1.19",
@ -22108,7 +22114,7 @@
"xmldom": { "xmldom": {
"version": "0.1.19", "version": "0.1.19",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.19.tgz", "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.19.tgz",
"integrity": "sha1-Yx/Ad3bv2EEYvyUXGzftTQdaCrw=", "integrity": "sha512-pDyxjQSFQgNHkU+yjvoF+GXVGJU7e9EnOg/KcGMDihBIKjTsOeDYaECwC/O9bsUWKY+Sd9izfE43JXC46EOHKA==",
"dev": true "dev": true
} }
} }
@ -22116,13 +22122,13 @@
"xmldom": { "xmldom": {
"version": "0.1.15", "version": "0.1.15",
"resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.15.tgz", "resolved": "https://registry.npmjs.org/xmldom/-/xmldom-0.1.15.tgz",
"integrity": "sha1-swSAYvG91S7cQhQkRZ8G3O6y+U0=", "integrity": "sha512-ssWmE9kBZudhl4iPLmXqaShPuASNKIQIikBzsloOjZqMyfbuQRn/ggz0k9NDa9YFI3+oFvp8t7TsqwmZLTvpoA==",
"dev": true "dev": true
}, },
"xpath": { "xpath": {
"version": "0.0.5", "version": "0.0.5",
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.5.tgz", "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.5.tgz",
"integrity": "sha1-RUA29u8PPfWvXUukoRn7dWdLPmw=", "integrity": "sha512-Y1Oyy8lyIDwWpmKIWBF0RZrQOP1fzE12G0ekSB1yzKPtbAdCI5sBCqBU/CAZUkKk81OXuq9tul/5lyNS+22iKg==",
"dev": true "dev": true
} }
} }
@ -22153,7 +22159,7 @@
"xml-crypto": { "xml-crypto": {
"version": "0.10.1", "version": "0.10.1",
"resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz", "resolved": "https://registry.npmjs.org/xml-crypto/-/xml-crypto-0.10.1.tgz",
"integrity": "sha1-+DL3TM9W8kr8rhFjofyrRNlndKg=", "integrity": "sha512-w64qUhByslUJ9D9nwfCyRUCXfVWA5WdzHevHT3BwAig2KOsDNYcuvE2soGUGUs0qp9cy+vGG6B/Ap8qCXPLN/g==",
"dev": true, "dev": true,
"requires": { "requires": {
"xmldom": "=0.1.19", "xmldom": "=0.1.19",
@ -22176,13 +22182,13 @@
"xpath": { "xpath": {
"version": "0.0.5", "version": "0.0.5",
"resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.5.tgz", "resolved": "https://registry.npmjs.org/xpath/-/xpath-0.0.5.tgz",
"integrity": "sha1-RUA29u8PPfWvXUukoRn7dWdLPmw=", "integrity": "sha512-Y1Oyy8lyIDwWpmKIWBF0RZrQOP1fzE12G0ekSB1yzKPtbAdCI5sBCqBU/CAZUkKk81OXuq9tul/5lyNS+22iKg==",
"dev": true "dev": true
}, },
"xtend": { "xtend": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/xtend/-/xtend-1.0.3.tgz", "resolved": "https://registry.npmjs.org/xtend/-/xtend-1.0.3.tgz",
"integrity": "sha1-P12Tc1PM7Y4IU5mlY/2yJUHClgo=", "integrity": "sha512-wv78b3q8kHDveC/C7Yq/UUrJXsAAM1t/j5m28h/ZlqYy0+eqByglhsWR88D2j3VImQzZlNIDsSbZ3QItwgWEGw==",
"dev": true "dev": true
} }
} }
@ -22327,7 +22333,7 @@
"select-hose": { "select-hose": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz",
"integrity": "sha1-Yl2GWPhlr0Psliv8N2o3NZpJlMo=", "integrity": "sha512-mEugaLK+YfkijB4fx0e6kImuJdCIt2LxCRcbEYPqRGCs4F2ogyfZU5IAZRdjCP8JPq2AtdNoC/Dux63d9Kiryg==",
"dev": true "dev": true
}, },
"selfsigned": { "selfsigned": {
@ -22444,7 +22450,7 @@
"serve-index": { "serve-index": {
"version": "1.9.1", "version": "1.9.1",
"resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz", "resolved": "https://registry.npmjs.org/serve-index/-/serve-index-1.9.1.tgz",
"integrity": "sha1-03aNabHn2C5c4FD/9bRTvqEqkjk=", "integrity": "sha512-pXHfKNP4qujrtteMrSBb0rc8HJ9Ms/GrXwcUtUtD5s4ewDJI8bT3Cz2zTVRMKtri49pLx2e0Ya8ziP5Ya2pZZw==",
"dev": true, "dev": true,
"requires": { "requires": {
"accepts": "~1.3.4", "accepts": "~1.3.4",
@ -22489,7 +22495,7 @@
"escape-html": { "escape-html": {
"version": "1.0.3", "version": "1.0.3",
"resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz",
"integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==",
"dev": true "dev": true
}, },
"mime-db": { "mime-db": {
@ -23408,7 +23414,7 @@
"statsd-parser": { "statsd-parser": {
"version": "0.0.4", "version": "0.0.4",
"resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz", "resolved": "https://registry.npmjs.org/statsd-parser/-/statsd-parser-0.0.4.tgz",
"integrity": "sha1-y9JDlTzELv/VSLXSI4jtaJ7GOb0=" "integrity": "sha512-7XO+ur89EalMXXFQaydsczB8sclr5nDsNIoUu0IzJx1pIbHUhO3LtpSzBwetIuU9DyTLMiVaJBMtWS/Nb2KR4g=="
}, },
"statuses": { "statuses": {
"version": "1.4.0", "version": "1.4.0",
@ -23433,7 +23439,7 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true "dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
@ -23499,7 +23505,7 @@
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
"dev": true, "dev": true,
"requires": { "requires": {
"wrappy": "1" "wrappy": "1"
@ -23531,7 +23537,7 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true "dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
@ -24086,7 +24092,7 @@
"strip-bom": { "strip-bom": {
"version": "3.0.0", "version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=", "integrity": "sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==",
"dev": true "dev": true
}, },
"strip-eof": { "strip-eof": {
@ -24345,7 +24351,7 @@
"tdigest": { "tdigest": {
"version": "0.1.1", "version": "0.1.1",
"resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz", "resolved": "https://registry.npmjs.org/tdigest/-/tdigest-0.1.1.tgz",
"integrity": "sha1-Ljyyw56kSeVdHmzZEReszKRYgCE=", "integrity": "sha512-CXcDY/NIgIbKZPx5H4JJNpq6JwJhU5Z4+yWj4ZghDc7/9nVajiRlPPyMXRePPPlBfcayUqtoCXjo7/Hm82ecUA==",
"requires": { "requires": {
"bintrees": "1.0.1" "bintrees": "1.0.1"
} }
@ -24689,7 +24695,7 @@
"isarray": { "isarray": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
"dev": true "dev": true
}, },
"process-nextick-args": { "process-nextick-args": {
@ -24788,7 +24794,7 @@
"to-arraybuffer": { "to-arraybuffer": {
"version": "1.0.1", "version": "1.0.1",
"resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz", "resolved": "https://registry.npmjs.org/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz",
"integrity": "sha1-fSKbH8xjfkZsoIEYCDanqr/4P0M=", "integrity": "sha512-okFlQcoGTi4LQBG/PgSYblw9VOyptsz2KJZqc6qtgGdes8VktzUQkj4BI2blit072iS8VODNcMA+tvnS9dnuMA==",
"dev": true "dev": true
}, },
"to-fast-properties": { "to-fast-properties": {
@ -25060,7 +25066,7 @@
"tty-browserify": { "tty-browserify": {
"version": "0.0.0", "version": "0.0.0",
"resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz", "resolved": "https://registry.npmjs.org/tty-browserify/-/tty-browserify-0.0.0.tgz",
"integrity": "sha1-oVe6QC2iTpv5V/mqadUk7tQpAaY=", "integrity": "sha512-JVa5ijo+j/sOoHGjw0sxw734b1LhBkQ3bvUGNdxnVXDCX81Yx7TFgnZygxrIIWn23hbfTaMYLwRmAxFyDuFmIw==",
"dev": true "dev": true
}, },
"tunnel-agent": { "tunnel-agent": {
@ -25508,7 +25514,7 @@
"url": { "url": {
"version": "0.10.3", "version": "0.10.3",
"resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz", "resolved": "https://registry.npmjs.org/url/-/url-0.10.3.tgz",
"integrity": "sha1-Ah5NnHcF8hu/N9A861h2dAJ3TGQ=", "integrity": "sha512-hzSUW2q06EqL1gKM/a+obYHLIO6ct2hwPuviqTTOcfFVc61UbfJ2Q32+uGL/HCPxKqrdGB5QUwIe7UqlDgwsOQ==",
"requires": { "requires": {
"punycode": "1.3.2", "punycode": "1.3.2",
"querystring": "0.2.0" "querystring": "0.2.0"
@ -25517,7 +25523,7 @@
"punycode": { "punycode": {
"version": "1.3.2", "version": "1.3.2",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0=" "integrity": "sha512-RofWgt/7fL5wP1Y7fxE7/EmTLzQVnB0ycyibJ0OOHIlJqTNzglYFxVwETOcIoJqJmpDXJ9xImDv+Fq34F/d4Dw=="
} }
} }
}, },
@ -25619,7 +25625,7 @@
"utils-flatten": { "utils-flatten": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/utils-flatten/-/utils-flatten-1.0.0.tgz", "resolved": "https://registry.npmjs.org/utils-flatten/-/utils-flatten-1.0.0.tgz",
"integrity": "sha1-AfMNMZO+RkxAsxdV5nQNDbDO8kM=", "integrity": "sha512-s21PUgUZ+XPvH8Wi8aj2FEqzZWeNEdemP7LB4u8u5wTDRO4xB+7czAYd3FY2O2rnu89U//khR0Ce8ka3//6M0w==",
"dev": true "dev": true
}, },
"utils-merge": { "utils-merge": {
@ -27924,7 +27930,7 @@
"xml-name-validator": { "xml-name-validator": {
"version": "2.0.1", "version": "2.0.1",
"resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-2.0.1.tgz",
"integrity": "sha1-TYuPHszTQZqjYgYb7O9RXh5VljU=", "integrity": "sha512-jRKe/iQYMyVJpzPH+3HL97Lgu5HrCfii+qSo+TfjKHtOnvbnvdVfMYrn9Q34YV81M2e5sviJlI6Ko9y+nByzvA==",
"dev": true "dev": true
}, },
"xml2js": { "xml2js": {

View file

@ -169,6 +169,7 @@
"less-plugin-autoprefix": "^2.0.0", "less-plugin-autoprefix": "^2.0.0",
"mini-css-extract-plugin": "^0.8.0", "mini-css-extract-plugin": "^0.8.0",
"mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", "mkdirp": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
"mock-fs": "^4.11.0",
"nodemon": "^1.14.3", "nodemon": "^1.14.3",
"optimize-css-assets-webpack-plugin": "^5.0.3", "optimize-css-assets-webpack-plugin": "^5.0.3",
"postcss-loader": "^3.0.0", "postcss-loader": "^3.0.0",

View file

@ -424,7 +424,7 @@ describe('ProjectStructureChanges', function() {
expect(update.userId).to.equal(owner._id) expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/main.tex') expect(update.pathname).to.equal('/main.tex')
expect(update.docLines).to.equal('Test') expect(update.docLines).to.equal('Test')
expect(version).to.equal(2) expect(version).to.equal(1)
}) })
it('should version the files created', function() { it('should version the files created', function() {
@ -437,7 +437,7 @@ describe('ProjectStructureChanges', function() {
expect(update.userId).to.equal(owner._id) expect(update.userId).to.equal(owner._id)
expect(update.pathname).to.equal('/1pixel.png') expect(update.pathname).to.equal('/1pixel.png')
expect(update.url).to.be.a('string') expect(update.url).to.be.a('string')
expect(version).to.equal(2) expect(version).to.equal(1)
}) })
}) })

View file

@ -36,18 +36,18 @@ describe('FolderStructureBuilder', function() {
describe('when given documents and files', function() { describe('when given documents and files', function() {
beforeEach(function() { beforeEach(function() {
const docUploads = [ const docUploads = [
{ dirname: '/', doc: { _id: 'doc-1', name: 'main.tex' } }, { path: '/main.tex', doc: { _id: 'doc-1', name: 'main.tex' } },
{ dirname: '/foo', doc: { _id: 'doc-2', name: 'other.tex' } }, { path: '/foo/other.tex', doc: { _id: 'doc-2', name: 'other.tex' } },
{ dirname: '/foo', doc: { _id: 'doc-3', name: 'other.bib' } }, { path: '/foo/other.bib', doc: { _id: 'doc-3', name: 'other.bib' } },
{ {
dirname: '/foo/foo1/foo2', path: '/foo/foo1/foo2/another.tex',
doc: { _id: 'doc-4', name: 'another.tex' } doc: { _id: 'doc-4', name: 'another.tex' }
} }
] ]
const fileUploads = [ const fileUploads = [
{ dirname: '/', fileRef: { _id: 'file-1', name: 'aaa.jpg' } }, { path: '/aaa.jpg', file: { _id: 'file-1', name: 'aaa.jpg' } },
{ dirname: '/foo', fileRef: { _id: 'file-2', name: 'bbb.jpg' } }, { path: '/foo/bbb.jpg', file: { _id: 'file-2', name: 'bbb.jpg' } },
{ dirname: '/bar', fileRef: { _id: 'file-3', name: 'ccc.jpg' } } { path: '/bar/ccc.jpg', file: { _id: 'file-3', name: 'ccc.jpg' } }
] ]
this.result = this.FolderStructureBuilder.buildFolderStructure( this.result = this.FolderStructureBuilder.buildFolderStructure(
docUploads, docUploads,
@ -103,8 +103,8 @@ describe('FolderStructureBuilder', function() {
describe('when given duplicate files', function() { describe('when given duplicate files', function() {
it('throws an error', function() { it('throws an error', function() {
const docUploads = [ const docUploads = [
{ dirname: '/foo', doc: { _id: 'doc-1', name: 'doc.tex' } }, { path: '/foo/doc.tex', doc: { _id: 'doc-1', name: 'doc.tex' } },
{ dirname: '/foo', doc: { _id: 'doc-2', name: 'doc.tex' } } { path: '/foo/doc.tex', doc: { _id: 'doc-2', name: 'doc.tex' } }
] ]
expect(() => expect(() =>
this.FolderStructureBuilder.buildFolderStructure(docUploads, []) this.FolderStructureBuilder.buildFolderStructure(docUploads, [])

View file

@ -1045,7 +1045,7 @@ describe('ProjectEntityMongoUpdateHandler', function() {
this.FolderStructureBuilder.buildFolderStructure this.FolderStructureBuilder.buildFolderStructure
.withArgs(this.docUploads, this.fileUploads) .withArgs(this.docUploads, this.fileUploads)
.returns(this.mockRootFolder) .returns(this.mockRootFolder)
this.updateExpectation = this.ProjectMock.expects('updateOne') this.updateExpectation = this.ProjectMock.expects('findOneAndUpdate')
.withArgs( .withArgs(
{ {
_id: this.project._id, _id: this.project._id,
@ -1053,14 +1053,15 @@ describe('ProjectEntityMongoUpdateHandler', function() {
'rootFolder.0.docs.0': { $exists: false }, 'rootFolder.0.docs.0': { $exists: false },
'rootFolder.0.files.0': { $exists: false } 'rootFolder.0.files.0': { $exists: false }
}, },
{ $set: { rootFolder: [this.mockRootFolder] }, $inc: { version: 1 } } { $set: { rootFolder: [this.mockRootFolder] }, $inc: { version: 1 } },
{ new: true, lean: true, fields: { version: 1 } }
) )
.chain('exec') .chain('exec')
}) })
describe('happy path', function() { describe('happy path', function() {
beforeEach(async function() { beforeEach(async function() {
this.updateExpectation.resolves({ n: 1 }) this.updateExpectation.resolves({ version: 1 })
await this.subject.promises.createNewFolderStructure( await this.subject.promises.createNewFolderStructure(
this.project._id, this.project._id,
this.docUploads, this.docUploads,
@ -1075,7 +1076,7 @@ describe('ProjectEntityMongoUpdateHandler', function() {
describe("when the update doesn't find a matching document", function() { describe("when the update doesn't find a matching document", function() {
beforeEach(async function() { beforeEach(async function() {
this.updateExpectation.resolves({ n: 0 }) this.updateExpectation.resolves(null)
}) })
it('throws an error', async function() { it('throws an error', async function() {

View file

@ -1,5 +1,6 @@
const sinon = require('sinon') const sinon = require('sinon')
const { expect } = require('chai') const { expect } = require('chai')
const mockFs = require('mock-fs')
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const { ObjectId } = require('mongodb') const { ObjectId } = require('mongodb')
@ -13,50 +14,6 @@ describe('FileSystemImportManager', function() {
this.newFolderId = new ObjectId() this.newFolderId = new ObjectId()
this.userId = new ObjectId() this.userId = new ObjectId()
this.folderPath = '/path/to/folder'
this.docName = 'test-doc.tex'
this.docPath = `/path/to/folder/${this.docName}`
this.docContent = 'one\ntwo\nthree'
this.docLines = this.docContent.split('\n')
this.fileName = 'test-file.jpg'
this.filePath = `/path/to/folder/${this.fileName}`
this.symlinkName = 'symlink'
this.symlinkPath = `/path/to/${this.symlinkName}`
this.ignoredName = '.DS_Store'
this.ignoredPath = `/path/to/folder/${this.ignoredName}`
this.folderEntries = [this.ignoredName, this.docName, this.fileName]
this.encoding = 'latin1'
this.fileStat = {
isFile: sinon.stub().returns(true),
isDirectory: sinon.stub().returns(false)
}
this.dirStat = {
isFile: sinon.stub().returns(false),
isDirectory: sinon.stub().returns(true)
}
this.symlinkStat = {
isFile: sinon.stub().returns(false),
isDirectory: sinon.stub().returns(false)
}
this.fs = {
promises: {
lstat: sinon.stub(),
readFile: sinon.stub(),
readdir: sinon.stub()
}
}
this.fs.promises.lstat.withArgs(this.filePath).resolves(this.fileStat)
this.fs.promises.lstat.withArgs(this.docPath).resolves(this.fileStat)
this.fs.promises.lstat.withArgs(this.symlinkPath).resolves(this.symlinkStat)
this.fs.promises.lstat.withArgs(this.folderPath).resolves(this.dirStat)
this.fs.promises.readFile
.withArgs(this.docPath, this.encoding)
.resolves(this.docContent)
this.fs.promises.readdir
.withArgs(this.folderPath)
.resolves(this.folderEntries)
this.EditorController = { this.EditorController = {
promises: { promises: {
addDoc: sinon.stub().resolves(), addDoc: sinon.stub().resolves(),
@ -66,25 +23,6 @@ describe('FileSystemImportManager', function() {
addFolder: sinon.stub().resolves({ _id: this.newFolderId }) addFolder: sinon.stub().resolves({ _id: this.newFolderId })
} }
} }
this.FileTypeManager = {
promises: {
isDirectory: sinon.stub().resolves(false),
getType: sinon.stub(),
shouldIgnore: sinon.stub().resolves(false)
}
}
this.FileTypeManager.promises.getType
.withArgs(this.fileName, this.filePath)
.resolves({ binary: true })
this.FileTypeManager.promises.getType
.withArgs(this.docName, this.docPath)
.resolves({ binary: false, encoding: this.encoding })
this.FileTypeManager.promises.isDirectory
.withArgs(this.folderPath)
.resolves(true)
this.FileTypeManager.promises.shouldIgnore
.withArgs(this.ignoredName)
.resolves(true)
this.logger = { this.logger = {
log() {}, log() {},
err() {} err() {}
@ -94,84 +32,147 @@ describe('FileSystemImportManager', function() {
console: console console: console
}, },
requires: { requires: {
fs: this.fs,
'../Editor/EditorController': this.EditorController, '../Editor/EditorController': this.EditorController,
'./FileTypeManager': this.FileTypeManager,
'logger-sharelatex': this.logger 'logger-sharelatex': this.logger
} }
}) })
}) })
describe('addFolderContents', function() { describe('importDir', function() {
describe('successfully', function() {
beforeEach(async function() { beforeEach(async function() {
await this.FileSystemImportManager.promises.addFolderContents( mockFs({
this.userId, 'import-test': {
this.projectId, 'main.tex': 'My thesis',
this.folderId, 'link-to-main.tex': mockFs.symlink({ path: 'import-test/main.tex' }),
this.folderPath, '.DS_Store': 'Should be ignored',
false images: {
'cat.jpg': Buffer.from([1, 2, 3, 4])
},
'line-endings': {
'unix.txt': 'one\ntwo\nthree',
'mac.txt': 'uno\rdos\rtres',
'windows.txt': 'ein\r\nzwei\r\ndrei',
'mixed.txt': 'uno\rdue\r\ntre\nquattro'
},
encodings: {
'utf16le.txt': Buffer.from('\ufeffétonnant!', 'utf16le'),
'latin1.txt': Buffer.from('tétanisant!', 'latin1')
}
},
symlink: mockFs.symlink({ path: 'import-test' })
})
this.entries = await this.FileSystemImportManager.promises.importDir(
'import-test'
) )
this.projectPaths = this.entries.map(x => x.projectPath)
}) })
it('should add each file in the folder which is not ignored', function() { afterEach(function() {
this.EditorController.promises.addDoc.should.have.been.calledWith( mockFs.restore()
this.projectId, })
this.folderId,
this.docName, it('should import regular docs', function() {
this.docLines, expect(this.entries).to.deep.include({
'upload', type: 'doc',
this.userId projectPath: '/main.tex',
) lines: ['My thesis']
this.EditorController.promises.addFile.should.have.been.calledWith(
this.projectId,
this.folderId,
this.fileName,
this.filePath,
null,
'upload',
this.userId
)
}) })
}) })
describe('with symlink', function() { it('should skip symlinks inside the import folder', function() {
it('should stop with an error', async function() { expect(this.projectPaths).not.to.include('/link-to-main.tex')
await expect(
this.FileSystemImportManager.promises.addFolderContents(
this.userId,
this.projectId,
this.folderId,
this.symlinkPath,
false
)
).to.be.rejectedWith('path is symlink')
this.EditorController.promises.addFolder.should.not.have.been.called
this.EditorController.promises.addDoc.should.not.have.been.called
this.EditorController.promises.addFile.should.not.have.been.called
}) })
it('should skip ignored files', function() {
expect(this.projectPaths).not.to.include('/.DS_Store')
})
it('should import binary files', function() {
expect(this.entries).to.deep.include({
type: 'file',
projectPath: '/images/cat.jpg',
fsPath: 'import-test/images/cat.jpg'
})
})
it('should deal with Mac/Windows/Unix line endings', function() {
expect(this.entries).to.deep.include({
type: 'doc',
projectPath: '/line-endings/unix.txt',
lines: ['one', 'two', 'three']
})
expect(this.entries).to.deep.include({
type: 'doc',
projectPath: '/line-endings/mac.txt',
lines: ['uno', 'dos', 'tres']
})
expect(this.entries).to.deep.include({
type: 'doc',
projectPath: '/line-endings/windows.txt',
lines: ['ein', 'zwei', 'drei']
})
expect(this.entries).to.deep.include({
type: 'doc',
projectPath: '/line-endings/mixed.txt',
lines: ['uno', 'due', 'tre', 'quattro']
})
})
it('should import documents with latin1 encoding', function() {
expect(this.entries).to.deep.include({
type: 'doc',
projectPath: '/encodings/latin1.txt',
lines: ['tétanisant!']
})
})
it('should import documents with utf16-le encoding', function() {
expect(this.entries).to.deep.include({
type: 'doc',
projectPath: '/encodings/utf16le.txt',
lines: ['\ufeffétonnant!']
})
})
it('should error when the root folder is a symlink', async function() {
await expect(this.FileSystemImportManager.promises.importDir('symlink'))
.to.be.rejected
}) })
}) })
describe('addEntity', function() { describe('addEntity', function() {
describe('with directory', function() { describe('with directory', function() {
describe('successfully', function() {
beforeEach(async function() { beforeEach(async function() {
mockFs({
path: {
to: {
folder: {
'doc.tex': 'one\ntwo\nthree',
'image.jpg': Buffer.from([1, 2, 3, 4])
}
}
}
})
await this.FileSystemImportManager.promises.addEntity( await this.FileSystemImportManager.promises.addEntity(
this.userId, this.userId,
this.projectId, this.projectId,
this.folderId, this.folderId,
this.folderName, 'folder',
this.folderPath, 'path/to/folder',
false false
) )
}) })
afterEach(function() {
mockFs.restore()
})
it('should add a folder to the project', function() { it('should add a folder to the project', function() {
this.EditorController.promises.addFolder.should.have.been.calledWith( this.EditorController.promises.addFolder.should.have.been.calledWith(
this.projectId, this.projectId,
this.folderId, this.folderId,
this.folderName, 'folder',
'upload' 'upload'
) )
}) })
@ -180,33 +181,40 @@ describe('FileSystemImportManager', function() {
this.EditorController.promises.addDoc.should.have.been.calledWith( this.EditorController.promises.addDoc.should.have.been.calledWith(
this.projectId, this.projectId,
this.newFolderId, this.newFolderId,
this.docName, 'doc.tex',
this.docLines, ['one', 'two', 'three'],
'upload', 'upload',
this.userId this.userId
) )
this.EditorController.promises.addFile.should.have.been.calledWith( this.EditorController.promises.addFile.should.have.been.calledWith(
this.projectId, this.projectId,
this.newFolderId, this.newFolderId,
this.fileName, 'image.jpg',
this.filePath, 'path/to/folder/image.jpg',
null, null,
'upload', 'upload',
this.userId this.userId
) )
}) })
}) })
})
describe('with binary file', function() { describe('with binary file', function() {
beforeEach(function() {
mockFs({ 'uploaded-file': Buffer.from([1, 2, 3, 4]) })
})
afterEach(function() {
mockFs.restore()
})
describe('with replace set to false', function() { describe('with replace set to false', function() {
beforeEach(async function() { beforeEach(async function() {
await this.FileSystemImportManager.promises.addEntity( await this.FileSystemImportManager.promises.addEntity(
this.userId, this.userId,
this.projectId, this.projectId,
this.folderId, this.folderId,
this.fileName, 'image.jpg',
this.filePath, 'uploaded-file',
false false
) )
}) })
@ -215,8 +223,8 @@ describe('FileSystemImportManager', function() {
this.EditorController.promises.addFile.should.have.been.calledWith( this.EditorController.promises.addFile.should.have.been.calledWith(
this.projectId, this.projectId,
this.folderId, this.folderId,
this.fileName, 'image.jpg',
this.filePath, 'uploaded-file',
null, null,
'upload', 'upload',
this.userId this.userId
@ -230,8 +238,8 @@ describe('FileSystemImportManager', function() {
this.userId, this.userId,
this.projectId, this.projectId,
this.folderId, this.folderId,
this.fileName, 'image.jpg',
this.filePath, 'uploaded-file',
true true
) )
}) })
@ -240,24 +248,42 @@ describe('FileSystemImportManager', function() {
this.EditorController.promises.upsertFile.should.have.been.calledWith( this.EditorController.promises.upsertFile.should.have.been.calledWith(
this.projectId, this.projectId,
this.folderId, this.folderId,
this.fileName, 'image.jpg',
this.filePath, 'uploaded-file',
null, null,
'upload', 'upload',
this.userId this.userId
) )
}) })
}) })
})
for (const [lineEndingDescription, lineEnding] of [
['Unix', '\n'],
['Mac', '\r'],
['Windows', '\r\n']
]) {
describe(`with text file (${lineEndingDescription} line endings)`, function() {
beforeEach(function() {
mockFs({
path: {
to: { 'uploaded-file': `one${lineEnding}two${lineEnding}three` }
}
})
})
afterEach(function() {
mockFs.restore()
})
describe('with text file', function() {
describe('with replace set to false', function() { describe('with replace set to false', function() {
beforeEach(async function() { beforeEach(async function() {
await this.FileSystemImportManager.promises.addEntity( await this.FileSystemImportManager.promises.addEntity(
this.userId, this.userId,
this.projectId, this.projectId,
this.folderId, this.folderId,
this.docName, 'doc.tex',
this.docPath, 'path/to/uploaded-file',
false false
) )
}) })
@ -266,66 +292,8 @@ describe('FileSystemImportManager', function() {
this.EditorController.promises.addDoc.should.have.been.calledWith( this.EditorController.promises.addDoc.should.have.been.calledWith(
this.projectId, this.projectId,
this.folderId, this.folderId,
this.docName, 'doc.tex',
this.docLines, ['one', 'two', 'three'],
'upload',
this.userId
)
})
})
describe('with windows line ending', function() {
beforeEach(async function() {
this.docContent = 'one\r\ntwo\r\nthree'
this.docLines = ['one', 'two', 'three']
this.fs.promises.readFile
.withArgs(this.docPath, this.encoding)
.resolves(this.docContent)
await this.FileSystemImportManager.promises.addEntity(
this.userId,
this.projectId,
this.folderId,
this.docName,
this.docPath,
false
)
})
it('should strip the \\r characters before adding', function() {
this.EditorController.promises.addDoc.should.have.been.calledWith(
this.projectId,
this.folderId,
this.docName,
this.docLines,
'upload',
this.userId
)
})
})
describe('with \r line endings', function() {
beforeEach(async function() {
this.docContent = 'one\rtwo\rthree'
this.docLines = ['one', 'two', 'three']
this.fs.promises.readFile
.withArgs(this.docPath, this.encoding)
.resolves(this.docContent)
await this.FileSystemImportManager.promises.addEntity(
this.userId,
this.projectId,
this.folderId,
this.docName,
this.docPath,
false
)
})
it('should treat the \\r characters as newlines', function() {
this.EditorController.promises.addDoc.should.have.been.calledWith(
this.projectId,
this.folderId,
this.docName,
this.docLines,
'upload', 'upload',
this.userId this.userId
) )
@ -338,8 +306,8 @@ describe('FileSystemImportManager', function() {
this.userId, this.userId,
this.projectId, this.projectId,
this.folderId, this.folderId,
this.docName, 'doc.tex',
this.docPath, 'path/to/uploaded-file',
true true
) )
}) })
@ -348,25 +316,35 @@ describe('FileSystemImportManager', function() {
this.EditorController.promises.upsertDoc.should.have.been.calledWith( this.EditorController.promises.upsertDoc.should.have.been.calledWith(
this.projectId, this.projectId,
this.folderId, this.folderId,
this.docName, 'doc.tex',
this.docLines, ['one', 'two', 'three'],
'upload', 'upload',
this.userId this.userId
) )
}) })
}) })
}) })
}) }
describe('with symlink', function() { describe('with symlink', function() {
beforeEach(function() {
mockFs({
path: { to: { symlink: mockFs.symlink({ path: '/etc/passwd' }) } }
})
})
afterEach(function() {
mockFs.restore()
})
it('should stop with an error', async function() { it('should stop with an error', async function() {
await expect( await expect(
this.FileSystemImportManager.promises.addEntity( this.FileSystemImportManager.promises.addEntity(
this.userId, this.userId,
this.projectId, this.projectId,
this.folderId, this.folderId,
this.symlinkName, 'main.tex',
this.symlinkPath, 'path/to/symlink',
false false
) )
).to.be.rejectedWith('path is symlink') ).to.be.rejectedWith('path is symlink')

View file

@ -2,6 +2,7 @@ const sinon = require('sinon')
const { expect } = require('chai') const { expect } = require('chai')
const timekeeper = require('timekeeper') const timekeeper = require('timekeeper')
const SandboxedModule = require('sandboxed-module') const SandboxedModule = require('sandboxed-module')
const { ObjectId } = require('mongodb')
const MODULE_PATH = const MODULE_PATH =
'../../../../app/src/Features/Uploads/ProjectUploadManager.js' '../../../../app/src/Features/Uploads/ProjectUploadManager.js'
@ -10,20 +11,55 @@ describe('ProjectUploadManager', function() {
beforeEach(function() { beforeEach(function() {
this.now = Date.now() this.now = Date.now()
timekeeper.freeze(this.now) timekeeper.freeze(this.now)
this.project_id = 'project-id-123' this.rootFolderId = new ObjectId()
this.folder_id = 'folder-id-123' this.ownerId = new ObjectId()
this.owner_id = 'owner-id-123' this.zipPath = '/path/to/zip/file-name.zip'
this.source = '/path/to/zip/file-name.zip' this.extractedZipPath = `/path/to/zip/file-name-${this.now}`
this.destination = `/path/to/zip/file-name-${this.now}` this.mainContent = 'Contents of main.tex'
this.root_folder_id = this.folder_id this.projectName = 'My project*'
this.owner_id = 'owner-id-123' this.fixedProjectName = 'My project'
this.name = 'Project name' this.uniqueProjectName = 'My project (1)'
this.othername = 'Other name'
this.project = { this.project = {
_id: this.project_id, _id: new ObjectId(),
rootFolder: [{ _id: this.root_folder_id }] rootFolder: [{ _id: this.rootFolderId }],
overleaf: { history: { id: 12345 } }
} }
this.doc = {
_id: new ObjectId(),
name: 'main.tex'
}
this.docFsPath = '/path/to/doc'
this.docLines = ['My thesis', 'by A. U. Thor']
this.file = {
_id: new ObjectId(),
name: 'image.png'
}
this.fileFsPath = '/path/to/file'
this.topLevelDestination = '/path/to/zip/file-extracted/nested' this.topLevelDestination = '/path/to/zip/file-extracted/nested'
this.newProjectVersion = 123
this.importEntries = [
{
type: 'doc',
projectPath: '/main.tex',
lines: this.docLines
},
{
type: 'file',
projectPath: `/${this.file.name}`,
fsPath: this.fileFsPath
}
]
this.docEntries = [
{
doc: this.doc,
path: `/${this.doc.name}`,
docLines: this.docLines.join('\n')
}
]
this.fileEntries = [
{ file: this.file, path: `/${this.file.name}`, url: this.fileStoreUrl }
]
this.fs = { this.fs = {
remove: sinon.stub().resolves() remove: sinon.stub().resolves()
@ -31,12 +67,42 @@ describe('ProjectUploadManager', function() {
this.ArchiveManager = { this.ArchiveManager = {
promises: { promises: {
extractZipArchive: sinon.stub().resolves(), extractZipArchive: sinon.stub().resolves(),
findTopLevelDirectory: sinon.stub().resolves(this.topLevelDestination) findTopLevelDirectory: sinon
.stub()
.withArgs(this.extractedZipPath)
.resolves(this.topLevelDestination)
}
}
this.Doc = sinon.stub().returns(this.doc)
this.DocstoreManager = {
promises: {
updateDoc: sinon.stub().resolves()
}
}
this.DocumentHelper = {
getTitleFromTexContent: sinon
.stub()
.withArgs(this.mainContent)
.returns(this.projectName)
}
this.DocumentUpdaterHandler = {
promises: {
updateProjectStructure: sinon.stub().resolves()
}
}
this.FileStoreHandler = {
promises: {
uploadFileFromDisk: sinon
.stub()
.resolves({ fileRef: this.file, url: this.fileStoreUrl })
} }
} }
this.FileSystemImportManager = { this.FileSystemImportManager = {
promises: { promises: {
addFolderContents: sinon.stub().resolves() importDir: sinon
.stub()
.withArgs(this.topLevelDestination)
.resolves(this.importEntries)
} }
} }
this.ProjectCreationHandler = { this.ProjectCreationHandler = {
@ -44,19 +110,27 @@ describe('ProjectUploadManager', function() {
createBlankProject: sinon.stub().resolves(this.project) createBlankProject: sinon.stub().resolves(this.project)
} }
} }
this.ProjectEntityMongoUpdateHandler = {
promises: {
createNewFolderStructure: sinon.stub().resolves(this.newProjectVersion)
}
}
this.ProjectRootDocManager = { this.ProjectRootDocManager = {
promises: { promises: {
setRootDocAutomatically: sinon.stub().resolves(), setRootDocAutomatically: sinon.stub().resolves(),
findRootDocFileFromDirectory: sinon findRootDocFileFromDirectory: sinon
.stub() .stub()
.resolves({ path: 'main.tex', content: this.othername }), .resolves({ path: 'main.tex', content: this.mainContent }),
setRootDocFromName: sinon.stub().resolves() setRootDocFromName: sinon.stub().resolves()
} }
} }
this.ProjectDetailsHandler = { this.ProjectDetailsHandler = {
fixProjectName: sinon.stub().returnsArg(0), fixProjectName: sinon
.stub()
.withArgs(this.projectName)
.returns(this.fixedProjectName),
promises: { promises: {
generateUniqueName: sinon.stub().resolves(this.othername) generateUniqueName: sinon.stub().resolves(this.uniqueProjectName)
} }
} }
this.ProjectDeleter = { this.ProjectDeleter = {
@ -64,8 +138,10 @@ describe('ProjectUploadManager', function() {
deleteProject: sinon.stub().resolves() deleteProject: sinon.stub().resolves()
} }
} }
this.DocumentHelper = { this.TpdsProjectFlusher = {
getTitleFromTexContent: sinon.stub().returns(this.othername) promises: {
flushProjectToTpds: sinon.stub().resolves()
}
} }
this.ProjectUploadManager = SandboxedModule.require(MODULE_PATH, { this.ProjectUploadManager = SandboxedModule.require(MODULE_PATH, {
@ -74,13 +150,21 @@ describe('ProjectUploadManager', function() {
}, },
requires: { requires: {
'fs-extra': this.fs, 'fs-extra': this.fs,
'./FileSystemImportManager': this.FileSystemImportManager,
'./ArchiveManager': this.ArchiveManager, './ArchiveManager': this.ArchiveManager,
'../../models/Doc': { Doc: this.Doc },
'../Docstore/DocstoreManager': this.DocstoreManager,
'../Documents/DocumentHelper': this.DocumentHelper,
'../DocumentUpdater/DocumentUpdaterHandler': this
.DocumentUpdaterHandler,
'../FileStore/FileStoreHandler': this.FileStoreHandler,
'./FileSystemImportManager': this.FileSystemImportManager,
'../Project/ProjectCreationHandler': this.ProjectCreationHandler, '../Project/ProjectCreationHandler': this.ProjectCreationHandler,
'../Project/ProjectEntityMongoUpdateHandler': this
.ProjectEntityMongoUpdateHandler,
'../Project/ProjectRootDocManager': this.ProjectRootDocManager, '../Project/ProjectRootDocManager': this.ProjectRootDocManager,
'../Project/ProjectDetailsHandler': this.ProjectDetailsHandler, '../Project/ProjectDetailsHandler': this.ProjectDetailsHandler,
'../Project/ProjectDeleter': this.ProjectDeleter, '../Project/ProjectDeleter': this.ProjectDeleter,
'../Documents/DocumentHelper': this.DocumentHelper '../ThirdPartyDataStore/TpdsProjectFlusher': this.TpdsProjectFlusher
} }
}) })
}) })
@ -93,61 +177,62 @@ describe('ProjectUploadManager', function() {
describe('when the title can be read from the root document', function() { describe('when the title can be read from the root document', function() {
beforeEach(async function() { beforeEach(async function() {
await this.ProjectUploadManager.promises.createProjectFromZipArchive( await this.ProjectUploadManager.promises.createProjectFromZipArchive(
this.owner_id, this.ownerId,
this.name, this.projectName,
this.source this.zipPath
) )
}) })
it('should extract the archive', function() { it('should extract the archive', function() {
this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith( this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith(
this.source, this.zipPath,
this.destination this.extractedZipPath
) )
}) })
it('should find the top level directory', function() { it('should create a project', function() {
this.ArchiveManager.promises.findTopLevelDirectory.should.have.been.calledWith(
this.destination
)
})
it('should insert the extracted archive into the folder', function() {
this.FileSystemImportManager.promises.addFolderContents.should.have.been.calledWith(
this.owner_id,
this.project_id,
this.folder_id,
this.topLevelDestination,
false
)
})
it('should create a project owned by the owner_id', function() {
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith( this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
this.owner_id this.ownerId,
this.uniqueProjectName
) )
}) })
it('should create a project with the correct name', function() { it('should initialize the file tree', function() {
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith( this.ProjectEntityMongoUpdateHandler.promises.createNewFolderStructure.should.have.been.calledWith(
sinon.match.any, this.project._id,
this.othername this.docEntries,
this.fileEntries
) )
}) })
it('should read the title from the tex contents', function() { it('should notify document updater', function() {
this.DocumentHelper.getTitleFromTexContent.should.have.been.called this.DocumentUpdaterHandler.promises.updateProjectStructure.should.have.been.calledWith(
this.project._id,
this.project.overleaf.history.id,
this.ownerId,
{
newDocs: this.docEntries,
newFiles: this.fileEntries,
newProject: { version: this.newProjectVersion }
}
)
})
it('should flush the project to TPDS', function() {
this.TpdsProjectFlusher.promises.flushProjectToTpds.should.have.been.calledWith(
this.project._id
)
}) })
it('should set the root document', function() { it('should set the root document', function() {
this.ProjectRootDocManager.promises.setRootDocFromName.should.have.been.calledWith( this.ProjectRootDocManager.promises.setRootDocFromName.should.have.been.calledWith(
this.project_id, this.project._id,
'main.tex' 'main.tex'
) )
}) })
it('should ensure the name is valid', function() { it('should remove the destination directory afterwards', function() {
this.ProjectDetailsHandler.fixProjectName.should.have.been.called this.fs.remove.should.have.been.calledWith(this.extractedZipPath)
}) })
}) })
@ -157,9 +242,9 @@ describe('ProjectUploadManager', function() {
{} {}
) )
await this.ProjectUploadManager.promises.createProjectFromZipArchive( await this.ProjectUploadManager.promises.createProjectFromZipArchive(
this.owner_id, this.ownerId,
this.name, this.projectName,
this.source this.zipPath
) )
}) })
@ -173,73 +258,78 @@ describe('ProjectUploadManager', function() {
describe('createProjectFromZipArchiveWithName', function() { describe('createProjectFromZipArchiveWithName', function() {
beforeEach(async function() { beforeEach(async function() {
await this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName( await this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
this.owner_id, this.ownerId,
this.name, this.projectName,
this.source this.zipPath
)
})
it('should create a project owned by the owner_id', function() {
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
this.owner_id
)
})
it('should create a project with the correct name', function() {
this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
sinon.match.any,
this.othername
)
})
it('should automatically set the root doc', function() {
this.ProjectRootDocManager.promises.setRootDocAutomatically.should.have.been.calledWith(
this.project_id
) )
}) })
it('should extract the archive', function() { it('should extract the archive', function() {
this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith( this.ArchiveManager.promises.extractZipArchive.should.have.been.calledWith(
this.source, this.zipPath,
this.destination this.extractedZipPath
) )
}) })
it('should find the top level directory', function() { it('should create a project owned by the owner_id', function() {
this.ArchiveManager.promises.findTopLevelDirectory.should.have.been.calledWith( this.ProjectCreationHandler.promises.createBlankProject.should.have.been.calledWith(
this.destination this.ownerId,
this.uniqueProjectName
) )
}) })
it('should insert the extracted archive into the folder', function() { it('should automatically set the root doc', function() {
this.FileSystemImportManager.promises.addFolderContents.should.have.been.calledWith( this.ProjectRootDocManager.promises.setRootDocAutomatically.should.have.been.calledWith(
this.owner_id, this.project._id
this.project_id, )
this.folder_id, })
this.topLevelDestination,
false it('should initialize the file tree', function() {
this.ProjectEntityMongoUpdateHandler.promises.createNewFolderStructure.should.have.been.calledWith(
this.project._id,
this.docEntries,
this.fileEntries
)
})
it('should notify document updater', function() {
this.DocumentUpdaterHandler.promises.updateProjectStructure.should.have.been.calledWith(
this.project._id,
this.project.overleaf.history.id,
this.ownerId,
{
newDocs: this.docEntries,
newFiles: this.fileEntries,
newProject: { version: this.newProjectVersion }
}
)
})
it('should flush the project to TPDS', function() {
this.TpdsProjectFlusher.promises.flushProjectToTpds.should.have.been.calledWith(
this.project._id
) )
}) })
it('should remove the destination directory afterwards', function() { it('should remove the destination directory afterwards', function() {
this.fs.remove.should.have.been.calledWith(this.destination) this.fs.remove.should.have.been.calledWith(this.extractedZipPath)
}) })
describe('when inserting the zip file contents into the root folder fails', function() { describe('when initializing the folder structure fails', function() {
beforeEach(async function() { beforeEach(async function() {
this.FileSystemImportManager.promises.addFolderContents.rejects() this.ProjectEntityMongoUpdateHandler.promises.createNewFolderStructure.rejects()
await expect( await expect(
this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName( this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
this.owner_id, this.ownerId,
this.name, this.projectName,
this.source this.zipPath
) )
).to.be.rejected ).to.be.rejected
}) })
it('should cleanup the blank project created', async function() { it('should cleanup the blank project created', async function() {
this.ProjectDeleter.promises.deleteProject.should.have.been.calledWith( this.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
this.project_id this.project._id
) )
}) })
}) })
@ -249,16 +339,16 @@ describe('ProjectUploadManager', function() {
this.ProjectRootDocManager.promises.setRootDocAutomatically.rejects() this.ProjectRootDocManager.promises.setRootDocAutomatically.rejects()
await expect( await expect(
this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName( this.ProjectUploadManager.promises.createProjectFromZipArchiveWithName(
this.owner_id, this.ownerId,
this.name, this.projectName,
this.source this.zipPath
) )
).to.be.rejected ).to.be.rejected
}) })
it('should cleanup the blank project created', function() { it('should cleanup the blank project created', function() {
this.ProjectDeleter.promises.deleteProject.should.have.been.calledWith( this.ProjectDeleter.promises.deleteProject.should.have.been.calledWith(
this.project_id this.project._id
) )
}) })
}) })