test: add IndexedDB tests (#1075)
* test: add IndexedDB tests Adds unit tests using fake-indexeddb. * remove wtfnode dep
This commit is contained in:
		
							parent
							
								
									93a3e85994
								
							
						
					
					
						commit
						5cde48c2c5
					
				
					 9 changed files with 311 additions and 9 deletions
				
			
		| 
						 | 
				
			
			@ -103,6 +103,7 @@
 | 
			
		|||
  "devDependencies": {
 | 
			
		||||
    "assert": "^1.4.1",
 | 
			
		||||
    "eslint-plugin-html": "^5.0.3",
 | 
			
		||||
    "fake-indexeddb": "^2.0.5",
 | 
			
		||||
    "mocha": "^6.0.2",
 | 
			
		||||
    "now": "^14.0.1",
 | 
			
		||||
    "standard": "^12.0.1",
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -42,6 +42,12 @@ function getOrCreateInstanceCache (cache, instanceName) {
 | 
			
		|||
export function clearCache (cache, instanceName) {
 | 
			
		||||
  delete cache.caches[instanceName]
 | 
			
		||||
}
 | 
			
		||||
export function clearAllCaches (instanceName) {
 | 
			
		||||
  let allCaches = [statusesCache, accountsCache, relationshipsCache, metaCache, notificationsCache]
 | 
			
		||||
  for (let cache of allCaches) {
 | 
			
		||||
    clearCache(cache, instanceName)
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
export function setInCache (cache, instanceName, key, value) {
 | 
			
		||||
  let instanceCache = getOrCreateInstanceCache(cache, instanceName)
 | 
			
		||||
  return instanceCache.set(key, value)
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,9 +15,10 @@ import { mark, stop } from '../_utils/marks'
 | 
			
		|||
import { deleteAll } from './utils'
 | 
			
		||||
import { createPinnedStatusKeyRange, createThreadKeyRange } from './keys'
 | 
			
		||||
import { getKnownInstances } from './knownInstances'
 | 
			
		||||
import noop from 'lodash-es/noop'
 | 
			
		||||
 | 
			
		||||
const BATCH_SIZE = 20
 | 
			
		||||
const TIME_AGO = 5 * 24 * 60 * 60 * 1000 // five days ago
 | 
			
		||||
export const TIME_AGO = 5 * 24 * 60 * 60 * 1000 // five days ago
 | 
			
		||||
const DELAY = 5 * 60 * 1000 // five minutes
 | 
			
		||||
 | 
			
		||||
function batchedGetAll (callGetAll, callback) {
 | 
			
		||||
| 
						 | 
				
			
			@ -97,7 +98,7 @@ function cleanupRelationships (relationshipsStore, cutoff) {
 | 
			
		|||
  )
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function cleanup (instanceName) {
 | 
			
		||||
export async function cleanup (instanceName) {
 | 
			
		||||
  console.log('cleanup', instanceName)
 | 
			
		||||
  mark(`cleanup:${instanceName}`)
 | 
			
		||||
  let db = await getDatabase(instanceName)
 | 
			
		||||
| 
						 | 
				
			
			@ -146,4 +147,5 @@ async function scheduledCleanup () {
 | 
			
		|||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const scheduleCleanup = debounce(scheduledCleanup, DELAY)
 | 
			
		||||
// we have unit tests that test indexedDB; we don't want this thing to run forever
 | 
			
		||||
export const scheduleCleanup = process.browser ? debounce(scheduledCleanup, DELAY) : noop
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -17,4 +17,7 @@ export const USERNAME_LOWERCASE = '__pinafore_acct_lc'
 | 
			
		|||
export const DB_VERSION_INITIAL = 9
 | 
			
		||||
export const DB_VERSION_SEARCH_ACCOUNTS = 10
 | 
			
		||||
export const DB_VERSION_SNOWFLAKE_IDS = 11
 | 
			
		||||
export const DB_VERSION_CURRENT = 11
 | 
			
		||||
 | 
			
		||||
// Using an object for these so that unit tests can change them
 | 
			
		||||
export const DB_VERSION_CURRENT = { version: 11 }
 | 
			
		||||
export const CURRENT_TIME = { now: () => Date.now() }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,13 +1,14 @@
 | 
			
		|||
import { DB_VERSION_CURRENT } from './constants'
 | 
			
		||||
import { addKnownInstance, deleteKnownInstance } from './knownInstances'
 | 
			
		||||
import { migrations } from './migrations'
 | 
			
		||||
import { clearAllCaches } from './cache'
 | 
			
		||||
 | 
			
		||||
const openReqs = {}
 | 
			
		||||
const databaseCache = {}
 | 
			
		||||
 | 
			
		||||
function createDatabase (instanceName) {
 | 
			
		||||
  return new Promise((resolve, reject) => {
 | 
			
		||||
    let req = indexedDB.open(instanceName, DB_VERSION_CURRENT)
 | 
			
		||||
    let req = indexedDB.open(instanceName, DB_VERSION_CURRENT.version)
 | 
			
		||||
    openReqs[instanceName] = req
 | 
			
		||||
    req.onerror = reject
 | 
			
		||||
    req.onblocked = () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -73,4 +74,17 @@ export function deleteDatabase (instanceName) {
 | 
			
		|||
    req.onerror = () => reject(req.error)
 | 
			
		||||
    req.onblocked = () => console.error(`database ${instanceName} blocked`)
 | 
			
		||||
  }).then(() => deleteKnownInstance(instanceName))
 | 
			
		||||
    .then(() => clearAllCaches(instanceName))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// this should probably only be used in unit tests
 | 
			
		||||
export function closeDatabase (instanceName) {
 | 
			
		||||
  // close any open requests
 | 
			
		||||
  let openReq = openReqs[instanceName]
 | 
			
		||||
  if (openReq && openReq.result) {
 | 
			
		||||
    openReq.result.close()
 | 
			
		||||
  }
 | 
			
		||||
  delete openReqs[instanceName]
 | 
			
		||||
  delete databaseCache[instanceName]
 | 
			
		||||
  clearAllCaches(instanceName)
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
import { dbPromise, getDatabase } from './databaseLifecycle'
 | 
			
		||||
import { getInCache, hasInCache, setInCache } from './cache'
 | 
			
		||||
import { ACCOUNT_ID, REBLOG_ID, STATUS_ID, TIMESTAMP, USERNAME_LOWERCASE } from './constants'
 | 
			
		||||
import {
 | 
			
		||||
  ACCOUNT_ID, REBLOG_ID, STATUS_ID, TIMESTAMP, USERNAME_LOWERCASE, CURRENT_TIME
 | 
			
		||||
} from './constants'
 | 
			
		||||
 | 
			
		||||
export async function getGenericEntityWithId (store, cache, instanceName, id) {
 | 
			
		||||
  if (hasInCache(cache, instanceName, id)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -50,6 +52,6 @@ export function cloneForStorage (obj) {
 | 
			
		|||
        break
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
  res[TIMESTAMP] = Date.now()
 | 
			
		||||
  res[TIMESTAMP] = CURRENT_TIME.now()
 | 
			
		||||
  return res
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										5
									
								
								tests/indexedDBShims.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								tests/indexedDBShims.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,5 @@
 | 
			
		|||
import indexedDB from 'fake-indexeddb'
 | 
			
		||||
import IDBKeyRange from 'fake-indexeddb/build/FDBKeyRange'
 | 
			
		||||
 | 
			
		||||
global.indexedDB = indexedDB
 | 
			
		||||
global.IDBKeyRange = IDBKeyRange
 | 
			
		||||
							
								
								
									
										192
									
								
								tests/unit/test-database.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										192
									
								
								tests/unit/test-database.js
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
				
			
			@ -0,0 +1,192 @@
 | 
			
		|||
/* global it describe beforeEach afterEach */
 | 
			
		||||
 | 
			
		||||
import '../indexedDBShims'
 | 
			
		||||
import assert from 'assert'
 | 
			
		||||
import { closeDatabase, deleteDatabase, getDatabase } from '../../src/routes/_database/databaseLifecycle'
 | 
			
		||||
import * as dbApi from '../../src/routes/_database/databaseApis'
 | 
			
		||||
import times from 'lodash-es/times'
 | 
			
		||||
import cloneDeep from 'lodash-es/cloneDeep'
 | 
			
		||||
import {
 | 
			
		||||
  TIMESTAMP, ACCOUNT_ID, STATUS_ID, REBLOG_ID, USERNAME_LOWERCASE,
 | 
			
		||||
  CURRENT_TIME, DB_VERSION_CURRENT, DB_VERSION_SEARCH_ACCOUNTS, DB_VERSION_SNOWFLAKE_IDS
 | 
			
		||||
} from '../../src/routes/_database/constants'
 | 
			
		||||
import { cleanup, TIME_AGO } from '../../src/routes/_database/cleanup'
 | 
			
		||||
 | 
			
		||||
const INSTANCE_NAME = 'localhost:3000'
 | 
			
		||||
 | 
			
		||||
const INSTANCE_INFO = {
 | 
			
		||||
  'uri': 'localhost:3000',
 | 
			
		||||
  'title': 'lolcathost',
 | 
			
		||||
  'description': 'blah',
 | 
			
		||||
  'foo': {
 | 
			
		||||
    'bar': true
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const createStatus = i => ({
 | 
			
		||||
  id: i.toString(),
 | 
			
		||||
  created_at: new Date().toISOString(),
 | 
			
		||||
  content: `Status #4{id}`,
 | 
			
		||||
  account: {
 | 
			
		||||
    id: '1'
 | 
			
		||||
  }
 | 
			
		||||
})
 | 
			
		||||
 | 
			
		||||
const stripDBFields = item => {
 | 
			
		||||
  let res = cloneDeep(item)
 | 
			
		||||
  delete res[TIMESTAMP]
 | 
			
		||||
  delete res[ACCOUNT_ID]
 | 
			
		||||
  delete res[STATUS_ID]
 | 
			
		||||
  delete res[REBLOG_ID]
 | 
			
		||||
  delete res[USERNAME_LOWERCASE]
 | 
			
		||||
  if (res.account) {
 | 
			
		||||
    delete res.account[TIMESTAMP]
 | 
			
		||||
  }
 | 
			
		||||
  return res
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
describe('test-database.js', function () {
 | 
			
		||||
  this.timeout(60000)
 | 
			
		||||
 | 
			
		||||
  describe('db-basic', () => {
 | 
			
		||||
    beforeEach(async () => {
 | 
			
		||||
      await getDatabase(INSTANCE_NAME)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    afterEach(async () => {
 | 
			
		||||
      await deleteDatabase(INSTANCE_NAME)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    it('basic indexeddb test', async () => {
 | 
			
		||||
      let info = await dbApi.getInstanceInfo(INSTANCE_NAME)
 | 
			
		||||
      assert(!info)
 | 
			
		||||
      await dbApi.setInstanceInfo(INSTANCE_NAME, INSTANCE_INFO)
 | 
			
		||||
      info = await dbApi.getInstanceInfo(INSTANCE_NAME)
 | 
			
		||||
      assert.deepStrictEqual(info, INSTANCE_INFO)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    it('basic indexeddb test 2', async () => {
 | 
			
		||||
      // sanity check to make sure that we have a clean DB between each test
 | 
			
		||||
      let info = await dbApi.getInstanceInfo(INSTANCE_NAME)
 | 
			
		||||
      assert(!info)
 | 
			
		||||
      await dbApi.setInstanceInfo(INSTANCE_NAME, INSTANCE_INFO)
 | 
			
		||||
      info = await dbApi.getInstanceInfo(INSTANCE_NAME)
 | 
			
		||||
      assert.deepStrictEqual(info, INSTANCE_INFO)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    it('stores and sorts some statuses', async () => {
 | 
			
		||||
      let allStatuses = times(40, createStatus)
 | 
			
		||||
      await dbApi.insertTimelineItems(INSTANCE_NAME, 'local', allStatuses)
 | 
			
		||||
      let statuses = await dbApi.getTimeline(INSTANCE_NAME, 'local', null, 20)
 | 
			
		||||
      let expected = allStatuses.slice().reverse().slice(0, 20)
 | 
			
		||||
      assert.deepStrictEqual(statuses.map(stripDBFields), expected)
 | 
			
		||||
 | 
			
		||||
      statuses = await dbApi.getTimeline(INSTANCE_NAME, 'local', statuses[statuses.length - 1].id, 20)
 | 
			
		||||
      expected = allStatuses.slice().reverse().slice(20, 40)
 | 
			
		||||
      assert.deepStrictEqual(statuses.map(stripDBFields), expected)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    it('cleans up old statuses', async () => {
 | 
			
		||||
      // Pretend we are inserting a status from a long time ago. Note that we
 | 
			
		||||
      // set a timestamp based on the *current* date when the status is inserted,
 | 
			
		||||
      // not the date that the status was composed.
 | 
			
		||||
 | 
			
		||||
      let longAgo = Date.now() - (TIME_AGO * 2)
 | 
			
		||||
 | 
			
		||||
      let oldStatus = {
 | 
			
		||||
        id: '1',
 | 
			
		||||
        created_at: new Date(longAgo).toISOString(),
 | 
			
		||||
        content: 'This is old',
 | 
			
		||||
        account: {
 | 
			
		||||
          id: '1'
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      let previousNow = CURRENT_TIME.now
 | 
			
		||||
      CURRENT_TIME.now = () => longAgo
 | 
			
		||||
 | 
			
		||||
      await dbApi.insertTimelineItems(INSTANCE_NAME, 'local', [oldStatus])
 | 
			
		||||
 | 
			
		||||
      CURRENT_TIME.now = previousNow
 | 
			
		||||
 | 
			
		||||
      let newStatus = {
 | 
			
		||||
        id: '2',
 | 
			
		||||
        created_at: new Date().toISOString(),
 | 
			
		||||
        content: 'This is new',
 | 
			
		||||
        account: {
 | 
			
		||||
          id: '2'
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      await dbApi.insertTimelineItems(INSTANCE_NAME, 'local', [newStatus])
 | 
			
		||||
      let statuses = await dbApi.getTimeline(INSTANCE_NAME, 'local', null, 20)
 | 
			
		||||
      assert.deepStrictEqual(statuses.map(stripDBFields), [newStatus, oldStatus])
 | 
			
		||||
 | 
			
		||||
      let status1 = await dbApi.getStatus(INSTANCE_NAME, '1')
 | 
			
		||||
      let status2 = await dbApi.getStatus(INSTANCE_NAME, '2')
 | 
			
		||||
 | 
			
		||||
      assert.deepStrictEqual(stripDBFields(status1), oldStatus)
 | 
			
		||||
      assert.deepStrictEqual(stripDBFields(status2), newStatus)
 | 
			
		||||
 | 
			
		||||
      await cleanup(INSTANCE_NAME)
 | 
			
		||||
 | 
			
		||||
      statuses = await dbApi.getTimeline(INSTANCE_NAME, 'local', null, 20)
 | 
			
		||||
      assert.deepStrictEqual(statuses.map(stripDBFields), [newStatus])
 | 
			
		||||
 | 
			
		||||
      status1 = await dbApi.getStatus(INSTANCE_NAME, '1')
 | 
			
		||||
      status2 = await dbApi.getStatus(INSTANCE_NAME, '2')
 | 
			
		||||
 | 
			
		||||
      assert(!!status1)
 | 
			
		||||
      assert.deepStrictEqual(stripDBFields(status2), newStatus)
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
 | 
			
		||||
  describe('db-migrations', () => {
 | 
			
		||||
    let oldCurrentVersion
 | 
			
		||||
 | 
			
		||||
    beforeEach(async () => {
 | 
			
		||||
      oldCurrentVersion = DB_VERSION_CURRENT.version
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    afterEach(async () => {
 | 
			
		||||
      DB_VERSION_CURRENT.version = oldCurrentVersion
 | 
			
		||||
      await deleteDatabase(INSTANCE_NAME)
 | 
			
		||||
    })
 | 
			
		||||
 | 
			
		||||
    it('migrates from v10 to v11', async () => {
 | 
			
		||||
      // open the db using the old version
 | 
			
		||||
      DB_VERSION_CURRENT.version = DB_VERSION_SEARCH_ACCOUNTS
 | 
			
		||||
      await getDatabase(INSTANCE_NAME)
 | 
			
		||||
 | 
			
		||||
      // insert some statuses
 | 
			
		||||
      let allStatuses = times(40, createStatus)
 | 
			
		||||
      await dbApi.insertTimelineItems(INSTANCE_NAME, 'local', allStatuses)
 | 
			
		||||
 | 
			
		||||
      let statuses = await dbApi.getTimeline(INSTANCE_NAME, 'local', null, 40)
 | 
			
		||||
      let expected = allStatuses.slice().reverse()
 | 
			
		||||
      assert.deepStrictEqual(statuses.map(stripDBFields), expected)
 | 
			
		||||
 | 
			
		||||
      // close the database
 | 
			
		||||
      closeDatabase(INSTANCE_NAME)
 | 
			
		||||
 | 
			
		||||
      // do a version upgrade
 | 
			
		||||
      DB_VERSION_CURRENT.version = DB_VERSION_SNOWFLAKE_IDS
 | 
			
		||||
      await getDatabase(INSTANCE_NAME)
 | 
			
		||||
 | 
			
		||||
      // check that the old statuses are correct
 | 
			
		||||
      statuses = await dbApi.getTimeline(INSTANCE_NAME, 'local', null, 40)
 | 
			
		||||
      expected = allStatuses.slice().reverse()
 | 
			
		||||
      assert.deepStrictEqual(statuses.map(stripDBFields), expected)
 | 
			
		||||
 | 
			
		||||
      // insert some more statuses for good measure
 | 
			
		||||
      let moreStatuses = times(20, i => 40 + i).map(createStatus)
 | 
			
		||||
 | 
			
		||||
      await dbApi.insertTimelineItems(INSTANCE_NAME, 'local', moreStatuses)
 | 
			
		||||
 | 
			
		||||
      statuses = await dbApi.getTimeline(INSTANCE_NAME, 'local', null, 60)
 | 
			
		||||
      expected = moreStatuses.slice().reverse().concat(allStatuses.reverse())
 | 
			
		||||
 | 
			
		||||
      assert.deepStrictEqual(statuses.map(stripDBFields), expected)
 | 
			
		||||
    })
 | 
			
		||||
  })
 | 
			
		||||
})
 | 
			
		||||
							
								
								
									
										81
									
								
								yarn.lock
									
										
									
									
									
								
							
							
						
						
									
										81
									
								
								yarn.lock
									
										
									
									
									
								
							| 
						 | 
				
			
			@ -1237,6 +1237,11 @@ balanced-match@^1.0.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
 | 
			
		||||
  integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
 | 
			
		||||
 | 
			
		||||
base64-arraybuffer-es6@0.4.2:
 | 
			
		||||
  version "0.4.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/base64-arraybuffer-es6/-/base64-arraybuffer-es6-0.4.2.tgz#b567d364065843113589b6c1436bd9492701c7fe"
 | 
			
		||||
  integrity sha512-HaJx92u12By863ZXVHZs4Bp1nkKaLpbs3Ec9SI1OKzq60Hz+Ks6z7UvdD8pIx61Ck3e8F9MH/IPEu5T0xKSbkQ==
 | 
			
		||||
 | 
			
		||||
base64-js@^1.0.2, base64-js@^1.1.2:
 | 
			
		||||
  version "1.3.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.0.tgz#cab1e6118f051095e58b5281aea8c1cd22bfc0e3"
 | 
			
		||||
| 
						 | 
				
			
			@ -2001,6 +2006,11 @@ core-js@^2.4.0, core-js@^2.5.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.3.tgz#4b70938bdffdaf64931e66e2db158f0892289c49"
 | 
			
		||||
  integrity sha512-l00tmFFZOBHtYhN4Cz7k32VM7vTn3rE2ANjQDxdEN6zmXZ/xq1jQuutnmHvMG1ZJ7xd72+TA5YpUK8wz3rWsfQ==
 | 
			
		||||
 | 
			
		||||
core-js@^2.4.1, core-js@^2.5.3:
 | 
			
		||||
  version "2.6.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.5.tgz#44bc8d249e7fb2ff5d00e0341a7ffb94fbf67895"
 | 
			
		||||
  integrity sha512-klh/kDpwX8hryYL14M9w/xei6vrv6sE8gTHDG7/T/+SEovB/G4ejwcfE/CBzO6Edsu+OETZMZ3wcX/EjUkrl5A==
 | 
			
		||||
 | 
			
		||||
core-util-is@1.0.2, core-util-is@~1.0.0:
 | 
			
		||||
  version "1.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7"
 | 
			
		||||
| 
						 | 
				
			
			@ -2447,6 +2457,13 @@ domelementtype@~1.1.1:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b"
 | 
			
		||||
  integrity sha1-vSh3PiZCiBrsUVRJJCmcXNgiGFs=
 | 
			
		||||
 | 
			
		||||
domexception@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/domexception/-/domexception-1.0.1.tgz#937442644ca6a31261ef36e3ec677fe805582c90"
 | 
			
		||||
  integrity sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    webidl-conversions "^4.0.2"
 | 
			
		||||
 | 
			
		||||
domhandler@^2.3.0:
 | 
			
		||||
  version "2.4.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803"
 | 
			
		||||
| 
						 | 
				
			
			@ -3023,6 +3040,15 @@ extsprintf@^1.2.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.0.tgz#e2689f8f356fad62cca65a3a91c5df5f9551692f"
 | 
			
		||||
  integrity sha1-4mifjzVvrWLMplo6kcXfX5VRaS8=
 | 
			
		||||
 | 
			
		||||
fake-indexeddb@^2.0.5:
 | 
			
		||||
  version "2.0.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/fake-indexeddb/-/fake-indexeddb-2.0.5.tgz#829685232f79bcb9d182b8dd33934e9e5657ed18"
 | 
			
		||||
  integrity sha512-C68kh3Ec3L6JZaTpRm6+TjY5AOs4bwtEOXazzb6733UL0F0jLR7j939e+TdlUmJdxumFQmXIzFhyLu5ZifQc5w==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    core-js "^2.4.1"
 | 
			
		||||
    realistic-structured-clone "^2.0.1"
 | 
			
		||||
    setimmediate "^1.0.5"
 | 
			
		||||
 | 
			
		||||
fast-deep-equal@^2.0.1:
 | 
			
		||||
  version "2.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49"
 | 
			
		||||
| 
						 | 
				
			
			@ -4542,6 +4568,11 @@ lodash.mergewith@^4.6.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/lodash.mergewith/-/lodash.mergewith-4.6.1.tgz#639057e726c3afbdb3e7d42741caa8d6e4335927"
 | 
			
		||||
  integrity sha512-eWw5r+PYICtEBgrBE5hhlT6aAa75f411bgDz/ZL2KZqYV03USvucsxcHUIlGTDTECs1eunpI7HOV7U+WLDvNdQ==
 | 
			
		||||
 | 
			
		||||
lodash.sortby@^4.7.0:
 | 
			
		||||
  version "4.7.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/lodash.sortby/-/lodash.sortby-4.7.0.tgz#edd14c824e2cc9c1e0b0a1b42bb5210516a42438"
 | 
			
		||||
  integrity sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=
 | 
			
		||||
 | 
			
		||||
lodash@4.17.11, "lodash@4.6.1 || ^4.16.1", lodash@^4.0.0, lodash@^4.14.0, lodash@^4.15.0, lodash@^4.17.10, lodash@^4.17.11, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.3.0, lodash@~4.17.10:
 | 
			
		||||
  version "4.17.11"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.11.tgz#b39ea6229ef607ecd89e2c8df12536891cac9b8d"
 | 
			
		||||
| 
						 | 
				
			
			@ -6126,6 +6157,16 @@ readdirp@^2.0.0, readdirp@^2.2.1:
 | 
			
		|||
    micromatch "^3.1.10"
 | 
			
		||||
    readable-stream "^2.0.2"
 | 
			
		||||
 | 
			
		||||
realistic-structured-clone@^2.0.1:
 | 
			
		||||
  version "2.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/realistic-structured-clone/-/realistic-structured-clone-2.0.2.tgz#2f8ec225b1f9af20efc79ac96a09043704414959"
 | 
			
		||||
  integrity sha512-5IEvyfuMJ4tjQOuKKTFNvd+H9GSbE87IcendSBannE28PTrbolgaVg5DdEApRKhtze794iXqVUFKV60GLCNKEg==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    core-js "^2.5.3"
 | 
			
		||||
    domexception "^1.0.1"
 | 
			
		||||
    typeson "^5.8.2"
 | 
			
		||||
    typeson-registry "^1.0.0-alpha.20"
 | 
			
		||||
 | 
			
		||||
redent@^1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/redent/-/redent-1.0.0.tgz#cf916ab1fd5f1f16dfb20822dd6ec7f730c2afde"
 | 
			
		||||
| 
						 | 
				
			
			@ -6575,7 +6616,7 @@ set-value@^2.0.0:
 | 
			
		|||
    is-plain-object "^2.0.3"
 | 
			
		||||
    split-string "^3.0.1"
 | 
			
		||||
 | 
			
		||||
setimmediate@^1.0.4:
 | 
			
		||||
setimmediate@^1.0.4, setimmediate@^1.0.5:
 | 
			
		||||
  version "1.0.5"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
 | 
			
		||||
  integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
 | 
			
		||||
| 
						 | 
				
			
			@ -7455,6 +7496,13 @@ tough-cookie@~2.4.3:
 | 
			
		|||
    psl "^1.1.24"
 | 
			
		||||
    punycode "^1.4.1"
 | 
			
		||||
 | 
			
		||||
tr46@^1.0.1:
 | 
			
		||||
  version "1.0.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/tr46/-/tr46-1.0.1.tgz#a8b13fd6bfd2489519674ccde55ba3693b706d09"
 | 
			
		||||
  integrity sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=
 | 
			
		||||
  dependencies:
 | 
			
		||||
    punycode "^2.1.0"
 | 
			
		||||
 | 
			
		||||
tree-kill@^1.1.0:
 | 
			
		||||
  version "1.2.1"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.1.tgz#5398f374e2f292b9dcc7b2e71e30a5c3bb6c743a"
 | 
			
		||||
| 
						 | 
				
			
			@ -7551,6 +7599,21 @@ typescript@^3.3.3:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.3.3333.tgz#171b2c5af66c59e9431199117a3bcadc66fdcfd6"
 | 
			
		||||
  integrity sha512-JjSKsAfuHBE/fB2oZ8NxtRTk5iGcg6hkYXMnZ3Wc+b2RSqejEqTaem11mHASMnFilHrax3sLK0GDzcJrekZYLw==
 | 
			
		||||
 | 
			
		||||
typeson-registry@^1.0.0-alpha.20:
 | 
			
		||||
  version "1.0.0-alpha.26"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/typeson-registry/-/typeson-registry-1.0.0-alpha.26.tgz#d1f337584196c5d5d112ad981e0dbbd2ced30c30"
 | 
			
		||||
  integrity sha512-R0wwXIYSiJMh+1XfvyUsCnEGVERoJcNrMl9e/ka30dJ+gQyh4/0NU9WHaqUm8oHtZzZYCz+A5fDRCiXYIq7H1Q==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    base64-arraybuffer-es6 "0.4.2"
 | 
			
		||||
    typeson "5.11.0"
 | 
			
		||||
    uuid "3.3.2"
 | 
			
		||||
    whatwg-url "7.0.0"
 | 
			
		||||
 | 
			
		||||
typeson@5.11.0, typeson@^5.8.2:
 | 
			
		||||
  version "5.11.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/typeson/-/typeson-5.11.0.tgz#a8273f00050be9eeef974aaa04a0c95a394f821a"
 | 
			
		||||
  integrity sha512-S5KtLzcU4dr4BXh8VuJDYugsRGsDQYlumCbrmwuAX1a1GNpbVYK4p9wluCIfTVPFvVyV6wRfExXX6Q1+YDItEQ==
 | 
			
		||||
 | 
			
		||||
uglify-js@3.4.x:
 | 
			
		||||
  version "3.4.9"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.4.9.tgz#af02f180c1207d76432e473ed24a28f4a782bae3"
 | 
			
		||||
| 
						 | 
				
			
			@ -7701,7 +7764,7 @@ utils-merge@1.0.1:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/utils-merge/-/utils-merge-1.0.1.tgz#9f95710f50a267947b2ccc124741c1028427e713"
 | 
			
		||||
  integrity sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=
 | 
			
		||||
 | 
			
		||||
uuid@^3.3.2:
 | 
			
		||||
uuid@3.3.2, uuid@^3.3.2:
 | 
			
		||||
  version "3.3.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.2.tgz#1b4af4955eb3077c501c23872fc6513811587131"
 | 
			
		||||
  integrity sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==
 | 
			
		||||
| 
						 | 
				
			
			@ -7749,6 +7812,11 @@ webauth@^1.1.0:
 | 
			
		|||
  resolved "https://registry.yarnpkg.com/webauth/-/webauth-1.1.0.tgz#64704f6b8026986605bc3ca629952e6e26fdd100"
 | 
			
		||||
  integrity sha1-ZHBPa4AmmGYFvDymKZUubib90QA=
 | 
			
		||||
 | 
			
		||||
webidl-conversions@^4.0.2:
 | 
			
		||||
  version "4.0.2"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-4.0.2.tgz#a855980b1f0b6b359ba1d5d9fb39ae941faa63ad"
 | 
			
		||||
  integrity sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==
 | 
			
		||||
 | 
			
		||||
webpack-bundle-analyzer@^3.0.4:
 | 
			
		||||
  version "3.0.4"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/webpack-bundle-analyzer/-/webpack-bundle-analyzer-3.0.4.tgz#095638487a664162f19e3b2fb7e621b7002af4b8"
 | 
			
		||||
| 
						 | 
				
			
			@ -7805,6 +7873,15 @@ webpack@^4.29.6:
 | 
			
		|||
    watchpack "^1.5.0"
 | 
			
		||||
    webpack-sources "^1.3.0"
 | 
			
		||||
 | 
			
		||||
whatwg-url@7.0.0:
 | 
			
		||||
  version "7.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-7.0.0.tgz#fde926fa54a599f3adf82dff25a9f7be02dc6edd"
 | 
			
		||||
  integrity sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==
 | 
			
		||||
  dependencies:
 | 
			
		||||
    lodash.sortby "^4.7.0"
 | 
			
		||||
    tr46 "^1.0.1"
 | 
			
		||||
    webidl-conversions "^4.0.2"
 | 
			
		||||
 | 
			
		||||
which-module@^1.0.0:
 | 
			
		||||
  version "1.0.0"
 | 
			
		||||
  resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f"
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		
		Reference in a new issue