From 18f6980596d1aaf398e61a83c0b7674678d8772e Mon Sep 17 00:00:00 2001 From: mrkvon Date: Sat, 13 Jan 2024 22:35:50 +0100 Subject: [PATCH] Add draft tests for simple email notifications and implement new initialization and email verification --- .env.sample | 10 + .gitignore | 2 + package.json | 5 + src/app.ts | 96 ++++--- src/config/index.ts | 18 +- src/controllers/integration.ts | 199 +++---------- src/middlewares/authorizeGroup.ts | 55 ++++ src/middlewares/solidAuth.ts | 1 + src/test/integration-finish.spec.ts | 79 +---- src/test/integration-start.spec.ts | 109 +++---- src/test/notification.spec.ts | 9 +- src/test/status.spec.ts | 43 +-- src/test/testSetup.spec.ts | 84 +++--- yarn.lock | 430 +++++++++++++++++++++++++++- 14 files changed, 705 insertions(+), 435 deletions(-) create mode 100644 src/middlewares/authorizeGroup.ts diff --git a/.env.sample b/.env.sample index 6629592..67a7f61 100644 --- a/.env.sample +++ b/.env.sample @@ -14,6 +14,9 @@ MAILER_IDENTITY_EMAIL=bot@example MAILER_IDENTITY_PASSWORD=password MAILER_IDENTITY_PROVIDER=http://localhost:3456 +# link to group of users who are allowed to use the service +ALLOWED_GROUPS= + # SMTP Transport for sending emails # string or undefined SMTP_TRANSPORT_HOST= @@ -39,3 +42,10 @@ DB_USERNAME= DB_PASSWORD= DB_HOST= DB_PORT= + +# JWT +# path to JWT (private) key +# you can use the command `openssl ecparam -name prime256v1 -genkey -noout -out ecdsa-p256-private.pem` to generate one +JWT_KEY=./ecdsa-p256-private.pem +# jwt algorithm +JWT_ALG=ES256 diff --git a/.gitignore b/.gitignore index a40dfe0..f4e004c 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,5 @@ output node_modules database.sqlite + +*.pem diff --git a/package.json b/package.json index 6942a55..cd51545 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "@types/bcryptjs": "^2.4.2", "@types/chai": "^4.3.5", "@types/co-body": "^6.1.0", + "@types/fs-extra": "^11.0.4", + "@types/jsonwebtoken": "^9.0.5", "@types/koa": "^2.13.7", "@types/koa-static": "^4.0.3", "@types/koa__cors": "^4.0.1", @@ -37,6 +39,7 @@ "eslint-plugin-prettier": "^5.0.0", "maildev": "^2.1.0", "mocha": "^10.2.0", + "msw": "^2.0.14", "prettier": "^3.0.0", "rdf-namespaces": "^1.11.0", "sinon": "^15.2.0", @@ -57,6 +60,8 @@ "cross-fetch": "^4.0.0", "css-authn": "^0.0.11", "dotenv": "^16.0.0", + "fs-extra": "^11.2.0", + "jsonwebtoken": "^9.0.2", "koa": "^2.14.2", "koa-helmet": "^7.0.2", "koa-static": "^5.0.0", diff --git a/src/app.ts b/src/app.ts index 059dc4b..40ac088 100644 --- a/src/app.ts +++ b/src/app.ts @@ -4,7 +4,7 @@ import Router from '@koa/router' import Koa from 'koa' import helmet from 'koa-helmet' import serve from 'koa-static' -import { isBehindProxy } from './config' +import { allowedGroups, isBehindProxy } from './config' import { checkVerificationLink, finishIntegration, @@ -12,6 +12,7 @@ import { } from './controllers/integration' import { getStatus } from './controllers/status' import { webhookReceiver } from './controllers/webhookReceiver' +import { authorizeGroups } from './middlewares/authorizeGroup' import { solidAuth } from './middlewares/solidAuth' import { validateBody } from './middlewares/validate' @@ -20,42 +21,69 @@ app.proxy = isBehindProxy const router = new Router() router + // .post( + // '/inbox', + // solidAuth, + // /* + // #swagger.requestBody = { + // required: true, + // content: { + // 'application/json': { + // schema: { + // type: 'object', + // properties: { + // '@context': { const: 'https://www.w3.org/ns/activitystreams' }, + // '@id': { type: 'string' }, + // '@type': { const: 'Add' }, + // actor: { type: 'string', format: 'uri' }, + // object: { type: 'string', format: 'uri' }, + // target: { type: 'string', format: 'email' }, + // }, + // required: ['@context', '@type', 'actor', 'object', 'target'], + // additionalProperties: false, + // }, + // }, + // }, + // } + // */ + // validateBody({ + // type: 'object', + // properties: { + // '@context': { const: 'https://www.w3.org/ns/activitystreams' }, + // '@id': { type: 'string' }, + // '@type': { const: 'Add' }, + // actor: { type: 'string', format: 'uri' }, + // object: { type: 'string', format: 'uri' }, + // target: { type: 'string', format: 'email' }, + // }, + // required: ['@context', '@type', 'actor', 'object', 'target'], + // additionalProperties: false, + // }), + // initializeIntegration, + // ) .post( - '/inbox', + '/init', solidAuth, - /* - #swagger.requestBody = { - required: true, - content: { - 'application/json': { - schema: { - type: 'object', - properties: { - '@context': { const: 'https://www.w3.org/ns/activitystreams' }, - '@id': { type: 'string' }, - '@type': { const: 'Add' }, - actor: { type: 'string', format: 'uri' }, - object: { type: 'string', format: 'uri' }, - target: { type: 'string', format: 'email' }, - }, - required: ['@context', '@type', 'actor', 'object', 'target'], - additionalProperties: false, - }, - }, - }, - } - */ + authorizeGroups(allowedGroups), + // #swagger.requestBody = { + // required: true, + // content: { + // 'application/json': { + // schema: { + // type: 'object', + // properties: { + // email: { type: 'string', format: 'email' }, + // }, + // required: ['email'], + // additionalProperties: false, + // }, + // }, + // }, + // } validateBody({ type: 'object', - properties: { - '@context': { const: 'https://www.w3.org/ns/activitystreams' }, - '@id': { type: 'string' }, - '@type': { const: 'Add' }, - actor: { type: 'string', format: 'uri' }, - object: { type: 'string', format: 'uri' }, - target: { type: 'string', format: 'email' }, - }, - required: ['@context', '@type', 'actor', 'object', 'target'], + properties: { email: { type: 'string', format: 'email' } }, + required: ['email'], additionalProperties: false, }), initializeIntegration, @@ -71,7 +99,7 @@ app bodyParser({ enableTypes: ['text', 'json'], extendTypes: { - json: ['application/ld+json'], + json: ['application/ld+json', 'application/json'], text: ['text/turtle'], }, }), diff --git a/src/config/index.ts b/src/config/index.ts index a050702..5832c24 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -39,8 +39,8 @@ export const emailSender = process.env.EMAIL_SENDER export const port: number = +(process.env.PORT ?? 3005) -// email verification expiration in milliseconds (1 hour) -export const emailVerificationExpiration = 3600 * 1000 +// email verification expiration in seconds (1 hour) +export const emailVerificationExpiration = 3600 // configuration of database in form of sequelize options export const database: Options = { @@ -54,3 +54,17 @@ export const database: Options = { } export const isBehindProxy = stringToBoolean(process.env.BEHIND_PROXY) + +const stringToArray = (value: string | undefined) => { + if (!value) return [] + return value.split(/\s*,\s*/) +} + +export const allowedGroups = stringToArray( + process.env.ALLOWED_GROUPS ?? 'https://example.com#us', +) + +export const jwt = { + key: process.env.JWT_KEY ?? './ecdsa-p256-private.pem', + alg: process.env.JWT_ALG ?? 'ES256', +} diff --git a/src/controllers/integration.ts b/src/controllers/integration.ts index eceb6e8..38b9d74 100644 --- a/src/controllers/integration.ts +++ b/src/controllers/integration.ts @@ -1,48 +1,29 @@ -import { compare, hash } from 'bcryptjs' -import fetch from 'cross-fetch' -import crypto from 'crypto' -import { getAuthenticatedFetch } from 'css-authn/dist/7.x' +import { readFile } from 'fs-extra' +import jsonwebtoken, { + JsonWebTokenError, + TokenExpiredError, +} from 'jsonwebtoken' import { Middleware } from 'koa' import { pick } from 'lodash' -import n3 from 'n3' -import parseLinkHeader from 'parse-link-header' import * as config from '../config' -import { EmailVerification, Integration } from '../config/sequelize' import { sendMail } from '../services/mailerService' export const initializeIntegration: Middleware = async ctx => { // we should receive info about webId and email address - const email: string = ctx.request.body.target - const webId: string = ctx.request.body.actor - const inbox: string = ctx.request.body.object + const email: string = ctx.request.body.email + const user: string = ctx.state.user - // webId should belong to the authenticated user + // make a jwt token that verifies email address - if (webId !== ctx.state.user) ctx.throw(403, {}) + const tokenExpiration = config.emailVerificationExpiration - // search for webId's inbox - - // try to subscribe to the inbox - - // send verification email to provided email address - const token = crypto.randomBytes(64).toString('base64url').slice(0, 72) // there is no sense in having more than 72, since bcrypt cuts remaining stuff away - // or we would need to use different hashing - const tokenHash = await hash(token, 12) - // tokens should expire (in 1 hour perhaps?) - const tokenExpiration = Date.now() + config.emailVerificationExpiration - - // save the webId, email, and hashedToken to database - await EmailVerification.create({ - webId, - email, - inbox, - tokenHash, - tokenExpiration, + const pem = await readFile(config.jwt.key, { encoding: 'utf-8' }) + const jwt = jsonwebtoken.sign({ webId: user, email }, pem, { + algorithm: 'ES256', + expiresIn: tokenExpiration, }) - const emailVerificationLink = `${ - config.baseUrl - }/verify-email?id=${encodeURIComponent(webId)}&token=${token}` + const emailVerificationLink = `${config.baseUrl}/verify-email?token=${jwt}` await sendMail({ from: config.emailSender, @@ -55,143 +36,45 @@ export const initializeIntegration: Middleware = async ctx => { } export const checkVerificationLink: Middleware = async (ctx, next) => { - const webId = ctx.request.query.id - const token = ctx.request.query.token + const jwt = ctx.request.query.token - if ( - typeof webId !== 'string' || - typeof token !== 'string' || - !webId || - !token - ) + if (typeof jwt !== 'string' || !jwt) return ctx.throw( 400, 'This is not a valid verification link. Have you received the link in your email?', ) - const verification = await EmailVerification.findOne({ where: { webId } }) - - if (!verification) return ctx.throw(400, 'Verification link is invalid') - - const isCorrectToken = await compare(token, verification.tokenHash) - - if (!isCorrectToken) return ctx.throw(400, 'Verification link is invalid') - - ctx.state.integration = pick( - verification.dataValues, - 'inbox', - 'email', - 'webId', - ) - - await verification.destroy() - - if (verification.tokenExpiration < Date.now()) { - return ctx.throw(400, 'Verification link is expired') + const pem = await readFile(config.jwt.key, { encoding: 'utf-8' }) + try { + const payload = jsonwebtoken.verify(jwt, pem) as { + webId: string + email: string + iat: number + exp: number + } + + ctx.state.integration = pick(payload, 'email', 'webId') + await next() + } catch (error) { + if (error instanceof TokenExpiredError) + return ctx.throw(400, 'Verification link is expired') + + if (error instanceof JsonWebTokenError) + return ctx.throw(400, 'Verification link is invalid') + + throw error } - - await next() } export const finishIntegration: Middleware = async ctx => { - const integrationData = ctx.state.integration as { + const { webId, email } = ctx.state.integration as { webId: string - inbox: string email: string } - // save the integration to database - await Integration.create(integrationData) - - try { - // subscribe to the inbox' webhook notifications - await subscribeForNotifications(integrationData.inbox) - ctx.response.body = - 'Email notifications have been successfully integrated to your inbox' - } catch (e) { - ctx.response.body = - "Email was successfully verified, but notifications won't work, yet. It looks like your Solid Pod doesn't support notifications. We'll implement a workaround in the future.\nError: " + - (e as Error).message - } finally { - ctx.response.status = 200 - } -} - -type Fetch = typeof fetch - -/** - * Discover a webhook channel to subscribe to a resource - */ -const getWebhookChannel = async ( - url: string, - fetch: Fetch, -): Promise => { - const resourceInfo = await fetch(url, { - method: 'HEAD', - }) - - if (!resourceInfo.ok) throw new Error('Fetch of resource not successful') - - const linkHeader = parseLinkHeader(resourceInfo.headers.get('link')) - - if (!linkHeader) throw new Error('Link header not found') - - const storageDescriptionUri = - linkHeader['http://www.w3.org/ns/solid/terms#storageDescription']?.url - - const descriptionResourceUri = linkHeader.describedby?.url - - if (!storageDescriptionUri) throw new Error('storage description not found') - - const storageDescriptionResponse = await fetch(storageDescriptionUri) - const storageDescription = await storageDescriptionResponse.text() - - // parse the RDF that we received - const parser = new n3.Parser({ - format: storageDescriptionResponse.headers.get('content-type') ?? undefined, - baseIRI: storageDescriptionUri, - }) - const triples = parser.parse(storageDescription) - - // find the webhook channel to subscribe to - // in storage description - const webhookChannelTriple = triples.find( - triple => - triple.predicate.id === - 'http://www.w3.org/ns/solid/notifications#channelType' && - triple.object.id === - 'http://www.w3.org/ns/solid/notifications#WebhookChannel2023', - ) - - if (!webhookChannelTriple) throw new Error('webhook channel not found') - - const dru = new URL(descriptionResourceUri as string, url).toString() - // we want to include looking into this resource, too - // TODO use this variable below - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const descriptionResource = await (await fetch(dru)).text() - - return webhookChannelTriple.subject.id -} - -const subscribeForNotifications = async (resourceUrl: string) => { - const authenticatedMailerFetch = await getAuthenticatedFetch( - config.mailerCredentials, - ) - - const webhookChannel = await getWebhookChannel( - resourceUrl, - authenticatedMailerFetch, - ) - - return await authenticatedMailerFetch(webhookChannel, { - method: 'POST', - body: JSON.stringify({ - '@context': ['https://www.w3.org/ns/solid/notification/v1'], - type: 'http://www.w3.org/ns/solid/notifications#WebhookChannel2023', - topic: resourceUrl, - sendTo: `${config.baseUrl}/webhook-receiver`, - }), - headers: { 'content-type': 'application/ld+json' }, - }) + const pem = await readFile(config.jwt.key, { encoding: 'utf-8' }) + const jwt = jsonwebtoken.sign({ webId, email }, pem, { algorithm: 'ES256' }) + ctx.response.body = jwt + ctx.set('content-type', 'text/plain') + ctx.response.status = 200 } diff --git a/src/middlewares/authorizeGroup.ts b/src/middlewares/authorizeGroup.ts new file mode 100644 index 0000000..267740d --- /dev/null +++ b/src/middlewares/authorizeGroup.ts @@ -0,0 +1,55 @@ +import { Middleware } from 'koa' +import { Parser } from 'n3' +import { vcard } from 'rdf-namespaces' + +export const authorizeGroups = + (groups: string[]): Middleware => + async (ctx, next) => { + // if array of groups are empty, we allow everybody (default) + if (groups.length === 0) return await next() + + const user = ctx.state.user + + const memberships = await Promise.allSettled( + groups.map(group => isGroupMember(user, group)), + ) + + const isAllowed = memberships.some( + membership => + membership.status === 'fulfilled' && membership.value === true, + ) + + if (!isAllowed) { + return ctx.throw( + 403, + 'Authenticated user is not a member of any allowed group', + ) + } + + await next() + } + +const isGroupMember = async (user: string, group: string) => { + const groupDocumentResponse = await fetch(group) + if (!groupDocumentResponse.ok) return false + const groupDocument = await groupDocumentResponse.text() + + const parser = new Parser({ baseIRI: group }) + + return await new Promise((resolve, reject) => { + parser.parse(groupDocument, (error, quad) => { + if (error) return reject(error) + + // finished without finding the membership + if (!quad) return resolve(false) + + // is the quad expressing the searched membership? + if ( + quad.subject.value === group && + quad.predicate.value === vcard.hasMember && + quad.object.value === user + ) + return resolve(true) + }) + }) +} diff --git a/src/middlewares/solidAuth.ts b/src/middlewares/solidAuth.ts index 4f1d99c..223408a 100644 --- a/src/middlewares/solidAuth.ts +++ b/src/middlewares/solidAuth.ts @@ -27,6 +27,7 @@ export const solidAuth: Middleware = async (ctx, next) => { }` ctx.throw(401, message) + return } // on success continue diff --git a/src/test/integration-finish.spec.ts b/src/test/integration-finish.spec.ts index 8bdb6d9..64ec991 100644 --- a/src/test/integration-finish.spec.ts +++ b/src/test/integration-finish.spec.ts @@ -6,14 +6,9 @@ import Mail from 'nodemailer/lib/mailer' import { SinonSandbox, SinonSpy, createSandbox } from 'sinon' import { baseUrl } from '../config' import * as mailerService from '../services/mailerService' -import { - authenticatedFetch, - authenticatedFetchNoNotifications, - person, - personNoNotifications, -} from './testSetup.spec' +import { authenticatedFetch } from './testSetup.spec' -describe('email verification via /verify-email?id=webId&token=base64Token', () => { +describe('email verification via /verify-email?token=jwt', () => { let sendMailSpy: SinonSpy<[options: Mail.Options], Promise> let verificationLink: string let sandbox: SinonSandbox @@ -30,20 +25,10 @@ describe('email verification via /verify-email?id=webId&token=base64Token', () = beforeEach(async () => { // initialize the integration - const initResponse = await authenticatedFetch(`${baseUrl}/inbox`, { + const initResponse = await authenticatedFetch(`${baseUrl}/init`, { method: 'post', - headers: { - 'content-type': - 'application/ld+json;profile="https://www.w3.org/ns/activitystreams"', - }, - body: JSON.stringify({ - '@context': 'https://www.w3.org/ns/activitystreams', - '@id': '', - '@type': 'Add', - actor: person.webId, - object: person.podUrl + 'profile/card', - target: 'email@example.com', - }), + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ email: 'email@example.com' }), }) expect(initResponse.status).to.equal(200) @@ -54,26 +39,25 @@ describe('email verification via /verify-email?id=webId&token=base64Token', () = expect(verificationLink).to.not.be.null }) - it('[correct token] should finish the integration of webId + inbox + email and respond 200', async () => { + it('[correct token] should respond with 200', async () => { const response = await fetch(verificationLink) expect(response.status).to.equal(200) }) - it('[incorrect token] should respond with 400', async () => { - const response = await fetch(verificationLink.slice(0, -2)) - expect(response.status).to.equal(400) - expect(await response.text()).to.equal('Verification link is invalid') - }) + it( + '[correct token] should return proof of verification for the user to save on their pod, or edit the pod directly', + ) + // this is a JWT token, it keeps email and webId of the person as payload - it('[integration for webId not started] should respond with 400', async () => { + it('[incorrect token] should respond with 400', async () => { const response = await fetch( - `${baseUrl}/verify-email?id=asdf&token=12345678`, + verificationLink.slice(0, -32) + '0'.repeat(32), ) expect(response.status).to.equal(400) expect(await response.text()).to.equal('Verification link is invalid') }) - it('[missing id or token] should respond with 400', async () => { + it('[missing token] should respond with 400', async () => { const response = await fetch(`${baseUrl}/verify-email`) expect(response.status).to.equal(400) expect(await response.text()).to.equal( @@ -88,41 +72,4 @@ describe('email verification via /verify-email?id=webId&token=base64Token', () = expect(response.status).to.equal(400) expect(await response.text()).to.equal('Verification link is expired') }) - - it('when we send out multiple verification emails, the last link should work') - - context("server doesn't support webhook notifications", () => { - beforeEach(async () => { - // initialize the integration - const initResponse = await authenticatedFetchNoNotifications( - `${baseUrl}/inbox`, - { - method: 'post', - headers: { - 'content-type': - 'application/ld+json;profile="https://www.w3.org/ns/activitystreams"', - }, - body: JSON.stringify({ - '@context': 'https://www.w3.org/ns/activitystreams', - '@id': '', - '@type': 'Add', - actor: personNoNotifications.webId, - object: personNoNotifications.podUrl + 'profile/card', - target: 'email@example.com', - }), - }, - ) - - expect(initResponse.status).to.equal(200) - // email was sent - const emailMessage = sendMailSpy.secondCall.firstArg.html - const $ = cheerio.load(emailMessage) - verificationLink = $('a').first().attr('href') as string - expect(verificationLink).to.not.be.null - }) - it("should verify email, but inform user that notifications aren't supported", async () => { - const response = await fetch(verificationLink) - expect(response.status).to.equal(200) - }) - }) }) diff --git a/src/test/integration-start.spec.ts b/src/test/integration-start.spec.ts index 9b25b0e..cac72a6 100644 --- a/src/test/integration-start.spec.ts +++ b/src/test/integration-start.spec.ts @@ -5,9 +5,9 @@ import Mail from 'nodemailer/lib/mailer' import { SinonSandbox, SinonSpy, createSandbox } from 'sinon' import { baseUrl } from '../config' import * as mailerService from '../services/mailerService' -import { authenticatedFetch, person } from './testSetup.spec' +import { authenticatedFetch, otherAuthenticatedFetch } from './testSetup.spec' -describe('Mailer integration via /inbox', () => { +describe('Initialize email integration via /init', () => { let sendMailSpy: SinonSpy<[options: Mail.Options], Promise> let sandbox: SinonSandbox @@ -20,52 +20,41 @@ describe('Mailer integration via /inbox', () => { sandbox.restore() }) - it('should be able to receive integration request to inbox', async () => { - const response = await authenticatedFetch(`${baseUrl}/inbox`, { - method: 'post', - headers: { - 'content-type': - 'application/ld+json;profile="https://www.w3.org/ns/activitystreams"', - }, - body: JSON.stringify({ - '@context': 'https://www.w3.org/ns/activitystreams', - '@id': '', - '@type': 'Add', - actor: person.webId, - object: person.podUrl + 'inbox/', - target: 'email@example.com', - }), + context('everything ok', () => { + let response: Response + beforeEach(async () => { + response = await authenticatedFetch(`${baseUrl}/init`, { + method: 'post', + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ email: 'email@example.com' }), + }) + }) + + it('should be able to receive integration request to /init', async () => { + expect(response.status).to.equal(200) }) - expect(sendMailSpy.calledOnce).to.be.true - expect(sendMailSpy.firstCall.firstArg).to.haveOwnProperty( - 'to', - 'email@example.com', - ) - expect(sendMailSpy.firstCall.firstArg) - .to.haveOwnProperty('text') - .include(`verify-email?id=${encodeURIComponent(person.webId)}&token=`) - expect(sendMailSpy.firstCall.firstArg) - .to.haveOwnProperty('html') - .include(`verify-email?id=${encodeURIComponent(person.webId)}&token=`) - expect(response.status).to.equal(200) + it('should send email with verification link', async () => { + expect(sendMailSpy.calledOnce).to.be.true + expect(sendMailSpy.firstCall.firstArg).to.haveOwnProperty( + 'to', + 'email@example.com', + ) + expect(sendMailSpy.firstCall.firstArg) + .to.haveOwnProperty('text') + .include(`verify-email?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.`) + expect(sendMailSpy.firstCall.firstArg) + .to.haveOwnProperty('html') + .include(`verify-email?token=eyJhbGciOiJFUzI1NiIsInR5cCI6IkpXVCJ9.`) + // maybe TODO we can perhaps also check the payload of the token + }) }) it('[invalid request body] should respond with 400', async () => { - const response = await authenticatedFetch(`${baseUrl}/inbox`, { + const response = await authenticatedFetch(`${baseUrl}/init`, { method: 'post', - headers: { - 'content-type': - 'application/ld+json;profile="https://www.w3.org/ns/activitystreams"', - }, - body: JSON.stringify({ - '@context': 'https://www.w3.org/ns/activitystrams', - '@id': '', - '@type': 'ads', - actor: 'asdf', - object: '/inbox', - target: 'yay', - }), + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ email: 'yay' }), }) expect(response.status).to.equal(400) @@ -73,47 +62,25 @@ describe('Mailer integration via /inbox', () => { context('person not signed in', () => { it('should respond with 401', async () => { - const response = await fetch(`${baseUrl}/inbox`, { + const response = await fetch(`${baseUrl}/init`, { method: 'post', - headers: { 'content-type': 'application/ld+json' }, - body: JSON.stringify({ - '@context': 'https://www.w3.org/ns/activitystreams', - '@id': '', - '@type': 'Add', - actor: person.webId, - object: person.podUrl + 'inbox/', - target: 'email@example.com', - }), + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ email: 'email@example.com' }), }) expect(response.status).to.equal(401) }) }) - context("authenticated person and actor don't match", () => { + context('person is not in the allowed group(s)', () => { it('should respond with 403', async () => { - const response = await authenticatedFetch(`${baseUrl}/inbox`, { + const response = await otherAuthenticatedFetch(`${baseUrl}/init`, { method: 'post', - headers: { 'content-type': 'application/ld+json' }, - body: JSON.stringify({ - '@context': 'https://www.w3.org/ns/activitystreams', - '@id': '', - '@type': 'Add', - actor: 'http://localhost:3456/person2/profile/card#me', - object: person.podUrl + 'inbox/', - target: 'email2@example.com', - }), + headers: { 'content-type': 'application/json' }, + body: JSON.stringify({ email: 'email@example.com' }), }) expect(response.status).to.equal(403) }) }) - - it( - 'should check that the inbox belongs to the person requesting subscription', - ) - - it('should check that it can read the inbox') - - it('should send email with verification link') }) diff --git a/src/test/notification.spec.ts b/src/test/notification.spec.ts index 2b07328..8aec890 100644 --- a/src/test/notification.spec.ts +++ b/src/test/notification.spec.ts @@ -11,7 +11,7 @@ import * as mailerService from '../services/mailerService' import { addRead, setupInbox } from '../setup' import { authenticatedFetch, person } from './testSetup.spec' -describe('received notification via /webhook-receiver', () => { +describe.skip('received notification via /inbox', () => { let sendMailSpy: SinonSpy<[options: Mail.Options], Promise> let verificationLink: string let sandbox: SinonSandbox @@ -70,7 +70,7 @@ describe('received notification via /webhook-receiver', () => { expect(finishResponse.status).to.equal(200) }) - it('[everything ok] should send email to email address when notifiation arrives', async function () { + it('[everything ok] should send email to email address when requested', async function () { this.timeout(10000) // create notification in inbox of person2 const authenticatedPerson2Fetch = await getAuthenticatedFetch({ @@ -104,5 +104,8 @@ describe('received notification via /webhook-receiver', () => { // TODO }) - it('[irrelevant update] should do nothing') + it('[not authenticated] should fail with 401') + it('[authenticated person not from group] should fail with 403') + + it('[requesting person not from group] should fail with 400') }) diff --git a/src/test/status.spec.ts b/src/test/status.spec.ts index c1f22e4..b7553b2 100644 --- a/src/test/status.spec.ts +++ b/src/test/status.spec.ts @@ -9,7 +9,7 @@ import * as mailerService from '../services/mailerService' import { addRead, setupInbox } from '../setup' import { authenticatedFetch, person } from './testSetup.spec' -describe('get info about integrations of current person with GET /status', () => { +describe.skip('get info about integrations of a person with GET /status', () => { let sendMailSpy: SinonSpy<[options: Mail.Options], Promise> let verificationLink: string let sandbox: SinonSandbox @@ -69,43 +69,6 @@ describe('get info about integrations of current person with GET /status', () => expect(response.status).to.equal(401) }) - it('should show list of resources (inboxes) current user is observing', async () => { - // finish the integration - const finishResponse = await fetch(verificationLink) - expect(finishResponse.status).to.equal(200) - - const response = await authenticatedFetch(`${baseUrl}/status`) - expect(response.status).to.equal(200) - - const body = await response.json() - - expect(body).to.deep.equal({ - actor: person.webId, - integrations: [ - { - object: `${person.podUrl}inbox/`, - target: 'email@example.com', - verified: true, - }, - ], - }) - }) - - it('should show unverified integrations', async () => { - const response = await authenticatedFetch(`${baseUrl}/status`) - expect(response.status).to.equal(200) - - const body = await response.json() - - expect(body).to.deep.equal({ - actor: person.webId, - integrations: [ - { - object: `${person.podUrl}inbox/`, - target: 'email@example.com', - verified: false, - }, - ], - }) - }) + it('[authenticated person not from group] should fail with 403') + it('[requested person not from group] should fail with 400') }) diff --git a/src/test/testSetup.spec.ts b/src/test/testSetup.spec.ts index a31aea4..b99cea2 100644 --- a/src/test/testSetup.spec.ts +++ b/src/test/testSetup.spec.ts @@ -1,7 +1,10 @@ import * as css from '@solid/community-server' +import fetch from 'cross-fetch' import { getAuthenticatedFetch } from 'css-authn/dist/7.x' import { IncomingMessage, Server, ServerResponse } from 'http' import MailDev from 'maildev' +import { HttpResponse, http } from 'msw' +import { SetupServer, setupServer } from 'msw/node' import app from '../app' import { port } from '../config' import { EmailVerification, Integration } from '../config/sequelize' @@ -9,7 +12,7 @@ import { createRandomAccount } from '../helpers' let server: Server let authenticatedFetch: typeof fetch -let authenticatedFetchNoNotifications: typeof fetch +let otherAuthenticatedFetch: typeof fetch let person: { idp: string podUrl: string @@ -18,7 +21,7 @@ let person: { password: string email: string } -let personNoNotifications: { +let otherPerson: { idp: string podUrl: string webId: string @@ -27,7 +30,7 @@ let personNoNotifications: { email: string } let cssServer: css.App -let cssServerNoNotifications: css.App +let mockServer: SetupServer before(async function () { this.timeout(60000) @@ -62,39 +65,6 @@ after(async () => { await cssServer.stop() }) -before(async function () { - this.timeout(60000) - const start = Date.now() - - // eslint-disable-next-line no-console - console.log('Starting CSS server without notifications') - // Community Solid Server (CSS) set up following example in https://github.com/CommunitySolidServer/hello-world-component/blob/main/test/integration/Server.test.ts - cssServerNoNotifications = await new css.AppRunner().create({ - loaderProperties: { - mainModulePath: css.joinFilePath(__dirname, '../../'), // ? - typeChecking: false, // ? - dumpErrorState: false, // disable CSS error dump - }, - config: css.joinFilePath(__dirname, './css-config-no-notifications.json'), // CSS config - variableBindings: {}, - // CSS cli options - // https://github.com/CommunitySolidServer/CommunitySolidServer/tree/main#-parameters - shorthand: { - port: 3457, - loggingLevel: 'off', - // seededPodConfigJson: css.joinFilePath(__dirname, './css-pod-seed.json'), // set up some Solid accounts - }, - }) - await cssServerNoNotifications.start() - - // eslint-disable-next-line no-console - console.log('CSS server started in', (Date.now() - start) / 1000, 'seconds') -}) - -after(async () => { - await cssServerNoNotifications.stop() -}) - before(done => { server = app.listen(port, done) }) @@ -131,25 +101,45 @@ beforeEach(async () => { password: person.password, provider: 'http://localhost:3456', }) -}) -beforeEach(async () => { - personNoNotifications = await createRandomAccount({ - solidServer: 'http://localhost:3457', + otherPerson = await createRandomAccount({ + solidServer: 'http://localhost:3456', }) - authenticatedFetchNoNotifications = await getAuthenticatedFetch({ - email: personNoNotifications.email, - password: personNoNotifications.password, - provider: 'http://localhost:3457', + otherAuthenticatedFetch = await getAuthenticatedFetch({ + email: otherPerson.email, + password: otherPerson.password, + provider: 'http://localhost:3456', }) }) +// Enable request interception. +beforeEach(async () => { + mockServer = setupServer( + // Describe network behavior with request handlers. + // Tip: move the handlers into their own module and + // import it across your browser and Node.js setups! + http.get('https://example.com/', (/*{ request, params, cookies }*/) => { + return HttpResponse.text(` + @prefix vcard: . + <#us> vcard:hasMember <${person.webId}> . + `) + }), + ) + mockServer.listen({ onUnhandledRequest() {} }) // quiet the unhandled request warnings +}) + +// Reset handlers so that each test could alter them +// without affecting other, unrelated tests. +afterEach(() => { + mockServer.resetHandlers() + mockServer.close() +}) + export { authenticatedFetch, - authenticatedFetchNoNotifications, cssServer, - cssServerNoNotifications, + otherAuthenticatedFetch, + otherPerson, person, - personNoNotifications, server, } diff --git a/yarn.lock b/yarn.lock index b7114f4..8d51d03 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14,6 +14,27 @@ dependencies: buffer "^6.0.3" +"@bundled-es-modules/cookie@^2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/cookie/-/cookie-2.0.0.tgz#c3b82703969a61cf6a46e959a012b2c257f6b164" + integrity sha512-Or6YHg/kamKHpxULAdSqhGqnWFneIXu1NKvvfBBzKGwpVsYuFIQ5aBPHDnnoR3ghW1nvSkALd+EF9iMtY7Vjxw== + dependencies: + cookie "^0.5.0" + +"@bundled-es-modules/js-levenshtein@^2.0.1": + version "2.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/js-levenshtein/-/js-levenshtein-2.0.1.tgz#b02bbbd546358ab77080a430f0911cfc2b3779c4" + integrity sha512-DERMS3yfbAljKsQc0U2wcqGKUWpdFjwqWuoMugEJlqBnKO180/n+4SR/J8MRDt1AN48X1ovgoD9KrdVXcaa3Rg== + dependencies: + js-levenshtein "^1.1.6" + +"@bundled-es-modules/statuses@^1.0.1": + version "1.0.1" + resolved "https://registry.yarnpkg.com/@bundled-es-modules/statuses/-/statuses-1.0.1.tgz#761d10f44e51a94902c4da48675b71a76cc98872" + integrity sha512-yn7BklA5acgcBr+7w064fGV+SGIFySjCKpqjcWgBAIfrAkY+4GQTJJHQMeT3V/sgz23VTEVV8TtOmkvJAhFVfg== + dependencies: + statuses "^2.0.1" + "@colors/colors@1.5.0": version "1.5.0" resolved "https://registry.yarnpkg.com/@colors/colors/-/colors-1.5.0.tgz#bb504579c1cae923e6576a4f5da43d25f97bdbd9" @@ -2535,6 +2556,23 @@ semver "^7.3.5" tar "^6.1.11" +"@mswjs/cookies@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-1.1.0.tgz#1528eb43630caf83a1d75d5332b30e75e9bb1b5b" + integrity sha512-0ZcCVQxifZmhwNBoQIrystCb+2sWBY2Zw8lpfJBPCHGCA/HWqehITeCRVIv4VMy8MPlaHo2w2pTHFV2pFfqKPw== + +"@mswjs/interceptors@^0.25.14": + version "0.25.14" + resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.25.14.tgz#70c1c9736b5c327337c0eb3da6d4895d7928049b" + integrity sha512-2dnIxl+obqIqjoPXTFldhe6pcdOrqiz+GcLaQQ6hmL02OldAF7nIC+rUgTWm+iF6lvmyCVhFFqbgbapNhR8eag== + dependencies: + "@open-draft/deferred-promise" "^2.2.0" + "@open-draft/logger" "^0.3.0" + "@open-draft/until" "^2.0.0" + is-node-process "^1.2.0" + outvariant "^1.2.1" + strict-event-emitter "^0.5.1" + "@nodelib/fs.scandir@2.1.5": version "2.1.5" resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz#7619c2eb21b25483f6d167548b4cfd5a7488c3d5" @@ -2572,6 +2610,24 @@ mkdirp "^1.0.4" rimraf "^3.0.2" +"@open-draft/deferred-promise@^2.2.0": + version "2.2.0" + resolved "https://registry.yarnpkg.com/@open-draft/deferred-promise/-/deferred-promise-2.2.0.tgz#4a822d10f6f0e316be4d67b4d4f8c9a124b073bd" + integrity sha512-CecwLWx3rhxVQF6V4bAgPS5t+So2sTbPgAzafKkVizyi7tlwpcFpdFqq+wqF2OwNBmqFuu6tOyouTuxgpMfzmA== + +"@open-draft/logger@^0.3.0": + version "0.3.0" + resolved "https://registry.yarnpkg.com/@open-draft/logger/-/logger-0.3.0.tgz#2b3ab1242b360aa0adb28b85f5d7da1c133a0954" + integrity sha512-X2g45fzhxH238HKO4xbSr7+wBS8Fvw6ixhTDuvLd5mqh6bJJCFAPwU9mPDxbcrRtfxv4u5IHCEH77BmxvXmmxQ== + dependencies: + is-node-process "^1.2.0" + outvariant "^1.4.0" + +"@open-draft/until@^2.0.0", "@open-draft/until@^2.1.0": + version "2.1.0" + resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-2.1.0.tgz#0acf32f470af2ceaf47f095cdecd40d68666efda" + integrity sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg== + "@pkgr/utils@^2.3.1": version "2.4.2" resolved "https://registry.yarnpkg.com/@pkgr/utils/-/utils-2.4.2.tgz#9e638bbe9a6a6f165580dc943f138fd3309a2cbc" @@ -2938,7 +2994,7 @@ "@types/qs" "*" "@types/serve-static" "*" -"@types/fs-extra@^11.0.2": +"@types/fs-extra@^11.0.2", "@types/fs-extra@^11.0.4": version "11.0.4" resolved "https://registry.yarnpkg.com/@types/fs-extra/-/fs-extra-11.0.4.tgz#e16a863bb8843fba8c5004362b5a73e17becca45" integrity sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ== @@ -2968,6 +3024,11 @@ dependencies: "@types/node" "*" +"@types/js-levenshtein@^1.1.1": + version "1.1.3" + resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.3.tgz#a6fd0bdc8255b274e5438e0bfb25f154492d1106" + integrity sha512-jd+Q+sD20Qfu9e2aEXogiO3vpOC1PYJOUdyN9gvs4Qrvkg4wF43L5OhqrPeokdv8TL0/mXoYfpkcoGZMNN2pkQ== + "@types/json-schema@^7.0.11": version "7.0.12" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.12.tgz#d70faba7039d5fca54c83c7dbab41051d2b6f6cb" @@ -2980,6 +3041,13 @@ dependencies: "@types/node" "*" +"@types/jsonwebtoken@^9.0.5": + version "9.0.5" + resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-9.0.5.tgz#0bd9b841c9e6c5a937c17656e2368f65da025588" + integrity sha512-VRLSGzik+Unrup6BsouBeHsf4d1hOEgYWTm/7Nmw1sXoN1+tRly/Gy/po3yeahnP4jfnQWWAhQAqcNfH7ngOkA== + dependencies: + "@types/node" "*" + "@types/keygrip@*": version "1.0.2" resolved "https://registry.yarnpkg.com/@types/keygrip/-/keygrip-1.0.2.tgz#513abfd256d7ad0bf1ee1873606317b33b1b2a72" @@ -3253,6 +3321,11 @@ dependencies: rdf-js "^4.0.2" +"@types/statuses@^2.0.1": + version "2.0.4" + resolved "https://registry.yarnpkg.com/@types/statuses/-/statuses-2.0.4.tgz#041143ba4a918e8f080f8b0ffbe3d4cb514e2315" + integrity sha512-eqNDvZsCNY49OAXB0Firg/Sc2BgoWsntsLUdybGFOhAfCD6QJ2n9HXUIHGqt5qjrxmMv4wS8WLAw43ZkKcJ8Pw== + "@types/tough-cookie@^4.0.5": version "4.0.5" resolved "https://registry.yarnpkg.com/@types/tough-cookie/-/tough-cookie-4.0.5.tgz#cb6e2a691b70cb177c6e3ae9c1d2e8b2ea8cd304" @@ -3516,6 +3589,13 @@ ansi-colors@4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== +ansi-escapes@^4.2.1: + version "4.3.2" + resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.2.tgz#6b2291d1db7d98b6521d5f1efa42d0f3a9feb65e" + integrity sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ== + dependencies: + type-fest "^0.21.3" + ansi-regex@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304" @@ -3654,6 +3734,15 @@ binary-extensions@^2.0.0: resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-2.2.0.tgz#75f502eeaf9ffde42fc98829645be4ea76bd9e2d" integrity sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA== +bl@^4.1.0: + version "4.1.0" + resolved "https://registry.yarnpkg.com/bl/-/bl-4.1.0.tgz#451535264182bec2fbbc83a62ab98cf11d9f7b3a" + integrity sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w== + dependencies: + buffer "^5.5.0" + inherits "^2.0.4" + readable-stream "^3.4.0" + body-parser@1.20.1: version "1.20.1" resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.1.tgz#b1812a8912c195cd371a3ee5e66faa2338a5c668" @@ -3716,6 +3805,19 @@ browser-stdout@1.3.1: resolved "https://registry.yarnpkg.com/browser-stdout/-/browser-stdout-1.3.1.tgz#baa559ee14ced73452229bad7326467c61fabd60" integrity sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw== +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 sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== + +buffer@^5.5.0: + version "5.7.1" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-5.7.1.tgz#ba62e7c13133053582197160851a8f648e99eed0" + integrity sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ== + dependencies: + base64-js "^1.3.1" + ieee754 "^1.1.13" + buffer@^6.0.3: version "6.0.3" resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" @@ -3839,7 +3941,7 @@ chai@^4.3.7: pathval "^1.1.1" type-detect "^4.0.5" -chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0: +chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0, chalk@^4.1.1, chalk@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.2.tgz#aac4e2b7734a740867aeb16bf02aad556a1e7a01" integrity sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA== @@ -3847,6 +3949,11 @@ chalk@^4.0.0, chalk@^4.0.2, chalk@^4.1.0: ansi-styles "^4.1.0" supports-color "^7.1.0" +chardet@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e" + integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA== + check-error@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/check-error/-/check-error-1.0.2.tgz#574d312edd88bb5dd8912e9286dd6c0aed4aac82" @@ -3877,7 +3984,7 @@ cheerio@^1.0.0-rc.12: parse5 "^7.0.0" parse5-htmlparser2-tree-adapter "^7.0.0" -chokidar@3.5.3: +chokidar@3.5.3, chokidar@^3.4.2: version "3.5.3" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-3.5.3.tgz#1cf37c8707b932bd1af1ae22c0432e2acd1903bd" integrity sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw== @@ -3902,6 +4009,23 @@ clean-stack@^2.0.0: resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b" integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A== +cli-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307" + integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw== + dependencies: + restore-cursor "^3.1.0" + +cli-spinners@^2.5.0: + version "2.9.2" + resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.9.2.tgz#1773a8f4b9c4d6ac31563df53b3fc1d79462fe41" + integrity sha512-ywqV+5MmyL4E7ybXgKys4DugZbX0FC6LnwrhjuykIjnK9k8OQacQ7axGKnjDXWNhns0xot3bZI5h55H8yo9cJg== + +cli-width@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" + integrity sha512-FxqpkPPwu1HjuN93Omfm4h8uIanXofW0RxVEW3k5RKx+mJJYSthzNhp32Kzxxy3YAEZ/Dc/EWN1vZRY0+kOhbw== + cliui@^7.0.2: version "7.0.4" resolved "https://registry.yarnpkg.com/cliui/-/cliui-7.0.4.tgz#a0265ee655476fc807aea9df3df8df7783808b4f" @@ -3920,6 +4044,11 @@ cliui@^8.0.1: strip-ansi "^6.0.1" wrap-ansi "^7.0.0" +clone@^1.0.2: + version "1.0.4" + resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" + integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== + clownface@^1.4.0: version "1.5.1" resolved "https://registry.yarnpkg.com/clownface/-/clownface-1.5.1.tgz#5471f462aa8a5945ad878305b832361214424759" @@ -4262,6 +4391,13 @@ default-browser@^4.0.0: execa "^7.1.1" titleize "^3.0.0" +defaults@^1.0.3: + version "1.0.4" + resolved "https://registry.yarnpkg.com/defaults/-/defaults-1.0.4.tgz#b0b02062c1e2aa62ff5d9528f0f98baa90978d7a" + integrity sha512-eFuaLoy/Rxalv2kr+lqMlUnrDWV+3j4pljOIJgLIhI058IQfWJ7vXhyEIHu+HtC738klGALYxOKDO0bQP3tg8A== + dependencies: + clone "^1.0.2" + defer-to-connect@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" @@ -4388,6 +4524,13 @@ dottie@^2.0.4: resolved "https://registry.yarnpkg.com/dottie/-/dottie-2.0.6.tgz#34564ebfc6ec5e5772272d466424ad5b696484d4" integrity sha512-iGCHkfUc5kFekGiqhe8B/mdaurD+lakO9txNnTvKtA6PISrw86LgqHvRzWYPyoE2Ph5aMIrCw9/uko6XHTKCwA== +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" + ee-first@1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d" @@ -4480,7 +4623,7 @@ escape-string-regexp@4.0.0, escape-string-regexp@^4.0.0: resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz#14ba83a5d373e3d311e5afca29cf5bfad965bf34" integrity sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA== -escape-string-regexp@~1.0.5: +escape-string-regexp@^1.0.5, escape-string-regexp@~1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4" integrity sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg== @@ -4710,6 +4853,15 @@ extend@~3.0.0: resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== +external-editor@^3.0.3: + version "3.1.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-3.1.0.tgz#cb03f740befae03ea4d283caed2741a83f335495" + integrity sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew== + dependencies: + chardet "^0.7.0" + iconv-lite "^0.4.24" + tmp "^0.0.33" + fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -4773,6 +4925,13 @@ fetch-sparql-endpoint@^4.0.0, fetch-sparql-endpoint@^4.1.0: sparqlxml-parse "^2.1.1" stream-to-string "^1.1.0" +figures@^3.0.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af" + integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg== + dependencies: + escape-string-regexp "^1.0.5" + file-entry-cache@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/file-entry-cache/-/file-entry-cache-6.0.1.tgz#211b2dd9659cb0394b073e7323ac3c933d522027" @@ -4871,6 +5030,15 @@ fs-extra@^11.1.1: jsonfile "^6.0.1" universalify "^2.0.0" +fs-extra@^11.2.0: + version "11.2.0" + resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-11.2.0.tgz#e70e17dfad64232287d01929399e0ea7c86b0e5b" + integrity sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw== + dependencies: + graceful-fs "^4.2.0" + jsonfile "^6.0.1" + universalify "^2.0.0" + fs-minipass@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb" @@ -5053,6 +5221,11 @@ graphql@^15.5.2: resolved "https://registry.yarnpkg.com/graphql/-/graphql-15.8.0.tgz#33410e96b012fa3bdb1091cc99a94769db212b38" integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw== +graphql@^16.8.1: + version "16.8.1" + resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.1.tgz#1930a965bef1170603702acdb68aedd3f3cf6f07" + integrity sha512-59LZHPdGZVh695Ud9lRzPBVTtlX9ZCV150Er2W43ro37wVof0ctenSaskPPjN7lVTIN8mSZt8PHUNKZuNQUuxw== + handlebars@^4.7.8: version "4.7.8" resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.7.8.tgz#41c42c18b1be2365439188c77c6afae71c0cd9e9" @@ -5112,6 +5285,11 @@ he@1.2.0: resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" integrity sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw== +headers-polyfill@^4.0.1: + version "4.0.2" + resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-4.0.2.tgz#9115a76eee3ce8fbf95b6e3c6bf82d936785b44a" + integrity sha512-EWGTfnTqAO2L/j5HZgoM/3z82L7necsJ0pO9Tp0X1wil3PDLrkypTBRgVO2ExehEEvUycejZD3FuRaXpZZc3kw== + helmet@^6.0.1: version "6.2.0" resolved "https://registry.yarnpkg.com/helmet/-/helmet-6.2.0.tgz#c29d62014be4c70b8ef092c9c5e54c8c26b8e16e" @@ -5245,7 +5423,7 @@ humanize-ms@^1.2.1: dependencies: ms "^2.0.0" -iconv-lite@0.4.24: +iconv-lite@0.4.24, iconv-lite@^0.4.24: version "0.4.24" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== @@ -5266,7 +5444,7 @@ iconv-lite@0.6.3, iconv-lite@^0.6.2: dependencies: safer-buffer ">= 2.1.2 < 3.0.0" -ieee754@^1.2.1: +ieee754@^1.1.13, ieee754@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/ieee754/-/ieee754-1.2.1.tgz#8eb7a10a63fff25d15a57b001586d177d1b0d352" integrity sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA== @@ -5322,7 +5500,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.4, inherits@^2.0.3: +inherits@2, inherits@2.0.4, inherits@^2.0.3, inherits@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -5332,6 +5510,27 @@ inherits@2.0.3: resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" integrity sha512-x00IRNXNy63jwGkJmzPigoySHbaqpNuzKbBOmzK+g2OdZpQ9w+sxCN+VSB3ja7IAge2OP2qpfxTjeNcyjmW1uw== +inquirer@^8.2.0: + version "8.2.6" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.6.tgz#733b74888195d8d400a67ac332011b5fae5ea562" + integrity sha512-M1WuAmb7pn9zdFRtQYk26ZBoY043Sse0wVDdk4Bppr+JOXyQYybdtvK+l9wUibhtjdjvtoiNy8tk+EgsYIUqKg== + dependencies: + ansi-escapes "^4.2.1" + chalk "^4.1.1" + cli-cursor "^3.1.0" + cli-width "^3.0.0" + external-editor "^3.0.3" + figures "^3.0.0" + lodash "^4.17.21" + mute-stream "0.0.8" + ora "^5.4.1" + run-async "^2.4.0" + rxjs "^7.5.5" + string-width "^4.1.0" + strip-ansi "^6.0.0" + through "^2.3.6" + wrap-ansi "^6.0.1" + ioredis@^5.3.2: version "5.3.2" resolved "https://registry.yarnpkg.com/ioredis/-/ioredis-5.3.2.tgz#9139f596f62fc9c72d873353ac5395bcf05709f7" @@ -5415,11 +5614,21 @@ is-inside-container@^1.0.0: dependencies: is-docker "^3.0.0" +is-interactive@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/is-interactive/-/is-interactive-1.0.0.tgz#cea6e6ae5c870a7b0a0004070b7b587e0252912e" + integrity sha512-2HvIEKRoqS62guEC+qBjpvRubdX910WCMuJTZ+I9yvqKU2/12eSL549HMwtabb4oupdj2sMP50k+XJfB/8JE6w== + is-lambda@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-lambda/-/is-lambda-1.0.1.tgz#3d9877899e6a53efc0160504cde15f82e6f061d5" integrity sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ== +is-node-process@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.2.0.tgz#ea02a1b90ddb3934a19aea414e88edef7e11d134" + integrity sha512-Vg4o6/fqPxIjtxgUH5QLJhwZ7gW5diGCVlXpuUfELC62CuxM1iHcRe51f2W1FDy04Ai4KJkagKjx3XaqyfRKXw== + is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -5502,6 +5711,11 @@ jose@^5.1.3: resolved "https://registry.yarnpkg.com/jose/-/jose-5.2.0.tgz#d0ffd7f7e31253f633eefb190a930cd14a916995" integrity sha512-oW3PCnvyrcm1HMvGTzqjxxfnEs9EoFOFWi2HsEGhlFVOXxTE3K9GKWVMFoFw06yPUqwpvEWic1BmtUZBI/tIjw== +js-levenshtein@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/js-levenshtein/-/js-levenshtein-1.1.6.tgz#c6cee58eb3550372df8deb85fad5ce66ce01d59d" + integrity sha512-X2BB11YZtrRqY4EnQcLX5Rh373zbK4alC1FW7D7MBhL2gtcC17cTnr6DmfHZeS0s2rTHjUTMMHfG7gO8SSdw+g== + js-yaml@4.1.0, js-yaml@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-4.1.0.tgz#c1fb65f8f5017901cdd2c951864ba18458a10602" @@ -5631,11 +5845,44 @@ jsonld-streaming-serializer@^2.1.0: jsonld-context-parser "^2.0.0" readable-stream "^4.0.0" +jsonwebtoken@^9.0.2: + version "9.0.2" + resolved "https://registry.yarnpkg.com/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz#65ff91f4abef1784697d40952bb1998c504caaf3" + integrity sha512-PRp66vJ865SSqOlgqS8hujT5U4AOgMfhrwYIuIhfKaoSCZcirrmASQr8CX7cUg+RMih+hgznrjp99o+W4pJLHQ== + 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 "^7.5.4" + just-extend@^4.0.2: version "4.2.1" resolved "https://registry.yarnpkg.com/just-extend/-/just-extend-4.2.1.tgz#ef5e589afb61e5d66b24eca749409a8939a8c744" integrity sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg== +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" + keygrip@~1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" @@ -5746,16 +5993,51 @@ lodash.get@^4.4.2: resolved "https://registry.yarnpkg.com/lodash.get/-/lodash.get-4.4.2.tgz#2d177f652fa31e939b4438d5341499dfa3825e99" integrity sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ== +lodash.includes@^4.3.0: + version "4.3.0" + resolved "https://registry.yarnpkg.com/lodash.includes/-/lodash.includes-4.3.0.tgz#60bb98a87cb923c68ca1e51325483314849f553f" + integrity sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w== + lodash.isarguments@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/lodash.isarguments/-/lodash.isarguments-3.1.0.tgz#2f573d85c6a24289ff00663b491c1d338ff3458a" integrity sha512-chi4NHZlZqZD18a0imDHnZPrDeBbTtVN7GXMwuGdRH9qotxAjYs3aVLKc7zNOG9eddR5Ksd8rvFEBc9SsggPpg== +lodash.isboolean@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz#6c2e171db2a257cd96802fd43b01b20d5f5870f6" + integrity sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg== + +lodash.isinteger@^4.0.4: + version "4.0.4" + resolved "https://registry.yarnpkg.com/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz#619c0af3d03f8b04c31f5882840b77b11cd68343" + integrity sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA== + +lodash.isnumber@^3.0.3: + version "3.0.3" + resolved "https://registry.yarnpkg.com/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz#3ce76810c5928d03352301ac287317f11c0b1ffc" + integrity sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw== + +lodash.isplainobject@^4.0.6: + version "4.0.6" + resolved "https://registry.yarnpkg.com/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz#7c526a52d89b45c45cc690b88163be0497f550cb" + integrity sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA== + +lodash.isstring@^4.0.1: + version "4.0.1" + resolved "https://registry.yarnpkg.com/lodash.isstring/-/lodash.isstring-4.0.1.tgz#d527dfb5456eca7cc9bb95d5daeaf88ba54a5451" + integrity sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw== + lodash.merge@^4.6.2: version "4.6.2" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" integrity sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ== +lodash.once@^4.0.0: + version "4.1.1" + resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" + integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== + lodash.orderby@^4.6.0: version "4.6.0" resolved "https://registry.yarnpkg.com/lodash.orderby/-/lodash.orderby-4.6.0.tgz#e697f04ce5d78522f54d9338b32b81a3393e4eb3" @@ -5766,7 +6048,7 @@ lodash@^4.17.21: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.21.tgz#679591c564c3bffaae8454cf0b3df370c3d6911c" integrity sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg== -log-symbols@4.1.0: +log-symbols@4.1.0, log-symbols@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-4.1.0.tgz#3fbdbb95b4683ac9fc785111e792e558d4abd503" integrity sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg== @@ -6105,6 +6387,38 @@ ms@2.1.3, ms@^2.0.0, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +msw@^2.0.14: + version "2.0.14" + resolved "https://registry.yarnpkg.com/msw/-/msw-2.0.14.tgz#1fdfcad215e3c2d2fe1647bce14420c53203abbc" + integrity sha512-RcKx/JM/IOvjBjOA+cfNzLkSoMv408Tf+5TdgvzitM8/cPeN68004wBLXvbc2Jvwm1Sj4h8azR/WdO1wJg9iSw== + dependencies: + "@bundled-es-modules/cookie" "^2.0.0" + "@bundled-es-modules/js-levenshtein" "^2.0.1" + "@bundled-es-modules/statuses" "^1.0.1" + "@mswjs/cookies" "^1.1.0" + "@mswjs/interceptors" "^0.25.14" + "@open-draft/until" "^2.1.0" + "@types/cookie" "^0.4.1" + "@types/js-levenshtein" "^1.1.1" + "@types/statuses" "^2.0.1" + chalk "^4.1.2" + chokidar "^3.4.2" + graphql "^16.8.1" + headers-polyfill "^4.0.1" + inquirer "^8.2.0" + is-node-process "^1.2.0" + js-levenshtein "^1.1.6" + outvariant "^1.4.0" + path-to-regexp "^6.2.0" + strict-event-emitter "^0.5.0" + type-fest "^2.19.0" + yargs "^17.3.1" + +mute-stream@0.0.8: + version "0.0.8" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.8.tgz#1630c42b2251ff81e2a283de96a5497ea92e5e0d" + integrity sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA== + n3@^1.16.2, n3@^1.16.3, n3@^1.17.0, n3@^1.6.3: version "1.17.0" resolved "https://registry.yarnpkg.com/n3/-/n3-1.17.0.tgz#224d87e235e91e2d3e3fea04043ed34b6524a8ac" @@ -6343,7 +6657,7 @@ one-time@^1.0.0: dependencies: fn.name "1.x.x" -onetime@^5.1.2: +onetime@^5.1.0, onetime@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.2.tgz#d0e96ebb56b07476df1dd9c4806e5237985ca45e" integrity sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg== @@ -6394,6 +6708,31 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +ora@^5.4.1: + version "5.4.1" + resolved "https://registry.yarnpkg.com/ora/-/ora-5.4.1.tgz#1b2678426af4ac4a509008e5e4ac9e9959db9e18" + integrity sha512-5b6Y85tPxZZ7QytO+BQzysW31HJku27cRIlkbAXaNx+BdcVi+LlRFmVXzeF6a7JCwJpyw5c4b+YSVImQIrBpuQ== + dependencies: + bl "^4.1.0" + chalk "^4.1.0" + cli-cursor "^3.1.0" + cli-spinners "^2.5.0" + is-interactive "^1.0.0" + is-unicode-supported "^0.1.0" + log-symbols "^4.1.0" + strip-ansi "^6.0.0" + wcwidth "^1.0.1" + +os-tmpdir@~1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" + integrity sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g== + +outvariant@^1.2.1, outvariant@^1.4.0: + version "1.4.2" + resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.4.2.tgz#f54f19240eeb7f15b28263d5147405752d8e2066" + integrity sha512-Ou3dJ6bA/UJ5GVHxah4LnqDwZRwAmWxrG3wtrHrbGnP4RnLCtA64A4F+ae7Y8ww660JaddSoArUR5HjipWSHAQ== + p-cancelable@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-3.0.0.tgz#63826694b54d61ca1c20ebcb6d3ecf5e14cd8050" @@ -6491,7 +6830,7 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-to-regexp@^6.2.1: +path-to-regexp@^6.2.0, path-to-regexp@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.1.tgz#d54934d6798eb9e5ef14e7af7962c945906918e5" integrity sha512-JLyh7xT1kizaEvcaXOQwOc2/Yhw6KZOvPf1S8401UyLk86CU79LN3vl7ztXGm/pZ+YjoyAJ4rxmHwbkBXJX+yw== @@ -7026,6 +7365,14 @@ responselike@^3.0.0: dependencies: lowercase-keys "^3.0.0" +restore-cursor@^3.1.0: + version "3.1.0" + resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e" + integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA== + dependencies: + onetime "^5.1.0" + signal-exit "^3.0.2" + retry-as-promised@^7.0.4: version "7.0.4" resolved "https://registry.yarnpkg.com/retry-as-promised/-/retry-as-promised-7.0.4.tgz#9df73adaeea08cb2948b9d34990549dc13d800a2" @@ -7055,6 +7402,11 @@ run-applescript@^5.0.0: dependencies: execa "^5.0.0" +run-async@^2.4.0: + version "2.4.1" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.4.1.tgz#8440eccf99ea3e70bd409d49aab88e10c189a455" + integrity sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ== + run-parallel@^1.1.9: version "1.2.0" resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.2.0.tgz#66d1368da7bdf921eb9d95bd1a9229e7f21a43ee" @@ -7062,12 +7414,19 @@ run-parallel@^1.1.9: dependencies: queue-microtask "^1.2.2" +rxjs@^7.5.5: + version "7.8.1" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" + integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== + dependencies: + tslib "^2.1.0" + safe-buffer@5.1.2, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" integrity sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g== -safe-buffer@5.2.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: +safe-buffer@5.2.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@~5.2.0: version "5.2.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.2.1.tgz#1eaf9fa9bdb1fdd4ec75f58f9cdb4e6b7827eec6" integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== @@ -7094,7 +7453,7 @@ semver@^6.0.0: resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4" integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA== -semver@^7.3.2, semver@^7.3.5, semver@^7.5.0, semver@^7.5.1: +semver@^7.3.2, semver@^7.3.5, semver@^7.5.0, semver@^7.5.1, semver@^7.5.4: version "7.5.4" resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e" integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA== @@ -7410,7 +7769,7 @@ standard-as-callback@^2.1.0: resolved "https://registry.yarnpkg.com/standard-as-callback/-/standard-as-callback-2.1.0.tgz#8953fc05359868a77b5b9739a665c5977bb7df45" integrity sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A== -statuses@2.0.1: +statuses@2.0.1, statuses@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63" integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ== @@ -7442,6 +7801,11 @@ streamsearch@^1.1.0: resolved "https://registry.yarnpkg.com/streamsearch/-/streamsearch-1.1.0.tgz#404dd1e2247ca94af554e841a8ef0eaa238da764" integrity sha512-Mcc5wHehp9aXz1ax6bZUyY5afg9u2rv5cqQI3mRrYkGC8rW2hM02jWuwjtL++LS5qinSyhj2QfLyNsuc+VsExg== +strict-event-emitter@^0.5.0, strict-event-emitter@^0.5.1: + version "0.5.1" + resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.5.1.tgz#1602ece81c51574ca39c6815e09f1a3e8550bd93" + integrity sha512-vMgjE/GGEPEFnhFub6pa4FmJBRBVOLpIII2hvCZ8Kzb7K0hlHo7mQv6xYrBvCL2LtAIBwFUK8wvuJgTVSQ5MFQ== + "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -7539,6 +7903,11 @@ text-table@^0.2.0: resolved "https://registry.yarnpkg.com/text-table/-/text-table-0.2.0.tgz#7f5ee823ae805207c00af2df4a84ec3fcfa570b4" integrity sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw== +through@^2.3.6: + version "2.3.8" + resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5" + integrity sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg== + tiny-case@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-case/-/tiny-case-1.0.3.tgz#d980d66bc72b5d5a9ca86fb7c9ffdb9c898ddd03" @@ -7549,6 +7918,13 @@ titleize@^3.0.0: resolved "https://registry.yarnpkg.com/titleize/-/titleize-3.0.0.tgz#71c12eb7fdd2558aa8a44b0be83b8a76694acd53" integrity sha512-KxVu8EYHDPBdUYdKZdKtU2aj2XfEx9AfjXxE/Aj0vT06w2icA09Vus1rh6eSu1y01akYg6BjIK/hxyLJINoMLQ== +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + to-regex-range@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/to-regex-range/-/to-regex-range-5.0.1.tgz#1648c44aae7c8d988a326018ed72f5b4dd0392e4" @@ -7627,6 +8003,11 @@ ts-node@^10.9.1: v8-compile-cache-lib "^3.0.1" yn "3.1.1" +tslib@^2.1.0: + version "2.6.2" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tslib@^2.5.0, tslib@^2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3" @@ -7654,6 +8035,11 @@ type-fest@^0.20.2: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.20.2.tgz#1bf207f4b28f91583666cb5fbd327887301cd5f4" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== +type-fest@^0.21.3: + version "0.21.3" + resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.21.3.tgz#d260a24b0198436e133fa26a524a6d65fa3b2e37" + integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== + type-fest@^2.19.0: version "2.19.0" resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" @@ -7815,6 +8201,13 @@ w3c-xmlserializer@^3.0.0: dependencies: xml-name-validator "^4.0.0" +wcwidth@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/wcwidth/-/wcwidth-1.0.1.tgz#f0b0dcf915bc5ff1528afadb2c0e17b532da2fe8" + integrity sha512-XHPEwS0q6TaxcvG85+8EYkbiCux2XtWG2mkc47Ng2A77BQu9+DqIOJldST4HgPkuea7dvKSj5VgX3P1d4rW8Tg== + dependencies: + defaults "^1.0.3" + web-streams-ponyfill@^1.4.2: version "1.4.2" resolved "https://registry.yarnpkg.com/web-streams-ponyfill/-/web-streams-ponyfill-1.4.2.tgz#0ae863cc5f7493903679f16b08cbf14d432b62f4" @@ -7940,6 +8333,15 @@ workerpool@6.2.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.2.1.tgz#46fc150c17d826b86a008e5a4508656777e9c343" integrity sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw== +wrap-ansi@^6.0.1: + version "6.2.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-6.2.0.tgz#e9393ba07102e6c91a3b221478f0257cd2856e53" + integrity sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" @@ -8032,7 +8434,7 @@ yargs@16.2.0: y18n "^5.0.5" yargs-parser "^20.2.2" -yargs@^17.7.2: +yargs@^17.3.1, yargs@^17.7.2: version "17.7.2" resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.7.2.tgz#991df39aca675a192b816e1e0363f9d75d2aa269" integrity sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==