Zoom/Hubspot Tests with MSW mocking of requests initiated from Next.js server (#3210)
* Add code from previous PR * Remove env variables from wrong place * Fix tests * Remove dead code * Package.json updates * Fix eslint error * Fix issue due to conflict resolution Co-authored-by: kodiakhq[bot] <49736102+kodiakhq[bot]@users.noreply.github.com> Co-authored-by: zomars <zomars@me.com>pull/3947/head
parent
e0744a4857
commit
4ef666a610
|
@ -0,0 +1,104 @@
|
|||
name: E2E Test - Integrations with Third Party
|
||||
on:
|
||||
push:
|
||||
branches: [ tests/with-msw ]
|
||||
pull_request_target: # So we can test on forks
|
||||
branches:
|
||||
- main
|
||||
paths-ignore:
|
||||
- apps/api/**
|
||||
- apps/console/**
|
||||
- apps/docs/**
|
||||
- apps/swagger/**
|
||||
- apps/website/**
|
||||
- apps/web/public/**
|
||||
jobs:
|
||||
test:
|
||||
timeout-minutes: 20
|
||||
name: E2E Integration
|
||||
strategy:
|
||||
matrix:
|
||||
node: ["16.x"]
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
|
||||
env:
|
||||
DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
|
||||
NEXT_PUBLIC_WEBAPP_URL: http://localhost:3000
|
||||
NEXT_PUBLIC_WEBSITE_URL: http://localhost:3000
|
||||
NEXTAUTH_SECRET: secret
|
||||
GOOGLE_API_CREDENTIALS: ${{ secrets.CI_GOOGLE_API_CREDENTIALS }}
|
||||
GOOGLE_LOGIN_ENABLED: true
|
||||
# CRON_API_KEY: xxx
|
||||
CALENDSO_ENCRYPTION_KEY: ${{ secrets.CI_CALENDSO_ENCRYPTION_KEY }}
|
||||
NEXT_PUBLIC_STRIPE_PUBLIC_KEY: ${{ secrets.CI_NEXT_PUBLIC_STRIPE_PUBLIC_KEY }}
|
||||
STRIPE_PRIVATE_KEY: ${{ secrets.CI_STRIPE_PRIVATE_KEY }}
|
||||
STRIPE_CLIENT_ID: ${{ secrets.CI_STRIPE_CLIENT_ID }}
|
||||
STRIPE_WEBHOOK_SECRET: ${{ secrets.CI_STRIPE_WEBHOOK_SECRET }}
|
||||
PAYMENT_FEE_PERCENTAGE: 0.005
|
||||
PAYMENT_FEE_FIXED: 10
|
||||
SAML_DATABASE_URL: postgresql://postgres:@localhost:5432/calendso
|
||||
SAML_ADMINS: pro@example.com
|
||||
NEXTAUTH_URL: http://localhost:3000/api/auth
|
||||
ZOOM_CLIENT_ID: ZOOM_CLIENT_ID
|
||||
ZOOM_CLIENT_SECRET: ZOOM_CLIENT_SECRET
|
||||
HUBSPOT_CLIENT_ID: HUBSPOT_CLIENT_ID
|
||||
HUBSPOT_CLIENT_SECRET: HUBSPOT_CLIENT_SECRET
|
||||
NEXT_PUBLIC_IS_E2E: 1
|
||||
# EMAIL_FROM: e2e@cal.com
|
||||
# EMAIL_SERVER_HOST: ${{ secrets.CI_EMAIL_SERVER_HOST }}
|
||||
# EMAIL_SERVER_PORT: ${{ secrets.CI_EMAIL_SERVER_PORT }}
|
||||
# EMAIL_SERVER_USER: ${{ secrets.CI_EMAIL_SERVER_USER }}
|
||||
# EMAIL_SERVER_PASSWORD: ${{ secrets.CI_EMAIL_SERVER_PASSWORD }}
|
||||
# MS_GRAPH_CLIENT_ID: xxx
|
||||
# MS_GRAPH_CLIENT_SECRET: xxx
|
||||
# ZOOM_CLIENT_ID: xxx
|
||||
# ZOOM_CLIENT_SECRET: xxx
|
||||
TURBO_TOKEN: ${{ secrets.TURBO_TOKEN }}
|
||||
TURBO_TEAM: ${{ secrets.TURBO_TEAM }}
|
||||
services:
|
||||
postgres:
|
||||
image: postgres:12.1
|
||||
env:
|
||||
POSTGRES_USER: postgres
|
||||
POSTGRES_DB: calendso
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- name: Checkout repo
|
||||
uses: actions/checkout@v2
|
||||
with:
|
||||
ref: ${{ github.event.pull_request.head.sha }} # So we can test on forks
|
||||
fetch-depth: 2
|
||||
- run: echo 'NODE_OPTIONS="--max_old_space_size=4096"' >> $GITHUB_ENV
|
||||
- name: Use Node ${{ matrix.node }}
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: ${{ matrix.node }}
|
||||
cache: "yarn"
|
||||
|
||||
- name: Cache playwright binaries
|
||||
uses: actions/cache@v2
|
||||
id: playwright-cache
|
||||
with:
|
||||
path: |
|
||||
~/Library/Caches/ms-playwright
|
||||
~/.cache/ms-playwright
|
||||
${{ github.workspace }}/node_modules/playwright
|
||||
key: cache-playwright-${{ hashFiles('**/yarn.lock') }}
|
||||
restore-keys: cache-playwright-
|
||||
- run: yarn --frozen-lockfile
|
||||
- name: Install playwright deps
|
||||
# if: steps.playwright-cache.outputs.cache-hit != 'true'
|
||||
run: yarn playwright install --with-deps
|
||||
- name: Run Tests
|
||||
# Force bypass cache because new environment variables were added that caused DB to change but build remains cached
|
||||
run: yarn test-e2e-integrations --force
|
||||
|
||||
- name: Upload Test Results
|
||||
if: ${{ always() }}
|
||||
uses: actions/upload-artifact@v2
|
||||
with:
|
||||
name: test-results-core
|
||||
path: apps/web/playwright-integrations/test-results
|
|
@ -10,8 +10,10 @@
|
|||
"dev": "next dev",
|
||||
"dx": "yarn dev",
|
||||
"test": "dotenv -e ./test/.env.test -- jest",
|
||||
"test-e2e": "NEXT_PUBLIC_IS_E2E=1 yarn playwright test --config=../../tests/config/playwright.config.ts --project=chromium",
|
||||
"test-e2e-integrations": "NEXT_PUBLIC_IS_E2E=1 yarn playwright test --config=playwright-integrations/config/playwright.config.ts --project=chromium",
|
||||
"test-e2e-integrations-quick": "QUICK=true E2E_DEV_SERVER=1 yarn test-e2e-integrations",
|
||||
"db-setup-tests": "dotenv -e ./test/.env.test -- yarn workspace @calcom/prisma prisma generate",
|
||||
"test-e2e": "cd ../.. && yarn playwright test --config=tests/config/playwright.config.ts --project=chromium",
|
||||
"playwright-report": "playwright show-report playwright/reports/playwright-html-report",
|
||||
"test-codegen": "yarn playwright codegen http://localhost:3000",
|
||||
"type-check": "tsc --pretty --noEmit",
|
||||
|
@ -132,6 +134,7 @@
|
|||
"@types/accept-language-parser": "1.5.2",
|
||||
"@types/async": "^3.2.15",
|
||||
"@types/bcryptjs": "^2.4.2",
|
||||
"@types/detect-port": "^1.3.2",
|
||||
"@types/glidejs__glide": "^3.4.2",
|
||||
"@types/jest": "^28.1.7",
|
||||
"@types/lodash": "^4.14.182",
|
||||
|
@ -150,6 +153,7 @@
|
|||
"@types/stripe": "^8.0.417",
|
||||
"@types/uuid": "8.3.1",
|
||||
"autoprefixer": "^10.4.7",
|
||||
"detect-port": "^1.3.0",
|
||||
"babel-jest": "^28.1.0",
|
||||
"copy-webpack-plugin": "^11.0.0",
|
||||
"env-cmd": "^10.1.0",
|
||||
|
@ -159,6 +163,7 @@
|
|||
"jest-mock-extended": "^2.0.7",
|
||||
"mockdate": "^3.0.5",
|
||||
"module-alias": "^2.2.2",
|
||||
"msw": "^0.42.3",
|
||||
"postcss": "^8.4.13",
|
||||
"tailwindcss": "^3.1.6",
|
||||
"ts-jest": "^28.0.8",
|
||||
|
|
|
@ -45,7 +45,7 @@ function ConnectOrDisconnectIntegrationButton(props: {
|
|||
<DisconnectIntegration
|
||||
id={credentialId}
|
||||
render={(btnProps) => (
|
||||
<Button {...btnProps} color="warn" data-testid="integration-connection-button">
|
||||
<Button {...btnProps} color="warn" data-testid={type + "-integration-disconnect-button"}>
|
||||
{t("disconnect")}
|
||||
</Button>
|
||||
)}
|
||||
|
@ -57,7 +57,7 @@ function ConnectOrDisconnectIntegrationButton(props: {
|
|||
<DisconnectIntegration
|
||||
id={credentialId}
|
||||
render={(btnProps) => (
|
||||
<Button {...btnProps} color="warn" data-testid="integration-connection-button">
|
||||
<Button {...btnProps} color="warn" data-testid={type + "-integration-disconnect-button"}>
|
||||
{t("disconnect")}
|
||||
</Button>
|
||||
)}
|
||||
|
@ -100,7 +100,7 @@ interface IntegrationsContainerProps {
|
|||
|
||||
const IntegrationsContainer = ({ variant, className = "" }: IntegrationsContainerProps): JSX.Element => {
|
||||
const { t } = useLocale();
|
||||
const query = trpc.useQuery(["viewer.integrations", { variant, onlyInstalled: true }], { suspense: true });
|
||||
const query = trpc.useQuery(["viewer.integrations", { variant, onlyInstalled: true }]);
|
||||
return (
|
||||
<QueryCell
|
||||
query={query}
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
test-results
|
|
@ -0,0 +1,68 @@
|
|||
import { devices, PlaywrightTestConfig } from "@playwright/test";
|
||||
import dotenv from "dotenv";
|
||||
import { addAliases } from "module-alias";
|
||||
import * as path from "path";
|
||||
|
||||
dotenv.config({ path: "./env" });
|
||||
|
||||
// Add aliases for the paths specified in the tsconfig.json file.
|
||||
// This is needed because playwright does not consider tsconfig.json
|
||||
// For more info, see:
|
||||
// https://stackoverflow.com/questions/69023682/typescript-playwright-error-cannot-find-module
|
||||
// https://github.com/microsoft/playwright/issues/7066#issuecomment-983984496
|
||||
addAliases({
|
||||
"@components": __dirname + "/apps/web/components",
|
||||
"@lib": __dirname + "/apps/web/lib",
|
||||
"@server": __dirname + "/apps/web/server",
|
||||
"@ee": __dirname + "/apps/web/ee",
|
||||
});
|
||||
|
||||
const outputDir = path.join(__dirname, "..", "test-results");
|
||||
const testDir = path.join(__dirname, "..", "tests");
|
||||
|
||||
const DEFAULT_NAVIGATION_TIMEOUT = 600000;
|
||||
|
||||
const headless = !!process.env.CI || !!process.env.PLAYWRIGHT_HEADLESS;
|
||||
process.env.PLAYWRIGHT_TEST_BASE_URL = "http://localhost:3000";
|
||||
const quickMode = process.env.QUICK === "true";
|
||||
|
||||
const config: PlaywrightTestConfig = {
|
||||
forbidOnly: !!process.env.CI,
|
||||
retries: 0,
|
||||
workers: quickMode ? 1 : 1,
|
||||
timeout: 60_000,
|
||||
maxFailures: headless ? 10 : undefined,
|
||||
reporter: [
|
||||
[process.env.CI ? "github" : "list"],
|
||||
["html", { outputFolder: path.join(outputDir, "reports/playwright-html-report"), open: "never" }],
|
||||
["junit", { outputFile: path.join(outputDir, "reports/results.xml") }],
|
||||
],
|
||||
outputDir,
|
||||
use: {
|
||||
baseURL: "http://localhost:3000/",
|
||||
locale: "en-US",
|
||||
trace: "retain-on-failure",
|
||||
headless,
|
||||
},
|
||||
projects: [
|
||||
{
|
||||
name: "chromium",
|
||||
testDir,
|
||||
use: {
|
||||
...devices["Desktop Chrome"],
|
||||
/** If navigation takes more than this, then something's wrong, let's fail fast. */
|
||||
navigationTimeout: DEFAULT_NAVIGATION_TIMEOUT,
|
||||
},
|
||||
},
|
||||
/* {
|
||||
name: "firefox",
|
||||
use: { ...devices["Desktop Firefox"] },
|
||||
},
|
||||
{
|
||||
name: "webkit",
|
||||
use: { ...devices["Desktop Safari"] },
|
||||
}, */
|
||||
],
|
||||
};
|
||||
|
||||
export default config;
|
|
@ -0,0 +1,51 @@
|
|||
import { test as base } from "@playwright/test";
|
||||
import { Server } from "http";
|
||||
import { rest } from "msw";
|
||||
import type { SetupServerApi } from "msw/node";
|
||||
|
||||
import { nextServer } from "../../playwright-integrations/next-server";
|
||||
import { createBookingsFixture } from "../../playwright/fixtures/bookings";
|
||||
import { createPaymentsFixture } from "../../playwright/fixtures/payments";
|
||||
import { createUsersFixture } from "../../playwright/fixtures/users";
|
||||
|
||||
interface Fixtures {
|
||||
users: ReturnType<typeof createUsersFixture>;
|
||||
bookings: ReturnType<typeof createBookingsFixture>;
|
||||
payments: ReturnType<typeof createPaymentsFixture>;
|
||||
server: Server;
|
||||
requestInterceptor: SetupServerApi;
|
||||
rest: typeof rest;
|
||||
}
|
||||
|
||||
/**
|
||||
* @see https://playwright.dev/docs/test-fixtures
|
||||
*/
|
||||
export const test = base.extend<Fixtures>({
|
||||
users: async ({ page }, use, workerInfo) => {
|
||||
const usersFixture = createUsersFixture(page, workerInfo);
|
||||
await use(usersFixture);
|
||||
},
|
||||
bookings: async ({ page }, use) => {
|
||||
const bookingsFixture = createBookingsFixture(page);
|
||||
await use(bookingsFixture);
|
||||
},
|
||||
payments: async ({ page }, use) => {
|
||||
const payemntsFixture = createPaymentsFixture(page);
|
||||
await use(payemntsFixture);
|
||||
},
|
||||
// This fixture runs for each worker, ensuring that every worker starts it's own Next.js instance on which we can attach MSW
|
||||
// A single worker can run many tests
|
||||
server: [
|
||||
async ({}, use) => {
|
||||
const server = await nextServer();
|
||||
await use(server);
|
||||
server.close();
|
||||
},
|
||||
{
|
||||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
||||
//@ts-ignore
|
||||
scope: "worker",
|
||||
auto: true,
|
||||
},
|
||||
],
|
||||
});
|
|
@ -0,0 +1,52 @@
|
|||
import detect from "detect-port";
|
||||
import { createServer, Server } from "http";
|
||||
import next from "next";
|
||||
import { parse } from "url";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-namespace
|
||||
declare let process: {
|
||||
env: {
|
||||
E2E_DEV_SERVER: string;
|
||||
PLAYWRIGHT_TEST_BASE_URL: string;
|
||||
NEXT_PUBLIC_WEBAPP_URL: string;
|
||||
NEXT_PUBLIC_WEBSITE_URL: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const nextServer = async ({ port = 3000 } = { port: 3000 }) => {
|
||||
// eslint-disable-next-line turbo/no-undeclared-env-vars
|
||||
const dev = process.env.E2E_DEV_SERVER === "1" ? true : false;
|
||||
if (dev) {
|
||||
port = await detect(Math.round((1 + Math.random()) * 3000));
|
||||
}
|
||||
process.env.PLAYWRIGHT_TEST_BASE_URL =
|
||||
process.env.NEXT_PUBLIC_WEBAPP_URL =
|
||||
process.env.NEXT_PUBLIC_WEBSITE_URL =
|
||||
"http://localhost:" + port;
|
||||
const app = next({
|
||||
dev: dev,
|
||||
port,
|
||||
hostname: "localhost",
|
||||
});
|
||||
console.log("Started Next Server", { dev, port });
|
||||
|
||||
await app.prepare();
|
||||
const handle = app.getRequestHandler();
|
||||
// start next server on arbitrary port
|
||||
const server: Server = await new Promise((resolve) => {
|
||||
const server = createServer((req, res) => {
|
||||
if (!req.url) {
|
||||
throw new Error("URL not present");
|
||||
}
|
||||
const parsedUrl = parse(req.url, true);
|
||||
handle(req, res, parsedUrl);
|
||||
});
|
||||
server.listen({ port: port }, () => {
|
||||
resolve(server);
|
||||
});
|
||||
server.on("error", (error) => {
|
||||
if (error) throw new Error("Could not start Next.js server -" + error.message);
|
||||
});
|
||||
});
|
||||
return server;
|
||||
};
|
|
@ -0,0 +1,430 @@
|
|||
import { expect, Page, Route } from "@playwright/test";
|
||||
import { rest } from "msw";
|
||||
import { setupServer } from "msw/node";
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
|
||||
import { prisma } from "@calcom/prisma";
|
||||
import {
|
||||
createHttpServer,
|
||||
selectFirstAvailableTimeSlotNextMonth,
|
||||
todo,
|
||||
waitFor,
|
||||
} from "@calcom/web/playwright/lib/testUtils";
|
||||
|
||||
import { test } from "../lib/fixtures";
|
||||
|
||||
declare let global: {
|
||||
E2E_EMAILS?: ({ text: string } | Record<string, unknown>)[];
|
||||
};
|
||||
|
||||
const requestInterceptor = setupServer(
|
||||
rest.post("https://api.hubapi.com/oauth/v1/token", (req, res, ctx) => {
|
||||
console.log(req.body);
|
||||
return res(ctx.status(200));
|
||||
})
|
||||
);
|
||||
requestInterceptor.listen({
|
||||
// Comment this to log which all requests are going that are unmocked
|
||||
onUnhandledRequest: "bypass",
|
||||
});
|
||||
requestInterceptor.use();
|
||||
|
||||
const addOauthBasedIntegration = async function ({
|
||||
page,
|
||||
slug,
|
||||
authorization,
|
||||
token,
|
||||
}: {
|
||||
page: Page;
|
||||
slug: string;
|
||||
authorization: {
|
||||
url: string;
|
||||
verify: (config: {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
requestHeaders: any;
|
||||
params: URLSearchParams;
|
||||
code: string;
|
||||
}) => Parameters<Route["fulfill"]>[0];
|
||||
};
|
||||
token: {
|
||||
url: string;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
verify: (config: { requestHeaders: any; params: URLSearchParams; code: string }) => {
|
||||
status: number;
|
||||
body: any;
|
||||
};
|
||||
};
|
||||
}) {
|
||||
const code = uuidv4();
|
||||
// Note the difference b/w MSW wildcard and Playwright wildards. Playwright requires query params to be explicitly specified.
|
||||
page.route(`${authorization.url}?**`, (route, request) => {
|
||||
const u = new URL(request.url());
|
||||
const result = authorization.verify({
|
||||
requestHeaders: request.allHeaders(),
|
||||
params: u.searchParams,
|
||||
code,
|
||||
});
|
||||
|
||||
return route.fulfill(result);
|
||||
});
|
||||
requestInterceptor.use(
|
||||
rest.post(token.url, (req, res, ctx) => {
|
||||
const params = new URLSearchParams(req.body as string);
|
||||
const result = token.verify({ requestHeaders: req.headers, params, code });
|
||||
|
||||
return res(ctx.status(result.status), ctx.json(result.body));
|
||||
})
|
||||
);
|
||||
|
||||
await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/apps/${slug}`);
|
||||
await page.click('[data-testid="install-app-button"]');
|
||||
};
|
||||
|
||||
const addLocationIntegrationToFirstEvent = async function ({ user }: { user: { username: string | null } }) {
|
||||
const eventType = await prisma.eventType.findFirst({
|
||||
where: {
|
||||
users: {
|
||||
some: {
|
||||
username: user.username,
|
||||
},
|
||||
},
|
||||
price: 0,
|
||||
},
|
||||
});
|
||||
|
||||
if (!eventType) {
|
||||
throw new Error("Event type not found");
|
||||
}
|
||||
await prisma.eventType.update({
|
||||
where: {
|
||||
id: eventType.id,
|
||||
},
|
||||
data: {
|
||||
locations: [{ type: "integrations:zoom" }],
|
||||
},
|
||||
});
|
||||
return eventType;
|
||||
};
|
||||
|
||||
async function bookEvent(page: Page, calLink: string) {
|
||||
// Let current month dates fully render.
|
||||
// There is a bug where if we don't let current month fully render and quickly click go to next month, current month get's rendered
|
||||
// This doesn't seem to be replicable with the speed of a person, only during automation.
|
||||
// It would also allow correct snapshot to be taken for current month.
|
||||
// eslint-disable-next-line playwright/no-wait-for-timeout
|
||||
await page.waitForTimeout(1000);
|
||||
await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/${calLink}`);
|
||||
|
||||
await page.locator('[data-testid="day"][data-disabled="false"]').nth(0).click();
|
||||
page.locator('[data-testid="time"]').nth(0).click();
|
||||
await page.waitForNavigation({
|
||||
url(url) {
|
||||
return url.pathname.includes("/book");
|
||||
},
|
||||
});
|
||||
const meetingId = 123456789;
|
||||
|
||||
requestInterceptor.use(
|
||||
rest.post("https://api.zoom.us/v2/users/me/meetings", (req, res, ctx) => {
|
||||
return res(
|
||||
ctx.status(200),
|
||||
ctx.json({
|
||||
id: meetingId,
|
||||
password: "TestPass",
|
||||
join_url: `https://zoom.us/j/${meetingId}`,
|
||||
})
|
||||
);
|
||||
})
|
||||
);
|
||||
// --- fill form
|
||||
await page.fill('[name="name"]', "Integration User");
|
||||
await page.fill('[name="email"]', "integration-user@example.com");
|
||||
await page.press('[name="email"]', "Enter");
|
||||
const response = await page.waitForResponse("**/api/book/event");
|
||||
const responseObj = await response.json();
|
||||
const bookingId = responseObj.uid;
|
||||
await page.waitForSelector("[data-testid=success-page]");
|
||||
// Make sure we're navigated to the success page
|
||||
await expect(page.locator("[data-testid=success-page]")).toBeVisible();
|
||||
expect(global.E2E_EMAILS?.length).toBe(2);
|
||||
expect(
|
||||
global.E2E_EMAILS?.every((email) => (email.text as string).includes(`https://zoom.us/j/${meetingId}`))
|
||||
).toBe(true);
|
||||
return bookingId;
|
||||
}
|
||||
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
|
||||
test.describe("Integrations", () => {
|
||||
test.beforeEach(() => {
|
||||
global.E2E_EMAILS = [];
|
||||
});
|
||||
const addZoomIntegration = async function ({ page }: { page: Page }) {
|
||||
await addOauthBasedIntegration({
|
||||
page,
|
||||
slug: "zoom",
|
||||
authorization: {
|
||||
url: "https://zoom.us/oauth/authorize",
|
||||
verify({ params, code }) {
|
||||
expect(params.get("redirect_uri")).toBeTruthy();
|
||||
return {
|
||||
status: 307,
|
||||
headers: {
|
||||
location: `${params.get("redirect_uri")}?code=${code}`,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
token: {
|
||||
url: "https://zoom.us/oauth/token",
|
||||
verify({ requestHeaders, code }) {
|
||||
const authorization = requestHeaders.get("authorization").replace("Basic ", "");
|
||||
const clientPair = Buffer.from(authorization, "base64").toString();
|
||||
const [clientId, clientSecret] = clientPair.split(":");
|
||||
// Ensure that zoom credentials are passed.
|
||||
// TODO: We should also ensure that these credentials are correct e.g. in this case should be READ from DB
|
||||
expect(clientId).toBeTruthy();
|
||||
expect(clientSecret).toBeTruthy();
|
||||
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
access_token:
|
||||
"eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJ2ZXIiOiI2IiwiY2xpZW50SWQiOiI8Q2xpZW50X0lEPiIsImNvZGUiOiI8Q29kZT4iLCJpc3MiOiJ1cm46em9vbTpjb25uZWN0OmNsaWVudGlkOjxDbGllbnRfSUQ-IiwiYXV0aGVudGljYXRpb25JZCI6IjxBdXRoZW50aWNhdGlvbl9JRD4iLCJ1c2VySWQiOiI8VXNlcl9JRD4iLCJncm91cE51bWJlciI6MCwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwiYWNjb3VudElkIjoiPEFjY291bnRfSUQ-IiwibmJmIjoxNTgwMTQ2OTkzLCJleHAiOjE1ODAxNTA1OTMsInRva2VuVHlwZSI6ImFjY2Vzc190b2tlbiIsImlhdCI6MTU4MDE0Njk5MywianRpIjoiPEpUST4iLCJ0b2xlcmFuY2VJZCI6MjV9.F9o_w7_lde4Jlmk_yspIlDc-6QGmVrCbe_6El-xrZehnMx7qyoZPUzyuNAKUKcHfbdZa6Q4QBSvpd6eIFXvjHw",
|
||||
token_type: "bearer",
|
||||
refresh_token:
|
||||
"eyJhbGciOiJIUzUxMiIsInYiOiIyLjAiLCJraWQiOiI8S0lEPiJ9.eyJ2ZXIiOiI2IiwiY2xpZW50SWQiOiI8Q2xpZW50X0lEPiIsImNvZGUiOiI8Q29kZT4iLCJpc3MiOiJ1cm46em9vbTpjb25uZWN0OmNsaWVudGlkOjxDbGllbnRfSUQ-IiwiYXV0aGVudGljYXRpb25JZCI6IjxBdXRoZW50aWNhdGlvbl9JRD4iLCJ1c2VySWQiOiI8VXNlcl9JRD4iLCJncm91cE51bWJlciI6MCwiYXVkIjoiaHR0cHM6Ly9vYXV0aC56b29tLnVzIiwiYWNjb3VudElkIjoiPEFjY291bnRfSUQ-IiwibmJmIjoxNTgwMTQ2OTkzLCJleHAiOjIwNTMxODY5OTMsInRva2VuVHlwZSI6InJlZnJlc2hfdG9rZW4iLCJpYXQiOjE1ODAxNDY5OTMsImp0aSI6IjxKVEk-IiwidG9sZXJhbmNlSWQiOjI1fQ.Xcn_1i_tE6n-wy6_-3JZArIEbiP4AS3paSD0hzb0OZwvYSf-iebQBr0Nucupe57HUDB5NfR9VuyvQ3b74qZAfA",
|
||||
expires_in: 3599,
|
||||
// Without this permission, meeting can't be created.
|
||||
scope: "meeting:write",
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
};
|
||||
test.describe("Zoom App", () => {
|
||||
test.afterEach(async () => {
|
||||
await prisma?.credential.deleteMany({
|
||||
where: {
|
||||
user: {
|
||||
email: "pro@example.com",
|
||||
},
|
||||
type: "zoom_video",
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
test("Can add integration", async ({ page, users }) => {
|
||||
const user = await users.create();
|
||||
await user.login();
|
||||
await addZoomIntegration({ page });
|
||||
await page.waitForNavigation({
|
||||
url: (url) => {
|
||||
return url.pathname === "/apps/installed";
|
||||
},
|
||||
});
|
||||
//TODO: Check that disconnect button is now visible
|
||||
});
|
||||
|
||||
test("can choose zoom as a location during booking", async ({ page, users }) => {
|
||||
const user = await users.create();
|
||||
await user.login();
|
||||
const eventType = await addLocationIntegrationToFirstEvent({ user });
|
||||
await addZoomIntegration({ page });
|
||||
await page.waitForNavigation({
|
||||
url: (url) => {
|
||||
return url.pathname === "/apps/installed";
|
||||
},
|
||||
});
|
||||
|
||||
await bookEvent(page, `${user.username}/${eventType.slug}`);
|
||||
// Ensure that zoom was informed about the meeting
|
||||
// Verify that email had zoom link
|
||||
// POST https://api.zoom.us/v2/users/me/meetings
|
||||
// Verify Header-> Authorization: "Bearer " + accessToken,
|
||||
/**
|
||||
* {
|
||||
topic: event.title,
|
||||
type: 2, // Means that this is a scheduled meeting
|
||||
start_time: event.startTime,
|
||||
duration: (new Date(event.endTime).getTime() - new Date(event.startTime).getTime()) / 60000,
|
||||
//schedule_for: "string", TODO: Used when scheduling the meeting for someone else (needed?)
|
||||
timezone: event.attendees[0].timeZone,
|
||||
//password: "string", TODO: Should we use a password? Maybe generate a random one?
|
||||
agenda: event.description,
|
||||
settings: {
|
||||
host_video: true,
|
||||
participant_video: true,
|
||||
cn_meeting: false, // TODO: true if host meeting in China
|
||||
in_meeting: false, // TODO: true if host meeting in India
|
||||
join_before_host: true,
|
||||
mute_upon_entry: false,
|
||||
watermark: false,
|
||||
use_pmi: false,
|
||||
approval_type: 2,
|
||||
audio: "both",
|
||||
auto_recording: "none",
|
||||
enforce_login: false,
|
||||
registrants_email_notification: true,
|
||||
},
|
||||
};
|
||||
*/
|
||||
});
|
||||
test("Can disconnect from integration", async ({ page, users }) => {
|
||||
const user = await users.create();
|
||||
await user.login();
|
||||
await addZoomIntegration({ page });
|
||||
await page.waitForNavigation({
|
||||
url: (url) => {
|
||||
return url.pathname === "/apps/installed";
|
||||
},
|
||||
});
|
||||
|
||||
// FIXME: First time reaching /apps/installed throws error in UI.
|
||||
// Temporary use this hack to fix it but remove this HACK before merge.
|
||||
/** HACK STARTS */
|
||||
await page.locator('[href="/apps"]').first().click();
|
||||
await page.waitForNavigation({
|
||||
url: (url) => {
|
||||
return url.pathname === "/apps";
|
||||
},
|
||||
});
|
||||
await page.locator('[href="/apps/installed"]').first().click();
|
||||
/** HACK ENDS */
|
||||
|
||||
await page.locator('[data-testid="zoom_video-integration-disconnect-button"]').click();
|
||||
await page.locator('[data-testid="confirm-button"]').click();
|
||||
await expect(page.locator('[data-testid="confirm-integration-disconnect-button"]')).toHaveCount(0);
|
||||
});
|
||||
});
|
||||
|
||||
test.describe("Hubspot App", () => {
|
||||
test("Can add integration", async ({ page, users }) => {
|
||||
const user = await users.create();
|
||||
await user.login();
|
||||
await addOauthBasedIntegration({
|
||||
page,
|
||||
slug: "hubspot",
|
||||
authorization: {
|
||||
url: "https://app.hubspot.com/oauth/authorize",
|
||||
verify({ params, code }) {
|
||||
expect(params.get("redirect_uri")).toBeTruthy();
|
||||
// TODO: We can check if client_id is correctly read from DB or not
|
||||
expect(params.get("client_id")).toBeTruthy();
|
||||
expect(params.get("scope")).toBe(
|
||||
["crm.objects.contacts.read", "crm.objects.contacts.write"].join(" ")
|
||||
);
|
||||
|
||||
return {
|
||||
// TODO: Should
|
||||
status: 307,
|
||||
headers: {
|
||||
location: `${params.get("redirect_uri")}?code=${code}`,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
token: {
|
||||
url: "https://api.hubapi.com/oauth/v1/token",
|
||||
verify({ params, code }) {
|
||||
expect(params.get("grant_type")).toBe("authorization_code");
|
||||
expect(params.get("code")).toBe(code);
|
||||
expect(params.get("client_id")).toBeTruthy();
|
||||
expect(params.get("client_secret")).toBeTruthy();
|
||||
return {
|
||||
status: 200,
|
||||
body: {
|
||||
expiresIn: "3600",
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
});
|
||||
await page.waitForNavigation({
|
||||
url: (url) => {
|
||||
return url.pathname === "/apps/installed";
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
todo("Can add Google Calendar");
|
||||
|
||||
todo("Can add Office 365 Calendar");
|
||||
|
||||
todo("Can add CalDav Calendar");
|
||||
|
||||
todo("Can add Apple Calendar");
|
||||
|
||||
test("add webhook & test that creating an event triggers a webhook call", async ({
|
||||
page,
|
||||
users,
|
||||
}, testInfo) => {
|
||||
const webhookReceiver = createHttpServer();
|
||||
const user = await users.create();
|
||||
const [eventType] = user.eventTypes;
|
||||
await user.login();
|
||||
await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/settings/developer`);
|
||||
|
||||
// --- add webhook
|
||||
await page.click('[data-testid="new_webhook"]');
|
||||
|
||||
await expect(page.locator(`[data-testid='WebhookDialogForm']`)).toBeVisible();
|
||||
|
||||
await page.fill('[name="subscriberUrl"]', webhookReceiver.url);
|
||||
|
||||
await page.fill('[name="secret"]', "secret");
|
||||
|
||||
await page.click("[type=submit]");
|
||||
|
||||
// dialog is closed
|
||||
await expect(page.locator(`[data-testid='WebhookDialogForm']`)).not.toBeVisible();
|
||||
// page contains the url
|
||||
expect(page.locator(`text='${webhookReceiver.url}'`)).toBeDefined();
|
||||
|
||||
// --- Book the first available day next month in the pro user's "30min"-event
|
||||
await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/${user.username}/${eventType.slug}`);
|
||||
await selectFirstAvailableTimeSlotNextMonth(page);
|
||||
|
||||
// --- fill form
|
||||
await page.fill('[name="name"]', "Test Testson");
|
||||
await page.fill('[name="email"]', "test@example.com");
|
||||
await page.press('[name="email"]', "Enter");
|
||||
|
||||
// --- check that webhook was called
|
||||
await waitFor(() => {
|
||||
expect(webhookReceiver.requestList.length).toBe(1);
|
||||
});
|
||||
|
||||
const [request] = webhookReceiver.requestList;
|
||||
const body = request.body as any;
|
||||
|
||||
// remove dynamic properties that differs depending on where you run the tests
|
||||
const dynamic = "[redacted/dynamic]";
|
||||
body.createdAt = dynamic;
|
||||
body.payload.startTime = dynamic;
|
||||
body.payload.endTime = dynamic;
|
||||
body.payload.location = dynamic;
|
||||
for (const attendee of body.payload.attendees) {
|
||||
attendee.timeZone = dynamic;
|
||||
attendee.language = dynamic;
|
||||
}
|
||||
body.payload.organizer.email = dynamic;
|
||||
body.payload.organizer.timeZone = dynamic;
|
||||
body.payload.organizer.language = dynamic;
|
||||
body.payload.uid = dynamic;
|
||||
body.payload.bookingId = dynamic;
|
||||
body.payload.additionalInformation = dynamic;
|
||||
body.payload.requiresConfirmation = dynamic;
|
||||
body.payload.eventTypeId = dynamic;
|
||||
|
||||
// if we change the shape of our webhooks, we can simply update this by clicking `u`
|
||||
// console.log("BODY", body);
|
||||
// Text files shouldn't have platform specific suffixes
|
||||
testInfo.snapshotSuffix = "";
|
||||
expect(JSON.stringify(body)).toMatchSnapshot(`webhookResponse.txt`);
|
||||
|
||||
webhookReceiver.close();
|
||||
});
|
||||
});
|
|
@ -1,13 +1,19 @@
|
|||
import type { Page, WorkerInfo } from "@playwright/test";
|
||||
import type Prisma from "@prisma/client";
|
||||
import { Prisma as PrismaType, UserPlan } from "@prisma/client";
|
||||
import { hash } from "bcryptjs";
|
||||
|
||||
import { hashPassword } from "@calcom/lib/auth";
|
||||
import { DEFAULT_SCHEDULE, getAvailabilityFromSchedule } from "@calcom/lib/availability";
|
||||
import { prisma } from "@calcom/prisma";
|
||||
|
||||
import { TimeZoneEnum } from "./types";
|
||||
|
||||
// Don't import hashPassword from app as that ends up importing next-auth and initializing it before NEXTAUTH_URL can be updated during tests.
|
||||
export async function hashPassword(password: string) {
|
||||
const hashedPassword = await hash(password, 12);
|
||||
return hashedPassword;
|
||||
}
|
||||
|
||||
type UserFixture = ReturnType<typeof createUserFixture>;
|
||||
|
||||
const userIncludes = PrismaType.validator<PrismaType.UserInclude>()({
|
||||
|
@ -66,7 +72,7 @@ export const createUsersFixture = (page: Page, workerInfo: WorkerInfo) => {
|
|||
},
|
||||
get: () => store.users,
|
||||
logout: async () => {
|
||||
await page.goto("/auth/logout");
|
||||
await page.goto(`${process.env.PLAYWRIGHT_TEST_BASE_URL}/auth/logout`);
|
||||
},
|
||||
deleteAll: async () => {
|
||||
const ids = store.users.map((u) => u.id);
|
||||
|
@ -161,8 +167,10 @@ export async function login(
|
|||
const signInLocator = loginLocator.locator('[type="submit"]');
|
||||
|
||||
//login
|
||||
await page.goto("/");
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await page.goto(process.env.PLAYWRIGHT_TEST_BASE_URL!);
|
||||
await emailLocator.fill(user.email ?? `${user.username}@example.com`);
|
||||
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||
await passwordLocator.fill(user.password ?? user.username!);
|
||||
await signInLocator.click();
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ test.describe("Stripe integration", () => {
|
|||
|
||||
/** If Stripe is added correctly we should see the "Disconnect" button */
|
||||
await expect(
|
||||
page.locator(`li:has-text("Stripe") >> [data-testid="integration-connection-button"]`)
|
||||
page.locator(`li:has-text("Stripe") >> [data-testid="stripe_payment-integration-disconnect-button"]`)
|
||||
).toContainText("Disconnect");
|
||||
|
||||
// Cleanup
|
||||
|
|
|
@ -1,89 +0,0 @@
|
|||
import { expect } from "@playwright/test";
|
||||
|
||||
import { test } from "./lib/fixtures";
|
||||
import { createHttpServer, selectFirstAvailableTimeSlotNextMonth, todo, waitFor } from "./lib/testUtils";
|
||||
|
||||
test.describe.configure({ mode: "parallel" });
|
||||
|
||||
test.describe("Integrations", () => {
|
||||
todo("Can add Zoom integration");
|
||||
|
||||
todo("Can add Google Calendar");
|
||||
|
||||
todo("Can add Office 365 Calendar");
|
||||
|
||||
todo("Can add CalDav Calendar");
|
||||
|
||||
todo("Can add Apple Calendar");
|
||||
|
||||
test("add webhook & test that creating an event triggers a webhook call", async ({
|
||||
page,
|
||||
users,
|
||||
}, testInfo) => {
|
||||
const webhookReceiver = createHttpServer();
|
||||
const user = await users.create();
|
||||
const [eventType] = user.eventTypes;
|
||||
await user.login();
|
||||
await page.goto("/settings/developer");
|
||||
|
||||
// --- add webhook
|
||||
await page.click('[data-testid="new_webhook"]');
|
||||
|
||||
await expect(page.locator(`[data-testid='WebhookDialogForm']`)).toBeVisible();
|
||||
|
||||
await page.fill('[name="subscriberUrl"]', webhookReceiver.url);
|
||||
|
||||
await page.fill('[name="secret"]', "secret");
|
||||
|
||||
await page.click("[type=submit]");
|
||||
|
||||
// dialog is closed
|
||||
await expect(page.locator(`[data-testid='WebhookDialogForm']`)).not.toBeVisible();
|
||||
// page contains the url
|
||||
expect(page.locator(`text='${webhookReceiver.url}'`)).toBeDefined();
|
||||
|
||||
// --- Book the first available day next month in the pro user's "30min"-event
|
||||
await page.goto(`/${user.username}/${eventType.slug}`);
|
||||
await selectFirstAvailableTimeSlotNextMonth(page);
|
||||
|
||||
// --- fill form
|
||||
await page.fill('[name="name"]', "Test Testson");
|
||||
await page.fill('[name="email"]', "test@example.com");
|
||||
await page.press('[name="email"]', "Enter");
|
||||
|
||||
// --- check that webhook was called
|
||||
await waitFor(() => {
|
||||
expect(webhookReceiver.requestList.length).toBe(1);
|
||||
});
|
||||
|
||||
const [request] = webhookReceiver.requestList;
|
||||
const body = request.body as any;
|
||||
|
||||
// remove dynamic properties that differs depending on where you run the tests
|
||||
const dynamic = "[redacted/dynamic]";
|
||||
body.createdAt = dynamic;
|
||||
body.payload.startTime = dynamic;
|
||||
body.payload.endTime = dynamic;
|
||||
body.payload.location = dynamic;
|
||||
for (const attendee of body.payload.attendees) {
|
||||
attendee.timeZone = dynamic;
|
||||
attendee.language = dynamic;
|
||||
}
|
||||
body.payload.organizer.email = dynamic;
|
||||
body.payload.organizer.timeZone = dynamic;
|
||||
body.payload.organizer.language = dynamic;
|
||||
body.payload.uid = dynamic;
|
||||
body.payload.bookingId = dynamic;
|
||||
body.payload.additionalInformation = dynamic;
|
||||
body.payload.requiresConfirmation = dynamic;
|
||||
body.payload.eventTypeId = dynamic;
|
||||
|
||||
// if we change the shape of our webhooks, we can simply update this by clicking `u`
|
||||
// console.log("BODY", body);
|
||||
// Text files shouldn't have platform specific suffixes
|
||||
testInfo.snapshotSuffix = "";
|
||||
expect(JSON.stringify(body)).toMatchSnapshot(`webhookResponse.txt`);
|
||||
|
||||
webhookReceiver.close();
|
||||
});
|
||||
});
|
|
@ -51,7 +51,8 @@
|
|||
"test-playwright": "yarn playwright test --config=tests/config/playwright.config.ts",
|
||||
"test": "turbo run test",
|
||||
"turbo-w": "node turbo-wrapper.js",
|
||||
"type-check": "turbo run type-check"
|
||||
"type-check": "turbo run type-check",
|
||||
"test-e2e-integrations": "turbo run test-e2e-integrations --scope=\"@calcom/web\" --concurrency=1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@snaplet/copycat": "^0.3.0",
|
||||
|
|
|
@ -21,8 +21,24 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse)
|
|||
}
|
||||
);
|
||||
|
||||
if (result.status !== 200) {
|
||||
let errorMessage = "Something is wrong with Zoom API";
|
||||
try {
|
||||
const responseBody = await result.json();
|
||||
errorMessage = responseBody.error;
|
||||
} catch (e) {}
|
||||
|
||||
res.status(400).json({ message: errorMessage });
|
||||
return;
|
||||
}
|
||||
|
||||
const responseBody = await result.json();
|
||||
|
||||
if (responseBody.error) {
|
||||
res.status(400).json({ message: responseBody.error });
|
||||
return;
|
||||
}
|
||||
|
||||
responseBody.expiry_date = Math.round(Date.now() + responseBody.expires_in * 1000);
|
||||
delete responseBody.expires_in;
|
||||
|
||||
|
|
|
@ -244,7 +244,7 @@ const ZoomVideoApiAdapter = (credential: Credential): VideoApiAdapter => {
|
|||
url: result.join_url,
|
||||
});
|
||||
}
|
||||
return Promise.reject(new Error("Failed to create meeting"));
|
||||
return Promise.reject(new Error("Failed to create meeting. Response is " + JSON.stringify(result)));
|
||||
},
|
||||
deleteMeeting: async (uid: string): Promise<void> => {
|
||||
await fetchZoomApi(`meetings/${uid}`, {
|
||||
|
|
|
@ -4,6 +4,10 @@ import dayjs, { Dayjs } from "@calcom/dayjs";
|
|||
import { getErrorFromUnknown } from "@calcom/lib/errors";
|
||||
import { serverConfig } from "@calcom/lib/serverConfig";
|
||||
|
||||
declare let global: {
|
||||
E2E_EMAILS?: Record<string, unknown>[];
|
||||
};
|
||||
|
||||
export default class BaseEmail {
|
||||
name = "";
|
||||
|
||||
|
@ -23,7 +27,12 @@ export default class BaseEmail {
|
|||
return {};
|
||||
}
|
||||
public sendEmail() {
|
||||
if (process.env.NEXT_PUBLIC_IS_E2E) return new Promise((r) => r("Skipped sendEmail for E2E"));
|
||||
if (process.env.NEXT_PUBLIC_IS_E2E) {
|
||||
global.E2E_EMAILS = global.E2E_EMAILS || [];
|
||||
global.E2E_EMAILS.push(this.getNodeMailerPayload());
|
||||
console.log("Skipped Sending Email");
|
||||
return new Promise((r) => r("Skipped sendEmail for E2E"));
|
||||
}
|
||||
new Promise((resolve, reject) =>
|
||||
nodemailer
|
||||
.createTransport(this.getMailerOptions().transport)
|
||||
|
|
|
@ -63,7 +63,7 @@ export default function ConfirmationDialogContent(props: PropsWithChildren<Confi
|
|||
<div className="mt-5 flex flex-row-reverse gap-x-2 sm:mt-8">
|
||||
<DialogClose disabled={isLoading} onClick={onConfirm} asChild>
|
||||
{confirmBtn || (
|
||||
<Button color="primary" loading={isLoading}>
|
||||
<Button data-testid="confirm-button" color="primary" loading={isLoading}>
|
||||
{isLoading ? loadingText : confirmBtnText}
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -28,6 +28,7 @@ export async function loginAsUser(username: string, browser: Browser) {
|
|||
async function globalSetup(/* config: FullConfig */) {
|
||||
loadEnvConfig(process.env.PWD);
|
||||
const browser = await chromium.launch();
|
||||
|
||||
await loginAsUser("onboarding", browser);
|
||||
// await loginAsUser("free-first-hidden", browser);
|
||||
await loginAsUser("pro", browser);
|
||||
|
|
|
@ -137,6 +137,10 @@
|
|||
"cache": false,
|
||||
"dependsOn": ["@calcom/prisma#db-seed", "@calcom/web#build"]
|
||||
},
|
||||
"test-e2e-integrations": {
|
||||
"cache": false,
|
||||
"dependsOn": ["@calcom/prisma#db-seed", "@calcom/web#build"]
|
||||
},
|
||||
"type-check": {
|
||||
"cache": false,
|
||||
"outputs": []
|
||||
|
|
159
yarn.lock
159
yarn.lock
|
@ -3213,6 +3213,26 @@
|
|||
call-me-maybe "^1.0.1"
|
||||
glob-to-regexp "^0.3.0"
|
||||
|
||||
"@mswjs/cookies@^0.2.0":
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/@mswjs/cookies/-/cookies-0.2.1.tgz#66a283b45970ffc5350d22657983a1377ea6bf97"
|
||||
integrity sha512-0tDfcPw5/s7QsNQqS3knAvAD5w5PF1nNPagRhKO/yECY+sMbJxoC2sLWnH7Lzmh52mTSVLKDhd1r92Q3kfljnQ==
|
||||
dependencies:
|
||||
"@types/set-cookie-parser" "^2.4.0"
|
||||
set-cookie-parser "^2.4.6"
|
||||
|
||||
"@mswjs/interceptors@^0.16.3":
|
||||
version "0.16.6"
|
||||
resolved "https://registry.yarnpkg.com/@mswjs/interceptors/-/interceptors-0.16.6.tgz#c1a777ed3f69b55bbbc725b2deb827f160c0107c"
|
||||
integrity sha512-7ax1sRx5s4ZWl0KvVhhcPOUoPbCCkVh8M8hYaqOyvoAQOiqLVzy+Z6Mh2ywPhYw4zudr5Mo/E8UT/zJBO/Wxrw==
|
||||
dependencies:
|
||||
"@open-draft/until" "^1.0.3"
|
||||
"@xmldom/xmldom" "^0.7.5"
|
||||
debug "^4.3.3"
|
||||
headers-polyfill "^3.0.4"
|
||||
outvariant "^1.2.1"
|
||||
strict-event-emitter "^0.2.4"
|
||||
|
||||
"@next-auth/prisma-adapter@^1.0.4":
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/@next-auth/prisma-adapter/-/prisma-adapter-1.0.4.tgz#3e7304ac0615b8bfe425c81f96c40b40cafb59f0"
|
||||
|
@ -3369,6 +3389,11 @@
|
|||
mkdirp "^1.0.4"
|
||||
rimraf "^3.0.2"
|
||||
|
||||
"@open-draft/until@^1.0.3":
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/@open-draft/until/-/until-1.0.3.tgz#db9cc719191a62e7d9200f6e7bab21c5b848adca"
|
||||
integrity sha512-Aq58f5HiWdyDlFffbbSjAlv596h/cOnt2DO1w3DOC7OJ5EHs0hd/nycJfiu9RJbT6Yk6F1knnRRXNSpxoIVZ9Q==
|
||||
|
||||
"@otplib/core@^12.0.1":
|
||||
version "12.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@otplib/core/-/core-12.0.1.tgz#73720a8cedce211fe5b3f683cd5a9c098eaf0f8d"
|
||||
|
@ -5919,6 +5944,11 @@
|
|||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/cookie@^0.4.1":
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/cookie/-/cookie-0.4.1.tgz#bfd02c1f2224567676c1545199f87c3a861d878d"
|
||||
integrity sha512-XW/Aa8APYr6jSVVA1y/DEIZX0/GMKLEVekNG727R8cs56ahETkRAy/3DR7+fJyh7oUgGwNQaRfXCun0+KbWY7Q==
|
||||
|
||||
"@types/cross-spawn@6.0.2":
|
||||
version "6.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/cross-spawn/-/cross-spawn-6.0.2.tgz#168309de311cd30a2b8ae720de6475c2fbf33ac7"
|
||||
|
@ -5938,6 +5968,11 @@
|
|||
dependencies:
|
||||
"@types/ms" "*"
|
||||
|
||||
"@types/detect-port@^1.3.2":
|
||||
version "1.3.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/detect-port/-/detect-port-1.3.2.tgz#8c06a975e472803b931ee73740aeebd0a2eb27ae"
|
||||
integrity sha512-xxgAGA2SAU4111QefXPSp5eGbDm/hW6zhvYl9IeEPZEry9F4d66QAHm5qpUXjb6IsevZV/7emAEx5MhP6O192g==
|
||||
|
||||
"@types/eslint-scope@^3.7.3":
|
||||
version "3.7.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.4.tgz#37fc1223f0786c39627068a12e94d6e6fc61de16"
|
||||
|
@ -6121,6 +6156,11 @@
|
|||
expect "^28.0.0"
|
||||
pretty-format "^28.0.0"
|
||||
|
||||
"@types/js-levenshtein@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-levenshtein/-/js-levenshtein-1.1.1.tgz#ba05426a43f9e4e30b631941e0aa17bf0c890ed5"
|
||||
integrity sha512-qC4bCqYGy1y/NP7dDVr7KJarn+PbX1nSpwA7JXdu0HxT3QYjO8MJ+cntENtHFVy2dRAyBV23OZ6MxsW1AM1L8g==
|
||||
|
||||
"@types/js-yaml@^4.0.0":
|
||||
version "4.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.5.tgz#738dd390a6ecc5442f35e7f03fa1431353f7e138"
|
||||
|
@ -6433,6 +6473,13 @@
|
|||
"@types/mime" "^1"
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/set-cookie-parser@^2.4.0":
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/set-cookie-parser/-/set-cookie-parser-2.4.2.tgz#b6a955219b54151bfebd4521170723df5e13caad"
|
||||
integrity sha512-fBZgytwhYAUkj/jC/FAV4RQ5EerRup1YQsXQCh8rZfiHkc4UahC192oH0smGwsXol3cL3A5oETuAHeQHmhXM4w==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/source-list-map@*":
|
||||
version "0.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9"
|
||||
|
@ -7009,7 +7056,7 @@
|
|||
react-date-picker "^8.4.0"
|
||||
react-fit "^1.4.0"
|
||||
|
||||
"@xmldom/xmldom@0.7.5", "@xmldom/xmldom@^0.7.0":
|
||||
"@xmldom/xmldom@0.7.5", "@xmldom/xmldom@^0.7.0", "@xmldom/xmldom@^0.7.5":
|
||||
version "0.7.5"
|
||||
resolved "https://registry.yarnpkg.com/@xmldom/xmldom/-/xmldom-0.7.5.tgz#09fa51e356d07d0be200642b0e4f91d8e6dd408d"
|
||||
integrity sha512-V3BIhmY36fXZ1OtVcI9W+FxQqxVLsPKcNjWigIaa81dLC9IolJl5Mt4Cvhmr0flUnjSpTdrbMTSbXqYqV5dT6A==
|
||||
|
@ -8753,6 +8800,14 @@ chalk@2.3.0:
|
|||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^4.0.0"
|
||||
|
||||
chalk@4.1.1:
|
||||
version "4.1.1"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-4.1.1.tgz#c80b3fab28bf6371e6863325eee67e618b77e6ad"
|
||||
integrity sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
|
||||
chalk@4.1.2, chalk@^4.0.0, 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"
|
||||
|
@ -9401,7 +9456,7 @@ cookie@0.4.1:
|
|||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.1.tgz#afd713fe26ebd21ba95ceb61f9a8116e50a537d1"
|
||||
integrity sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==
|
||||
|
||||
cookie@0.4.2, cookie@^0.4.1:
|
||||
cookie@0.4.2, cookie@^0.4.1, cookie@^0.4.2:
|
||||
version "0.4.2"
|
||||
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432"
|
||||
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
|
||||
|
@ -9830,7 +9885,7 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9:
|
|||
dependencies:
|
||||
ms "2.0.0"
|
||||
|
||||
debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.4:
|
||||
debug@4, debug@4.3.4, debug@^4.0.0, debug@^4.1.0, debug@^4.1.1, debug@^4.3.1, debug@^4.3.2, debug@^4.3.3, debug@^4.3.4:
|
||||
version "4.3.4"
|
||||
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
|
||||
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
|
||||
|
@ -11400,7 +11455,7 @@ eventemitter3@^4.0.4:
|
|||
resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f"
|
||||
integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==
|
||||
|
||||
events@^3.0.0, events@^3.1.0, events@^3.2.0:
|
||||
events@^3.0.0, events@^3.1.0, events@^3.2.0, events@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400"
|
||||
integrity sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==
|
||||
|
@ -12827,6 +12882,11 @@ grapheme-splitter@^1.0.4:
|
|||
resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
|
||||
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
|
||||
|
||||
graphql@^16.3.0:
|
||||
version "16.5.0"
|
||||
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.5.0.tgz#41b5c1182eaac7f3d47164fb247f61e4dfb69c85"
|
||||
integrity sha512-qbHgh8Ix+j/qY+a/ZcJnFQ+j8ezakqPiHwPiZhV/3PgGlgf96QMBB5/f2rkiC9sgLoy/xvT6TSiaf2nTHJh5iA==
|
||||
|
||||
gray-matter@^4.0.3:
|
||||
version "4.0.3"
|
||||
resolved "https://registry.yarnpkg.com/gray-matter/-/gray-matter-4.0.3.tgz#e893c064825de73ea1f5f7d88c7a9f7274288798"
|
||||
|
@ -13142,6 +13202,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@^3.0.4:
|
||||
version "3.0.7"
|
||||
resolved "https://registry.yarnpkg.com/headers-polyfill/-/headers-polyfill-3.0.7.tgz#725c4f591e6748f46b036197eae102c92b959ff4"
|
||||
integrity sha512-JoLCAdCEab58+2/yEmSnOlficyHFpIl0XJqwu3l+Unkm1gXpFUYsThz6Yha3D6tNhocWkCPfyW0YVIGWFqTi7w==
|
||||
|
||||
highlight.js@^10.4.1, highlight.js@^10.7.1, highlight.js@~10.7.0:
|
||||
version "10.7.3"
|
||||
resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531"
|
||||
|
@ -13671,6 +13736,27 @@ inquirer@8.2.1:
|
|||
strip-ansi "^6.0.0"
|
||||
through "^2.3.6"
|
||||
|
||||
inquirer@^8.2.0:
|
||||
version "8.2.4"
|
||||
resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-8.2.4.tgz#ddbfe86ca2f67649a67daa6f1051c128f684f0b4"
|
||||
integrity sha512-nn4F01dxU8VeKfq192IjLsxu0/OmMZ4Lg3xKAns148rCaXP6ntAoEkVYZThWjwON8AlzdZZi6oqnhNbxUG9hVg==
|
||||
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 "^7.0.0"
|
||||
|
||||
internal-slot@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.3.tgz#7347e307deeea2faac2ac6205d4bc7d34967f59c"
|
||||
|
@ -14045,6 +14131,11 @@ is-negative-zero@^2.0.2:
|
|||
resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150"
|
||||
integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==
|
||||
|
||||
is-node-process@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/is-node-process/-/is-node-process-1.0.1.tgz#4fc7ac3a91e8aac58175fe0578abbc56f2831b23"
|
||||
integrity sha512-5IcdXuf++TTNt3oGl9EBdkvndXA8gmc4bz/Y+mdEpWh3Mcn/+kOw6hI7LD5CocqJWMzeb0I0ClndRVNdEPuJXQ==
|
||||
|
||||
is-number-object@^1.0.4:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/is-number-object/-/is-number-object-1.0.7.tgz#59d50ada4c45251784e9904f5246c742f07a42fc"
|
||||
|
@ -14863,6 +14954,11 @@ js-file-download@^0.4.12:
|
|||
resolved "https://registry.yarnpkg.com/js-file-download/-/js-file-download-0.4.12.tgz#10c70ef362559a5b23cdbdc3bd6f399c3d91d821"
|
||||
integrity sha512-rML+NkoD08p5Dllpjo0ffy4jRHeY6Zsapvr/W86N7E0yuzAO6qa5X9+xog6zQNlH102J7IXljNY2FtS6Lj3ucg==
|
||||
|
||||
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-sha3@0.8.0, js-sha3@^0.8.0:
|
||||
version "0.8.0"
|
||||
resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840"
|
||||
|
@ -16905,6 +17001,32 @@ ms@2.1.3, ms@^2.1.1, ms@^2.1.2:
|
|||
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2"
|
||||
integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==
|
||||
|
||||
msw@^0.42.3:
|
||||
version "0.42.3"
|
||||
resolved "https://registry.yarnpkg.com/msw/-/msw-0.42.3.tgz#150c475e2cb6d53c67503bd0e3f6251bfd075328"
|
||||
integrity sha512-zrKBIGCDsNUCZLd3DLSeUtRruZ0riwJgORg9/bSDw3D0PTI8XUGAK3nC0LJA9g0rChGuKaWK/SwObA8wpFrz4g==
|
||||
dependencies:
|
||||
"@mswjs/cookies" "^0.2.0"
|
||||
"@mswjs/interceptors" "^0.16.3"
|
||||
"@open-draft/until" "^1.0.3"
|
||||
"@types/cookie" "^0.4.1"
|
||||
"@types/js-levenshtein" "^1.1.1"
|
||||
chalk "4.1.1"
|
||||
chokidar "^3.4.2"
|
||||
cookie "^0.4.2"
|
||||
graphql "^16.3.0"
|
||||
headers-polyfill "^3.0.4"
|
||||
inquirer "^8.2.0"
|
||||
is-node-process "^1.0.1"
|
||||
js-levenshtein "^1.1.6"
|
||||
node-fetch "^2.6.7"
|
||||
outvariant "^1.3.0"
|
||||
path-to-regexp "^6.2.0"
|
||||
statuses "^2.0.0"
|
||||
strict-event-emitter "^0.2.0"
|
||||
type-fest "^1.2.2"
|
||||
yargs "^17.3.1"
|
||||
|
||||
multibase@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/multibase/-/multibase-0.7.0.tgz#1adfc1c50abe05eefeb5091ac0c2728d6b84581b"
|
||||
|
@ -17796,6 +17918,11 @@ otplib@^12.0.1:
|
|||
"@otplib/preset-default" "^12.0.1"
|
||||
"@otplib/preset-v11" "^12.0.1"
|
||||
|
||||
outvariant@^1.2.1, outvariant@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/outvariant/-/outvariant-1.3.0.tgz#c39723b1d2cba729c930b74bf962317a81b9b1c9"
|
||||
integrity sha512-yeWM9k6UPfG/nzxdaPlJkB2p08hCg4xP6Lx99F+vP8YF7xyZVfTmJjrrNalkmzudD4WFvNLVudQikqUmF8zhVQ==
|
||||
|
||||
p-all@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-all/-/p-all-2.1.0.tgz#91419be56b7dee8fe4c5db875d55e0da084244a0"
|
||||
|
@ -18217,6 +18344,11 @@ path-to-regexp@0.1.7:
|
|||
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c"
|
||||
integrity sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=
|
||||
|
||||
path-to-regexp@^6.2.0:
|
||||
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==
|
||||
|
||||
path-type@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441"
|
||||
|
@ -20664,6 +20796,11 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
|
|||
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
|
||||
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
|
||||
|
||||
set-cookie-parser@^2.4.6:
|
||||
version "2.5.0"
|
||||
resolved "https://registry.yarnpkg.com/set-cookie-parser/-/set-cookie-parser-2.5.0.tgz#96b59525e1362c94335c3c761100bb6e8f2da4b0"
|
||||
integrity sha512-cHMAtSXilfyBePduZEBVPTCftTQWz6ehWJD5YNUg4mqvRosrrjKbo4WS8JkB0/RxonMoohHm7cOGH60mDkRQ9w==
|
||||
|
||||
set-value@^2.0.0, set-value@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/set-value/-/set-value-2.0.1.tgz#a18d40530e6f07de4228c7defe4227af8cad005b"
|
||||
|
@ -21113,7 +21250,7 @@ static-extend@^0.1.1:
|
|||
define-property "^0.2.5"
|
||||
object-copy "^0.1.0"
|
||||
|
||||
statuses@2.0.1:
|
||||
statuses@2.0.1, statuses@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/statuses/-/statuses-2.0.1.tgz#55cb000ccf1d48728bd23c685a063998cf1a1b63"
|
||||
integrity sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==
|
||||
|
@ -21189,6 +21326,13 @@ stream-shift@^1.0.0:
|
|||
resolved "https://registry.yarnpkg.com/stream-shift/-/stream-shift-1.0.1.tgz#d7088281559ab2778424279b0877da3c392d5a3d"
|
||||
integrity sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==
|
||||
|
||||
strict-event-emitter@^0.2.0, strict-event-emitter@^0.2.4:
|
||||
version "0.2.4"
|
||||
resolved "https://registry.yarnpkg.com/strict-event-emitter/-/strict-event-emitter-0.2.4.tgz#365714f0c95f059db31064ca745d5b33e5b30f6e"
|
||||
integrity sha512-xIqTLS5azUH1djSUsLH9DbP6UnM/nI18vu8d43JigCQEoVsnY+mrlE+qv6kYqs6/1OkMnMIiL6ffedQSZStuoQ==
|
||||
dependencies:
|
||||
events "^3.3.0"
|
||||
|
||||
strict-uri-encode@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz#279b225df1d582b1f54e65addd4352e18faa0713"
|
||||
|
@ -22496,6 +22640,11 @@ type-fest@^0.8.1:
|
|||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.8.1.tgz#09e249ebde851d3b1e48d27c105444667f17b83d"
|
||||
integrity sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==
|
||||
|
||||
type-fest@^1.2.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-1.4.0.tgz#e9fb813fe3bf1744ec359d55d1affefa76f14be1"
|
||||
integrity sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==
|
||||
|
||||
type-flag@^2.1.0:
|
||||
version "2.2.0"
|
||||
resolved "https://registry.yarnpkg.com/type-flag/-/type-flag-2.2.0.tgz#56ed3a79a3011bafba3ceb9d1a5acb633ade7884"
|
||||
|
|
Loading…
Reference in New Issue