From 53d42de62cae311e28decf50591e521128c9bfcb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20=C3=96brink?= Date: Mon, 21 Dec 2020 15:59:49 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20Loads=20schedule=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 Loads schedule ✅ Closes: #13 * feat: 🎸 Made running code from node easier * feat: 🎸 News ✅ Closes: #8 * Accurate method calls and some parsing * fix: 🎸 Calendar and classmates are now called and parsed correctly --- .gitignore | 2 - README.md | 8 ++ lib/children.ts | 18 ++- lib/h2m.d.ts | 1 + lib/index.ts | 24 +++- lib/news.ts | 18 +++ lib/parse.test.ts | 278 +++++++++++++++++++++++++++++++++++++++++++--- lib/parse.ts | 71 ++++++++++-- lib/types.ts | 51 +++++---- package.json | 5 +- run.js | 63 +++++++++++ yarn.lock | 165 ++++++++++++++++++++++++++- 12 files changed, 646 insertions(+), 58 deletions(-) create mode 100644 lib/h2m.d.ts create mode 100644 lib/news.ts create mode 100644 run.js diff --git a/.gitignore b/.gitignore index f64e8436..67045665 100644 --- a/.gitignore +++ b/.gitignore @@ -102,5 +102,3 @@ dist # TernJS port file .tern-port - -run.js \ No newline at end of file diff --git a/README.md b/README.md index 3cb14b3e..bd45c90f 100644 --- a/README.md +++ b/README.md @@ -81,3 +81,11 @@ const sessionCookie = "some value"; api.setSessionCookie(sessionCookie); // will trigger `on('login')` event and set `.isLoggedIn = true` ``` + +## Try it out + +1. Clone and enter repo: `git clone git@github.com:kolplattformen/embedded-api.git && cd embedded-api` +2. Install dependencies: `yarn` +3. Build package: `yarn build` +4. Run example: `node run [your personal number]` +5. Sign in with mobile BankID diff --git a/lib/children.ts b/lib/children.ts index e87032d8..5045e577 100644 --- a/lib/children.ts +++ b/lib/children.ts @@ -1,8 +1,11 @@ +import { Moment } from 'moment' import routes from './routes' import { CalendarItem, Child, Classmate, Fetch, RequestInit, } from './types' -import { etjanst, child, calendarItem } from './parse' +import { + etjanst, child, calendarItem, +} from './parse' export const list = (fetch: Fetch, init?: RequestInit) => async (): Promise => { const url = routes.children @@ -11,16 +14,25 @@ export const list = (fetch: Fetch, init?: RequestInit) => async (): Promise async (childId: string): Promise => { +export const calendar = (fetch: Fetch, init?: RequestInit) => async (childId: string): Promise => { const url = routes.calendar(childId) const response = await fetch(url, init) const data = await response.json() return etjanst(data).map(calendarItem) } -export const classmates = (fetch: Fetch, init?:RequestInit) => async (childId: string): Promise => { +export const classmates = (fetch: Fetch, init?: RequestInit) => async (childId: string): Promise => { const url = routes.classmates(childId) const response = await fetch(url, init) const data = await response.json() return etjanst(data) } + +export const schedule = (fetch: Fetch, init?: RequestInit) => ( + async (childId: string, from: Moment, to: moment.Moment): Promise => { + const url = routes.schedule(childId, from.format('YYYY-MM-DD'), to.format('YYYY-MM-DD')) + const response = await fetch(url, init) + const data = await response.json() + return etjanst(data) + } +) diff --git a/lib/h2m.d.ts b/lib/h2m.d.ts new file mode 100644 index 00000000..0334e203 --- /dev/null +++ b/lib/h2m.d.ts @@ -0,0 +1 @@ +declare module 'h2m' diff --git a/lib/index.ts b/lib/index.ts index d44f3136..263133c5 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -1,3 +1,4 @@ +import { Moment } from 'moment' import { EventEmitter } from 'events' import { checkStatus, getSessionCookie, login, LoginStatus, @@ -5,7 +6,10 @@ import { import { CalendarItem, Child, Classmate, Fetch, RequestInit, } from './types' -import { calendar, classmates, list } from './children' +import { + calendar, classmates, list, schedule, +} from './children' +import { news, News } from './news' interface AsyncishFunction { (): void | Promise } @@ -50,13 +54,23 @@ export class Api extends EventEmitter { return data } - async getCalendar(childId: string): Promise { - const data = await calendar(this.fetch, this.session)(childId) + async getCalendar(child: Child): Promise { + const data = await calendar(this.fetch, this.session)(child.id) return data } - async getClassmates(childId: string): Promise { - const data = await classmates(this.fetch, this.session)(childId) + async getClassmates(child: Child): Promise { + const data = await classmates(this.fetch, this.session)(child.sdsId) + return data + } + + async getSchedule(child: Child, from: Moment, to: Moment): Promise { + const data = await schedule(this.fetch, this.session)(child.sdsId, from, to) + return data + } + + async getNews(child: Child): Promise { + const data = await news(this.fetch, this.session)(child.id) return data } diff --git a/lib/news.ts b/lib/news.ts new file mode 100644 index 00000000..74a865f5 --- /dev/null +++ b/lib/news.ts @@ -0,0 +1,18 @@ +import { etjanst, newsItem } from './parse' +import routes from './routes' +import { Fetch, NewsItem, RequestInit } from './types' + +export class News { + public items: NewsItem[] + + constructor(items: NewsItem[]) { + this.items = items + } +} + +export const news = (fetch: Fetch, init?: RequestInit) => async (childId: string): Promise => { + const url = routes.news(childId) + const response = await fetch(url, init) + const data = await response.json() + return new News(etjanst(data).newsItems.map(newsItem)) +} diff --git a/lib/parse.test.ts b/lib/parse.test.ts index 222ab9f2..a5bfc07f 100644 --- a/lib/parse.test.ts +++ b/lib/parse.test.ts @@ -1,8 +1,12 @@ -import { etjanst, EtjanstResponse } from "./parse" +import * as moment from 'moment' +import { + etjanst, newsItem, EtjanstResponse, child, calendarItem, classmate, scheduleItem, +} from "./parse" +import { NewsItem } from "./types" describe('parse', () => { + let response: EtjanstResponse describe('etjanst', () => { - let response: EtjanstResponse beforeEach(() => { response = { Success: true, @@ -10,20 +14,6 @@ describe('parse', () => { Data: [ { Name: 'Some name', - Id: '42C3997E-D772-423F-9290-6FEEB3CB2DA7', - SDSId: '786E3393-F044-4660-9105-B444DEB289AA', - Status: 'GR', - UserType: 'Student', - SchoolId: 'DE2E1293-0F40-4B91-9D91-1E99355DC257', - SchoolName: null, - GroupId: null, - GroupName: null, - Classes: 'VHsidan_0495CABC-77DB-41D7-824B-8B4D63E50D15;Section_AD1BB3B2-C1EE-4DFE-8209-CB6D42CE23D7;Section_0E67D0BF-594C-4C1B-9291-E753926DCD40;VHsidan_1C94EC54-9798-401C-B973-2454246D95DA', - isSameSDSId: false, - ResultUnitId: null, - ResultUnitName: null, - UnitId: null, - UnitName: null } ] } @@ -40,5 +30,261 @@ describe('parse', () => { const parsed = etjanst(response) expect(parsed[0].name).toEqual(response.Data[0].Name) }) + describe('children', () => { + beforeEach(() => { + response = { + Success: true, + Error: null, + Data: [ + { + Name: 'Some name', + Id: '42C3997E-D772-423F-9290-6FEEB3CB2DA7', + SDSId: '786E3393-F044-4660-9105-B444DEB289AA', + Status: 'GR', + UserType: 'Student', + SchoolId: 'DE2E1293-0F40-4B91-9D91-1E99355DC257', + SchoolName: null, + GroupId: null, + GroupName: null, + Classes: 'VHsidan_0495CABC-77DB-41D7-824B-8B4D63E50D15;Section_AD1BB3B2-C1EE-4DFE-8209-CB6D42CE23D7;Section_0E67D0BF-594C-4C1B-9291-E753926DCD40;VHsidan_1C94EC54-9798-401C-B973-2454246D95DA', + isSameSDSId: false, + ResultUnitId: null, + ResultUnitName: null, + UnitId: null, + UnitName: null + } + ] + } + }) + it('parses children correctly', () => { + expect(etjanst(response).map(child)).toEqual([{ + name: 'Some name', + id: '42C3997E-D772-423F-9290-6FEEB3CB2DA7', + sdsId: '786E3393-F044-4660-9105-B444DEB289AA', + schoolId: 'DE2E1293-0F40-4B91-9D91-1E99355DC257', + status: 'GR', + }]) + }) + }) + describe('calendar', () => { + beforeEach(() => { + response = { + Success: true, + Error: null, + Data: [ + { + Title: 'Jullov', + Id: 29, + Description: 'hello', + Location: null, + EventDate: '2020-12-21', + EventDateTime: '09:00', + LongEventDateTime: '2020-12-21 09:00', + EndDate: '2021-01-08', + EndDateTime: '10:00', + LongEndDateTime: '2021-01-08 10:00', + EventDateDayNumber: '21', + EventDateMonthName: 'dec', + EventDateMonthFullName: 'december', + FullDateDescription: '2020-12-21 09:00 - 2021-01-08 10:00', + IsSameDay: false, + AllDayEvent: false, + ListId: null, + Mentor: null, + }, + ] + } + }) + it('parses calendar correctly', () => { + expect(etjanst(response).map(calendarItem)).toEqual([{ + id: 29, + location: null, + title: 'Jullov', + description: 'hello', + startDate: moment(new Date('2020-12-21 09:00')), + endDate: moment(new Date('2021-01-08 10:00')), + allDay: false, + }]) + }) + }) + describe('classmates', () => { + beforeEach(() => { + response = { + Success: true, + Error: null, + Data: [ + { + ID: 0, + BATCH: null, + SIS_ID: '22F0CFC7-09C7-45DC-9388-AE9A9EA1356B', + USERNAME: null, + SCHOOL_SIS_ID: null, + EMAILADDRESS: null, + STATUS: null, + ERRORCODE: 0, + PRIMARY_SCHOOL_SIS_ID: null, + MENTOR_SIS_ID: null, + FIRSTNAME: 'Bo', + LASTNAME: 'Burström', + ACTIVE: false, + Guardians: [ + { + SOCIALNUMBER: null, + DISPLAYNAME: null, + FIRSTNAME: 'Allan', + LASTNAME: 'Fridell', + ADDRESS: 'Hult södregård', + CITY: null, + POCODE: null, + TELHOME: null, + TELMOBILE: '0690-6346216', + EMAILHOME: 'allan.fridell@mailinater.com', + SECTION_NAME: null, + SECTION_ID: null, + TERM_STARTDATE: null, + TERM_ENDDATE: null, + GROUPTYPE: null, + STUDENT_FIRSTNAME: null, + STUDENT_LASTNAME: null, + STUDENT_ID: null + } + ], + ClassName: '7C', + ClassId: 'B2BF465B-581B-43AC-9CA7-F11BB0ED4646' + }, + ] + } + }) + it('parses class mates correctly', () => { + expect(etjanst(response).map(classmate)).toEqual([{ + sisId: '22F0CFC7-09C7-45DC-9388-AE9A9EA1356B', + firstname: 'Bo', + lastname: 'Burström', + className: '7C', + guardians: [ + { + firstname: 'Allan', + lastname: 'Fridell', + address: 'Hult södregård', + mobile: '0690-6346216', + email: 'allan.fridell@mailinater.com', + } + ] + }]) + }) + }) + describe('schedule', () => { + beforeEach(() => { + response = { + "Success": true, + "Error": null, + "Data": [ + { + "Title": "Canceled: Julavslutning 8C", + "Id": 0, + "Description": "Nåt kul", + "Location": "Lakritskolan", + "EventDate": "2020-12-14", + "EventDateTime": "14:10", + "LongEventDateTime": "2020-12-14 14:10", + "EndDate": "2020-12-14", + "EndDateTime": "14:40", + "LongEndDateTime": "2020-12-14 14:40", + "EventDateDayNumber": "14", + "EventDateMonthName": "dec", + "EventDateMonthFullName": "december", + "FullDateDescription": "2020-12-14 14:10 - 2020-12-14 14:40", + "IsSameDay": true, + "AllDayEvent": false, + "ListId": null, + "Mentor": null + } + ] + } + }) + it('parses schedule correctly', () => { + expect(etjanst(response).map(scheduleItem)).toEqual([{ + title: 'Canceled: Julavslutning 8C', + description: 'Nåt kul', + location: 'Lakritskolan', + startDate: moment(new Date('2020-12-14 14:10')), + endDate: moment(new Date('2020-12-14 14:40')), + oneDayEvent: true, + allDayEvent: false, + }]) + }) + }) + describe('news', () => { + beforeEach(() => { + response = { + Success: true, + Error: null, + Data: { + CurrentChild: null, + NewsItems: [ + { + NewsId: 'news id', + SiteId: 'elevstockholm.sharepoint.com,27892ACC-BA2E-4DEC-97B8-25F7098C3BF6,A239466A-9A52-42FF-8A3F-D94C342F2700', + NewsListId: '3EC323A1-EA16-4D24-84C8-DAA49E76F9F4', + NewsItemId: 'elevstockholm.sharepoint.com,27892ACC-BA2E-4DEC-97B8-25F7098C3BF6,A239466A-9A52-42FF-8A3F-D94C342F2700_99', + Header: 'Problemet med att se betyg i bild, slöjd och teknik löst!', + PublicationDate: '/Date(1608304542000)/', + PubDateSE: '18 december 2020 16:15', + ModifiedDate: '/Date(1608304680000)/', + ModDateSE: '18 december 2020 16:18', + Source: 'Livets hårda skolklasser', + Preamble: 'Hej,Nu är problemet löst! Alla betyg syns som de ska.God jul!...', + BannerImageUrl: 'A703552D-DBF3-45B0-8E67-6E062105A0C5.jpeg', + BannerImageGuid: 'A703552D-DBF3-45B0-8E67-6E062105A0C5', + BannerImageListId: 'FFBE49E9-BDE1-4C75-BA0E-D98D4E2FCF21', + Body: '

