The UUID library now uses overrides instead of the inherited Buffer methods for better deserialization. TODO: add conversions across layers from database to controller. There is initial effort in implementing Auth0.master
@@ -15,12 +15,13 @@ | |||||
- [ ] In the front-end, the client provides a ringtone ID to request a ringtone from the back-end. | - [ ] In the front-end, the client provides a ringtone ID to request a ringtone from the back-end. | ||||
- [ ] In the back-end, the server sends the ringtone data to the front-end. | - [ ] In the back-end, the server sends the ringtone data to the front-end. | ||||
- As a client, I want to browse ringtones. | - As a client, I want to browse ringtones. | ||||
- [ ] In the front-end, the client provides an optional skip and take arguments to request multiple ringtones from the | |||||
- [ ] In the front-end, the client provides optional skip and take arguments to request multiple ringtones from the | |||||
back-end. | back-end. | ||||
- [X] In the back-end, the server sends the ringtones to the front-end. | - [X] In the back-end, the server sends the ringtones to the front-end. | ||||
- As a client, I want to view the ringtones made by a composer. | - As a client, I want to view the ringtones made by a composer. | ||||
- [ ] In the front-end | |||||
- [ ] In the back-end | |||||
- [ ] In the front-end, the client provides a composer's user ID and optional skip and take arguments to request | |||||
multiple ringtones from the back-end. | |||||
- [X] In the back-end, the server sends the ringtones to the front-end. | |||||
## Search | ## Search | ||||
@@ -30,7 +31,7 @@ | |||||
front-end. | front-end. | ||||
- As a client, I want to search for composers. | - As a client, I want to search for composers. | ||||
- [ ] In the front-end, the client requests provides a search keyword to request for composers from the back-end. | - [ ] In the front-end, the client requests provides a search keyword to request for composers from the back-end. | ||||
- [ ] In the back-end, the server retrieves the composers whose name matches the search keyword provided by the | |||||
- [X] In the back-end, the server retrieves the composers whose name matches the search keyword provided by the | |||||
front-end. | front-end. | ||||
## Composer | ## Composer | ||||
@@ -0,0 +1,2 @@ | |||||
Name: Happy Birthday | |||||
Data: 8g4 8g4 4a4 4g4 4c5 2b4 8g4 8g4 4a4 4g4 4d5 2c5 8g4 8g4 4g5 4e5 8c5 8c5 4b4 4a4 8f5 8f5 4e5 4c5 4d5 2c5. |
@@ -1,5 +1,7 @@ | |||||
import {CSSProperties, FC, FormEventHandler, useEffect, useRef, useState} from 'react'; | |||||
import {FC, FormEventHandler, useEffect, useRef, useState} from 'react'; | |||||
import {models} from '@tonality/library-common'; | import {models} from '@tonality/library-common'; | ||||
import Uuid from '@tonality/library-uuid'; | |||||
import {ALLOWED_DURATIONS, toRawDuration} from '@tonality/library-song-utils'; | |||||
import { LeftSidebarWithMenu } from '@theoryofnekomata/viewfinder' | import { LeftSidebarWithMenu } from '@theoryofnekomata/viewfinder' | ||||
import styled from 'styled-components'; | import styled from 'styled-components'; | ||||
import MusicalKeyboard from '@theoryofnekomata/react-musical-keyboard'; | import MusicalKeyboard from '@theoryofnekomata/react-musical-keyboard'; | ||||
@@ -8,7 +10,6 @@ import TextArea from '../../../molecules/forms/TextArea'; | |||||
import ActionButton from '../../../molecules/forms/ActionButton'; | import ActionButton from '../../../molecules/forms/ActionButton'; | ||||
import ToggleButton from '../../../molecules/forms/ToggleButton'; | import ToggleButton from '../../../molecules/forms/ToggleButton'; | ||||
import NumericInput from '../../../molecules/forms/NumericInput'; | import NumericInput from '../../../molecules/forms/NumericInput'; | ||||
import {ALLOWED_DURATIONS, toRawDuration} from '@tonality/library-song-utils'; | |||||
import Timer from '../../../molecules/timer/Timer'; | import Timer from '../../../molecules/timer/Timer'; | ||||
const KeyboardContainer = styled(LeftSidebarWithMenu.ContentContainer)({ | const KeyboardContainer = styled(LeftSidebarWithMenu.ContentContainer)({ | ||||
@@ -150,20 +151,25 @@ const CreateRingtoneForm: FC<Props> = ({ | |||||
<FormContents> | <FormContents> | ||||
{ | { | ||||
typeof (defaultValues.id as unknown) === 'string' | |||||
defaultValues.id | |||||
&& ( | && ( | ||||
<input | <input | ||||
type="hidden" | type="hidden" | ||||
name="id" | name="id" | ||||
defaultValue={defaultValues.id} | |||||
defaultValue={Uuid.parse(defaultValues.id.toString()).toString()} | |||||
/> | |||||
) | |||||
} | |||||
{ | |||||
defaultValues.composerUserId | |||||
&& ( | |||||
<input | |||||
type="hidden" | |||||
name="composerId" | |||||
defaultValue={Uuid.parse(defaultValues.composerUserId.toString()).toString()} | |||||
/> | /> | ||||
) | ) | ||||
} | } | ||||
<input | |||||
type="hidden" | |||||
name="composerId" | |||||
defaultValue={defaultValues.composerId} | |||||
/> | |||||
<Primary> | <Primary> | ||||
<TextInput | <TextInput | ||||
label={labels['name'] || 'Name'} | label={labels['name'] || 'Name'} | ||||
@@ -218,7 +224,7 @@ const CreateRingtoneForm: FC<Props> = ({ | |||||
<ActionButton | <ActionButton | ||||
onClick={typeof togglePlayback === 'function' ? togglePlayback({ formRef, setPlayTimestamp, setPlaying, }) : undefined} | onClick={typeof togglePlayback === 'function' ? togglePlayback({ formRef, setPlayTimestamp, setPlaying, }) : undefined} | ||||
> | > | ||||
{playing ? '⏹' : '▶️'} | |||||
{playing ? '■' : '▶'} | |||||
</ActionButton> | </ActionButton> | ||||
</OtherTools> | </OtherTools> | ||||
</Toolbar> | </Toolbar> | ||||
@@ -19,10 +19,18 @@ const Padding = styled('div')({ | |||||
padding: '2rem 0', | padding: '2rem 0', | ||||
}) | }) | ||||
const Avatar = styled('img')({ | |||||
objectFit: 'cover', | |||||
objectPosition: 'center', | |||||
width: '3rem', | |||||
height: '3rem', | |||||
borderRadius: '50%', | |||||
}) | |||||
type Props = { | type Props = { | ||||
onSearch?: FormEventHandler, | onSearch?: FormEventHandler, | ||||
onSubmit?: FormEventHandler, | onSubmit?: FormEventHandler, | ||||
composer: models.Composer, | |||||
composer: any, | |||||
currentRingtone?: models.Ringtone, | currentRingtone?: models.Ringtone, | ||||
composerRingtones: models.Ringtone[], | composerRingtones: models.Ringtone[], | ||||
@@ -55,9 +63,17 @@ const CreateRingtoneTemplate: FC<Props> = ({ | |||||
<Brand /> | <Brand /> | ||||
} | } | ||||
userLink={ | userLink={ | ||||
<div> | |||||
User | |||||
</div> | |||||
<Link | |||||
href={{ | |||||
query: { | |||||
popup: 'user', | |||||
}, | |||||
}} | |||||
> | |||||
<Avatar | |||||
src={composer.picture} | |||||
/> | |||||
</Link> | |||||
} | } | ||||
topBarComponent={TopBarComponent} | topBarComponent={TopBarComponent} | ||||
sidebarMenuComponent={SidebarMenuComponent} | sidebarMenuComponent={SidebarMenuComponent} | ||||
@@ -125,7 +141,7 @@ const CreateRingtoneTemplate: FC<Props> = ({ | |||||
onSubmit={onSubmit} | onSubmit={onSubmit} | ||||
defaultValues={{ | defaultValues={{ | ||||
...currentRingtone, | ...currentRingtone, | ||||
composerId: composer.id, | |||||
composerUserId: composer.id, | |||||
}} | }} | ||||
action="/api/a/create/ringtone" | action="/api/a/create/ringtone" | ||||
labels={{ | labels={{ | ||||
@@ -14,7 +14,12 @@ export default class RingtoneClient { | |||||
save = async (e: FormEvent & { submitter: HTMLInputElement | HTMLButtonElement }) => { | save = async (e: FormEvent & { submitter: HTMLInputElement | HTMLButtonElement }) => { | ||||
e.preventDefault() | e.preventDefault() | ||||
const values = getFormValues(e.target as HTMLFormElement, { submitter: e.submitter }) | |||||
const {tempo, name, data} = getFormValues(e.target as HTMLFormElement, { submitter: e.submitter }) | |||||
const values = { | |||||
name, | |||||
data, | |||||
tempo: Number(tempo), | |||||
} | |||||
const response = await this.fetchClient(endpoints.create(values)) | const response = await this.fetchClient(endpoints.create(values)) | ||||
alert(response.statusText) | alert(response.statusText) | ||||
} | } | ||||
@@ -1,12 +1,15 @@ | |||||
import {models} from '@tonality/library-common'; | import {models} from '@tonality/library-common'; | ||||
import {FetchClientParams, Method} from '../../utils/api/fetch'; | import {FetchClientParams, Method} from '../../utils/api/fetch'; | ||||
const token = 'eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCIsImtpZCI6Ijc3bnFZZjV1b0lRQ25xbld0UndaMyJ9.eyJpc3MiOiJodHRwczovL21vZGFsLmpwLmF1dGgwLmNvbS8iLCJzdWIiOiJYOE1PT2JKdk5QdG5lRDVMbzVJYjY0a1c1dVRWR2hVdUBjbGllbnRzIiwiYXVkIjoiaHR0cHM6Ly9tb2RhbC5qcC5hdXRoMC5jb20vYXBpL3YyLyIsImlhdCI6MTYyMzA3Mzg5NywiZXhwIjoxNjIzMTYwMjk3LCJhenAiOiJYOE1PT2JKdk5QdG5lRDVMbzVJYjY0a1c1dVRWR2hVdSIsImd0eSI6ImNsaWVudC1jcmVkZW50aWFscyJ9.WiRaFG375CbyHN3XIhf6joY3CYh89h7Xwt6vReWDOS7wKEMVRtXvd-VEBwxf_ibfIAdrB5qibjF5JwtCl18Kd8m2fEDVwj8z8qUKyChPipNezCGfbfqz6Kv_ykf06KEBwypsVwdk2YhAcxdspWuilMUPizAPkno8GXbjFYOpeZobwA4Y50zeKDWRP6SPCM94dlN7zf3myu98wBqOk4KiXH-cyO_dIVF42KTnnZlVfkEmJoLJUmSUUbRNPwrx6k-eQ2uP0whvwfhZRwo5u0uVxnnQBcEK0fTQ9CDJPKxbwULFkjfN0nLfxOLcRMdPFMNtcWEFcFDr6LHGnqDCEG4lEw' | |||||
export const create = (body: Partial<models.Ringtone>): FetchClientParams => ({ | export const create = (body: Partial<models.Ringtone>): FetchClientParams => ({ | ||||
method: Method.POST, | method: Method.POST, | ||||
url: ['', 'api', 'ringtones'].join('/'), | url: ['', 'api', 'ringtones'].join('/'), | ||||
body: JSON.stringify(body), | body: JSON.stringify(body), | ||||
headers: { | headers: { | ||||
'Content-Type': 'application/json', | 'Content-Type': 'application/json', | ||||
'Authorization': `Bearer ${token}` | |||||
}, | }, | ||||
}) | }) | ||||
@@ -1,6 +1,7 @@ | |||||
import {GetServerSideProps, NextPage} from 'next' | import {GetServerSideProps, NextPage} from 'next' | ||||
import {useEffect, useState} from 'react'; | import {useEffect, useState} from 'react'; | ||||
import {models} from '@tonality/library-common' | import {models} from '@tonality/library-common' | ||||
import {useUser} from '@auth0/nextjs-auth0' | |||||
import CreateRingtoneTemplate from '../../../../components/templates/CreateRingtone' | import CreateRingtoneTemplate from '../../../../components/templates/CreateRingtone' | ||||
import RingtoneClient from '../../../../modules/ringtone/client' | import RingtoneClient from '../../../../modules/ringtone/client' | ||||
import {getSession, withPageAuthRequired} from '@auth0/nextjs-auth0'; | import {getSession, withPageAuthRequired} from '@auth0/nextjs-auth0'; | ||||
@@ -9,7 +10,7 @@ import SoundManager from '../../../../utils/sound/SoundManager'; | |||||
import ComposerClient from '../../../../modules/composer/client'; | import ComposerClient from '../../../../modules/composer/client'; | ||||
type Props = { | type Props = { | ||||
user: models.Composer, | |||||
user: models.User, | |||||
composerRingtones: models.Ringtone[], | composerRingtones: models.Ringtone[], | ||||
} | } | ||||
@@ -20,6 +21,7 @@ const MyCreateRingtonePage: NextPage<Props> = ({ | |||||
const [hydrated, setHydrated] = useState(false) | const [hydrated, setHydrated] = useState(false) | ||||
const [ringtoneClient, setRingtoneClient] = useState<RingtoneClient>(null) | const [ringtoneClient, setRingtoneClient] = useState<RingtoneClient>(null) | ||||
const [composerClient, setComposerClient] = useState<ComposerClient>(null) | const [composerClient, setComposerClient] = useState<ComposerClient>(null) | ||||
const theUser = useUser() | |||||
useEffect(() => { | useEffect(() => { | ||||
setHydrated(true) | setHydrated(true) | ||||
@@ -4,7 +4,7 @@ import {models} from '@tonality/library-common' | |||||
import RingtoneClient from '../../modules/ringtone/client' | import RingtoneClient from '../../modules/ringtone/client' | ||||
type Props = { | type Props = { | ||||
user: models.Composer, | |||||
user: models.User, | |||||
composerRingtones: models.Ringtone[], | composerRingtones: models.Ringtone[], | ||||
} | } | ||||
@@ -32,7 +32,7 @@ const MyCreateRingtonePage: NextPage<Props> = ({ | |||||
export const getServerSideProps: GetServerSideProps = async () => { | export const getServerSideProps: GetServerSideProps = async () => { | ||||
const user = { | const user = { | ||||
id: '00000000-0000-0000-000000000000', | |||||
id: '00000000-0000-0000-0000-000000000000', | |||||
name: 'TheoryOfNekomata', | name: 'TheoryOfNekomata', | ||||
bio: '', | bio: '', | ||||
} | } | ||||
@@ -1,4 +1,4 @@ | |||||
import {Note, parseNote, PITCH_CLASSES} from "./note"; | |||||
import {Note, parseNote, PITCH_CLASSES} from './note'; | |||||
import {toRawDuration} from './duration'; | import {toRawDuration} from './duration'; | ||||
export interface Playable { | export interface Playable { | ||||
@@ -1,18 +1,42 @@ | |||||
import {parse, stringify, v4} from 'uuid'; | import {parse, stringify, v4} from 'uuid'; | ||||
function toString(this: Uuid) { | |||||
return stringify(this) | |||||
} | |||||
function toBuffer(this: Uuid) { | |||||
return Buffer.from(this) | |||||
} | |||||
const constructUuidBase = (bytes: any) => { | |||||
const uuidBase = Buffer.from(bytes) as Uuid | |||||
uuidBase.toString = toString.bind(uuidBase); | |||||
const uuidBaseExtend = uuidBase as unknown as Record<string, unknown> | |||||
uuidBaseExtend['toBuffer'] = toBuffer.bind(uuidBase) | |||||
return uuidBase | |||||
} | |||||
export interface UuidBuffer extends Buffer { | |||||
toBuffer?(): Buffer, | |||||
} | |||||
export default class Uuid extends Buffer { | export default class Uuid extends Buffer { | ||||
static new() { | |||||
static new(): UuidBuffer { | |||||
const raw = v4() | const raw = v4() | ||||
const bytes = parse(raw) as Uint8Array | const bytes = parse(raw) as Uint8Array | ||||
return Uuid.from(bytes) as Uuid | |||||
return constructUuidBase(bytes) | |||||
} | |||||
static from(bytes: any): UuidBuffer { | |||||
return constructUuidBase(bytes) | |||||
} | } | ||||
toString() { | |||||
toString(): string { | |||||
return stringify(this) | return stringify(this) | ||||
} | } | ||||
static parse(s: string) { | |||||
static parse(s: string): UuidBuffer { | |||||
const bytes = parse(s) as Uint8Array | const bytes = parse(s) as Uint8Array | ||||
return Uuid.from(bytes) as Uuid | |||||
return constructUuidBase(bytes) | |||||
} | } | ||||
} | } |
@@ -1,7 +1,5 @@ | |||||
PORT= | PORT= | ||||
DATABASE_URL= | |||||
DATABASE_USERNAME= | DATABASE_USERNAME= | ||||
DATABASE_PASSWORD= | DATABASE_PASSWORD= | ||||
@@ -17,10 +17,11 @@ | |||||
"license": "MIT", | "license": "MIT", | ||||
"dependencies": { | "dependencies": { | ||||
"@abraham/reflection": "^0.8.0", | "@abraham/reflection": "^0.8.0", | ||||
"@prisma/client": "^2.24.1", | |||||
"@prisma/client": "2.21.2", | |||||
"bcrypt": "^5.0.1", | "bcrypt": "^5.0.1", | ||||
"dotenv": "^9.0.2", | "dotenv": "^9.0.2", | ||||
"fastify": "^3.0.0", | "fastify": "^3.0.0", | ||||
"fastify-auth0-verify": "^0.5.2", | |||||
"fastify-autoload": "^3.3.1", | "fastify-autoload": "^3.3.1", | ||||
"fastify-cli": "^2.11.0", | "fastify-cli": "^2.11.0", | ||||
"fastify-cors": "^6.0.1", | "fastify-cors": "^6.0.1", | ||||
@@ -28,6 +29,7 @@ | |||||
"fastify-sensible": "^3.1.0", | "fastify-sensible": "^3.1.0", | ||||
"knex": "^0.95.5", | "knex": "^0.95.5", | ||||
"tsyringe": "^4.5.0", | "tsyringe": "^4.5.0", | ||||
"unfetch": "^4.2.0", | |||||
"uuid": "^8.3.2" | "uuid": "^8.3.2" | ||||
}, | }, | ||||
"devDependencies": { | "devDependencies": { | ||||
@@ -39,7 +41,7 @@ | |||||
"cross-env": "^7.0.3", | "cross-env": "^7.0.3", | ||||
"fastify-tsconfig": "^1.0.1", | "fastify-tsconfig": "^1.0.1", | ||||
"jest": "^26.6.3", | "jest": "^26.6.3", | ||||
"prisma": "^2.24.1", | |||||
"prisma": "2.21.2", | |||||
"ts-jest": "^26.5.6", | "ts-jest": "^26.5.6", | ||||
"typescript": "^4.1.3" | "typescript": "^4.1.3" | ||||
}, | }, | ||||
@@ -16,7 +16,7 @@ model Ringtone { | |||||
createdAt DateTime @default(now()) @map("created_at") | createdAt DateTime @default(now()) @map("created_at") | ||||
updatedAt DateTime @default(now()) @map("updated_at") | updatedAt DateTime @default(now()) @map("updated_at") | ||||
deletedAt DateTime? @map("deleted_at") | deletedAt DateTime? @map("deleted_at") | ||||
user User @relation(fields: [composerUserId], references: [id]) | |||||
composer User @relation(fields: [composerUserId], references: [id]) | |||||
@@map("ringtone") | @@map("ringtone") | ||||
} | } | ||||
@@ -16,6 +16,8 @@ const app: FastifyPluginAsync<AppOptions> = async ( | |||||
// Place here your custom code! | // Place here your custom code! | ||||
const modules = await Promise.all([ | const modules = await Promise.all([ | ||||
import('./global'), | import('./global'), | ||||
import('./modules/credentials'), | |||||
import('./modules/auth'), | |||||
import('./modules/password'), | import('./modules/password'), | ||||
import('./modules/user'), | import('./modules/user'), | ||||
import('./modules/ringtone'), | import('./modules/ringtone'), | ||||
@@ -39,8 +41,6 @@ const app: FastifyPluginAsync<AppOptions> = async ( | |||||
dir: join(__dirname, 'routes'), | dir: join(__dirname, 'routes'), | ||||
options: opts, | options: opts, | ||||
}); | }); | ||||
}; | }; | ||||
export default app; | export default app; | ||||
export {app}; |
@@ -0,0 +1,30 @@ | |||||
import {inject, singleton} from 'tsyringe'; | |||||
import AuthService from './service'; | |||||
import {Controller, ControllerResponse} from '../../utils/helpers'; | |||||
import Credentials from '../credentials/type'; | |||||
type AuthController = Controller<{ | |||||
logIn: ControllerResponse<Credentials>, | |||||
}> | |||||
export default AuthController | |||||
@singleton() | |||||
export class AuthControllerImpl implements AuthController { | |||||
constructor( | |||||
@inject('AuthService') | |||||
private readonly authService: AuthService | |||||
) {} | |||||
logIn = async (request: any, reply: any) => { | |||||
try { | |||||
const data = await this.authService.logIn(request.body['username'], request.body['password']) | |||||
return { | |||||
data | |||||
} | |||||
} catch (err) { | |||||
reply.raw.statusMessage = 'Invalid Credentials' | |||||
reply.unauthorized() | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,8 @@ | |||||
import {DependencyContainer} from 'tsyringe'; | |||||
import {AuthServiceImpl} from './service'; | |||||
import {AuthControllerImpl} from './controller'; | |||||
export default (container: DependencyContainer) => { | |||||
container.register('AuthController', { useClass: AuthControllerImpl }) | |||||
container.register('AuthService', { useClass: AuthServiceImpl }) | |||||
} |
@@ -0,0 +1,37 @@ | |||||
import {inject, singleton} from 'tsyringe'; | |||||
import UserService from '../user/service'; | |||||
import PasswordService from '../password/service'; | |||||
import CredentialsService from '../credentials/service'; | |||||
import Credentials from '../credentials/type'; | |||||
export default interface AuthService { | |||||
logIn(username: string, password: string): Promise<Credentials> | |||||
} | |||||
@singleton() | |||||
export class AuthServiceImpl implements AuthService { | |||||
constructor( | |||||
@inject('UserService') | |||||
private readonly userService: UserService, | |||||
@inject('PasswordService') | |||||
private readonly passwordService: PasswordService, | |||||
@inject('CredentialsService') | |||||
private readonly credentialsService: CredentialsService, | |||||
) {} | |||||
async logIn(username: string, password: string): Promise<Credentials> { | |||||
const user = await this.userService.getByUsername(username) | |||||
if (!user) { | |||||
throw new Error('Invalid credentials.') | |||||
} | |||||
const valid = this.passwordService.compare(password, user.password) | |||||
if (!valid) { | |||||
throw new Error('Invalid credentials.') | |||||
} | |||||
const credentials = await this.credentialsService.request() | |||||
credentials.profile = await this.userService.getProfileByUsername(username) | |||||
return credentials | |||||
} | |||||
} |
@@ -0,0 +1,6 @@ | |||||
import {DependencyContainer} from 'tsyringe'; | |||||
import {CredentialsServiceImpl} from './service'; | |||||
export default (container: DependencyContainer) => { | |||||
container.register('CredentialsService', { useClass: CredentialsServiceImpl }) | |||||
} |
@@ -0,0 +1,34 @@ | |||||
import {URL} from 'url'; | |||||
import unfetch from 'unfetch'; | |||||
import Credentials from './type'; | |||||
import {singleton} from 'tsyringe'; | |||||
export default interface CredentialsService { | |||||
request(): Promise<Credentials> | |||||
} | |||||
@singleton() | |||||
export class CredentialsServiceImpl { | |||||
async request(): Promise<Credentials> { | |||||
const tokenUrl = new URL('/oauth/token', process.env.AUTH0_DOMAIN) | |||||
const audienceUrl = new URL('/api/v2', process.env.AUTH0_DOMAIN) | |||||
const response = await unfetch(tokenUrl.toString(), { | |||||
method: 'POST', | |||||
body: JSON.stringify({ | |||||
client_id: process.env.AUTH0_CLIENT_ID, | |||||
client_secret: process.env.AUTH0_SECRET, | |||||
audience: audienceUrl.toString(), | |||||
grant_type: 'client_credentials', | |||||
}) | |||||
}) | |||||
if (!response.ok) { | |||||
throw new Error('Unable to request credentials.') | |||||
} | |||||
const { access_token: accessToken, expires_in: expiresIn, token_type: tokenType } = await response.json() | |||||
return { | |||||
accessToken, | |||||
expiresIn, | |||||
tokenType, | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,6 @@ | |||||
export default interface Credentials { | |||||
[k: string]: string | number | unknown, | |||||
accessToken: string, | |||||
expiresIn: number, | |||||
tokenType: string, | |||||
} |
@@ -18,6 +18,12 @@ type RingtoneController = Controller<{ | |||||
export default RingtoneController | export default RingtoneController | ||||
const serializeRingtone = d => ({ | |||||
...d, | |||||
composerUserId: d.composerUserId.toString(), | |||||
id: d.id.toString(), | |||||
}) | |||||
@singleton() | @singleton() | ||||
export class RingtoneControllerImpl implements RingtoneController { | export class RingtoneControllerImpl implements RingtoneController { | ||||
constructor( | constructor( | ||||
@@ -28,7 +34,8 @@ export class RingtoneControllerImpl implements RingtoneController { | |||||
get = async (request: any, reply: any) => { | get = async (request: any, reply: any) => { | ||||
try { | try { | ||||
const id = Uuid.parse(request.params['id']) | const id = Uuid.parse(request.params['id']) | ||||
const data = await this.ringtoneService.get(id) | |||||
const rawData = await this.ringtoneService.get(id) | |||||
const data = serializeRingtone(rawData) | |||||
if (typeof (data.deletedAt as Date) !== 'undefined') { | if (typeof (data.deletedAt as Date) !== 'undefined') { | ||||
reply.raw.statusMessage = 'Ringtone Deleted Previously' | reply.raw.statusMessage = 'Ringtone Deleted Previously' | ||||
reply.gone() | reply.gone() | ||||
@@ -58,10 +65,12 @@ export class RingtoneControllerImpl implements RingtoneController { | |||||
const skip = !isNaN(skipNumber) ? skipNumber : 0 | const skip = !isNaN(skipNumber) ? skipNumber : 0 | ||||
const take = !isNaN(takeNumber) ? takeNumber : 10 | const take = !isNaN(takeNumber) ? takeNumber : 10 | ||||
const rawData = await this.ringtoneService.browse(skip, take) | |||||
const data = rawData.map(serializeRingtone) | |||||
reply.raw.statusMessage = 'Multiple Ringtones Retrieved' | reply.raw.statusMessage = 'Multiple Ringtones Retrieved' | ||||
return { | return { | ||||
data: await this.ringtoneService.browse(skip, take), | |||||
data, | |||||
skip, | skip, | ||||
take, | take, | ||||
} | } | ||||
@@ -74,9 +83,12 @@ export class RingtoneControllerImpl implements RingtoneController { | |||||
search = async (request: any, reply: any) => { | search = async (request: any, reply: any) => { | ||||
try { | try { | ||||
const { 'q': query } = request.query | const { 'q': query } = request.query | ||||
const rawData = await this.ringtoneService.search(query) | |||||
const data = rawData.map(serializeRingtone) | |||||
reply.raw.statusMessage = 'Search Results Retrieved' | reply.raw.statusMessage = 'Search Results Retrieved' | ||||
return { | return { | ||||
data: await this.ringtoneService.search(query), | |||||
data, | |||||
} | } | ||||
} catch (err) { | } catch (err) { | ||||
reply.raw.statusMessage = 'Search Error' | reply.raw.statusMessage = 'Search Error' | ||||
@@ -85,12 +97,15 @@ export class RingtoneControllerImpl implements RingtoneController { | |||||
} | } | ||||
create = async (request: any, reply: any) => { | create = async (request: any, reply: any) => { | ||||
// TODO parse and validate body | |||||
try { | try { | ||||
const { composerUserId, ...etcBody } = request.body | |||||
const data = await this.ringtoneService.create({ | |||||
...etcBody, | |||||
composerUserId: Uuid.parse(composerUserId), | |||||
const rawData = await this.ringtoneService.create({ | |||||
...request.body, | |||||
// TODO map auth credentials to user | |||||
composerUserId: Uuid.parse(request.body.composerUserId), | |||||
}) | }) | ||||
const data = serializeRingtone(rawData) | |||||
reply.raw.statusMessage = 'Ringtone Created' | reply.raw.statusMessage = 'Ringtone Created' | ||||
reply.raw.statusCode = 201 | reply.raw.statusCode = 201 | ||||
return { | return { | ||||
@@ -106,10 +121,11 @@ export class RingtoneControllerImpl implements RingtoneController { | |||||
try { | try { | ||||
// TODO validate data | // TODO validate data | ||||
const id = Uuid.parse(request.params['id']) | const id = Uuid.parse(request.params['id']) | ||||
const data = await this.ringtoneService.update({ | |||||
const rawData = await this.ringtoneService.update({ | |||||
...request.body, | ...request.body, | ||||
id, | id, | ||||
}) | }) | ||||
const data = serializeRingtone(rawData) | |||||
if (data.deletedAt) { | if (data.deletedAt) { | ||||
reply.raw.statusMessage = 'Ringtone Deleted Previously' | reply.raw.statusMessage = 'Ringtone Deleted Previously' | ||||
reply.gone() | reply.gone() | ||||
@@ -133,7 +149,8 @@ export class RingtoneControllerImpl implements RingtoneController { | |||||
try { | try { | ||||
// TODO validate data | // TODO validate data | ||||
const id = Uuid.parse(request.params['id']) | const id = Uuid.parse(request.params['id']) | ||||
const data = await this.ringtoneService.softDelete(id) | |||||
const rawData = await this.ringtoneService.softDelete(id) | |||||
const data = serializeRingtone(rawData) | |||||
if (!data) { | if (!data) { | ||||
reply.raw.statusMessage = 'Ringtone Not Found' | reply.raw.statusMessage = 'Ringtone Not Found' | ||||
reply.notFound() | reply.notFound() | ||||
@@ -153,7 +170,8 @@ export class RingtoneControllerImpl implements RingtoneController { | |||||
try { | try { | ||||
// TODO validate data | // TODO validate data | ||||
const id = Uuid.parse(request.params['id']) | const id = Uuid.parse(request.params['id']) | ||||
const data = await this.ringtoneService.undoDelete(id) | |||||
const rawData = await this.ringtoneService.undoDelete(id) | |||||
const data = serializeRingtone(rawData) | |||||
if (!data) { | if (!data) { | ||||
reply.raw.statusMessage = 'Ringtone Not Found' | reply.raw.statusMessage = 'Ringtone Not Found' | ||||
reply.notFound() | reply.notFound() | ||||
@@ -1,12 +1,13 @@ | |||||
import {inject, singleton} from 'tsyringe' | import {inject, singleton} from 'tsyringe' | ||||
import {models} from '@tonality/library-common' | import {models} from '@tonality/library-common' | ||||
import Uuid from '@tonality/library-uuid' | |||||
import Uuid, {UuidBuffer} from '@tonality/library-uuid'; | |||||
import {PrismaClient} from '@prisma/client' | import {PrismaClient} from '@prisma/client' | ||||
export default interface RingtoneService { | export default interface RingtoneService { | ||||
get(id: Uuid): Promise<models.Ringtone> | get(id: Uuid): Promise<models.Ringtone> | ||||
browseByComposer(composerUserId: Uuid, skip?: number, take?: number): Promise<models.Ringtone[]> | |||||
browse(skip?: number, take?: number): Promise<models.Ringtone[]> | browse(skip?: number, take?: number): Promise<models.Ringtone[]> | ||||
search(q?: string): Promise<models.Ringtone> | |||||
search(q?: string): Promise<models.Ringtone[]> | |||||
create(data: Partial<models.Ringtone>): Promise<models.Ringtone> | create(data: Partial<models.Ringtone>): Promise<models.Ringtone> | ||||
update(data: Partial<models.Ringtone>): Promise<models.Ringtone> | update(data: Partial<models.Ringtone>): Promise<models.Ringtone> | ||||
softDelete(id: Uuid): Promise<models.Ringtone> | softDelete(id: Uuid): Promise<models.Ringtone> | ||||
@@ -14,6 +15,15 @@ export default interface RingtoneService { | |||||
hardDelete(id: Uuid): Promise<void> | hardDelete(id: Uuid): Promise<void> | ||||
} | } | ||||
const serializeRingtone = d => { | |||||
return ({ | |||||
...d, | |||||
composerUserId: Uuid.from(d.composerUserId).toString(), | |||||
id: Uuid.from(d.id).toString(), | |||||
}) | |||||
} | |||||
@singleton() | @singleton() | ||||
export class RingtoneServiceImpl { | export class RingtoneServiceImpl { | ||||
constructor( | constructor( | ||||
@@ -21,6 +31,22 @@ export class RingtoneServiceImpl { | |||||
private readonly prismaClient: PrismaClient, | private readonly prismaClient: PrismaClient, | ||||
) {} | ) {} | ||||
async browseByComposer(composerUserId: Uuid, skip: number = 0, take: number = 10): Promise<models.Ringtone[]> { | |||||
const rawData = await this.prismaClient.ringtone.findMany({ | |||||
where: { | |||||
composer: { | |||||
id: { | |||||
equals: composerUserId, | |||||
} | |||||
} | |||||
}, | |||||
skip, | |||||
take, | |||||
}) | |||||
return rawData.map(serializeRingtone) | |||||
} | |||||
async get(id: Uuid): Promise<models.Ringtone> { | async get(id: Uuid): Promise<models.Ringtone> { | ||||
const ringtone = await this.prismaClient.ringtone.findUnique({ | const ringtone = await this.prismaClient.ringtone.findUnique({ | ||||
where: { | where: { | ||||
@@ -32,14 +58,16 @@ export class RingtoneServiceImpl { | |||||
throw new Error('Ringtone not found!') | throw new Error('Ringtone not found!') | ||||
} | } | ||||
return ringtone | |||||
return serializeRingtone(ringtone) | |||||
} | } | ||||
async browse(skip: number = 0, take: number = 10): Promise<models.Ringtone[]> { | async browse(skip: number = 0, take: number = 10): Promise<models.Ringtone[]> { | ||||
return this.prismaClient.ringtone.findMany({ | |||||
const rawData = await this.prismaClient.ringtone.findMany({ | |||||
skip, | skip, | ||||
take, | take, | ||||
}) | }) | ||||
return rawData.map(serializeRingtone) | |||||
} | } | ||||
async search(q: string = ''): Promise<models.Ringtone[]> { | async search(q: string = ''): Promise<models.Ringtone[]> { | ||||
@@ -54,31 +82,33 @@ export class RingtoneServiceImpl { | |||||
async create(data: Partial<models.Ringtone>): Promise<models.Ringtone> { | async create(data: Partial<models.Ringtone>): Promise<models.Ringtone> { | ||||
const { createdAt, updatedAt, deletedAt, composerUserId, name, ...safeData } = data | const { createdAt, updatedAt, deletedAt, composerUserId, name, ...safeData } = data | ||||
return this.prismaClient.ringtone.create({ | |||||
const rawData = await this.prismaClient.ringtone.create({ | |||||
data: { | data: { | ||||
...safeData, | ...safeData, | ||||
id: Uuid.new(), | |||||
composerUserId: composerUserId as Uuid, | |||||
id: Uuid.new().toBuffer(), | |||||
composerUserId: (composerUserId as UuidBuffer).toBuffer(), | |||||
name: name as string, | name: name as string, | ||||
}, | }, | ||||
}) | }) | ||||
return serializeRingtone(rawData) | |||||
} | } | ||||
async update(data: Partial<models.Ringtone>): Promise<models.Ringtone> { | async update(data: Partial<models.Ringtone>): Promise<models.Ringtone> { | ||||
const { createdAt, updatedAt, deletedAt, ...safeData } = data | const { createdAt, updatedAt, deletedAt, ...safeData } = data | ||||
return this.prismaClient.ringtone.update({ | |||||
const rawData = await this.prismaClient.ringtone.update({ | |||||
where: { | where: { | ||||
id: data.id as Uuid, | |||||
id: (data.id as UuidBuffer).toBuffer(), | |||||
}, | }, | ||||
data: { | data: { | ||||
...safeData, | ...safeData, | ||||
updatedAt: new Date(), | updatedAt: new Date(), | ||||
}, | }, | ||||
}) | }) | ||||
return serializeRingtone(rawData) | |||||
} | } | ||||
async softDelete(id: Uuid): Promise<models.Ringtone> { | async softDelete(id: Uuid): Promise<models.Ringtone> { | ||||
return this.prismaClient.ringtone.update({ | |||||
const rawData = await this.prismaClient.ringtone.update({ | |||||
where: { | where: { | ||||
id, | id, | ||||
}, | }, | ||||
@@ -86,10 +116,11 @@ export class RingtoneServiceImpl { | |||||
deletedAt: new Date(), | deletedAt: new Date(), | ||||
}, | }, | ||||
}) | }) | ||||
return serializeRingtone(rawData) | |||||
} | } | ||||
async undoDelete(id: Uuid): Promise<models.Ringtone> { | async undoDelete(id: Uuid): Promise<models.Ringtone> { | ||||
return this.prismaClient.ringtone.update({ | |||||
const rawData = this.prismaClient.ringtone.update({ | |||||
where: { | where: { | ||||
id, | id, | ||||
}, | }, | ||||
@@ -97,6 +128,7 @@ export class RingtoneServiceImpl { | |||||
deletedAt: null, | deletedAt: null, | ||||
}, | }, | ||||
}) | }) | ||||
return serializeRingtone(rawData) | |||||
} | } | ||||
async hardDelete(id: Uuid): Promise<void> { | async hardDelete(id: Uuid): Promise<void> { | ||||
@@ -6,7 +6,8 @@ import PasswordService from '../password/service' | |||||
export default interface UserService { | export default interface UserService { | ||||
get(id: Uuid): Promise<models.UserProfile> | get(id: Uuid): Promise<models.UserProfile> | ||||
getByUsername(username: string): Promise<models.UserProfile> | |||||
getByUsername(username: string): Promise<models.User> | |||||
getProfileByUsername(username: string): Promise<models.UserProfile> | |||||
browse(skip?: number, take?: number): Promise<models.UserProfile[]> | browse(skip?: number, take?: number): Promise<models.UserProfile[]> | ||||
search(q?: string): Promise<models.UserProfile[]> | search(q?: string): Promise<models.UserProfile[]> | ||||
create(profile: Partial<models.UserProfile>, username: string, newPassword: string, confirmNewPassword: string): Promise<models.UserProfile> | create(profile: Partial<models.UserProfile>, username: string, newPassword: string, confirmNewPassword: string): Promise<models.UserProfile> | ||||
@@ -38,7 +39,21 @@ export class UserServiceImpl implements UserService { | |||||
return user | return user | ||||
} | } | ||||
async getByUsername(username: string): Promise<models.UserProfile> { | |||||
async getByUsername(username: string): Promise<models.User> { | |||||
const user = await this.prismaClient.user.findUnique({ | |||||
where: { | |||||
username, | |||||
}, | |||||
}) | |||||
if (!user) { | |||||
throw new Error('User not found!') | |||||
} | |||||
return user | |||||
} | |||||
async getProfileByUsername(username: string): Promise<models.UserProfile> { | |||||
const user = await this.prismaClient.userProfile.findFirst({ | const user = await this.prismaClient.userProfile.findFirst({ | ||||
where: { | where: { | ||||
user: { | user: { | ||||
@@ -0,0 +1,9 @@ | |||||
import FastifyAuth0Verify, { FastifyAuth0VerifyOptions } from 'fastify-auth0-verify'; | |||||
import fp from 'fastify-plugin'; | |||||
export default fp<FastifyAuth0VerifyOptions>(async (fastify, opts) => { | |||||
fastify.register(FastifyAuth0Verify, { | |||||
domain: process.env.AUTH0_DOMAIN, | |||||
secret: process.env.AUTH0_SECRET | |||||
}) | |||||
}); |
@@ -0,0 +1,14 @@ | |||||
import {FastifyPluginAsync} from 'fastify' | |||||
import {container} from 'tsyringe' | |||||
import AuthController from '../../../modules/auth/controller' | |||||
const auth: FastifyPluginAsync = async (fastify): Promise<void> => { | |||||
const ringtoneController = container.resolve<AuthController>('AuthController') | |||||
fastify.route({ | |||||
url: '/log-in', | |||||
method: 'POST', | |||||
handler: ringtoneController.logIn, | |||||
}) | |||||
} | |||||
export default auth |
@@ -4,13 +4,52 @@ import RingtoneController from '../../../modules/ringtone/controller' | |||||
const ringtones: FastifyPluginAsync = async (fastify): Promise<void> => { | const ringtones: FastifyPluginAsync = async (fastify): Promise<void> => { | ||||
const ringtoneController = container.resolve<RingtoneController>('RingtoneController') | const ringtoneController = container.resolve<RingtoneController>('RingtoneController') | ||||
fastify.get('/', ringtoneController.browse) | |||||
fastify.get('/:id', ringtoneController.get) | |||||
fastify.post('/', ringtoneController.create) | |||||
fastify.patch('/:id', ringtoneController.update) | |||||
fastify.post('/:id/delete', ringtoneController.softDelete) | |||||
fastify.delete('/:id/delete', ringtoneController.undoDelete) | |||||
fastify.delete('/:id', ringtoneController.hardDelete) | |||||
fastify.route({ | |||||
url: '/:id', | |||||
method: 'GET', | |||||
handler: ringtoneController.get, | |||||
}) | |||||
fastify.route({ | |||||
url: '/', | |||||
method: 'GET', | |||||
handler: ringtoneController.browse, | |||||
}) | |||||
fastify.route({ | |||||
url: '/', | |||||
method: 'POST', | |||||
handler: ringtoneController.create, | |||||
preValidation: fastify.authenticate, | |||||
}) | |||||
fastify.route({ | |||||
url: '/:id', | |||||
method: 'PATCH', | |||||
handler: ringtoneController.update, | |||||
preValidation: fastify.authenticate, | |||||
}) | |||||
fastify.route({ | |||||
url: '/:id/delete', | |||||
method: 'POST', | |||||
handler: ringtoneController.softDelete, | |||||
preValidation: fastify.authenticate, | |||||
}) | |||||
fastify.route({ | |||||
url: '/:id/delete', | |||||
method: 'DELETE', | |||||
handler: ringtoneController.undoDelete, | |||||
preValidation: fastify.authenticate, | |||||
}) | |||||
fastify.route({ | |||||
url: '/:id', | |||||
method: 'DELETE', | |||||
handler: ringtoneController.hardDelete, | |||||
preValidation: fastify.authenticate, | |||||
}) | |||||
} | } | ||||
export default ringtones | export default ringtones |
@@ -3,7 +3,8 @@ | |||||
"compilerOptions": { | "compilerOptions": { | ||||
"outDir": "dist", | "outDir": "dist", | ||||
"experimentalDecorators": true, | "experimentalDecorators": true, | ||||
"emitDecoratorMetadata": true | |||||
"emitDecoratorMetadata": true, | |||||
"strict": false | |||||
}, | }, | ||||
"include": [ | "include": [ | ||||
"src/**/*.ts" | "src/**/*.ts" | ||||
@@ -515,22 +515,22 @@ | |||||
semver "^7.3.4" | semver "^7.3.4" | ||||
tar "^6.1.0" | tar "^6.1.0" | ||||
"@prisma/client@^2.24.1": | |||||
version "2.24.1" | |||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.24.1.tgz#c4f26fb4d768dd52dd20a17e626f10e69cc0b85c" | |||||
integrity sha512-vllhf36g3oI98GF1Q5IPmnR5MYzBPeCcl/Xiz6EAi4DMOxE069o9ka5BAqYbUG2USx8JuKw09QdMnDrp3Kyn8g== | |||||
"@prisma/client@2.21.2": | |||||
version "2.21.2" | |||||
resolved "https://registry.yarnpkg.com/@prisma/client/-/client-2.21.2.tgz#ca8489832da1d61add429390210be4d7896e5e29" | |||||
integrity sha512-UjkOXYpxLuHyoMDsP2m0LTcxhrjQa1dEOLFe3aDrO/BLrs/2yUxyPdtwSKxizRXFzuXSGkKIK225vcjZRuMpAg== | |||||
dependencies: | dependencies: | ||||
"@prisma/engines-version" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" | |||||
"@prisma/engines-version" "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d" | |||||
"@prisma/engines-version@2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4": | |||||
version "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" | |||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#2c5813ef98bcbe659b18b521f002f5c8aabbaae2" | |||||
integrity sha512-60Do+ByVfHnhJ2id5h/lXOZnDQNIf5pz3enkKWOmyr744Z2IxkBu65jRckFfMN5cPtmXDre/Ay/GKm0aoeLwrw== | |||||
"@prisma/engines-version@2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d": | |||||
version "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d" | |||||
resolved "https://registry.yarnpkg.com/@prisma/engines-version/-/engines-version-2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d.tgz#b749bae4173eb766dafc298aaa7d883c2dbe555b" | |||||
integrity sha512-9/fE1gdPWmjbMjXUJjrTMt848TsgEnSjZCcJ1wu9OAcRlAKKJBLehftqC3gSEShDijvMYgeTdGU5snMpwmv4vg== | |||||
"@prisma/engines@2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4": | |||||
version "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" | |||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4.tgz#7e542d510f0c03f41b73edbb17254f5a0b272a4d" | |||||
integrity sha512-29/xO9kqeQka+wN5Ev10l5L4XQXNVXdPToJs1M29VZ2imQsNsL4rtz26m3qGM54IoGWwwfTVdvuVRxKnDl2rig== | |||||
"@prisma/engines@2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d": | |||||
version "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d" | |||||
resolved "https://registry.yarnpkg.com/@prisma/engines/-/engines-2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d.tgz#aafed60c9506bc766e49ea60b9f8ce7da2385bc6" | |||||
integrity sha512-L57tvSoom2GDWDqik4wrAUBvLTAv5MTm2OOzNMBKsv0w5cX7ONoZ8KnGQN+csmdJpQVBs93dIvIBm72OO+l/9Q== | |||||
"@sinonjs/commons@^1.7.0": | "@sinonjs/commons@^1.7.0": | ||||
version "1.8.3" | version "1.8.3" | ||||
@@ -620,6 +620,13 @@ | |||||
jest-diff "^26.0.0" | jest-diff "^26.0.0" | ||||
pretty-format "^26.0.0" | pretty-format "^26.0.0" | ||||
"@types/jsonwebtoken@^8.5.0": | |||||
version "8.5.1" | |||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#56958cb2d80f6d74352bd2e501a018e2506a8a84" | |||||
integrity sha512-rNAPdomlIUX0i0cg2+I+Q1wOUr531zHBQ+cV/28PJ39bSPKjahatZZ2LMuhiguETkCgLVzfruw/ZvNMNkKoSzw== | |||||
dependencies: | |||||
"@types/node" "*" | |||||
"@types/node@*", "@types/node@^15.0.0": | "@types/node@*", "@types/node@^15.0.0": | ||||
version "15.0.3" | version "15.0.3" | ||||
resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.3.tgz#ee09fcaac513576474c327da5818d421b98db88a" | resolved "https://registry.yarnpkg.com/@types/node/-/node-15.0.3.tgz#ee09fcaac513576474c327da5818d421b98db88a" | ||||
@@ -1082,6 +1089,11 @@ bser@2.1.1: | |||||
dependencies: | dependencies: | ||||
node-int64 "^0.4.0" | node-int64 "^0.4.0" | ||||
buffer-equal-constant-time@1.0.1: | |||||
version "1.0.1" | |||||
resolved "https://registry.yarnpkg.com/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz#f8e71132f7ffe6e01a5c9697a4c6f3e48d5cc819" | |||||
integrity sha1-+OcRMvf/5uAaXJaXpMbz5I1cyBk= | |||||
buffer-from@1.x, buffer-from@^1.0.0: | buffer-from@1.x, buffer-from@^1.0.0: | ||||
version "1.1.1" | version "1.1.1" | ||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" | resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef" | ||||
@@ -1263,6 +1275,11 @@ cliui@^7.0.2: | |||||
strip-ansi "^6.0.0" | strip-ansi "^6.0.0" | ||||
wrap-ansi "^7.0.0" | wrap-ansi "^7.0.0" | ||||
clone@2.x: | |||||
version "2.1.2" | |||||
resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" | |||||
integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= | |||||
close-with-grace@^1.0.0: | close-with-grace@^1.0.0: | ||||
version "1.1.0" | version "1.1.0" | ||||
resolved "https://registry.yarnpkg.com/close-with-grace/-/close-with-grace-1.1.0.tgz#91a48cf2019b5ae6e67b0255a32abcfd9bbca233" | resolved "https://registry.yarnpkg.com/close-with-grace/-/close-with-grace-1.1.0.tgz#91a48cf2019b5ae6e67b0255a32abcfd9bbca233" | ||||
@@ -1606,6 +1623,13 @@ ecc-jsbn@~0.1.1: | |||||
jsbn "~0.1.0" | jsbn "~0.1.0" | ||||
safer-buffer "^2.1.0" | safer-buffer "^2.1.0" | ||||
ecdsa-sig-formatter@1.0.11: | |||||
version "1.0.11" | |||||
resolved "https://registry.yarnpkg.com/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz#ae0f0fa2d85045ef14a817daa3ce9acd0489e5bf" | |||||
integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== | |||||
dependencies: | |||||
safe-buffer "^5.0.1" | |||||
electron-to-chromium@^1.3.723: | electron-to-chromium@^1.3.723: | ||||
version "1.3.727" | version "1.3.727" | ||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" | resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.727.tgz#857e310ca00f0b75da4e1db6ff0e073cc4a91ddf" | ||||
@@ -1841,6 +1865,24 @@ fast-safe-stringify@^2.0.7: | |||||
resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" | resolved "https://registry.yarnpkg.com/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz#124aa885899261f68aedb42a7c080de9da608743" | ||||
integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== | integrity sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA== | ||||
fastfall@^1.5.0: | |||||
version "1.5.1" | |||||
resolved "https://registry.yarnpkg.com/fastfall/-/fastfall-1.5.1.tgz#3fee03331a49d1d39b3cdf7a5e9cd66f475e7b94" | |||||
integrity sha1-P+4DMxpJ0dObPN96XpzWb0dee5Q= | |||||
dependencies: | |||||
reusify "^1.0.0" | |||||
fastify-auth0-verify@^0.5.2: | |||||
version "0.5.2" | |||||
resolved "https://registry.yarnpkg.com/fastify-auth0-verify/-/fastify-auth0-verify-0.5.2.tgz#26d7e51d9f251d515f3edde97669ac21ecd0aa67" | |||||
integrity sha512-MHNolhH4BRRQEzUWHdSfa03/Aeas/NYvjaRTopF17ovta55ErWJa3e6UpxmXKjMArFP/NeyZkcZ3ZBci71Fz1Q== | |||||
dependencies: | |||||
fastify-jwt "^3.0.0" | |||||
fastify-plugin "^3.0.0" | |||||
http-errors "^1.7.3" | |||||
node-cache "^5.0.1" | |||||
node-fetch "^2.6.1" | |||||
fastify-autoload@^3.3.1: | fastify-autoload@^3.3.1: | ||||
version "3.7.1" | version "3.7.1" | ||||
resolved "https://registry.yarnpkg.com/fastify-autoload/-/fastify-autoload-3.7.1.tgz#64dd843c5fe340d4c42d3d9353521e70c4c28b4f" | resolved "https://registry.yarnpkg.com/fastify-autoload/-/fastify-autoload-3.7.1.tgz#64dd843c5fe340d4c42d3d9353521e70c4c28b4f" | ||||
@@ -1887,6 +1929,17 @@ fastify-error@^0.3.0: | |||||
resolved "https://registry.yarnpkg.com/fastify-error/-/fastify-error-0.3.1.tgz#8eb993e15e3cf57f0357fc452af9290f1c1278d2" | resolved "https://registry.yarnpkg.com/fastify-error/-/fastify-error-0.3.1.tgz#8eb993e15e3cf57f0357fc452af9290f1c1278d2" | ||||
integrity sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ== | integrity sha512-oCfpcsDndgnDVgiI7bwFKAun2dO+4h84vBlkWsWnz/OUK9Reff5UFoFl241xTiLeHWX/vU9zkDVXqYUxjOwHcQ== | ||||
fastify-jwt@^3.0.0: | |||||
version "3.0.0" | |||||
resolved "https://registry.yarnpkg.com/fastify-jwt/-/fastify-jwt-3.0.0.tgz#d0cf2bfc02cf12b3826f534258f559baf9c8518f" | |||||
integrity sha512-gFo6AQRz71TEteKisNcgXNYei1QzNBzXH59J00PBoRHhK3YtADF/teQxrKTyR2GeIBwuQnVJNRtsFRgLd7adTA== | |||||
dependencies: | |||||
"@types/jsonwebtoken" "^8.5.0" | |||||
fastify-plugin "^3.0.0" | |||||
http-errors "^1.8.0" | |||||
jsonwebtoken "^8.5.1" | |||||
steed "^1.1.3" | |||||
fastify-plugin@^3.0.0: | fastify-plugin@^3.0.0: | ||||
version "3.0.0" | version "3.0.0" | ||||
resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-3.0.0.tgz#cf1b8c8098e3b5a7c8c30e6aeb06903370c054ca" | resolved "https://registry.yarnpkg.com/fastify-plugin/-/fastify-plugin-3.0.0.tgz#cf1b8c8098e3b5a7c8c30e6aeb06903370c054ca" | ||||
@@ -1936,13 +1989,29 @@ fastify@^3.0.0: | |||||
semver "^7.3.2" | semver "^7.3.2" | ||||
tiny-lru "^7.0.0" | tiny-lru "^7.0.0" | ||||
fastq@^1.6.1: | |||||
fastparallel@^2.2.0: | |||||
version "2.4.0" | |||||
resolved "https://registry.yarnpkg.com/fastparallel/-/fastparallel-2.4.0.tgz#65fbec1a5e5902494be772cf5765cbaaece08688" | |||||
integrity sha512-sacwQ7wwKlQXsa7TN24UvMBLZNLmVcPhmxccC9riFqb3N+fSczJL8eWdnZodZ/KijGVgNBBfvF/NeXER08uXnQ== | |||||
dependencies: | |||||
reusify "^1.0.4" | |||||
xtend "^4.0.2" | |||||
fastq@^1.3.0, fastq@^1.6.1: | |||||
version "1.11.0" | version "1.11.0" | ||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" | resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.11.0.tgz#bb9fb955a07130a918eb63c1f5161cc32a5d0858" | ||||
integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== | integrity sha512-7Eczs8gIPDrVzT+EksYBcupqMyxSHXXrHOLRRxU2/DicV8789MRBRR8+Hc2uWzUupOs4YS4JzBmBxjjCVBxD/g== | ||||
dependencies: | dependencies: | ||||
reusify "^1.0.4" | reusify "^1.0.4" | ||||
fastseries@^1.7.0: | |||||
version "1.7.2" | |||||
resolved "https://registry.yarnpkg.com/fastseries/-/fastseries-1.7.2.tgz#d22ce13b9433dff3388d91dbd6b8bda9b21a0f4b" | |||||
integrity sha1-0izhO5Qz3/M4jZHb1ri9qbIaD0s= | |||||
dependencies: | |||||
reusify "^1.0.0" | |||||
xtend "^4.0.0" | |||||
fb-watchman@^2.0.0: | fb-watchman@^2.0.0: | ||||
version "2.0.1" | version "2.0.1" | ||||
resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" | resolved "https://registry.yarnpkg.com/fb-watchman/-/fb-watchman-2.0.1.tgz#fc84fb39d2709cf3ff6d743706157bb5708a8a85" | ||||
@@ -2279,7 +2348,7 @@ html-escaper@^2.0.0: | |||||
resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" | resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" | ||||
integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== | integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== | ||||
http-errors@^1.7.3: | |||||
http-errors@^1.7.3, http-errors@^1.8.0: | |||||
version "1.8.0" | version "1.8.0" | ||||
resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" | resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.0.tgz#75d1bbe497e1044f51e4ee9e704a62f28d336507" | ||||
integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== | integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A== | ||||
@@ -3079,6 +3148,22 @@ json5@2.x, json5@^2.1.2: | |||||
dependencies: | dependencies: | ||||
minimist "^1.2.5" | minimist "^1.2.5" | ||||
jsonwebtoken@^8.5.1: | |||||
version "8.5.1" | |||||
resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-8.5.1.tgz#00e71e0b8df54c2121a1f26137df2280673bcc0d" | |||||
integrity sha512-XjwVfRS6jTMsqYs0EsuJ4LGxXV14zQybNd4L2r0UvbVnSF9Af8x7p5MzbJ90Ioz/9TI41/hTCvznF/loiSzn8w== | |||||
dependencies: | |||||
jws "^3.2.2" | |||||
lodash.includes "^4.3.0" | |||||
lodash.isboolean "^3.0.3" | |||||
lodash.isinteger "^4.0.4" | |||||
lodash.isnumber "^3.0.3" | |||||
lodash.isplainobject "^4.0.6" | |||||
lodash.isstring "^4.0.1" | |||||
lodash.once "^4.0.0" | |||||
ms "^2.1.1" | |||||
semver "^5.6.0" | |||||
jsprim@^1.2.2: | jsprim@^1.2.2: | ||||
version "1.4.1" | version "1.4.1" | ||||
resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" | resolved "https://registry.yarnpkg.com/jsprim/-/jsprim-1.4.1.tgz#313e66bc1e5cc06e438bc1b7499c2e5c56acb6a2" | ||||
@@ -3089,6 +3174,23 @@ jsprim@^1.2.2: | |||||
json-schema "0.2.3" | json-schema "0.2.3" | ||||
verror "1.10.0" | verror "1.10.0" | ||||
jwa@^1.4.1: | |||||
version "1.4.1" | |||||
resolved "https://registry.yarnpkg.com/jwa/-/jwa-1.4.1.tgz#743c32985cb9e98655530d53641b66c8645b039a" | |||||
integrity sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA== | |||||
dependencies: | |||||
buffer-equal-constant-time "1.0.1" | |||||
ecdsa-sig-formatter "1.0.11" | |||||
safe-buffer "^5.0.1" | |||||
jws@^3.2.2: | |||||
version "3.2.2" | |||||
resolved "https://registry.yarnpkg.com/jws/-/jws-3.2.2.tgz#001099f3639468c9414000e99995fa52fb478304" | |||||
integrity sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA== | |||||
dependencies: | |||||
jwa "^1.4.1" | |||||
safe-buffer "^5.0.1" | |||||
kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: | kind-of@^3.0.2, kind-of@^3.0.3, kind-of@^3.2.0: | ||||
version "3.2.2" | version "3.2.2" | ||||
resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" | resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-3.2.2.tgz#31ea21a734bab9bbb0f32466d893aea51e4a3c64" | ||||
@@ -3186,6 +3288,41 @@ locate-path@^5.0.0: | |||||
dependencies: | dependencies: | ||||
p-locate "^4.1.0" | p-locate "^4.1.0" | ||||
lodash.includes@^4.3.0: | |||||
version "4.3.0" | |||||
resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" | |||||
integrity sha1-YLuYqHy5I8aMoeUTJUgzFISfVT8= | |||||
lodash.isboolean@^3.0.3: | |||||
version "3.0.3" | |||||
resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" | |||||
integrity sha1-bC4XHbKiV82WgC/UOwGyDV9YcPY= | |||||
lodash.isinteger@^4.0.4: | |||||
version "4.0.4" | |||||
resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" | |||||
integrity sha1-YZwK89A/iwTDH1iChAt3sRzWg0M= | |||||
lodash.isnumber@^3.0.3: | |||||
version "3.0.3" | |||||
resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" | |||||
integrity sha1-POdoEMWSjQM1IwGsKHMX8RwLH/w= | |||||
lodash.isplainobject@^4.0.6: | |||||
version "4.0.6" | |||||
resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" | |||||
integrity sha1-fFJqUtibRcRcxpC4gWO+BJf1UMs= | |||||
lodash.isstring@^4.0.1: | |||||
version "4.0.1" | |||||
resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" | |||||
integrity sha1-1SfftUVuynzJu5XV2ur4i6VKVFE= | |||||
lodash.once@^4.0.0: | |||||
version "4.1.1" | |||||
resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" | |||||
integrity sha1-DdOXEhPHxW34gJd9UEyI+0cal6w= | |||||
lodash.toarray@^4.4.0: | lodash.toarray@^4.4.0: | ||||
version "4.4.0" | version "4.4.0" | ||||
resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" | resolved "https://registry.yarnpkg.com/lodash.toarray/-/lodash.toarray-4.4.0.tgz#24c4bfcd6b2fba38bfd0594db1179d8e9b656561" | ||||
@@ -3453,6 +3590,13 @@ node-addon-api@^3.1.0: | |||||
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" | resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-3.2.1.tgz#81325e0a2117789c0128dab65e7e38f07ceba161" | ||||
integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== | integrity sha512-mmcei9JghVNDYydghQmeDX8KoAm0FAiYyIcUt/N4nhyAipB17pllZQDOJD2fotxABnt4Mdz+dKTO7eftLg4d0A== | ||||
node-cache@^5.0.1: | |||||
version "5.1.2" | |||||
resolved "https://registry.yarnpkg.com/node-cache/-/node-cache-5.1.2.tgz#f264dc2ccad0a780e76253a694e9fd0ed19c398d" | |||||
integrity sha512-t1QzWwnk4sjLWaQAS8CHgOJ+RAfmHpxFWmc36IWTiWHQfs0w5JDMBS1b1ZxQteo0vVVuWJvIUKHDkkeK7vIGCg== | |||||
dependencies: | |||||
clone "2.x" | |||||
node-emoji@^1.10.0: | node-emoji@^1.10.0: | ||||
version "1.10.0" | version "1.10.0" | ||||
resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" | resolved "https://registry.yarnpkg.com/node-emoji/-/node-emoji-1.10.0.tgz#8886abd25d9c7bb61802a658523d1f8d2a89b2da" | ||||
@@ -3943,12 +4087,12 @@ pretty-ms@^5.0.0: | |||||
dependencies: | dependencies: | ||||
parse-ms "^2.1.0" | parse-ms "^2.1.0" | ||||
prisma@^2.24.1: | |||||
version "2.24.1" | |||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.24.1.tgz#f8f4cb8baf407a71800256160277f69603bd43a3" | |||||
integrity sha512-L+ykMpttbWzpTNsy+PPynnEX/mS1s5zs49euXBrMjxXh1M6/f9MYlTNAj+iP90O9ZSaURSpNa+1jzatPghqUcQ== | |||||
prisma@2.21.2: | |||||
version "2.21.2" | |||||
resolved "https://registry.yarnpkg.com/prisma/-/prisma-2.21.2.tgz#a73b4cbe92a884aa98b317684d6741871b5e94a5" | |||||
integrity sha512-Ux9ovDIUHsMNLGLtuo6BBKCuuBVLpZmhM2LXF+VBUQvsbmsVfp3u5CRyHGEqaZqMibYQJISy7YZYF/RgozHKkQ== | |||||
dependencies: | dependencies: | ||||
"@prisma/engines" "2.24.1-2.18095475d5ee64536e2f93995e48ad800737a9e4" | |||||
"@prisma/engines" "2.21.0-36.e421996c87d5f3c8f7eeadd502d4ad402c89464d" | |||||
process-nextick-args@~2.0.0: | process-nextick-args@~2.0.0: | ||||
version "2.0.1" | version "2.0.1" | ||||
@@ -4193,7 +4337,7 @@ ret@~0.2.0: | |||||
resolved "https://registry.yarnpkg.com/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c" | resolved "https://registry.yarnpkg.com/ret/-/ret-0.2.2.tgz#b6861782a1f4762dce43402a71eb7a283f44573c" | ||||
integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ== | integrity sha512-M0b3YWQs7R3Z917WRQy1HHA7Ba7D8hvZg6UE5mLykJxQVE2ju0IXbGlaHPPlkY+WN7wFP+wUMXmBFA0aV6vYGQ== | ||||
reusify@^1.0.4: | |||||
reusify@^1.0.0, reusify@^1.0.4: | |||||
version "1.0.4" | version "1.0.4" | ||||
resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" | resolved "https://registry.yarnpkg.com/reusify/-/reusify-1.0.4.tgz#90da382b1e126efc02146e90845a88db12925d76" | ||||
integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== | integrity sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw== | ||||
@@ -4295,7 +4439,7 @@ semver-store@^0.3.0: | |||||
resolved "https://registry.yarnpkg.com/semver-store/-/semver-store-0.3.0.tgz#ce602ff07df37080ec9f4fb40b29576547befbe9" | resolved "https://registry.yarnpkg.com/semver-store/-/semver-store-0.3.0.tgz#ce602ff07df37080ec9f4fb40b29576547befbe9" | ||||
integrity sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg== | integrity sha512-TcZvGMMy9vodEFSse30lWinkj+JgOBvPn8wRItpQRSayhc+4ssDs335uklkfvQQJgL/WvmHLVj4Ycv2s7QCQMg== | ||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0: | |||||
"semver@2 || 3 || 4 || 5", semver@^5.3.0, semver@^5.5.0, semver@^5.6.0: | |||||
version "5.7.1" | version "5.7.1" | ||||
resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" | resolved "https://registry.yarnpkg.com/semver/-/semver-5.7.1.tgz#a954f931aeba508d307bbf069eff0c01c96116f7" | ||||
integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== | integrity sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ== | ||||
@@ -4571,6 +4715,17 @@ stealthy-require@^1.1.1: | |||||
resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" | resolved "https://registry.yarnpkg.com/stealthy-require/-/stealthy-require-1.1.1.tgz#35b09875b4ff49f26a777e509b3090a3226bf24b" | ||||
integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= | integrity sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks= | ||||
steed@^1.1.3: | |||||
version "1.1.3" | |||||
resolved "https://registry.yarnpkg.com/steed/-/steed-1.1.3.tgz#f1525dd5adb12eb21bf74749537668d625b9abc5" | |||||
integrity sha1-8VJd1a2xLrIb90dJU3Zo1iW5q8U= | |||||
dependencies: | |||||
fastfall "^1.5.0" | |||||
fastparallel "^2.2.0" | |||||
fastq "^1.3.0" | |||||
fastseries "^1.7.0" | |||||
reusify "^1.0.0" | |||||
string-length@^4.0.1: | string-length@^4.0.1: | ||||
version "4.0.2" | version "4.0.2" | ||||
resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" | resolved "https://registry.yarnpkg.com/string-length/-/string-length-4.0.2.tgz#a8a8dc7bd5c1a82b9b3c8b87e125f66871b6e57a" | ||||
@@ -4948,6 +5103,11 @@ typescript@^4.1.3: | |||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" | resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.2.4.tgz#8610b59747de028fda898a8aef0e103f156d0961" | ||||
integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== | integrity sha512-V+evlYHZnQkaz8TRBuxTA92yZBPotr5H+WhQ7bD3hZUndx5tGOa1fuCgeSjxAzM1RiN5IzvadIXTVefuuwZCRg== | ||||
unfetch@^4.2.0: | |||||
version "4.2.0" | |||||
resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.2.0.tgz#7e21b0ef7d363d8d9af0fb929a5555f6ef97a3be" | |||||
integrity sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA== | |||||
union-value@^1.0.0: | union-value@^1.0.0: | ||||
version "1.0.1" | version "1.0.1" | ||||
resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" | resolved "https://registry.yarnpkg.com/union-value/-/union-value-1.0.1.tgz#0b6fe7b835aecda61c6ea4d4f02c14221e109847" | ||||
@@ -5188,6 +5348,11 @@ xmlchars@^2.2.0: | |||||
resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" | resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" | ||||
integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== | integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== | ||||
xtend@^4.0.0, xtend@^4.0.2: | |||||
version "4.0.2" | |||||
resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" | |||||
integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== | |||||
y18n@^4.0.0: | y18n@^4.0.0: | ||||
version "4.0.3" | version "4.0.3" | ||||
resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" | resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" | ||||