Zomars/refactor emails followup (#1216)

This commit is contained in:
Omar López 2021-12-07 08:48:08 -07:00 committed by GitHub
parent d76ef4a007
commit ec2acedf34
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 417 additions and 166 deletions

View File

@ -8,7 +8,7 @@
NEXT_PUBLIC_LICENSE_CONSENT=''
# DATABASE_URL='postgresql://<user>:<pass>@<db-host>:<db-port>/<db-name>'
DATABASE_URL="postgresql://postgres:@localhost:5432/calendso?schema=public"
DATABASE_URL="postgresql://postgres:@localhost:5450/calendso"
GOOGLE_API_CREDENTIALS='secret'

257
@types/ical.d.ts vendored Normal file
View File

@ -0,0 +1,257 @@
// SPDX-FileCopyrightText: © 2019 EteSync Authors
// SPDX-License-Identifier: GPL-3.0-only
// https://github.com/mozilla-comm/ical.js/issues/367#issuecomment-568493517
declare module "ical.js" {
function parse(input: string): any[];
export class helpers {
public updateTimezones(vcal: Component): Component;
}
class Component {
public fromString(str: string): Component;
public name: string;
constructor(jCal: any[] | string, parent?: Component);
public toJSON(): any[];
public getFirstSubcomponent(name?: string): Component | null;
public getAllSubcomponents(name?: string): Component[];
public getFirstPropertyValue<T = any>(name?: string): T;
public getFirstProperty(name?: string): Property;
public getAllProperties(name?: string): Property[];
public addProperty(property: Property): Property;
public addPropertyWithValue(name: string, value: string | number | Record<string, unknown>): Property;
public hasProperty(name?: string): boolean;
public updatePropertyWithValue(name: string, value: string | number | Record<string, unknown>): Property;
public removeAllProperties(name?: string): boolean;
public addSubcomponent(component: Component): Component;
}
export class Event {
public uid: string;
public summary: string;
public startDate: Time;
public endDate: Time;
public description: string;
public location: string;
public attendees: Property[];
/**
* The sequence value for this event. Used for scheduling.
*
* @type {number}
* @memberof Event
*/
public sequence: number;
/**
* The duration. This can be the result directly from the property, or the
* duration calculated from start date and end date. Setting the property
* will remove any `dtend` properties.
*
* @type {Duration}
* @memberof Event
*/
public duration: Duration;
/**
* The organizer value as an uri. In most cases this is a mailto: uri,
* but it can also be something else, like urn:uuid:...
*/
public organizer: string;
/** The sequence value for this event. Used for scheduling */
public sequence: number;
/** The recurrence id for this event */
public recurrenceId: Time;
public component: Component;
public constructor(
component?: Component | null,
options?: { strictExceptions: boolean; exepctions: Array<Component | Event> }
);
public isRecurring(): boolean;
public iterator(startTime?: Time): RecurExpansion;
}
export class Property {
public name: string;
public type: string;
constructor(jCal: any[] | string, parent?: Component);
public getFirstValue<T = any>(): T;
public getValues<T = any>(): T[];
public setParameter(name: string, value: string | string[]): void;
public setValue(value: string | Record<string, unknown>): void;
public setValues(values: (string | Record<string, unknown>)[]): void;
public toJSON(): any;
}
interface TimeJsonData {
year?: number;
month?: number;
day?: number;
hour?: number;
minute?: number;
second?: number;
isDate?: boolean;
}
export class Time {
public fromString(str: string): Time;
public fromJSDate(aDate: Date | null, useUTC: boolean): Time;
public fromData(aData: TimeJsonData): Time;
public now(): Time;
public isDate: boolean;
public timezone: string;
public zone: Timezone;
public year: number;
public month: number;
public day: number;
public hour: number;
public minute: number;
public second: number;
constructor(data?: TimeJsonData);
public compare(aOther: Time): number;
public clone(): Time;
public convertToZone(zone: Timezone): Time;
public adjust(
aExtraDays: number,
aExtraHours: number,
aExtraMinutes: number,
aExtraSeconds: number,
aTimeopt?: Time
): void;
public addDuration(aDuration: Duration): void;
public subtractDateTz(aDate: Time): Duration;
public toUnixTime(): number;
public toJSDate(): Date;
public toJSON(): TimeJsonData;
public get icaltype(): "date" | "date-time";
}
export class Duration {
public weeks: number;
public days: number;
public hours: number;
public minutes: number;
public seconds: number;
public isNegative: boolean;
public icalclass: string;
public icaltype: string;
}
export class RecurExpansion {
public complete: boolean;
public dtstart: Time;
public last: Time;
public next(): Time;
public fromData(options);
public toJSON();
constructor(options: {
/** Start time of the event */
dtstart: Time;
/** Component for expansion, required if not resuming. */
component?: Component;
});
}
export class Timezone {
public utcTimezone: Timezone;
public localTimezone: Timezone;
public convert_time(tt: Time, fromZone: Timezone, toZone: Timezone): Time;
public tzid: string;
public component: Component;
constructor(
data:
| Component
| {
component: string | Component;
tzid?: string;
location?: string;
tznames?: string;
latitude?: number;
longitude?: number;
}
);
}
export class TimezoneService {
public get(tzid: string): Timezone | null;
public has(tzid: string): boolean;
public register(tzid: string, zone: Timezone | Component);
public remove(tzid: string): Timezone | null;
}
export type FrequencyValues =
| "YEARLY"
| "MONTHLY"
| "WEEKLY"
| "DAILY"
| "HOURLY"
| "MINUTELY"
| "SECONDLY";
export enum WeekDay {
SU = 1,
MO,
TU,
WE,
TH,
FR,
SA,
}
export class RecurData {
public freq?: FrequencyValues;
public interval?: number;
public wkst?: WeekDay;
public until?: Time;
public count?: number;
public bysecond?: number[] | number;
public byminute?: number[] | number;
public byhour?: number[] | number;
public byday?: string[] | string;
public bymonthday?: number[] | number;
public byyearday?: number[] | number;
public byweekno?: number[] | number;
public bymonth?: number[] | number;
public bysetpos?: number[] | number;
}
export class RecurIterator {
public next(): Time;
}
export class Recur {
constructor(data?: RecurData);
public until: Time | null;
public freq: FrequencyValues;
public count: number | null;
public clone(): Recur;
public toJSON(): Omit<RecurData, "until"> & { until?: string };
public iterator(startTime?: Time): RecurIterator;
public isByCount(): boolean;
}
}

View File

@ -19,6 +19,8 @@ import {
import logger from "@lib/logger";
import { VideoCallData } from "@lib/videoClient";
import { Ensure } from "./types/utils";
const log = logger.getChildLogger({ prefix: ["[lib] calendarClient"] });
export type Person = { name: string; email: string; timeZone: string };
@ -61,7 +63,7 @@ export interface CalendarEvent {
paymentInfo?: PaymentInfo | null;
}
export interface IntegrationCalendar extends Partial<SelectedCalendar> {
export interface IntegrationCalendar extends Ensure<Partial<SelectedCalendar>, "externalId"> {
primary?: boolean;
name?: string;
}
@ -89,11 +91,9 @@ function getCalendarAdapterOrNull(credential: Credential): CalendarApiAdapter |
case "office365_calendar":
return Office365CalendarApiAdapter(credential);
case "caldav_calendar":
// FIXME types wrong & type casting should not be needed
return new CalDavCalendar(credential) as never as CalendarApiAdapter;
return new CalDavCalendar(credential);
case "apple_calendar":
// FIXME types wrong & type casting should not be needed
return new AppleCalendar(credential) as never as CalendarApiAdapter;
return new AppleCalendar(credential);
}
return null;
}

View File

@ -24,8 +24,6 @@ dayjs.extend(utc);
const log = logger.getChildLogger({ prefix: ["[[lib] apple calendar"] });
type EventBusyDate = Record<"start" | "end", Date>;
export class AppleCalendar implements CalendarApiAdapter {
private url: string;
private credentials: Record<string, string>;
@ -34,7 +32,7 @@ export class AppleCalendar implements CalendarApiAdapter {
constructor(credential: Credential) {
const decryptedCredential = JSON.parse(
symmetricDecrypt(credential.key, process.env.CALENDSO_ENCRYPTION_KEY)
symmetricDecrypt(credential.key as string, process.env.CALENDSO_ENCRYPTION_KEY!)
);
const username = decryptedCredential.username;
const password = decryptedCredential.password;
@ -52,12 +50,12 @@ export class AppleCalendar implements CalendarApiAdapter {
});
}
convertDate(date: string): number[] {
convertDate(date: string): [number, number, number] {
return dayjs(date)
.utc()
.toArray()
.slice(0, 6)
.map((v, i) => (i === 1 ? v + 1 : v));
.map((v, i) => (i === 1 ? v + 1 : v)) as [number, number, number];
}
getDuration(start: string, end: string): DurationObject {
@ -70,11 +68,11 @@ export class AppleCalendar implements CalendarApiAdapter {
return attendees.map(({ email, name }) => ({ name, email, partstat: "NEEDS-ACTION" }));
}
async createEvent(event: CalendarEvent): Promise<Record<string, unknown>> {
async createEvent(event: CalendarEvent) {
try {
const calendars = await this.listCalendars();
const uid = uuidv4();
const { error, value: iCalString } = await createEvent({
const { error, value: iCalString } = createEvent({
uid,
startInputType: "utc",
start: this.convertDate(event.startTime),
@ -86,15 +84,9 @@ export class AppleCalendar implements CalendarApiAdapter {
attendees: this.getAttendees(event.attendees),
});
if (error) {
log.debug("Error creating iCalString");
return {};
}
if (error) throw new Error("Error creating iCalString");
if (!iCalString) {
log.debug("Error creating iCalString");
return {};
}
if (!iCalString) throw new Error("Error creating iCalString");
await Promise.all(
calendars.map((calendar) => {
@ -112,6 +104,9 @@ export class AppleCalendar implements CalendarApiAdapter {
return {
uid,
id: uid,
type: "apple_calendar",
password: "",
url: "",
};
} catch (reason) {
console.error(reason);
@ -132,7 +127,7 @@ export class AppleCalendar implements CalendarApiAdapter {
}
}
const { error, value: iCalString } = await createEvent({
const { error, value: iCalString } = createEvent({
uid,
startInputType: "utc",
start: this.convertDate(event.startTime),
@ -201,11 +196,7 @@ export class AppleCalendar implements CalendarApiAdapter {
}
}
async getAvailability(
dateFrom: string,
dateTo: string,
selectedCalendars: IntegrationCalendar[]
): Promise<EventBusyDate[]> {
async getAvailability(dateFrom: string, dateTo: string, selectedCalendars: IntegrationCalendar[]) {
try {
const selectedCalendarIds = selectedCalendars
.filter((e) => e.integration === this.integrationName)
@ -229,8 +220,8 @@ export class AppleCalendar implements CalendarApiAdapter {
ids.map(async (calId) => {
return (await this.getEvents(calId, dateFrom, dateTo)).map((event) => {
return {
start: event.startDate,
end: event.endDate,
start: event.startDate.toISOString(),
end: event.endDate.toISOString(),
};
});
})
@ -272,8 +263,8 @@ export class AppleCalendar implements CalendarApiAdapter {
calId: string,
dateFrom: string | null,
dateTo: string | null,
objectUrls: string[] | null
): Promise<unknown[]> {
objectUrls?: string[] | null
) {
try {
const objects = await fetchCalendarObjects({
calendar: {
@ -290,54 +281,48 @@ export class AppleCalendar implements CalendarApiAdapter {
headers: this.headers,
});
const events =
objects &&
objects?.length > 0 &&
objects
.map((object) => {
if (object?.data) {
const jcalData = ICAL.parse(object.data);
const vcalendar = new ICAL.Component(jcalData);
const vevent = vcalendar.getFirstSubcomponent("vevent");
const event = new ICAL.Event(vevent);
const events = objects
.filter((e) => !!e.data)
.map((object) => {
const jcalData = ICAL.parse(object.data);
const vcalendar = new ICAL.Component(jcalData);
const vevent = vcalendar.getFirstSubcomponent("vevent");
const event = new ICAL.Event(vevent);
const calendarTimezone = vcalendar.getFirstSubcomponent("vtimezone")
? vcalendar.getFirstSubcomponent("vtimezone").getFirstPropertyValue("tzid")
: "";
const calendarTimezone =
vcalendar.getFirstSubcomponent("vtimezone")?.getFirstPropertyValue("tzid") || "";
const startDate = calendarTimezone
? dayjs(event.startDate).tz(calendarTimezone)
: new Date(event.startDate.toUnixTime() * 1000);
const endDate = calendarTimezone
? dayjs(event.endDate).tz(calendarTimezone)
: new Date(event.endDate.toUnixTime() * 1000);
const startDate = calendarTimezone
? dayjs(event.startDate.toJSDate()).tz(calendarTimezone)
: new Date(event.startDate.toUnixTime() * 1000);
const endDate = calendarTimezone
? dayjs(event.endDate.toJSDate()).tz(calendarTimezone)
: new Date(event.endDate.toUnixTime() * 1000);
return {
uid: event.uid,
etag: object.etag,
url: object.url,
summary: event.summary,
description: event.description,
location: event.location,
sequence: event.sequence,
startDate,
endDate,
duration: {
weeks: event.duration.weeks,
days: event.duration.days,
hours: event.duration.hours,
minutes: event.duration.minutes,
seconds: event.duration.seconds,
isNegative: event.duration.isNegative,
},
organizer: event.organizer,
attendees: event.attendees.map((a) => a.getValues()),
recurrenceId: event.recurrenceId,
timezone: calendarTimezone,
};
}
})
.filter((e) => e != null);
return {
uid: event.uid,
etag: object.etag,
url: object.url,
summary: event.summary,
description: event.description,
location: event.location,
sequence: event.sequence,
startDate,
endDate,
duration: {
weeks: event.duration.weeks,
days: event.duration.days,
hours: event.duration.hours,
minutes: event.duration.minutes,
seconds: event.duration.seconds,
isNegative: event.duration.isNegative,
},
organizer: event.organizer,
attendees: event.attendees.map((a) => a.getValues()),
recurrenceId: event.recurrenceId,
timezone: calendarTimezone,
};
});
return events;
} catch (reason) {

View File

@ -24,8 +24,6 @@ dayjs.extend(utc);
const log = logger.getChildLogger({ prefix: ["[lib] caldav"] });
type EventBusyDate = Record<"start" | "end", Date>;
export class CalDavCalendar implements CalendarApiAdapter {
private url: string;
private credentials: Record<string, string>;
@ -34,7 +32,7 @@ export class CalDavCalendar implements CalendarApiAdapter {
constructor(credential: Credential) {
const decryptedCredential = JSON.parse(
symmetricDecrypt(credential.key, process.env.CALENDSO_ENCRYPTION_KEY)
symmetricDecrypt(credential.key as string, process.env.CALENDSO_ENCRYPTION_KEY!)
);
const username = decryptedCredential.username;
const url = decryptedCredential.url;
@ -53,12 +51,12 @@ export class CalDavCalendar implements CalendarApiAdapter {
});
}
convertDate(date: string): number[] {
convertDate(date: string): [number, number, number] {
return dayjs(date)
.utc()
.toArray()
.slice(0, 6)
.map((v, i) => (i === 1 ? v + 1 : v));
.map((v, i) => (i === 1 ? v + 1 : v)) as [number, number, number];
}
getDuration(start: string, end: string): DurationObject {
@ -71,15 +69,14 @@ export class CalDavCalendar implements CalendarApiAdapter {
return attendees.map(({ email, name }) => ({ name, email, partstat: "NEEDS-ACTION" }));
}
async createEvent(event: CalendarEvent): Promise<Record<string, unknown>> {
async createEvent(event: CalendarEvent) {
try {
const calendars = await this.listCalendars();
const uid = uuidv4();
const { error, value: iCalString } = await createEvent({
const { error, value: iCalString } = createEvent({
uid,
startInputType: "utc",
// FIXME types
start: this.convertDate(event.startTime),
duration: this.getDuration(event.startTime, event.endTime),
title: event.title,
@ -89,15 +86,9 @@ export class CalDavCalendar implements CalendarApiAdapter {
attendees: this.getAttendees(event.attendees),
});
if (error) {
log.debug("Error creating iCalString");
return {};
}
if (error) throw new Error("Error creating iCalString");
if (!iCalString) {
log.debug("Error creating iCalString");
return {};
}
if (!iCalString) throw new Error("Error creating iCalString");
await Promise.all(
calendars.map((calendar) => {
@ -115,6 +106,9 @@ export class CalDavCalendar implements CalendarApiAdapter {
return {
uid,
id: uid,
type: "caldav_calendar",
password: "",
url: "",
};
} catch (reason) {
log.error(reason);
@ -138,7 +132,6 @@ export class CalDavCalendar implements CalendarApiAdapter {
const { error, value: iCalString } = await createEvent({
uid,
startInputType: "utc",
// FIXME - types wrong
start: this.convertDate(event.startTime),
duration: this.getDuration(event.startTime, event.endTime),
title: event.title,
@ -205,12 +198,7 @@ export class CalDavCalendar implements CalendarApiAdapter {
}
}
// FIXME - types wrong
async getAvailability(
dateFrom: string,
dateTo: string,
selectedCalendars: IntegrationCalendar[]
): Promise<EventBusyDate[]> {
async getAvailability(dateFrom: string, dateTo: string, selectedCalendars: IntegrationCalendar[]) {
try {
const selectedCalendarIds = selectedCalendars
.filter((e) => e.integration === this.integrationName)
@ -235,8 +223,8 @@ export class CalDavCalendar implements CalendarApiAdapter {
ids.map(async (calId) => {
return (await this.getEvents(calId, dateFrom, dateTo)).map((event) => {
return {
start: event.startDate,
end: event.endDate,
start: event.startDate.toISOString(),
end: event.endDate.toISOString(),
};
});
})
@ -274,7 +262,7 @@ export class CalDavCalendar implements CalendarApiAdapter {
}
}
async getEvents(calId: string, dateFrom: string | null, dateTo: string | null): Promise<unknown[]> {
async getEvents(calId: string, dateFrom: string | null, dateTo: string | null) {
try {
const objects = await fetchCalendarObjects({
calendar: {
@ -295,50 +283,47 @@ export class CalDavCalendar implements CalendarApiAdapter {
}
const events = objects
.filter((e) => !!e.data)
.map((object) => {
if (object?.data) {
const jcalData = ICAL.parse(object.data);
const vcalendar = new ICAL.Component(jcalData);
const vevent = vcalendar.getFirstSubcomponent("vevent");
const event = new ICAL.Event(vevent);
const jcalData = ICAL.parse(object.data);
const vcalendar = new ICAL.Component(jcalData);
const vevent = vcalendar.getFirstSubcomponent("vevent");
const event = new ICAL.Event(vevent);
const calendarTimezone = vcalendar.getFirstSubcomponent("vtimezone")
? vcalendar.getFirstSubcomponent("vtimezone").getFirstPropertyValue("tzid")
: "";
const calendarTimezone =
vcalendar.getFirstSubcomponent("vtimezone")?.getFirstPropertyValue("tzid") || "";
const startDate = calendarTimezone
? dayjs(event.startDate).tz(calendarTimezone)
: new Date(event.startDate.toUnixTime() * 1000);
const endDate = calendarTimezone
? dayjs(event.endDate).tz(calendarTimezone)
: new Date(event.endDate.toUnixTime() * 1000);
const startDate = calendarTimezone
? dayjs(event.startDate.toJSDate()).tz(calendarTimezone)
: new Date(event.startDate.toUnixTime() * 1000);
const endDate = calendarTimezone
? dayjs(event.endDate.toJSDate()).tz(calendarTimezone)
: new Date(event.endDate.toUnixTime() * 1000);
return {
uid: event.uid,
etag: object.etag,
url: object.url,
summary: event.summary,
description: event.description,
location: event.location,
sequence: event.sequence,
startDate,
endDate,
duration: {
weeks: event.duration.weeks,
days: event.duration.days,
hours: event.duration.hours,
minutes: event.duration.minutes,
seconds: event.duration.seconds,
isNegative: event.duration.isNegative,
},
organizer: event.organizer,
attendees: event.attendees.map((a) => a.getValues()),
recurrenceId: event.recurrenceId,
timezone: calendarTimezone,
};
}
})
.filter((e) => e != null);
return {
uid: event.uid,
etag: object.etag,
url: object.url,
summary: event.summary,
description: event.description,
location: event.location,
sequence: event.sequence,
startDate,
endDate,
duration: {
weeks: event.duration.weeks,
days: event.duration.days,
hours: event.duration.hours,
minutes: event.duration.minutes,
seconds: event.duration.seconds,
isNegative: event.duration.isNegative,
},
organizer: event.organizer,
attendees: event.attendees.map((a) => a.getValues()),
recurrenceId: event.recurrenceId,
timezone: calendarTimezone,
};
});
return events;
} catch (reason) {

View File

@ -7,7 +7,7 @@ import { CalendarApiAdapter, CalendarEvent, IntegrationCalendar } from "@lib/cal
import prisma from "@lib/prisma";
export interface ConferenceData {
createRequest: calendar_v3.Schema$CreateConferenceRequest;
createRequest?: calendar_v3.Schema$CreateConferenceRequest;
}
const googleAuth = (credential: Credential) => {
@ -91,7 +91,19 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
if (err) {
reject(err);
}
resolve(Object.values(apires.data.calendars).flatMap((item) => item["busy"]));
let result: Prisma.PromiseReturnType<CalendarApiAdapter["getAvailability"]> = [];
if (apires?.data.calendars) {
result = Object.values(apires.data.calendars).reduce((c, i) => {
i.busy?.forEach((busyTime) => {
c.push({
start: busyTime.start || "",
end: busyTime.end || "",
});
});
return c;
}, [] as typeof result);
}
resolve(result);
}
);
})
@ -146,7 +158,14 @@ export const GoogleCalendarApiAdapter = (credential: Credential): CalendarApiAda
console.error("There was an error contacting google calendar service: ", err);
return reject(err);
}
return resolve(event.data);
return resolve({
...event.data,
id: event.data.id || "",
hangoutLink: event.data.hangoutLink || "",
type: "google_calendar",
password: "",
url: "",
});
}
);
})

View File

@ -11,7 +11,9 @@
"db-migrate": "yarn prisma migrate dev",
"db-seed": "yarn ts-node scripts/seed.ts",
"db-nuke": "docker-compose down --volumes --remove-orphans",
"dx": "cross-env BASE_URL=http://localhost:3000 JWT_SECRET=secret DATABASE_URL=postgresql://postgres:@localhost:5450/calendso run-s db-up db-migrate db-seed dev",
"db-setup": "run-s db-up db-migrate db-seed",
"db-reset": "run-s db-nuke db-setup",
"dx": "env-cmd run-s db-setup dev",
"test": "jest",
"test-playwright": "jest --config jest.playwright.config.js",
"test-codegen": "yarn playwright codegen http://localhost:3000",
@ -91,8 +93,8 @@
"react-use-intercom": "1.4.0",
"short-uuid": "^4.2.0",
"stripe": "^8.191.0",
"tsdav": "^1.1.5",
"superjson": "1.8.0",
"tsdav": "^1.1.5",
"tslog": "^3.2.1",
"uuid": "^8.3.2",
"zod": "^3.8.2"
@ -117,7 +119,7 @@
"@typescript-eslint/parser": "^4.33.0",
"autoprefixer": "^10.3.1",
"babel-jest": "^27.3.1",
"cross-env": "^7.0.3",
"env-cmd": "10.1.0",
"eslint": "^7.32.0",
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-prettier": "^3.4.0",
@ -149,4 +151,4 @@
"prisma format"
]
}
}
}

View File

@ -1,4 +1,3 @@
import dayjs from "dayjs";
import { kont } from "kont";
import { loginProvider } from "./lib/loginProvider";
@ -35,14 +34,11 @@ describe("webhooks", () => {
// page contains the url
await expect(page).toHaveSelector(`text='${webhookReceiver.url}'`);
// --- go to tomorrow in the pro user's "30min"-event
const tomorrow = dayjs().add(1, "day");
const tomorrowFormatted = tomorrow.format("YYYY-MM-DDZZ");
await page.goto(`http://localhost:3000/pro/30min?date=${encodeURIComponent(tomorrowFormatted)}`);
// click first time available
await page.click("[data-testid=time]");
// --- Book the first available day next month in the pro user's "30min"-event
await page.goto(`http://localhost:3000/pro/30min`);
await page.click('[data-testid="incrementMonth"]');
await page.click('[data-testid="day"]');
await page.click('[data-testid="time"]');
// --- fill form
await page.fill('[name="name"]', "Test Testson");

View File

@ -43,6 +43,7 @@
},
"include": [
"next-env.d.ts",
"@types/*.d.ts",
"**/*.ts",
"**/*.tsx"
],

View File

@ -3152,6 +3152,11 @@ commander@^3.0.2:
resolved "https://registry.yarnpkg.com/commander/-/commander-3.0.2.tgz#6837c3fb677ad9933d1cfba42dd14d5117d6b39e"
integrity sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==
commander@^4.0.0:
version "4.1.1"
resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068"
integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==
commander@^5.1.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/commander/-/commander-5.1.0.tgz#46abbd1652f8e059bddaef99bbdcb2ad9cf179ae"
@ -3273,13 +3278,6 @@ create-require@^1.1.0:
resolved "https://registry.yarnpkg.com/create-require/-/create-require-1.1.1.tgz#c1d7e8f1e5f6cfc9ff65f9cd352d37348756c333"
integrity sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==
cross-env@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-env/-/cross-env-7.0.3.tgz#865264b29677dc015ba8418918965dd232fc54cf"
integrity sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==
dependencies:
cross-spawn "^7.0.1"
cross-fetch@3.1.4:
version "3.1.4"
resolved "https://registry.yarnpkg.com/cross-fetch/-/cross-fetch-3.1.4.tgz#9723f3a3a247bf8b89039f3a380a9244e8fa2f39"
@ -3298,7 +3296,7 @@ cross-spawn@^6.0.5:
shebang-command "^1.2.0"
which "^1.2.9"
cross-spawn@^7.0.0, cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
cross-spawn@^7.0.0, cross-spawn@^7.0.2, cross-spawn@^7.0.3:
version "7.0.3"
resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6"
integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==
@ -3702,6 +3700,14 @@ enquirer@^2.3.5, enquirer@^2.3.6:
dependencies:
ansi-colors "^4.1.1"
env-cmd@10.1.0:
version "10.1.0"
resolved "https://registry.yarnpkg.com/env-cmd/-/env-cmd-10.1.0.tgz#c7f5d3b550c9519f137fdac4dd8fb6866a8c8c4b"
integrity sha512-mMdWTT9XKN7yNth/6N6g2GuKuJTsKMDHlQFUDacb/heQRRWOTIZ42t1rMHnQu4jYxU1ajdTeJM+9eEETlqToMA==
dependencies:
commander "^4.0.0"
cross-spawn "^7.0.0"
errno@^0.1.1, errno@~0.1.1:
version "0.1.8"
resolved "https://registry.npmjs.org/errno/-/errno-0.1.8.tgz#8bb3e9c7d463be4976ff888f76b4809ebc2e811f"