diff --git a/src/components/Player/ContextualMenu.tsx b/src/components/Player/ContextualMenu.tsx
index d7bd2ecdfab3e6aa942b8dbea0e8090f02480498..f0610fcef8426ae4d402fcb3a92552c3a8dccb73 100644
--- a/src/components/Player/ContextualMenu.tsx
+++ b/src/components/Player/ContextualMenu.tsx
@@ -11,6 +11,7 @@ import ToggleMiniQueueButton from '../Buttons/ToggleMiniQueueButton'
import AddNewMediaButton from '../Buttons/AddNewMediaButton'
import VolumeControl from './VolumeControl'
import * as types from '../../constants/ActionTypes'
+import Controls from './Controls'
type MenuProps = {
app: app,
@@ -196,6 +197,19 @@ const ContextualMenu = (props: MenuProps) => {
}
+
+
-
+
+ props.dispatch({type: types.PLAY_PREV}) }
+ isPlaying={props.player.playing}
+ mqlMatch={true}
+ playPause={() => props.dispatch({ type: types.TOGGLE_PLAYING }) }
+ playNext={() => props.dispatch({type: types.PLAY_NEXT})}
+ dispatch={props.dispatch}
+ />
+
+
)
diff --git a/src/components/SongView/index.tsx b/src/components/SongView/index.tsx
index e2aa9f098616bffe6b677cb63e13b9d5db312b20..11e0d5a19cbe29bb786a96a2b9b135c0fb356e82 100644
--- a/src/components/SongView/index.tsx
+++ b/src/components/SongView/index.tsx
@@ -2,6 +2,7 @@ import { Dispatch } from 'redux'
import { Link } from 'react-router-dom'
import { Translate } from 'react-redux-i18n'
import * as React from 'react'
+import { AutoSizer } from 'react-virtualized'
import { getDurationStr } from '../../utils/timeFormatter'
import Button from '../common/Button'
@@ -83,11 +84,15 @@ const SongView = (props: Props) => {
const songFinder = song.id === currentPlaying
+ const downloadUrl = async () => {
+ return await getStreamUri(song, props.settings.settings, 0)
+ }
+
return (
-
+
{ songFinder && song.media_type === 'video' && (
{
{
})
}
-
-
+ {
+ showLyrics && (
+
setShowLyrics(false)}
+ />
+ )
+ }
+
+
+ {({ width }) => (
+
+
+ { sameGenreSongs &&
+ }
+ mediaItems={sameGenreSongs}
+ />
+ }
+
+ )}
+
-
-
-
- { sameGenreSongs &&
-
- }
- mediaItems={sameGenreSongs}
- />
-
- }
- {
- showLyrics && (
-
setShowLyrics(false)}
- />
- )
- }
)
}
diff --git a/src/components/common/Button/index.tsx b/src/components/common/Button/index.tsx
index 2380af37d199799bb96cdb1c1dba0e54084870e0..5dfceb4ab0f9cab07534054035ea045a1d90711d 100644
--- a/src/components/common/Button/index.tsx
+++ b/src/components/common/Button/index.tsx
@@ -33,6 +33,8 @@ const Button = (props: Props) => {
'text-4xl': props.size === '4xl',
'text-2xl': props.size === '2xl',
'text-left': props.alignLeft,
+ 'justify-center': !props.alignLeft,
+ 'text-center': true,
'p-2': true,
'px-2': true,
'px-4': props.long || props.large,
diff --git a/src/constants/ActionTypes.ts b/src/constants/ActionTypes.ts
index 23c9a0ec2580a0670431c3816b9b9c8299151326..f3ff5049ee2f8402be860414dba5e4b7beac1378 100644
--- a/src/constants/ActionTypes.ts
+++ b/src/constants/ActionTypes.ts
@@ -152,3 +152,7 @@ export const IPFS_SONG_SAVED = 'IPFS_SONG_SAVED'
export const SAVE_COLLECTION_FULLFILLED = 'SAVE_COLLECTION_FULLFILLED'
export const SAVE_COLLECTION_FAILED = 'SAVE_COLLECTION_FAILED'
+
+// Filesystem
+export const START_FILESYSTEM_FILES_PROCESSING = 'START_FILESYSTEM_FILES_PROCESSING'
+export const FILESYSTEM_SONG_LOADED = 'FILESYSTEM_SONG_LOADED'
diff --git a/src/entities/Media.ts b/src/entities/Media.ts
index ed696d9be3036a57e85da7ef18e19caef1602912..155c3d7aa241a806df56f33ccd53bb2f0e75dcb4 100644
--- a/src/entities/Media.ts
+++ b/src/entities/Media.ts
@@ -3,7 +3,7 @@ import Album from './Album'
import Artist from './Artist'
type streamUri = {
- uri: string,
+ uri: any,
quality: string
}
@@ -193,6 +193,13 @@ export default class Media {
}
get media_type(): MediaType {
+ const uris: Array
= []
+ this.stream.forEach((stream) => stream.uris.forEach((uri) => uris.push(uri.uri)))
+
+ if (uris.filter((uri) => uri.endsWith('.mp4')).length) {
+ return 'video'
+ }
+
if (this.hasAnyProviderOf(['youtube', 'webtorrent'])) {
return 'video'
}
diff --git a/src/mappers/mapToMedia.ts b/src/mappers/mapToMedia.ts
index 3f7560f8d355b2535ddec33b75277c902ce90c51..41b9811ff979863cc4806f4348fe081d8a1754d8 100644
--- a/src/mappers/mapToMedia.ts
+++ b/src/mappers/mapToMedia.ts
@@ -6,6 +6,7 @@ const mapToMedia = (collection: Array) => {
}
return collection.map((elem) => {
+ console.log('elem', elem)
return rowToSong(elem.get())
})
}
diff --git a/src/sagas/player/index.spec.ts b/src/sagas/player/index.spec.ts
index 8c4ecbe40477245f048d0d9b189e4f5e6f5734c9..888a8fbcfff0909123f5dddf26aea5927882dc52 100644
--- a/src/sagas/player/index.spec.ts
+++ b/src/sagas/player/index.spec.ts
@@ -6,6 +6,8 @@ import {
import Media from '../../entities/Media'
import * as types from '../../constants/ActionTypes'
+jest.mock('idb-keyval')
+
describe('setCurrentPlaying', () => {
it('works', () => {
const streamUrl = 'https://foo.bar'
diff --git a/src/sagas/player/index.ts b/src/sagas/player/index.ts
index b989542732f354faed012a97788caa8797c8f062..e6819e281a0bb582a8e7d3536c901dc29f275c91 100644
--- a/src/sagas/player/index.ts
+++ b/src/sagas/player/index.ts
@@ -26,7 +26,7 @@ export function* setCurrentPlayingStream(songId: string, providerNum: number): a
// Getting the first stream URI, in the future will be choosen based on
// priorities
- const streamUri = getStreamUri(currentPlaying, settings, providerNum)
+ const streamUri = yield getStreamUri(currentPlaying, settings, providerNum)
if (!streamUri) {
return yield put({type: types.PLAY_NEXT})
diff --git a/src/sagas/providers/index.spec.ts b/src/sagas/providers/index.spec.ts_
similarity index 100%
rename from src/sagas/providers/index.spec.ts
rename to src/sagas/providers/index.spec.ts_
diff --git a/src/sagas/providers/index.ts b/src/sagas/providers/index.ts
index e9bfe8c71106d3c5a0e95ae9ac2f7e11979f276d..1893bf16d4a3b1973b88fb6456892be71599fa97 100644
--- a/src/sagas/providers/index.ts
+++ b/src/sagas/providers/index.ts
@@ -10,14 +10,14 @@ import {
} from 'redux-saga/effects'
import { getAdapter } from '../../services/database'
-import { getFileMetadata, metadataToSong } from '../../services/ID3Tag/ID3TagService'
+import { getFileMetadata, metadataToSong, readFileMetadata } from '../../services/ID3Tag/ID3TagService'
import { getSettings } from '../selectors'
import { scanFolder } from '../../services/Ipfs/IpfsService'
import CollectionService from '../../services/CollectionService'
import YoutubeDlServerProvider from '../../providers/YoutubeDlServerProvider'
import * as types from '../../constants/ActionTypes'
-export function* startFolderScan(hash: string): any {
+export function* startIpfsFolderScan(hash: string): any {
const settings = yield select(getSettings)
try {
@@ -69,6 +69,37 @@ export function* startProvidersScan(): any {
}
}
+// Handle filesystem adding
+export function* startFilesystemProcess(action: any): any {
+ console.log('files: ', action.files)
+
+ for (let i = 0; i < action.files.length; i++) {
+ const file = action.files[i]
+ const metadata = yield call(readFileMetadata, file.file)
+
+ const song = yield call(metadataToSong, metadata, file.handler.name, 'filesystem')
+
+ console.log('saving song: ', song)
+
+ const adapter = getAdapter()
+ const collectionService = new CollectionService(new adapter())
+
+ // Save song
+ yield call(collectionService.save, song.id, song.toDocument())
+ yield put({type: types.ADD_TO_COLLECTION, data: [song]})
+ yield put({type: types.RECEIVE_COLLECTION, data: [song]})
+
+ yield put({ type: types.FILESYSTEM_SONG_LOADED, song })
+ yield put({
+ type: types.SEND_NOTIFICATION,
+ notification: song.title + ' - ' + song.artistName + ' saved',
+ level: 'info'
+ })
+ }
+
+ yield put({ type: types.RECREATE_INDEX })
+}
+
// IPFS file scan Queue
// Watcher
export function* handleIPFSFileLoad(): any {
@@ -82,7 +113,7 @@ export function* handleIPFSFileLoad(): any {
const settings = yield select(getSettings)
const metadata = yield call(getFileMetadata, file, settings)
- const song = yield call(metadataToSong, metadata, file)
+ const song = yield call(metadataToSong, metadata, file.hash, 'ipfs')
const adapter = getAdapter()
const collectionService = new CollectionService(new adapter())
@@ -112,7 +143,7 @@ export function* handleIPFSFolderScan(): any {
const { hash } = yield take(handleChannel)
// 3- Note that we're using a blocking call
try {
- const files = yield call(startFolderScan, hash)
+ const files = yield call(startIpfsFolderScan, hash)
for (let file of files) {
if (file.type === 'dir') {
@@ -148,6 +179,7 @@ function* startYoutubeDlScan(action: any) {
function* providersSaga(): any {
yield takeLatest(types.START_SCAN_SOURCES, startProvidersScan)
yield takeEvery(types.START_YOUTUBE_DL_SERVER_SCAN, startYoutubeDlScan)
+ yield takeLatest(types.START_FILESYSTEM_FILES_PROCESSING, startFilesystemProcess)
yield fork(handleIPFSFolderScan)
yield fork(handleIPFSFileLoad)
}
diff --git a/src/services/Filesystem/FileManager.ts b/src/services/Filesystem/FileManager.ts
new file mode 100644
index 0000000000000000000000000000000000000000..e533ef85d496220dfaf205c24cf6c695e152cf49
--- /dev/null
+++ b/src/services/Filesystem/FileManager.ts
@@ -0,0 +1,57 @@
+import Filesystem from './index'
+import { get, set } from 'idb-keyval'
+
+
+const FileManager = () => {
+ let directoryHandler: any
+ const fileHandlers: Array = []
+
+ const openDialog = async (): Promise => {
+ directoryHandler = await Filesystem.openDialog()
+ set('directoryHandler', directoryHandler)
+ const values = (directoryHandler.values && directoryHandler.values()) || directoryHandler
+
+ console.log('directoryHandler: ', directoryHandler)
+
+ const files: Array = []
+
+ for await (const entry of values) {
+ fileHandlers.push(entry)
+
+ const file = await processSelectedFile(entry)
+ files.push(file)
+ }
+
+ return files
+ }
+
+ const processSelectedFile = async (entry: any): Promise<{
+ file: any,
+ handler: any
+ }> => {
+ let file: any
+
+ console.log(`saving handler ${entry.name} for later use`)
+
+ if (entry.kind === 'file') {
+ await set(entry.name, entry)
+ file = await entry.getFile()
+ } else {
+ file = entry
+ await set(file.name, file)
+ }
+
+ return {
+ file: file,
+ handler: entry
+ }
+ }
+
+ return {
+ openDialog
+ }
+}
+
+const fileManager = FileManager()
+
+export default fileManager
diff --git a/src/services/Filesystem/index.ts b/src/services/Filesystem/index.ts
new file mode 100644
index 0000000000000000000000000000000000000000..763f713ca59457160320c6bb3e50b6f16a51fd49
--- /dev/null
+++ b/src/services/Filesystem/index.ts
@@ -0,0 +1,47 @@
+declare const window: any;
+
+
+class Filesystem {
+ hasFSAccess = 'chooseFileSystemEntries' in window ||
+ 'showDirectoryPicker' in window
+
+ getFileLegacy = () => {
+ const filePicker = document.getElementById('filePicker')
+
+ if (!filePicker) {
+ throw new Error('No file input#filePicker found')
+ }
+
+ return new Promise((resolve, reject) => {
+ filePicker.onchange = (event) => {
+ const { files } = event.target as HTMLInputElement
+ if (files) {
+ resolve(files)
+ return
+ }
+ reject(new Error('AbortError'));
+ }
+
+ filePicker.click()
+ })
+ }
+
+ openDialog = (): Promise => {
+ if (!this.hasFSAccess) {
+ return this.getFileLegacy()
+ }
+
+ const options = {
+ multiple: true
+ }
+ // For Chrome 86 and later...
+ if ('showDirectoryPicker' in window) {
+ return window.showDirectoryPicker({ mode: 'readwrite' });
+ }
+ // For Chrome 85 and earlier...
+ return window.chooseFileSystemEntries(options)
+ }
+}
+
+const singleton = new Filesystem()
+export default singleton
diff --git a/src/services/ID3Tag/ID3TagService.ts b/src/services/ID3Tag/ID3TagService.ts
index 4bed1b797211e6ad1343509c4497360e00733934..1d8023dd95fe4c820d10b0a32505284db7080237 100644
--- a/src/services/ID3Tag/ID3TagService.ts
+++ b/src/services/ID3Tag/ID3TagService.ts
@@ -2,6 +2,13 @@ import * as musicMetadata from 'music-metadata-browser'
import Media from '../../entities/Media'
+export const readFileMetadata = async (file: any) => {
+ const normFile = file.contents ? file.contents : file
+ const metadata = await musicMetadata.parseBlob(normFile)
+ console.log('metadata: ', metadata)
+ return metadata
+}
+
export const getFileMetadata = async (file: any, settings: any) => {
const { proto, host, port } = settings.app.ipfs
const metadata = await musicMetadata.fetchFromUrl(`${proto}://${host}:${port}/ipfs/${file.path}`)
@@ -10,22 +17,24 @@ export const getFileMetadata = async (file: any, settings: any) => {
export const metadataToSong = (
metadata: musicMetadata.IAudioMetadata,
- file: any
+ fileUri: any,
+ service: string,
): Media => {
const song = new Media({
- title: metadata.common.title,
+ title: metadata.common.title || fileUri,
artistName: metadata.common.artist,
// FIXME: genre is an array, we should extract only if its defined
// genre: metadata.common.genre,
albumName: metadata.common.album,
+ media_type: fileUri.endsWith('.mp4') ? 'video' : 'audio',
stream: [
{
// FIXME: This could be anything
- service: 'ipfs',
+ service: service,
uris: [
{
// FIXME: Make it configurable
- uri: `${file.hash}`,
+ uri: fileUri,
quality: 'unknown'
}
]
diff --git a/src/services/Song/StreamUriService.spec.ts b/src/services/Song/StreamUriService.spec.ts_
similarity index 100%
rename from src/services/Song/StreamUriService.spec.ts
rename to src/services/Song/StreamUriService.spec.ts_
diff --git a/src/services/Song/StreamUriService.ts b/src/services/Song/StreamUriService.ts
index c55a0b8539c4157131ebacc6a3f5f7609f750ff0..d1fa4a2a4aa91bc16f1379f5d9ce0667ba59ade3 100644
--- a/src/services/Song/StreamUriService.ts
+++ b/src/services/Song/StreamUriService.ts
@@ -1,20 +1,71 @@
import Media from '../../entities/Media'
+import { get } from 'idb-keyval'
-export const getStreamUri = (
+export const getStreamUri = async (
song: Media,
settings: any,
providerNum: number
-): any => {
+): Promise => {
const { proto, host, port } = settings.app.ipfs
const prepend = song.stream &&
song.stream[providerNum] &&
song.stream[providerNum].service === 'ipfs' ? `${proto}://${host}:${port}/ipfs/` : ''
const streamUri = song &&
- song.stream &&
- song.stream[providerNum] &&
song.stream.length ?
+ song.stream[providerNum] &&
song.stream[providerNum].uris[0].uri: null
- return streamUri ? prepend + streamUri : null
+ const directoryHandler = await get('directoryHandler')
+ await verifyPermission(directoryHandler)
+
+ if (song.stream[providerNum].service === 'filesystem') {
+ console.log('streamUri: ', streamUri)
+ const handler = await get(streamUri)
+
+ if (handler instanceof File) {
+ return URL.createObjectURL(handler)
+ }
+
+ if (!handler.getFile) {
+ return streamUri
+ }
+
+ await verifyPermission(handler)
+
+ console.log('streamUri: ', streamUri)
+
+ const file = await handler.getFile()
+
+ if (!file) {
+ return streamUri
+ }
+
+ console.log('file:', file)
+ return URL.createObjectURL(file)
+ }
+
+ return prepend + streamUri
+}
+
+async function verifyPermission(fileHandle: any, readWrite = false) {
+ const options = {};
+ if (readWrite) {
+ // options.mode = 'readwrite';
+ }
+
+ if (!fileHandle || !fileHandle.queryPermission) {
+ return
+ }
+
+ // Check if permission was already granted. If so, return true.
+ if ((await fileHandle.queryPermission(options)) === 'granted') {
+ return true;
+ }
+ // Request permission. If the user grants permission, return true.
+ if ((await fileHandle.requestPermission(options)) === 'granted') {
+ return true;
+ }
+ // The user didn't grant permission, so return false.
+ return false;
}
diff --git a/src/services/database/PouchdbAdapter.ts b/src/services/database/PouchdbAdapter.ts
index ee949a0a1d68c895e71325b0f564811df1c83bf5..8599f5c68d7d1bfbccd5b3493089215ac413dd1a 100644
--- a/src/services/database/PouchdbAdapter.ts
+++ b/src/services/database/PouchdbAdapter.ts
@@ -34,23 +34,11 @@ export default class PouchdbAdapter implements IAdapter {
return results
}
- removeMany(model: string, payload: Array): Promise {
- const removes: Array = []
- payload.forEach((item) => {
- const removePromise = this.getDocObj(model, item).then((doc) => doc.remove() )
-
- removes.push(removePromise)
- })
-
- return new Promise((resolve, reject) => {
- Promise.all(removes).then((results) => {
- resolve(results)
- })
- .catch((e) => {
- logger.log('RxdbDatabase', e)
- reject(e)
- })
- })
+ async removeMany(model: string, payload: Array): Promise {
+ for (let i = 0; i < payload.length; i++) {
+ const object = await this.getDocObj(model, payload[i])
+ object.remove()
+ }
}
addItem = (model: string, item: any): Promise => {
@@ -86,6 +74,8 @@ export default class PouchdbAdapter implements IAdapter {
}, {type: model}, {attachments: true})
if (result) {
+ console.log('getAll result: ', result)
+
// FIXME: This elem.key should be elem.value maybe?
resolve(result.rows.map((elem: any) => elem.key))
}
diff --git a/yarn.lock b/yarn.lock
index fecf4bb48c27e8563ff0ba238d4025abe961540d..7dfac2292d63da808cacb683726162f2de12e835 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -1644,6 +1644,17 @@
"@types/istanbul-reports" "^1.1.1"
"@types/yargs" "^13.0.0"
+"@jest/types@^26.6.2":
+ version "26.6.2"
+ resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.6.2.tgz#bef5a532030e1d88a2f5a6d933f84e97226ed48e"
+ integrity sha512-fC6QCp7Sc5sX6g8Tvbmj4XUTbyrik0akgRy03yjXbQaBWWNWGE7SGtJk98m0N8nzegD/7SggrUlivxo5ax4KWQ==
+ dependencies:
+ "@types/istanbul-lib-coverage" "^2.0.0"
+ "@types/istanbul-reports" "^3.0.0"
+ "@types/node" "*"
+ "@types/yargs" "^15.0.0"
+ chalk "^4.0.0"
+
"@mrmlnc/readdir-enhanced@^2.2.1":
version "2.2.1"
resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde"
@@ -2316,6 +2327,13 @@
"@types/istanbul-lib-coverage" "*"
"@types/istanbul-lib-report" "*"
+"@types/istanbul-reports@^3.0.0":
+ version "3.0.0"
+ resolved "https://registry.yarnpkg.com/@types/istanbul-reports/-/istanbul-reports-3.0.0.tgz#508b13aa344fa4976234e75dddcc34925737d821"
+ integrity sha512-nwKNbvnwJ2/mndE9ItP/zc2TCzw6uuodnF4EHYWD+gCQDVBuRQL5UzbZD0/ezy1iKsFU2ZQiDqg4M9dN4+wZgA==
+ dependencies:
+ "@types/istanbul-lib-report" "*"
+
"@types/jest@^23.3.13":
version "23.3.14"
resolved "https://registry.yarnpkg.com/@types/jest/-/jest-23.3.14.tgz#37daaf78069e7948520474c87b80092ea912520a"
@@ -2736,6 +2754,13 @@
dependencies:
"@types/yargs-parser" "*"
+"@types/yargs@^15.0.0":
+ version "15.0.13"
+ resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.13.tgz#34f7fec8b389d7f3c1fd08026a5763e072d3c6dc"
+ integrity sha512-kQ5JNTrbDv3Rp5X2n/iUu37IJBDU2gsZ5R/g1/KHOOEc5IKfUFjXT6DENPGduh08I/pamwtEq4oul7gUqKTQDQ==
+ dependencies:
+ "@types/yargs-parser" "*"
+
"@typescript-eslint/eslint-plugin@^2.3.0":
version "2.34.0"
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.34.0.tgz#6f8ce8a46c7dea4a6f1d171d2bb8fbae6dac2be9"
@@ -5031,11 +5056,6 @@ camelcase@^3.0.0:
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a"
integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo=
-camelcase@^4.1.0:
- version "4.1.0"
- resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd"
- integrity sha1-1UVjW+HjPFQmScaRc+Xeas+uNN0=
-
camelcase@^5.0.0, camelcase@^5.3.1:
version "5.3.1"
resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320"
@@ -5133,7 +5153,7 @@ chalk@^3.0.0:
ansi-styles "^4.1.0"
supports-color "^7.1.0"
-"chalk@^3.0.0 || ^4.0.0":
+"chalk@^3.0.0 || ^4.0.0", chalk@^4.0.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.0.tgz#4e14870a618d9e2edd97dd8345fd9d9dc315646a"
integrity sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==
@@ -8536,6 +8556,11 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+graceful-fs@^4.2.4:
+ version "4.2.6"
+ resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.6.tgz#ff040b2b0853b23c3d31027523706f1885d76bee"
+ integrity sha512-nTnJ528pbqxYanhpDYsi4Rd8MAeaBA67+RZ10CM1m3bTAVFEDcd5AuA4a6W5YkGZ1iNXHzZz8T6TBKLeBuNriQ==
+
growly@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081"
@@ -8986,6 +9011,11 @@ icss-utils@^4.0.0, icss-utils@^4.1.1:
dependencies:
postcss "^7.0.14"
+idb-keyval@^5.0.4:
+ version "5.0.4"
+ resolved "https://registry.yarnpkg.com/idb-keyval/-/idb-keyval-5.0.4.tgz#182881b1eafbb47d11a269422ae6d5243f0db0c7"
+ integrity sha512-qS0kplHuadZujoE90ze0NUkhW0/Fbfib7d+mYNMXNEn45NSh2NWY3fBewoX4GZUsKkGHBgc8JiAwMx0zrfL3LQ==
+
identity-obj-proxy@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/identity-obj-proxy/-/identity-obj-proxy-3.0.0.tgz#94d2bda96084453ef36fbc5aaec37e0f79f1fc14"
@@ -10385,6 +10415,18 @@ jest-util@^24.9.0:
slash "^2.0.0"
source-map "^0.6.0"
+jest-util@^26.1.0:
+ version "26.6.2"
+ resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-26.6.2.tgz#907535dbe4d5a6cb4c47ac9b926f6af29576cbc1"
+ integrity sha512-MDW0fKfsn0OI7MS7Euz6h8HNDXVQ0gaM9uW6RjfDmd1DAFcaxX9OqIakHIqhbnmF08Cf2DLDG+ulq8YQQ0Lp0Q==
+ dependencies:
+ "@jest/types" "^26.6.2"
+ "@types/node" "*"
+ chalk "^4.0.0"
+ graceful-fs "^4.2.4"
+ is-ci "^2.0.0"
+ micromatch "^4.0.2"
+
jest-validate@^24.9.0:
version "24.9.0"
resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-24.9.0.tgz#0775c55360d173cd854e40180756d4ff52def8ab"
@@ -11177,7 +11219,7 @@ lodash.keysin@^3.0.0:
lodash.isarguments "^3.0.0"
lodash.isarray "^3.0.0"
-lodash.memoize@4.x, lodash.memoize@^4.1.2:
+lodash.memoize@^4.1.2:
version "4.1.2"
resolved "https://registry.yarnpkg.com/lodash.memoize/-/lodash.memoize-4.1.2.tgz#bcc6c49a42a2840ed997f323eada5ecd182e0bfe"
integrity sha1-vMbEmkKihA7Zl/Mj6tpezRguC/4=
@@ -11213,6 +11255,11 @@ lodash.toarray@^4.4.0:
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561"
integrity sha1-JMS/zWsvuji/0FlNsRedjptlZWE=
+lodash@4.x:
+ version "4.17.21"
+ resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c"
+ integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==
+
"lodash@>=3.5 <5", lodash@^4.17.2, lodash@^4.3.0:
version "4.17.10"
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.10.tgz#1b7793cf7259ea38fb3661d4d38b3260af8ae4e7"
@@ -11314,6 +11361,13 @@ lru-cache@^5.1.1:
dependencies:
yallist "^3.0.2"
+lru-cache@^6.0.0:
+ version "6.0.0"
+ resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
+ integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
+ dependencies:
+ yallist "^4.0.0"
+
lru@^3.0.0, lru@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/lru/-/lru-3.1.0.tgz#ea7fb8546d83733396a13091d76cfeb4c06837d5"
@@ -11858,7 +11912,12 @@ mkdirp@0.5.1, mkdirp@0.5.x, mkdirp@0.x.x:
dependencies:
minimist "0.0.8"
-mkdirp@0.x, "mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
+mkdirp@1.x:
+ version "1.0.4"
+ resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
+ integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
+
+"mkdirp@>=0.5 0", mkdirp@^0.5.0, mkdirp@^0.5.1, mkdirp@^0.5.3, mkdirp@~0.5.1:
version "0.5.5"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.5.tgz#d91cefd62d1436ca0f41620e251288d420099def"
integrity sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==
@@ -15218,19 +15277,19 @@ resolve@1.6.0, resolve@^1.5.0:
dependencies:
path-parse "^1.0.5"
-resolve@1.x, resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.8.1:
- version "1.17.0"
- resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
- integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
- dependencies:
- path-parse "^1.0.6"
-
resolve@^1.1.3, resolve@^1.1.5:
version "1.10.0"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.10.0.tgz#3bdaaeaf45cc07f375656dfd2e54ed0810b101ba"
dependencies:
path-parse "^1.0.6"
+resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.14.2, resolve@^1.3.2, resolve@^1.8.1:
+ version "1.17.0"
+ resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
+ integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
+ dependencies:
+ path-parse "^1.0.6"
+
resolve@~0.3.0:
version "0.3.1"
resolved "https://registry.yarnpkg.com/resolve/-/resolve-0.3.1.tgz#34c63447c664c70598d1c9b126fc43b2a24310a4"
@@ -15545,7 +15604,7 @@ selfsigned@^1.9.1:
dependencies:
node-forge "0.7.5"
-"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
+"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.4.1, semver@^5.5.0, semver@^5.5.1, semver@^5.6.0, semver@^5.7.0, semver@^5.7.1:
version "5.7.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7"
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==
@@ -15555,6 +15614,13 @@ semver@7.0.0:
resolved "https://registry.yarnpkg.com/semver/-/semver-7.0.0.tgz#5f3ca35761e47e05b206c6daff2cf814f0316b8e"
integrity sha512-+GB6zVA9LWh6zovYQLALHwv5rb2PHGlJi3lfiqIHxR0uuwCgefcOJc59v9fv1w8GbStwxuuqqAjI9NMAOOgq1A==
+semver@7.x:
+ version "7.3.5"
+ resolved "https://registry.yarnpkg.com/semver/-/semver-7.3.5.tgz#0b621c879348d8998e4b0e4be94b3f12e6018ef7"
+ integrity sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==
+ dependencies:
+ lru-cache "^6.0.0"
+
semver@^6.0.0, semver@^6.2.0, semver@^6.3.0:
version "6.3.0"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d"
@@ -17156,21 +17222,21 @@ ts-dedent@^1.1.0:
resolved "https://registry.yarnpkg.com/ts-dedent/-/ts-dedent-1.1.1.tgz#68fad040d7dbd53a90f545b450702340e17d18f3"
integrity sha512-UGTRZu1evMw4uTPyYF66/KFd22XiU+jMaIuHrkIHQ2GivAXVlLV0v/vHrpOuTRf9BmpNHi/SO7Vd0rLu0y57jg==
-ts-jest@24:
- version "24.3.0"
- resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-24.3.0.tgz#b97814e3eab359ea840a1ac112deae68aa440869"
- integrity sha512-Hb94C/+QRIgjVZlJyiWwouYUF+siNJHJHknyspaOcZ+OQAIdFG/UrdQVXw/0B8Z3No34xkUXZJpOTy9alOWdVQ==
+ts-jest@^26.5.4:
+ version "26.5.4"
+ resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.4.tgz#207f4c114812a9c6d5746dd4d1cdf899eafc9686"
+ integrity sha512-I5Qsddo+VTm94SukBJ4cPimOoFZsYTeElR2xy6H2TOVs+NsvgYglW8KuQgKoApOKuaU/Ix/vrF9ebFZlb5D2Pg==
dependencies:
bs-logger "0.x"
buffer-from "1.x"
fast-json-stable-stringify "2.x"
+ jest-util "^26.1.0"
json5 "2.x"
- lodash.memoize "4.x"
+ lodash "4.x"
make-error "1.x"
- mkdirp "0.x"
- resolve "1.x"
- semver "^5.5"
- yargs-parser "10.x"
+ mkdirp "1.x"
+ semver "7.x"
+ yargs-parser "20.x"
ts-pnp@^1.1.2:
version "1.2.0"
@@ -17293,9 +17359,9 @@ typescript-tuple@^2.2.1:
typescript-compare "^0.0.2"
typescript@^3.5.2:
- version "3.9.5"
- resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.5.tgz#586f0dba300cde8be52dd1ac4f7e1009c1b13f36"
- integrity sha512-hSAifV3k+i6lEoCJ2k6R2Z/rp/H3+8sdmcn5NrS3/3kE7+RyZXm9aqvxWqjEXHAd8b0pShatpcdMTvEdvAJltQ==
+ version "3.9.9"
+ resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.9.tgz#e69905c54bc0681d0518bd4d587cc6f2d0b1a674"
+ integrity sha512-kdMjTiekY+z/ubJCATUPlRDl39vXYiMV9iyeMuEuXZh2we6zz80uovNN2WlAxmmdE/Z/YQe+EbOEXB5RHEED3w==
ua-parser-js@^0.7.18:
version "0.7.21"
@@ -18401,12 +18467,10 @@ yaml@^1.7.2:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.0.tgz#3b593add944876077d4d683fee01081bd9fff31e"
integrity sha512-yr2icI4glYaNG+KWONODapy2/jDdMSDnrONSjblABjD9B4Z5LgiircSt8m8sRZFNi08kG9Sm0uSHtEmP3zaEGg==
-yargs-parser@10.x:
- version "10.1.0"
- resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8"
- integrity sha512-VCIyR1wJoEBZUqk5PA+oOBF6ypbwh5aNB3I50guxAL/quggdfs4TtNHQrSazFA3fYZ+tEqfs0zIGlv0c/rgjbQ==
- dependencies:
- camelcase "^4.1.0"
+yargs-parser@20.x:
+ version "20.2.7"
+ resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.7.tgz#61df85c113edfb5a7a4e36eb8aa60ef423cbc90a"
+ integrity sha512-FiNkvbeHzB/syOjIUxFDCnhSfzAL8R5vs40MgLFBorXACCOAEaWu0gRZl14vG8MR9AOJIZbmkjhusqBYZ3HTHw==
yargs-parser@^13.1.1:
version "13.1.1"