Hej,

Nu är problemet löst! Alla betyg syns som de ska. 

God jul!

', + BodyNoHtml: null, + AuthorDisplayName: 'Eva-Lotta Rönnberg', + altText: 'Nyhetsbild. Bildtext ej tillgänglig.' + }, + ], + ViewGlobalTranslations: {}, + ViewLocalTranslations: {}, + Children: null, + Status: null, + GlobalTranslationIds: [ + 'InformationalHeader', + 'ContactUsMessageLabel', + 'Send', + 'RequiredFieldMessageInfo', + 'Sex', + 'Male', + 'Female', + 'SSN', + 'FirstName', + 'LastName', + 'Email', + 'Zip', + 'Address', + 'ValidationRequiredFieldMessage', + 'ValidationErrorMessage' + ], + LocalTranslationIds: ['IndexPageHeading1'] + } + } + }) + it('parses news items (except body) correctly', () => { + const [item]: [NewsItem] = etjanst(response).newsItems.map(newsItem) + + expect(item.id).toEqual('news id') + expect(item.header).toEqual('Problemet med att se betyg i bild, slöjd och teknik löst!') + expect(item.imageUrl).toEqual('A703552D-DBF3-45B0-8E67-6E062105A0C5.jpeg') + expect(item.intro).toEqual('Hej,Nu är problemet löst! Alla betyg syns som de ska.God jul!...') + expect(item.modified).toEqual(moment(new Date('18 december 2020 16:18'))) + expect(item.published).toEqual(moment(new Date('18 december 2020 16:15'))) + }) + it('parses body correctly', () => { + const [item]: [NewsItem] = etjanst(response).newsItems.map(newsItem) + + const expected = 'Hej, Nu är problemet löst! Alla betyg syns som de ska. God jul!' + const trimmed = (item.body || '').split('\n').map(t => t.trim()).join(' ') + expect(trimmed).toEqual(expected) + }) + }) }) }) diff --git a/lib/parse.ts b/lib/parse.ts index e26907c6..35d85a24 100644 --- a/lib/parse.ts +++ b/lib/parse.ts @@ -1,14 +1,19 @@ -import { CalendarItem, Child } from './types' +import * as moment from 'moment' +import * as h2m from 'h2m' +import { htmlDecode } from 'js-htmlencode' +import { + CalendarItem, Child, Classmate, Guardian, NewsItem, ScheduleItem, +} from './types' const camel = require('camelcase-keys') export interface EtjanstResponse { Success: boolean - Error: string|null - Data: any|any[] + Error: string | null + Data: any | any[] } -export const etjanst = (response: EtjanstResponse): any|any[] => { +export const etjanst = (response: EtjanstResponse): any | any[] => { if (!response.Success) { throw new Error(response.Error || '') } @@ -21,8 +26,58 @@ export const child = ({ id, sdsId, name, status, schoolId, }) -export const calendarItem = ({ - id, title, description, location, startDate, endDate, allDay, -}: any): CalendarItem => ({ - id, title, description, location, startDate, endDate, allDay, +export const guardian = ({ + emailhome, firstname, lastname, address, telmobile, +}: any): Guardian => ({ + firstname, + lastname, + address, + mobile: telmobile, + email: emailhome, +}) + +export const classmate = ({ + sisId, firstname, lastname, className, guardians = [], +}: any): Classmate => ({ + sisId, + firstname, + lastname, + className, + guardians: guardians.map(guardian), +}) + +export const calendarItem = ({ + id, title, description, location, longEventDateTime, longEndDateTime, allDayEvent, +}: any): CalendarItem => ({ + id, + title, + description, + location, + allDay: allDayEvent, + startDate: longEventDateTime ? moment(new Date(longEventDateTime)) : undefined, + endDate: longEndDateTime ? moment(new Date(longEndDateTime)) : undefined, +}) + +export const newsItem = ({ + newsId, header, preamble, body, bannerImageUrl, pubDateSe, modDateSe, +}: any): NewsItem => ({ + header, + id: newsId, + intro: preamble, + imageUrl: bannerImageUrl, + body: htmlDecode(h2m(body)), + published: moment(new Date(pubDateSe)), + modified: moment(new Date(modDateSe)), +}) + +export const scheduleItem = ({ + title, description, location, longEventDateTime, longEndDateTime, isSameDay, allDayEvent, +}: any): ScheduleItem => ({ + title, + description, + location, + allDayEvent, + startDate: moment(new Date(longEventDateTime)), + endDate: moment(new Date(longEndDateTime)), + oneDayEvent: isSameDay, }) diff --git a/lib/types.ts b/lib/types.ts index 2ceb41f8..6ce95397 100644 --- a/lib/types.ts +++ b/lib/types.ts @@ -1,3 +1,5 @@ +import { Moment } from 'moment' + export interface RequestInit { headers?: any method?: string @@ -28,12 +30,12 @@ export interface AuthTicket { * @interface CalendarItem */ export interface CalendarItem { - id?: number; - title?: string; + id: number; + title: string; description?: string; location?: string; - startDate?: string; - endDate?: string; + startDate?: Moment; + endDate?: Moment; allDay?: boolean; } @@ -42,14 +44,14 @@ export interface CalendarItem { * @interface Child */ export interface Child { - id?: string; + id: string; /** *

