From 2bf21aa28fcaadbdbb40e0ee65f12f2ab3b2c2fa Mon Sep 17 00:00:00 2001 From: Sander Vocke Date: Tue, 4 Aug 2020 19:56:28 +0200 Subject: [PATCH] Add ordering. --- client/src/App.tsx | 8 +- client/src/api.ts | 20 +++ client/src/components/BrowseWindow.tsx | 35 +----- .../src/components/DraggableItemListItem.tsx | 19 +++ client/src/components/ItemList.tsx | 4 +- client/src/components/QueryBrowseWindow.tsx | 13 +- client/src/types/DragTypes.tsx | 3 + package.json | 4 + server/endpoints/QueryEndpointHandler.ts | 23 ++++ yarn.lock | 116 ++++++++++++++++-- 10 files changed, 193 insertions(+), 52 deletions(-) create mode 100644 client/src/components/DraggableItemListItem.tsx create mode 100644 client/src/types/DragTypes.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index bc5068b..eb77a66 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -3,6 +3,8 @@ import React, { useState, useEffect } from 'react'; import AppBar, { ActiveTab as AppBarActiveTab } from './components/AppBar'; import { Query, isQuery, QueryKeys } from './types/Query'; import QueryBrowseWindow, { TypesIncluded } from './components/QueryBrowseWindow'; +import { DndProvider } from 'react-dnd'; +import { HTML5Backend } from 'react-dnd-html5-backend'; import { BrowserRouter as Router, @@ -30,7 +32,7 @@ function AppBody() { const location = useLocation(); const queryParams = new URLSearchParams(location.search); - const [ types, setTypes ] = useState({songs: true, artists: true, tags: true}); + const [types, setTypes] = useState({ songs: true, artists: true, tags: true }); // If we have an invalid query, change to the default one. const itemQuery: Query | undefined = JSURL.tryParse(queryParams.get('query'), undefined); @@ -88,7 +90,9 @@ function AppBody() { function App() { return ( - + + + ); } diff --git a/client/src/api.ts b/client/src/api.ts index f119434..d80b82b 100644 --- a/client/src/api.ts +++ b/client/src/api.ts @@ -19,12 +19,18 @@ export interface ArtistDetails { name: String, storeLinks?: String[], } +export function isArtistDetails(q: any): q is ArtistDetails { + return 'artistId' in q; +} export interface TagDetails { tagId: Number, name: String, parent?: TagDetails, storeLinks?: String[], } +export function isTagDetails(q: any): q is TagDetails { + return 'tagId' in q; +} export interface RankingDetails { rankingId: Number, type: ItemType, // The item type being ranked @@ -32,6 +38,9 @@ export interface RankingDetails { context: ArtistDetails | TagDetails, value: Number, // The ranking (higher = better) } +export function isRankingDetails(q: any): q is RankingDetails { + return 'rankingId' in q; +} export interface SongDetails { songId: Number, title: String, @@ -40,6 +49,9 @@ export interface SongDetails { storeLinks?: String[], rankings?: RankingDetails[], } +export function isSongDetails(q: any): q is SongDetails { + return 'songId' in q; +} // Query for items (POST). export const QueryEndpoint = '/query'; @@ -60,6 +72,9 @@ export enum QueryElemProperty { artistName = "artistName", albumName = "albumName", } +export enum OrderBy { + Name = 0 +} export interface QueryElem { prop?: QueryElemProperty, propOperand?: any, @@ -67,6 +82,10 @@ export interface QueryElem { children?: QueryElem[] childrenOperator?: QueryElemOp, } +export interface Ordering { + orderBy: OrderBy, + ascending: boolean, +} export interface Query extends QueryElem { } export interface QueryRequest { query: Query, @@ -76,6 +95,7 @@ export interface QueryRequest { artistLimit: number, tagOffset: number, tagLimit: number, + ordering: Ordering, } export interface QueryResponse { songs: SongDetails[], diff --git a/client/src/components/BrowseWindow.tsx b/client/src/components/BrowseWindow.tsx index 277e0ef..4c0cc1a 100644 --- a/client/src/components/BrowseWindow.tsx +++ b/client/src/components/BrowseWindow.tsx @@ -2,37 +2,14 @@ import React from 'react'; import { Paper } from '@material-ui/core'; import { DisplayItem } from '../types/DisplayItem'; -import ItemListItem from './ItemListItem'; +import DraggableItemListItem from './DraggableItemListItem'; import ItemList from './ItemList'; import * as serverApi from '../api'; import StoreIcon from '@material-ui/icons/Store'; import { ReactComponent as GooglePlayIcon } from '../assets/googleplaymusic_icon.svg'; - -export interface SongItem extends serverApi.SongDetails { - songSignature: any -} -export function isSongItem(q: any): q is SongItem { - return 'songSignature' in q; -} -export function toSongItem(i: serverApi.SongDetails) { - const r: any = i; - r['songSignature'] = true; - return r; -} - -export interface ArtistItem extends serverApi.ArtistDetails { - artistSignature: any -} -export function isArtistItem(q: any): q is ArtistItem { - return 'artistSignature' in q; -} -export function toArtistItem(i: serverApi.ArtistDetails) { - const r: any = i; - r['artistSignature'] = true; - return r; -} - +type SongItem = serverApi.SongDetails; +type ArtistItem = serverApi.ArtistDetails; export type Item = SongItem | ArtistItem; const getStoreIcon = (url: String) => { @@ -43,7 +20,7 @@ const getStoreIcon = (url: String) => { } function toDisplayItem(item: Item): DisplayItem | undefined { - if (isSongItem(item)) { + if (serverApi.isSongDetails(item)) { return { title: item.title, artistNames: item.artists && item.artists.map((artist: serverApi.ArtistDetails) => { @@ -59,7 +36,7 @@ function toDisplayItem(item: Item): DisplayItem | undefined { } }) || [], } - } else if (isArtistItem(item)) { + } else if (serverApi.isArtistDetails(item)) { return { name: item.name ? item.name : "Unknown", tagNames: [], // TODO @@ -84,7 +61,7 @@ export default function BrowseWindow(props: IProps) { {props.items.map((item: Item) => { const di = toDisplayItem(item); - return di && ; + return di && ; })} ; diff --git a/client/src/components/DraggableItemListItem.tsx b/client/src/components/DraggableItemListItem.tsx new file mode 100644 index 0000000..305f2aa --- /dev/null +++ b/client/src/components/DraggableItemListItem.tsx @@ -0,0 +1,19 @@ +import React from 'react'; +import ItemListItem from './ItemListItem'; +import { useDrag } from 'react-dnd'; +import { dragTypes } from '../types/DragTypes'; + +export default function DraggableItemListItem(props: any) { + const [{ isDragging: boolean }, drag] = useDrag({ + item: { type: dragTypes.ListItem }, + collect: (monitor: any) => ({ + isDragging: !!monitor.isDragging(), + }), + }); + + return
+ +
; +} \ No newline at end of file diff --git a/client/src/components/ItemList.tsx b/client/src/components/ItemList.tsx index 2a1ec18..422224d 100644 --- a/client/src/components/ItemList.tsx +++ b/client/src/components/ItemList.tsx @@ -17,9 +17,7 @@ export default function ItemList(props:any) { return (
- {props.children.map((child: any) => { - return child; - })} + {props.children}
); diff --git a/client/src/components/QueryBrowseWindow.tsx b/client/src/components/QueryBrowseWindow.tsx index e25b48a..6ebdcc2 100644 --- a/client/src/components/QueryBrowseWindow.tsx +++ b/client/src/components/QueryBrowseWindow.tsx @@ -3,7 +3,7 @@ import React, { useState, useEffect } from 'react'; import { Query, toApiQuery } from '../types/Query'; import FilterControl from './FilterControl'; import * as serverApi from '../api'; -import BrowseWindow, { toSongItem, toArtistItem, Item } from './BrowseWindow'; +import BrowseWindow, { Item } from './BrowseWindow'; import { FormControl, FormLabel, FormGroup, FormControlLabel, Checkbox } from '@material-ui/core'; const _ = require('lodash'); @@ -73,12 +73,9 @@ export default function QueryBrowseWindow(props: IProps) { const [artists, setArtists] = useState([]); //const [tags, setTags] = useState([]); - const songItems: Item[] = songs.map(toSongItem); - const artistItems: Item[] = artists.map(toArtistItem); - var items: Item[] = []; - props.typesIncluded.songs && items.push(...songItems); - props.typesIncluded.artists && items.push(...artistItems); + props.typesIncluded.songs && items.push(...songs); + props.typesIncluded.artists && items.push(...artists); useEffect(() => { if (!props.query) { return; } @@ -92,6 +89,10 @@ export default function QueryBrowseWindow(props: IProps) { artistLimit: 5, tagOffset: 0, tagLimit: 5, + ordering: { + orderBy: serverApi.OrderBy.Name, + ascending: true, + } } const requestOpts = { method: 'POST', diff --git a/client/src/types/DragTypes.tsx b/client/src/types/DragTypes.tsx new file mode 100644 index 0000000..e30b478 --- /dev/null +++ b/client/src/types/DragTypes.tsx @@ -0,0 +1,3 @@ +export const dragTypes = { + ListItem: 'list item' +} \ No newline at end of file diff --git a/package.json b/package.json index 93487a5..7537a84 100644 --- a/package.json +++ b/package.json @@ -8,5 +8,9 @@ }, "devDependencies": { "concurrently": "^4.0.1" + }, + "dependencies": { + "react-dnd": "^11.1.3", + "react-dnd-html5-backend": "^11.1.3" } } diff --git a/server/endpoints/QueryEndpointHandler.ts b/server/endpoints/QueryEndpointHandler.ts index b7a2f33..7edb833 100644 --- a/server/endpoints/QueryEndpointHandler.ts +++ b/server/endpoints/QueryEndpointHandler.ts @@ -40,6 +40,18 @@ const sequelizeProps: any = { } }; +const sequelizeOrderColumns: any = { + [QueryType.Song]: { + [api.OrderBy.Name]: 'title' + }, + [QueryType.Artist]: { + [api.OrderBy.Name]: 'name' + }, + [QueryType.Tag]: { + [api.OrderBy.Name]: 'name' + }, +} + // Returns the "where" clauses for Sequelize, per object type. const getSequelizeWhere = (queryElem: api.QueryElem, type: QueryType) => { var where: any = { @@ -66,6 +78,14 @@ const getSequelizeWhere = (queryElem: api.QueryElem, type: QueryType) => { return where; } +function getSequelizeOrder(order: api.Ordering, type: QueryType) { + const ascstring = order.ascending ? 'ASC' : 'DESC'; + + return [ + [ sequelizeOrderColumns[type][order.orderBy], ascstring ] + ]; +} + export const QueryEndpointHandler: EndpointHandler = async (req: any, res: any) => { if (!api.checkQueryRequest(req.body)) { const e: EndpointError = { @@ -81,6 +101,7 @@ export const QueryEndpointHandler: EndpointHandler = async (req: any, res: any) // NOTE: have to disable limit and offset because of bug: https://github.com/sequelize/sequelize/issues/11938. // Custom pagination is implemented before responding. where: getSequelizeWhere(reqObject.query, QueryType.Song), + order: getSequelizeOrder(reqObject.ordering, QueryType.Song), include: [models.Artist, models.Album, models.Tag, models.Ranking], //limit: reqObject.limit, //offset: reqObject.offset, @@ -89,6 +110,7 @@ export const QueryEndpointHandler: EndpointHandler = async (req: any, res: any) // NOTE: have to disable limit and offset because of bug: https://github.com/sequelize/sequelize/issues/11938. // Custom pagination is implemented before responding. where: getSequelizeWhere(reqObject.query, QueryType.Artist), + order: getSequelizeOrder(reqObject.ordering, QueryType.Artist), include: [models.Song, models.Album, models.Tag], //limit: reqObject.limit, //offset: reqObject.offset, @@ -97,6 +119,7 @@ export const QueryEndpointHandler: EndpointHandler = async (req: any, res: any) // NOTE: have to disable limit and offset because of bug: https://github.com/sequelize/sequelize/issues/11938. // Custom pagination is implemented before responding. where: getSequelizeWhere(reqObject.query, QueryType.Tag), + order: getSequelizeOrder(reqObject.ordering, QueryType.Tag), include: [models.Song, models.Album, models.Artist], //limit: reqObject.limit, //offset: reqObject.offset, diff --git a/yarn.lock b/yarn.lock index 3c13c2e..460647f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,42 @@ # yarn lockfile v1 +"@react-dnd/asap@^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/@react-dnd/asap/-/asap-4.0.0.tgz#b300eeed83e9801f51bd66b0337c9a6f04548651" + integrity sha512-0XhqJSc6pPoNnf8DhdsPHtUhRzZALVzYMTzRwV4VI6DJNJ/5xxfL9OQUwb8IH5/2x7lSf7nAZrnzUD+16VyOVQ== + +"@react-dnd/invariant@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@react-dnd/invariant/-/invariant-2.0.0.tgz#09d2e81cd39e0e767d7da62df9325860f24e517e" + integrity sha512-xL4RCQBCBDJ+GRwKTFhGUW8GXa4yoDfJrPbLblc3U09ciS+9ZJXJ3Qrcs/x2IODOdIE5kQxvMmE2UKyqUictUw== + +"@react-dnd/shallowequal@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@react-dnd/shallowequal/-/shallowequal-2.0.0.tgz#a3031eb54129f2c66b2753f8404266ec7bf67f0a" + integrity sha512-Pc/AFTdwZwEKJxFJvlxrSmGe/di+aAOBn60sremrpLo6VI/6cmiUYNNwlI5KNYttg7uypzA3ILPMPgxB2GYZEg== + +"@types/hoist-non-react-statics@^3.3.1": + version "3.3.1" + resolved "https://registry.yarnpkg.com/@types/hoist-non-react-statics/-/hoist-non-react-statics-3.3.1.tgz#1124aafe5118cb591977aeb1ceaaed1070eb039f" + integrity sha512-iMIqiko6ooLrTh1joXodJK5X9xeEALT1kM5G3ZLhD3hszxBdIEd5C75U834D9mLcINgD4OyZf5uQXjkuYydWvA== + dependencies: + "@types/react" "*" + hoist-non-react-statics "^3.3.0" + +"@types/prop-types@*": + version "15.7.3" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.3.tgz#2ab0d5da2e5815f94b0b9d4b95d1e5f243ab2ca7" + integrity sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw== + +"@types/react@*": + version "16.9.44" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.9.44.tgz#da84b179c031aef67dc92c33bd3401f1da2fa3bc" + integrity sha512-BtLoJrXdW8DVZauKP+bY4Kmiq7ubcJq+H/aCpRfvPF7RAT3RwR73Sg8szdc2YasbAlWBDrQ6Q+AFM0KwtQY+WQ== + dependencies: + "@types/prop-types" "*" + csstype "^3.0.2" + ansi-regex@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df" @@ -85,6 +121,11 @@ cross-spawn@^6.0.0: shebang-command "^1.2.0" which "^1.2.9" +csstype@^3.0.2: + version "3.0.2" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.2.tgz#ee5ff8f208c8cd613b389f7b222c9801ca62b3f7" + integrity sha512-ofovWglpqoqbfLNOTBNZLSbMuGrblAf1efvvArGKOZMBrIoJeu5UsAipQolkijtyQx5MtAzT/J9IHj/CEY1mJw== + date-fns@^1.30.1: version "1.30.1" resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c" @@ -95,6 +136,15 @@ decamelize@^1.2.0: resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= +dnd-core@^11.1.3: + version "11.1.3" + resolved "https://registry.yarnpkg.com/dnd-core/-/dnd-core-11.1.3.tgz#f92099ba7245e49729d2433157031a6267afcc98" + integrity sha512-QugF55dNW+h+vzxVJ/LSJeTeUw9MCJ2cllhmVThVPEtF16ooBkxj0WBE5RB+AceFxMFo1rO6bJKXtqKl+JNnyA== + dependencies: + "@react-dnd/asap" "^4.0.0" + "@react-dnd/invariant" "^2.0.0" + redux "^4.0.4" + end-of-stream@^1.1.0: version "1.4.4" resolved "https://registry.yarnpkg.com/end-of-stream/-/end-of-stream-1.4.4.tgz#5ae64a5f45057baf3626ec14da0ca5e4b2431eb0" @@ -156,6 +206,13 @@ has-flag@^3.0.0: resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd" integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0= +hoist-non-react-statics@^3.3.0: + version "3.3.2" + resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== + dependencies: + react-is "^16.7.0" + hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -193,6 +250,11 @@ isexe@^2.0.0: resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10" integrity sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9" @@ -218,6 +280,13 @@ lodash@^4.17.15: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== +loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + map-age-cleaner@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/map-age-cleaner/-/map-age-cleaner-0.1.3.tgz#7d583a7306434c055fe474b0f45078e6e1b4b92a" @@ -352,6 +421,28 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" +react-dnd-html5-backend@^11.1.3: + version "11.1.3" + resolved "https://registry.yarnpkg.com/react-dnd-html5-backend/-/react-dnd-html5-backend-11.1.3.tgz#2749f04f416ec230ea193f5c1fbea2de7dffb8f7" + integrity sha512-/1FjNlJbW/ivkUxlxQd7o3trA5DE33QiRZgxent3zKme8DwF4Nbw3OFVhTRFGaYhHFNL1rZt6Rdj1D78BjnNLw== + dependencies: + dnd-core "^11.1.3" + +react-dnd@^11.1.3: + version "11.1.3" + resolved "https://registry.yarnpkg.com/react-dnd/-/react-dnd-11.1.3.tgz#f9844f5699ccc55dfc81462c2c19f726e670c1af" + integrity sha512-8rtzzT8iwHgdSC89VktwhqdKKtfXaAyC4wiqp0SywpHG12TTLvfOoL6xNEIUWXwIEWu+CFfDn4GZJyynCEuHIQ== + dependencies: + "@react-dnd/shallowequal" "^2.0.0" + "@types/hoist-non-react-statics" "^3.3.1" + dnd-core "^11.1.3" + hoist-non-react-statics "^3.3.0" + +react-is@^16.7.0: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + read-pkg@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/read-pkg/-/read-pkg-4.0.1.tgz#963625378f3e1c4d48c85872b5a6ec7d5d093237" @@ -361,6 +452,14 @@ read-pkg@^4.0.1: parse-json "^4.0.0" pify "^3.0.0" +redux@^4.0.4: + version "4.0.5" + resolved "https://registry.yarnpkg.com/redux/-/redux-4.0.5.tgz#4db5de5816e17891de8a80c424232d06f051d93f" + integrity sha512-VSz1uMAH24DM6MF72vcojpYPtrTUu3ByVWfPL1nPfVRb5mZVTve5GnNCUV53QM/BZ66xfWrm0CTWoM+Xlz8V1w== + dependencies: + loose-envify "^1.4.0" + symbol-observable "^1.2.0" + require-directory@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/require-directory/-/require-directory-2.1.1.tgz#8c64ad5fd30dab1c976e2344ffe7f792a6a6df42" @@ -493,6 +592,11 @@ supports-color@^5.3.0: dependencies: has-flag "^3.0.0" +symbol-observable@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804" + integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ== + tree-kill@^1.2.1: version "1.2.2" resolved "https://registry.yarnpkg.com/tree-kill/-/tree-kill-1.2.2.tgz#4ca09a9092c88b73a7cdc5e8a01b507b0790a0cc" @@ -503,18 +607,6 @@ tslib@^1.9.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.13.0.tgz#c881e13cc7015894ed914862d276436fa9a47043" integrity sha512-i/6DQjL8Xf3be4K/E6Wgpekn5Qasl1usyw++dAA35Ue5orEn65VIxOA+YvNNl9HV3qv70T7CNwjODHZrLwvd1Q== -typescript-require@^0.2.10: - version "0.2.10" - resolved "https://registry.yarnpkg.com/typescript-require/-/typescript-require-0.2.10.tgz#8c8ee2aa75f3530b560b849c2927cd3697eba68e" - integrity sha1-jI7iqnXzUwtWC4ScKSfNNpfrpo4= - dependencies: - typescript "^1.5.3" - -typescript@^1.5.3: - version "1.8.10" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-1.8.10.tgz#b475d6e0dff0bf50f296e5ca6ef9fbb5c7320f1e" - integrity sha1-tHXW4N/wv1DyluXKbvn7tccyDx4= - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a"