Special ID used to access certain subsystems

* @type {string} * @memberof Child */ - sdsId?: string; - name?: string; + sdsId: string; + name: string; /** *

F - förskola, GR - grundskola?

* @type {string} @@ -64,16 +66,11 @@ export interface Child { * @interface Classmate */ export interface Classmate { - sisId?: string; - /** - *

The name of the class of this classmate

- * @type {string} - * @memberof Classmate - */ + sisId: string; className?: string; - firstname?: string; - lastname?: string; - guardians?: Guardian[]; + firstname: string; + lastname: string; + guardians: Guardian[]; } /** @@ -82,8 +79,8 @@ export interface Classmate { */ export interface Guardian { email?: string; - firstname?: string; - lastname?: string; + firstname: string; + lastname: string; mobile?: string; address?: string; } @@ -98,8 +95,8 @@ export interface NewsItem { header?: string; intro?: string; body?: string; - published?: string; - modified?: string; + published: Moment; + modified?: Moment; imageUrl?: string; } @@ -126,3 +123,17 @@ export interface Notification { category?: string; messageType?: string; } + +/** + * @export + * @interface ScheduleItem + */ +export interface ScheduleItem { + title: string + description?: string + location?: string + startDate: Moment + endDate: Moment + oneDayEvent: boolean + allDayEvent: boolean +} diff --git a/package.json b/package.json index e3ecaa30..5e64a0a1 100644 --- a/package.json +++ b/package.json @@ -35,6 +35,9 @@ }, "dependencies": { "camelcase-keys": "^6.2.2", - "events": "^3.2.0" + "events": "^3.2.0", + "h2m": "^0.7.0", + "js-htmlencode": "^0.3.0", + "moment": "^2.29.1" } } diff --git a/run.js b/run.js new file mode 100644 index 00000000..eaf338b7 --- /dev/null +++ b/run.js @@ -0,0 +1,63 @@ +const moment = require('moment') +const nodeFetch = require('node-fetch') +const { CookieJar } = require('tough-cookie') +const fetchCookie = require('fetch-cookie/node-fetch') + +const init = require('./dist').default + +const [, , personalNumber] = process.argv + +if (!personalNumber) { + console.error('You must pass in a valid personal number, eg `node run 197001011111`') + process.exit(1) +} + +async function run() { + const cookieJar = new CookieJar() + const fetch = fetchCookie(nodeFetch, cookieJar) + + try { + + const api = init(fetch, () => cookieJar.removeAllCookies()) + const status = await api.login(personalNumber) + status.on('PENDING', () => console.log('PENDING')) + status.on('USER_SIGN', () => console.log('USER_SIGN')) + status.on('ERROR', () => console.error('ERROR')) + status.on('OK', () => console.log('OK')) + + api.on('login', async () => { + console.log('Logged in') + + console.log('children') + const children = await api.getChildren() + // console.log(children) + + // console.log('calendar') + // const calendar = await api.getCalendar(children[0]) + // console.log(calendar) + + // console.log('classmates') + // const classmates = await api.getClassmates(children[0]) + // console.log(classmates) + + // console.log('schedule') + // const schedule = await api.getSchedule(children[0], moment().subtract(1, 'week'), moment()) + // console.log(schedule) + + // console.log('news') + // const news = await api.getNews(children[0]) + // console.log(news) + + await api.logout() + }) + + api.on('logout', () => { + console.log('Logged out') + process.exit(0) + }) + } catch (err) { + console.error(err) + } +} + +run() diff --git a/yarn.lock b/yarn.lock index f7ca5e3a..df61ef82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -833,6 +833,11 @@ anymatch@^3.0.3: normalize-path "^3.0.0" picomatch "^2.0.4" +arch@^2.1.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/arch/-/arch-2.2.0.tgz#1bc47818f305764f23ab3306b0bfc086c5a29d11" + integrity sha512-Of/R0wqp83cgHozfIYLbBMnej79U/SVGOOyuB3VVFv1NRM/PSFMK12x9KVtiYzJqmnU5WR2qp0Z5rHb7sWGnFQ== + argparse@^1.0.7: version "1.0.10" resolved "https://registry.yarnpkg.com/argparse/-/argparse-1.0.10.tgz#bcd6791ea5ae09725e17e5ad988134cd40b3d911" @@ -1169,6 +1174,14 @@ class-utils@^0.3.5: isobject "^3.0.0" static-extend "^0.1.1" +clipboardy@~1.2.3: + version "1.2.3" + resolved "https://registry.yarnpkg.com/clipboardy/-/clipboardy-1.2.3.tgz#0526361bf78724c1f20be248d428e365433c07ef" + integrity sha512-2WNImOvCRe6r63Gk9pShfkwXsVtKCroMAevIbiae021mS850UkWPbevxsBz3tnvjZIEGvlwaqCPsw+4ulzNgJA== + dependencies: + arch "^2.1.0" + execa "^0.8.0" + cliui@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-6.0.0.tgz#511d702c0c4e41ca156d7d0e96021f23e13225b1" @@ -1227,6 +1240,11 @@ combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: dependencies: delayed-stream "~1.0.0" +commander@^2.9.0: + version "2.20.3" + resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" + integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== + component-emitter@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/component-emitter/-/component-emitter-1.3.0.tgz#16e4070fba8ae29b679f2215853ee181ab2eabc0" @@ -1264,6 +1282,15 @@ core-util-is@1.0.2: resolved "https://registry.yarnpkg.com/core-util-is/-/core-util-is-1.0.2.tgz#b5fd54220aa2bc5ab57aab7140c940754503c1a7" integrity sha1-tf1UIgqivFq1eqtxQMlAdUUDwac= +cross-spawn@^5.0.1: + version "5.1.0" + resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" + integrity sha1-6L0O/uWPz/b4+UUQoKVUu/ojVEk= + dependencies: + lru-cache "^4.0.1" + shebang-command "^1.2.0" + which "^1.2.9" + cross-spawn@^6.0.0: version "6.0.5" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-6.0.5.tgz#4a5ec7c64dfae22c3a14124dbacdee846d80cbc4" @@ -1422,6 +1449,24 @@ doctrine@^3.0.0: dependencies: esutils "^2.0.2" +dom-serializer@0: + version "0.2.2" + resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51" + integrity sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g== + dependencies: + domelementtype "^2.0.1" + entities "^2.0.0" + +domelementtype@1, domelementtype@^1.3.1: + version "1.3.1" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.1.tgz#d048c44b37b0d10a7f2a3d5fee3f4333d790481f" + integrity sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w== + +domelementtype@^2.0.1: + version "2.1.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-2.1.0.tgz#a851c080a6d1c3d94344aed151d99f669edf585e" + integrity sha512-LsTgx/L5VpD+Q8lmsXSHW2WpA+eBlZ9HPf3erD1IoPF00/3JKHZ3BknUVA2QGDNu69ZNmyFmCWBSO45XjYKC5w== + domexception@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/domexception/-/domexception-2.0.1.tgz#fb44aefba793e1574b0af6aed2801d057529f304" @@ -1429,6 +1474,21 @@ domexception@^2.0.1: dependencies: webidl-conversions "^5.0.0" +domhandler@^2.3.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.2.tgz#8805097e933d65e85546f726d60f5eb88b44f803" + integrity sha512-JiK04h0Ht5u/80fdLMCEmV4zkNh2BcoMFBmZ/91WtYZ8qVXSKjiw7fXMgFPnHcSZgOo3XdinHvmnDUeMf5R4wA== + dependencies: + domelementtype "1" + +domutils@^1.5.1: + version "1.7.0" + resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.7.0.tgz#56ea341e834e06e6748af7a1cb25da67ea9f8c2a" + integrity sha512-Lgd2XcJ/NjEw+7tFvfKxOzCYKZsdct5lczQ2ZaQY8Djz7pfAD3Gbp8ySJWtreII/vDlMVmxwa6pHmdxIYgttDg== + dependencies: + dom-serializer "0" + domelementtype "1" + ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -1461,6 +1521,16 @@ enquirer@^2.3.5: dependencies: ansi-colors "^4.1.1" +entities@^1.1.1: + version "1.1.2" + resolved "https://registry.yarnpkg.com/entities/-/entities-1.1.2.tgz#bdfa735299664dfafd34529ed4f8522a275fea56" + integrity sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w== + +entities@^2.0.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/entities/-/entities-2.1.0.tgz#992d3129cf7df6870b96c57858c249a120f8b8b5" + integrity sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w== + error-ex@^1.2.0, error-ex@^1.3.1: version "1.3.2" resolved "https://registry.yarnpkg.com/error-ex/-/error-ex-1.3.2.tgz#b4ac40648107fdcdcfae242f428bea8a14d4f1bf" @@ -1709,6 +1779,19 @@ exec-sh@^0.3.2: resolved "https://registry.yarnpkg.com/exec-sh/-/exec-sh-0.3.4.tgz#3a018ceb526cc6f6df2bb504b2bfe8e3a4934ec5" integrity sha512-sEFIkc61v75sWeOe72qyrqg2Qg0OuLESziUDk/O/z2qgS15y2gWVFrI6f2Qn/qw/0/NCfCEsmNA4zOjkwEZT1A== +execa@^0.8.0: + version "0.8.0" + resolved "https://registry.yarnpkg.com/execa/-/execa-0.8.0.tgz#d8d76bbc1b55217ed190fd6dd49d3c774ecfc8da" + integrity sha1-2NdrvBtVIX7RkP1t1J08d07PyNo= + dependencies: + cross-spawn "^5.0.1" + get-stream "^3.0.0" + is-stream "^1.1.0" + npm-run-path "^2.0.0" + p-finally "^1.0.0" + signal-exit "^3.0.0" + strip-eof "^1.0.0" + execa@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/execa/-/execa-1.0.0.tgz#c6236a5bb4df6d6f15e88e7f017798216749ddd8" @@ -1990,6 +2073,11 @@ get-package-type@^0.1.0: resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" integrity sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q== +get-stream@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14" + integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ= + get-stream@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-4.1.0.tgz#c1b255575f3dc21d59bfc79cd3d2b46b1c3a54b5" @@ -2069,6 +2157,16 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= +h2m@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/h2m/-/h2m-0.7.0.tgz#b7fae87f6af585aea06000f691dc1f72f809cebc" + integrity sha512-HxuYZGcbG5lqt9t4YNT1+HuZ6E7CLh0urd5zT4LqoRKCf7VCx0bGzIDOuEdIz6po7XY/Fby6kbOql6ATtBofqg== + dependencies: + clipboardy "~1.2.3" + commander "^2.9.0" + htmlparser2 "^3.9.0" + request "^2.67.0" + har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" @@ -2152,6 +2250,18 @@ html-escaper@^2.0.0: resolved "https://registry.yarnpkg.com/html-escaper/-/html-escaper-2.0.2.tgz#dfd60027da36a36dfcbe236262c00a5822681453" integrity sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg== +htmlparser2@^3.9.0: + version "3.10.1" + resolved "https://registry.yarnpkg.com/htmlparser2/-/htmlparser2-3.10.1.tgz#bd679dc3f59897b6a34bb10749c855bb53a9392f" + integrity sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ== + dependencies: + domelementtype "^1.3.1" + domhandler "^2.3.0" + domutils "^1.5.1" + entities "^1.1.1" + inherits "^2.0.1" + readable-stream "^3.1.1" + http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -2212,7 +2322,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2: +inherits@2, inherits@^2.0.1, inherits@^2.0.3: version "2.0.4" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c" integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ== @@ -2857,6 +2967,11 @@ jest@^26.6.3: import-local "^3.0.2" jest-cli "^26.6.3" +js-htmlencode@^0.3.0: + version "0.3.0" + resolved "https://registry.yarnpkg.com/js-htmlencode/-/js-htmlencode-0.3.0.tgz#b1ce293df94e96f8a8a08b1f3368f977bd255731" + integrity sha1-sc4pPflOlviooIsfM2j5d70lVzE= + js-tokens@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" @@ -3056,6 +3171,14 @@ lodash@^4.17.15, lodash@^4.17.19, lodash@^4.17.20: resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.20.tgz#b44a9b6297bcb698f1c51a3545a2b3b368d59c52" integrity sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA== +lru-cache@^4.0.1: + version "4.1.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-4.1.5.tgz#8bbe50ea85bed59bc9e33dcab8235ee9bcf443cd" + integrity sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g== + dependencies: + pseudomap "^1.0.2" + yallist "^2.1.2" + lru-cache@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94" @@ -3178,6 +3301,11 @@ mkdirp@1.x: resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e" integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw== +moment@^2.29.1: + version "2.29.1" + resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.1.tgz#b2be769fa31940be9eeea6469c075e35006fa3d3" + integrity sha512-kHmoybcPV8Sqy59DwNDY3Jefr64lK/by/da0ViFcuA4DH0vQg5Q6Ze5VimxkfQNSC+Mls/Kx53s7TjP1RhFEDQ== + ms@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/ms/-/ms-2.0.0.tgz#5608aeadfc00be6c2901df5f9861788de0d597c8" @@ -3587,6 +3715,11 @@ prompts@^2.0.1: kleur "^3.0.3" sisteransi "^1.0.5" +pseudomap@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/pseudomap/-/pseudomap-1.0.2.tgz#f052a28da70e618917ef0a8ac34c1ae5a68286b3" + integrity sha1-8FKijacOYYkX7wqKw0wa5aaChrM= + psl@^1.1.28, psl@^1.1.33: version "1.8.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.8.0.tgz#9326f8bcfb013adcc005fdff056acce020e51c24" @@ -3656,6 +3789,15 @@ read-pkg@^5.2.0: parse-json "^5.0.0" type-fest "^0.6.0" +readable-stream@^3.1.1: + version "3.6.0" + resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.0.tgz#337bbda3adc0706bd3e024426a286d4b4b2c9198" + integrity sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA== + dependencies: + inherits "^2.0.3" + string_decoder "^1.1.1" + util-deprecate "^1.0.1" + regex-not@^1.0.0, regex-not@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/regex-not/-/regex-not-1.0.2.tgz#1f4ece27e00b0b65e0247a6810e6a85d83a5752c" @@ -3700,7 +3842,7 @@ request-promise-native@^1.0.8: stealthy-require "^1.1.1" tough-cookie "^2.3.3" -request@^2.88.2: +request@^2.67.0, request@^2.88.2: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" integrity sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw== @@ -3793,7 +3935,7 @@ run-parallel@^1.1.9: resolved "https://registry.yarnpkg.com/run-parallel/-/run-parallel-1.1.10.tgz#60a51b2ae836636c81377df16cb107351bcd13ef" integrity sha512-zb/1OuZ6flOlH6tQyMPUrE3x3Ulxjlo9WIVXR4yVYi4H9UXQaeIsPbLn2R3O3vQCnDKkAl2qHiuocKKX4Tz/Sw== -safe-buffer@^5.0.1, safe-buffer@^5.1.2: +safe-buffer@^5.0.1, safe-buffer@^5.1.2, 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== @@ -4097,6 +4239,13 @@ string.prototype.trimstart@^1.0.1: call-bind "^1.0.0" define-properties "^1.1.3" +string_decoder@^1.1.1: + version "1.3.0" + resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" + integrity sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA== + dependencies: + safe-buffer "~5.2.0" + strip-ansi@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.0.tgz#0b1571dd7669ccd4f3e06e14ef1eed26225ae532" @@ -4405,6 +4554,11 @@ use@^3.1.0: resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== +util-deprecate@^1.0.1: + version "1.0.2" + resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf" + integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= + uuid@^3.3.2: version "3.4.0" resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.4.0.tgz#b23e4358afa8a202fe7a100af1f5f883f02007ee" @@ -4566,6 +4720,11 @@ y18n@^4.0.0: resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.1.tgz#8db2b83c31c5d75099bb890b23f3094891e247d4" integrity sha512-wNcy4NvjMYL8gogWWYAO7ZFWFfHcbdbE57tZO8e4cbpj8tfUcwrwqSl3ad8HxpYWCdXcJUCeKKZS62Av1affwQ== +yallist@^2.1.2: + version "2.1.2" + resolved "https://registry.yarnpkg.com/yallist/-/yallist-2.1.2.tgz#1c11f9218f076089a47dd512f93c6699a6a81d52" + integrity sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= + yallist@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"