feat: dynamic graphQL schema generation based on user workspace (#1725)

* wip: refacto and start creating custom resolver

* feat: findMany & findUnique of a custom entity

* feat: wip pagination

* feat: initial metadata migration

* feat: universal findAll with pagination

* fix: clean small stuff in pagination

* fix: test

* fix: miss file

* feat: rename custom into universal

* feat: create metadata schema in default database

* Multi-tenant db schemas POC

fix tests and use query builders

remove synchronize

restore updatedAt

remove unnecessary import

use queryRunner

fix camelcase

add migrations for standard objects

Multi-tenant db schemas POC

fix tests and use query builders

remove synchronize

restore updatedAt

remove unnecessary import

use queryRunner

fix camelcase

add migrations for standard objects

poc: conditional schema at runtime

wip: try to create resolver in Nest.JS context

fix

* feat: wip add pg_graphql

* feat: setup pg_graphql during database init

* wip: dynamic resolver

* poc: dynamic resolver and query using pg_graphql

* feat: pg_graphql use ARG in Dockerfile

* feat: clean findMany & findOne dynamic resolver

* feat: get correct schema based on access token

* fix: remove old file

* fix: tests

* fix: better comment

* fix: e2e test not working, error format change due to yoga

* remove typeorm entity generation + fix jwt + fix search_path + remove anon

* fix conflict

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
Co-authored-by: corentin <corentin@twenty.com>
This commit is contained in:
Jérémy M 2023-09-28 16:27:34 +02:00 committed by GitHub
parent 485bc64b4f
commit 629bdbbf50
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
35 changed files with 1860 additions and 124 deletions

View File

@ -28,7 +28,9 @@ services:
volumes:
- db_data:/var/lib/postgresql/data
environment:
POSTGRES_PASSWORD: twenty
- POSTGRES_USER=twenty
- POSTGRES_PASSWORD=twenty
- POSTGRES_DB=default
ports:
- "5432:5432"
volumes:

View File

@ -1,5 +1,15 @@
FROM postgres:13.7 as postgres
ARG PG_MAIN_VERSION=14
FROM postgres:${PG_MAIN_VERSION} as postgres
ARG PG_MAIN_VERSION
ARG PG_GRAPHQL_VERSION=1.3.0
ARG TARGETARCH=arm64
RUN apt update && apt install -y curl
# Install precompiled pg_graphql extensions
RUN curl -L "https://github.com/supabase/pg_graphql/releases/download/v${PG_GRAPHQL_VERSION}/pg_graphql-v${PG_GRAPHQL_VERSION}-pg${PG_MAIN_VERSION}-${TARGETARCH}-linux-gnu.deb" -o pg_graphql.deb
RUN dpkg --install pg_graphql.deb
COPY init.sql /docker-entrypoint-initdb.d/

View File

@ -1,25 +1,46 @@
-- Create the default database for development
CREATE DATABASE "default";
-- Create the tests database for e2e testing
CREATE DATABASE "test";
-- Create a twenty user
CREATE USER twenty PASSWORD 'twenty';
ALTER USER twenty CREATEDB;
-- Inflect names for pg_graphql
COMMENT ON SCHEMA "public" IS '@graphql({"inflect_names": true})';
-- Connect to the "default" database
\c "default";
-- Create extension
CREATE EXTENSION IF NOT EXISTS pg_graphql;
-- Create the metadata schema if it doesn't exist
CREATE SCHEMA IF NOT EXISTS "metadata";
GRANT ALL ON SCHEMA metadata TO twenty;
-- Create extension uuid-ossp
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
-- Connect to the "default" database
-- Create GraphQL Entrypoint
create function graphql(
"operationName" text default null,
query text default null,
variables jsonb default null,
extensions jsonb default null
)
returns jsonb
language sql
as $$
select graphql.resolve(
query := query,
variables := coalesce(variables, '{}'),
"operationName" := "operationName",
extensions := extensions
);
$$;
-- Create the tests database for e2e testing
CREATE DATABASE "test";
-- Connect to the "test" database for e2e testing
\c "test";
-- Create the metadata schema if it doesn't exist
CREATE SCHEMA IF NOT EXISTS "metadata";
GRANT ALL ON SCHEMA metadata TO twenty;
-- Create extension uuid-ossp
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";

View File

@ -6,6 +6,7 @@
"private": true,
"license": "UNLICENSED",
"scripts": {
"postinstall": "patch-package",
"prebuild": "rimraf dist",
"build": "nest build",
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
@ -35,11 +36,12 @@
"@aws-sdk/credential-providers": "^3.363.0",
"@casl/ability": "^6.5.0",
"@casl/prisma": "1.4.0",
"@graphql-yoga/nestjs": "^2.1.0",
"@nestjs/apollo": "^11.0.5",
"@nestjs/common": "^9.0.0",
"@nestjs/config": "^2.3.2",
"@nestjs/core": "^9.0.0",
"@nestjs/graphql": "^11.0.6",
"@nestjs/graphql": "^12.0.8",
"@nestjs/jwt": "^10.0.3",
"@nestjs/passport": "^9.0.3",
"@nestjs/platform-express": "^9.0.0",
@ -62,12 +64,15 @@
"class-validator": "^0.14.0",
"date-fns": "^2.30.0",
"file-type": "16.5.4",
"graphql": "^16.7.1",
"graphql": "^16.8.0",
"graphql-fields": "^2.0.3",
"graphql-type-json": "^0.3.2",
"graphql-upload": "^13.0.0",
"graphql-yoga": "^4.0.4",
"jest-mock-extended": "^3.0.4",
"jsonwebtoken": "^9.0.0",
"lodash.camelcase": "^4.3.0",
"lodash.isempty": "^4.4.0",
"lodash.isobject": "^3.0.2",
"lodash.kebabcase": "^4.1.1",
"lodash.merge": "^4.6.2",
@ -77,7 +82,9 @@
"passport-google-oauth20": "^2.0.0",
"passport-jwt": "^4.0.1",
"passport-local": "^1.0.0",
"patch-package": "^8.0.0",
"pg": "^8.11.3",
"postinstall-postinstall": "^2.1.0",
"prisma-graphql-type-decimal": "^3.0.0",
"reflect-metadata": "^0.1.13",
"rimraf": "^3.0.2",
@ -96,8 +103,10 @@
"@types/bytes": "^3.1.1",
"@types/date-fns": "^2.6.0",
"@types/express": "^4.17.13",
"@types/graphql-fields": "^1.3.6",
"@types/graphql-upload": "^8.0.12",
"@types/jest": "28.1.8",
"@types/lodash.isempty": "^4.4.7",
"@types/lodash.isobject": "^3.0.7",
"@types/lodash.kebabcase": "^4.1.7",
"@types/lodash.snakecase": "^4.1.7",
@ -127,6 +136,9 @@
"tsconfig-paths": "4.1.0",
"typescript": "^4.9.4"
},
"resolutions": {
"graphql": "16.8.0"
},
"prisma": {
"schema": "src/database/schema.prisma",
"seed": "ts-node src/database/seeds/index.ts"

View File

@ -0,0 +1,329 @@
diff --git a/node_modules/@graphql-yoga/nestjs/dist/cjs/index.js b/node_modules/@graphql-yoga/nestjs/dist/cjs/index.js
index 1684394..8a92c3c 100644
--- a/node_modules/@graphql-yoga/nestjs/dist/cjs/index.js
+++ b/node_modules/@graphql-yoga/nestjs/dist/cjs/index.js
@@ -5,6 +5,7 @@ const tslib_1 = require("tslib");
const graphql_1 = require("graphql");
const graphql_yoga_1 = require("graphql-yoga");
const common_1 = require("@nestjs/common");
+const schema_1 = require("@graphql-tools/schema");
const graphql_2 = require("@nestjs/graphql");
class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
async start(options) {
@@ -27,7 +28,7 @@ class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
async stop() {
// noop
}
- registerExpress(options, { preStartHook } = {}) {
+ registerExpress({ conditionalSchema, ...options }, { preStartHook } = {}) {
const app = this.httpAdapterHost.httpAdapter.getInstance();
preStartHook?.(app);
// nest's logger doesnt have the info method
@@ -42,6 +43,21 @@ class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
}
const yoga = (0, graphql_yoga_1.createYoga)({
...options,
+ schema: async (request) => {
+ const schemas = [];
+ if (options.schema) {
+ schemas.push(options.schema);
+ }
+ if (conditionalSchema) {
+ const conditionalSchemaResult = typeof conditionalSchema === 'function' ? await conditionalSchema(request) : await conditionalSchema;
+ if (conditionalSchemaResult) {
+ schemas.push(conditionalSchemaResult);
+ }
+ }
+ return (0, schema_1.mergeSchemas)({
+ schemas,
+ });
+ },
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use nest logger
@@ -54,11 +70,26 @@ class AbstractYogaDriver extends graphql_2.AbstractGraphQLDriver {
this.yoga = yoga;
app.use(yoga.graphqlEndpoint, (req, res) => yoga(req, res, { req, res }));
}
- registerFastify(options, { preStartHook } = {}) {
+ registerFastify({ conditionalSchema, ...options }, { preStartHook } = {}) {
const app = this.httpAdapterHost.httpAdapter.getInstance();
preStartHook?.(app);
const yoga = (0, graphql_yoga_1.createYoga)({
...options,
+ schema: async (request) => {
+ const schemas = [];
+ if (options.schema) {
+ schemas.push(options.schema);
+ }
+ if (conditionalSchema) {
+ const conditionalSchemaResult = typeof conditionalSchema === 'function' ? await conditionalSchema(request) : await conditionalSchema;
+ if (conditionalSchemaResult) {
+ schemas.push(conditionalSchemaResult);
+ }
+ }
+ return (0, schema_1.mergeSchemas)({
+ schemas,
+ });
+ },
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use fastify logger
diff --git a/node_modules/@graphql-yoga/nestjs/dist/esm/index.js b/node_modules/@graphql-yoga/nestjs/dist/esm/index.js
index 7068c51..8ba5d2a 100644
--- a/node_modules/@graphql-yoga/nestjs/dist/esm/index.js
+++ b/node_modules/@graphql-yoga/nestjs/dist/esm/index.js
@@ -2,6 +2,7 @@ import { __decorate } from "tslib";
import { printSchema } from 'graphql';
import { createYoga, filter, pipe } from 'graphql-yoga';
import { Injectable, Logger } from '@nestjs/common';
+import { mergeSchemas } from '@graphql-tools/schema';
import { AbstractGraphQLDriver, GqlSubscriptionService, } from '@nestjs/graphql';
export class AbstractYogaDriver extends AbstractGraphQLDriver {
async start(options) {
@@ -24,7 +25,7 @@ export class AbstractYogaDriver extends AbstractGraphQLDriver {
async stop() {
// noop
}
- registerExpress(options, { preStartHook } = {}) {
+ registerExpress({ conditionalSchema, ...options }, { preStartHook } = {}) {
const app = this.httpAdapterHost.httpAdapter.getInstance();
preStartHook?.(app);
// nest's logger doesnt have the info method
@@ -39,6 +40,21 @@ export class AbstractYogaDriver extends AbstractGraphQLDriver {
}
const yoga = createYoga({
...options,
+ schema: async (request) => {
+ const schemas = [];
+ if (options.schema) {
+ schemas.push(options.schema);
+ }
+ if (conditionalSchema) {
+ const conditionalSchemaResult = typeof conditionalSchema === 'function' ? await conditionalSchema(request) : await conditionalSchema;
+ if (conditionalSchemaResult) {
+ schemas.push(conditionalSchemaResult);
+ }
+ }
+ return mergeSchemas({
+ schemas,
+ });
+ },
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use nest logger
@@ -51,11 +67,26 @@ export class AbstractYogaDriver extends AbstractGraphQLDriver {
this.yoga = yoga;
app.use(yoga.graphqlEndpoint, (req, res) => yoga(req, res, { req, res }));
}
- registerFastify(options, { preStartHook } = {}) {
+ registerFastify({ conditionalSchema, ...options }, { preStartHook } = {}) {
const app = this.httpAdapterHost.httpAdapter.getInstance();
preStartHook?.(app);
const yoga = createYoga({
...options,
+ schema: async (request) => {
+ const schemas = [];
+ if (options.schema) {
+ schemas.push(options.schema);
+ }
+ if (conditionalSchema) {
+ const conditionalSchemaResult = typeof conditionalSchema === 'function' ? await conditionalSchema(request) : await conditionalSchema;
+ if (conditionalSchemaResult) {
+ schemas.push(conditionalSchemaResult);
+ }
+ }
+ return mergeSchemas({
+ schemas,
+ });
+ },
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use fastify logger
diff --git a/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.cts b/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.cts
index 2c6a965..fd86dac 100644
--- a/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.cts
+++ b/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.cts
@@ -1,7 +1,8 @@
import type { Express, Request as ExpressRequest, Response as ExpressResponse } from 'express';
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
-import { YogaServerInstance, YogaServerOptions } from 'graphql-yoga';
+import { YogaServerInstance, YogaServerOptions, GraphQLSchemaWithContext, PromiseOrValue, YogaInitialContext } from 'graphql-yoga';
import { AbstractGraphQLDriver, GqlModuleOptions, SubscriptionConfig } from '@nestjs/graphql';
+export type YogaSchemaDefinition<TContext> = PromiseOrValue<GraphQLSchemaWithContext<TContext>> | ((context: TContext & YogaInitialContext) => PromiseOrValue<GraphQLSchemaWithContext<TContext>>);
export type YogaDriverPlatform = 'express' | 'fastify';
export type YogaDriverServerContext<Platform extends YogaDriverPlatform> = Platform extends 'fastify' ? {
req: FastifyRequest;
@@ -10,7 +11,9 @@ export type YogaDriverServerContext<Platform extends YogaDriverPlatform> = Platf
req: ExpressRequest;
res: ExpressResponse;
};
-export type YogaDriverServerOptions<Platform extends YogaDriverPlatform> = Omit<YogaServerOptions<YogaDriverServerContext<Platform>, never>, 'context' | 'schema'>;
+export type YogaDriverServerOptions<Platform extends YogaDriverPlatform> = Omit<YogaServerOptions<YogaDriverServerContext<Platform>, never>, 'context' | 'schema'> & {
+ conditionalSchema?: YogaSchemaDefinition<YogaDriverServerContext<Platform>> | undefined;
+};
export type YogaDriverServerInstance<Platform extends YogaDriverPlatform> = YogaServerInstance<YogaDriverServerContext<Platform>, never>;
export type YogaDriverConfig<Platform extends YogaDriverPlatform = 'express'> = GqlModuleOptions & YogaDriverServerOptions<Platform> & {
/**
@@ -26,10 +29,10 @@ export declare abstract class AbstractYogaDriver<Platform extends YogaDriverPlat
protected yoga: YogaDriverServerInstance<Platform>;
start(options: YogaDriverConfig<Platform>): Promise<void>;
stop(): Promise<void>;
- protected registerExpress(options: YogaDriverConfig<'express'>, { preStartHook }?: {
+ protected registerExpress({ conditionalSchema, ...options }: YogaDriverConfig<'express'>, { preStartHook }?: {
preStartHook?: (app: Express) => void;
}): void;
- protected registerFastify(options: YogaDriverConfig<'fastify'>, { preStartHook }?: {
+ protected registerFastify({ conditionalSchema, ...options }: YogaDriverConfig<'fastify'>, { preStartHook }?: {
preStartHook?: (app: FastifyInstance) => void;
}): void;
subscriptionWithFilter<TPayload, TVariables, TContext>(instanceRef: unknown, filterFn: (payload: TPayload, variables: TVariables, context: TContext) => boolean | Promise<boolean>, createSubscribeContext: Function): (args_0: TPayload, args_1: TVariables, args_2: TContext) => Promise<import("graphql-yoga").Repeater<TPayload, void, unknown>>;
diff --git a/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.ts b/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.ts
index 2c6a965..fd86dac 100644
--- a/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.ts
+++ b/node_modules/@graphql-yoga/nestjs/dist/typings/index.d.ts
@@ -1,7 +1,8 @@
import type { Express, Request as ExpressRequest, Response as ExpressResponse } from 'express';
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
-import { YogaServerInstance, YogaServerOptions } from 'graphql-yoga';
+import { YogaServerInstance, YogaServerOptions, GraphQLSchemaWithContext, PromiseOrValue, YogaInitialContext } from 'graphql-yoga';
import { AbstractGraphQLDriver, GqlModuleOptions, SubscriptionConfig } from '@nestjs/graphql';
+export type YogaSchemaDefinition<TContext> = PromiseOrValue<GraphQLSchemaWithContext<TContext>> | ((context: TContext & YogaInitialContext) => PromiseOrValue<GraphQLSchemaWithContext<TContext>>);
export type YogaDriverPlatform = 'express' | 'fastify';
export type YogaDriverServerContext<Platform extends YogaDriverPlatform> = Platform extends 'fastify' ? {
req: FastifyRequest;
@@ -10,7 +11,9 @@ export type YogaDriverServerContext<Platform extends YogaDriverPlatform> = Platf
req: ExpressRequest;
res: ExpressResponse;
};
-export type YogaDriverServerOptions<Platform extends YogaDriverPlatform> = Omit<YogaServerOptions<YogaDriverServerContext<Platform>, never>, 'context' | 'schema'>;
+export type YogaDriverServerOptions<Platform extends YogaDriverPlatform> = Omit<YogaServerOptions<YogaDriverServerContext<Platform>, never>, 'context' | 'schema'> & {
+ conditionalSchema?: YogaSchemaDefinition<YogaDriverServerContext<Platform>> | undefined;
+};
export type YogaDriverServerInstance<Platform extends YogaDriverPlatform> = YogaServerInstance<YogaDriverServerContext<Platform>, never>;
export type YogaDriverConfig<Platform extends YogaDriverPlatform = 'express'> = GqlModuleOptions & YogaDriverServerOptions<Platform> & {
/**
@@ -26,10 +29,10 @@ export declare abstract class AbstractYogaDriver<Platform extends YogaDriverPlat
protected yoga: YogaDriverServerInstance<Platform>;
start(options: YogaDriverConfig<Platform>): Promise<void>;
stop(): Promise<void>;
- protected registerExpress(options: YogaDriverConfig<'express'>, { preStartHook }?: {
+ protected registerExpress({ conditionalSchema, ...options }: YogaDriverConfig<'express'>, { preStartHook }?: {
preStartHook?: (app: Express) => void;
}): void;
- protected registerFastify(options: YogaDriverConfig<'fastify'>, { preStartHook }?: {
+ protected registerFastify({ conditionalSchema, ...options }: YogaDriverConfig<'fastify'>, { preStartHook }?: {
preStartHook?: (app: FastifyInstance) => void;
}): void;
subscriptionWithFilter<TPayload, TVariables, TContext>(instanceRef: unknown, filterFn: (payload: TPayload, variables: TVariables, context: TContext) => boolean | Promise<boolean>, createSubscribeContext: Function): (args_0: TPayload, args_1: TVariables, args_2: TContext) => Promise<import("graphql-yoga").Repeater<TPayload, void, unknown>>;
diff --git a/node_modules/@graphql-yoga/nestjs/src/index.ts b/node_modules/@graphql-yoga/nestjs/src/index.ts
index ce142f6..cda4117 100644
--- a/node_modules/@graphql-yoga/nestjs/src/index.ts
+++ b/node_modules/@graphql-yoga/nestjs/src/index.ts
@@ -1,9 +1,10 @@
import type { Express, Request as ExpressRequest, Response as ExpressResponse } from 'express';
import type { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify';
-import { printSchema } from 'graphql';
-import { createYoga, filter, pipe, YogaServerInstance, YogaServerOptions } from 'graphql-yoga';
+import { GraphQLSchema, printSchema } from 'graphql';
+import { createYoga, filter, pipe, YogaServerInstance, YogaServerOptions, GraphQLSchemaWithContext, PromiseOrValue, YogaInitialContext } from 'graphql-yoga';
import type { ExecutionParams } from 'subscriptions-transport-ws';
import { Injectable, Logger } from '@nestjs/common';
+import { mergeSchemas } from '@graphql-tools/schema';
import {
AbstractGraphQLDriver,
GqlModuleOptions,
@@ -11,6 +12,12 @@ import {
SubscriptionConfig,
} from '@nestjs/graphql';
+export type YogaSchemaDefinition<TContext> =
+ | PromiseOrValue<GraphQLSchemaWithContext<TContext>>
+ | ((
+ context: TContext & YogaInitialContext,
+ ) => PromiseOrValue<GraphQLSchemaWithContext<TContext>>);
+
export type YogaDriverPlatform = 'express' | 'fastify';
export type YogaDriverServerContext<Platform extends YogaDriverPlatform> =
@@ -27,7 +34,9 @@ export type YogaDriverServerContext<Platform extends YogaDriverPlatform> =
export type YogaDriverServerOptions<Platform extends YogaDriverPlatform> = Omit<
YogaServerOptions<YogaDriverServerContext<Platform>, never>,
'context' | 'schema'
->;
+> & {
+ conditionalSchema?: YogaSchemaDefinition<YogaDriverServerContext<Platform>> | undefined;
+};
export type YogaDriverServerInstance<Platform extends YogaDriverPlatform> = YogaServerInstance<
YogaDriverServerContext<Platform>,
@@ -78,7 +87,7 @@ export abstract class AbstractYogaDriver<
}
protected registerExpress(
- options: YogaDriverConfig<'express'>,
+ { conditionalSchema, ...options}: YogaDriverConfig<'express'>,
{ preStartHook }: { preStartHook?: (app: Express) => void } = {},
) {
const app: Express = this.httpAdapterHost.httpAdapter.getInstance();
@@ -98,6 +107,25 @@ export abstract class AbstractYogaDriver<
const yoga = createYoga<YogaDriverServerContext<'express'>>({
...options,
+ schema: async request => {
+ const schemas: GraphQLSchema[] = [];
+
+ if (options.schema) {
+ schemas.push(options.schema);
+ }
+
+ if (conditionalSchema) {
+ const conditionalSchemaResult = typeof conditionalSchema === 'function' ? await conditionalSchema(request) : await conditionalSchema;
+
+ if (conditionalSchemaResult) {
+ schemas.push(conditionalSchemaResult);
+ }
+ }
+
+ return mergeSchemas({
+ schemas,
+ });
+ },
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use nest logger
@@ -115,7 +143,7 @@ export abstract class AbstractYogaDriver<
}
protected registerFastify(
- options: YogaDriverConfig<'fastify'>,
+ { conditionalSchema, ...options }: YogaDriverConfig<'fastify'>,
{ preStartHook }: { preStartHook?: (app: FastifyInstance) => void } = {},
) {
const app: FastifyInstance = this.httpAdapterHost.httpAdapter.getInstance();
@@ -124,6 +152,25 @@ export abstract class AbstractYogaDriver<
const yoga = createYoga<YogaDriverServerContext<'fastify'>>({
...options,
+ schema: async request => {
+ const schemas: GraphQLSchema[] = [];
+
+ if (options.schema) {
+ schemas.push(options.schema);
+ }
+
+ if (conditionalSchema) {
+ const conditionalSchemaResult = typeof conditionalSchema === 'function' ? await conditionalSchema(request) : await conditionalSchema;
+
+ if (conditionalSchemaResult) {
+ schemas.push(conditionalSchemaResult);
+ }
+ }
+
+ return mergeSchemas({
+ schemas,
+ });
+ },
graphqlEndpoint: options.path,
// disable logging by default
// however, if `true` use fastify logger

View File

@ -1,11 +1,13 @@
import { Module } from '@nestjs/common';
import { GraphQLModule } from '@nestjs/graphql';
import { ConfigModule } from '@nestjs/config';
import { ApolloDriver, ApolloDriverConfig } from '@nestjs/apollo';
import { ModuleRef } from '@nestjs/core';
import { ApolloServerPluginLandingPageLocalDefault } from '@apollo/server/plugin/landingPage/default';
import { GraphQLError } from 'graphql';
import { YogaDriver, YogaDriverConfig } from '@graphql-yoga/nestjs';
import GraphQLJSON from 'graphql-type-json';
import { GraphQLError, GraphQLSchema } from 'graphql';
import { ExtractJwt } from 'passport-jwt';
import { TokenExpiredError, verify } from 'jsonwebtoken';
import { AppService } from './app.service';
@ -15,24 +17,77 @@ import { PrismaModule } from './database/prisma.module';
import { HealthModule } from './health/health.module';
import { AbilityModule } from './ability/ability.module';
import { TenantModule } from './tenant/tenant.module';
import { SchemaGenerationService } from './tenant/schema-generation/schema-generation.service';
import { EnvironmentService } from './integrations/environment/environment.service';
import {
JwtAuthStrategy,
JwtPayload,
} from './core/auth/strategies/jwt.auth.strategy';
@Module({
imports: [
ConfigModule.forRoot({
isGlobal: true,
}),
GraphQLModule.forRoot<ApolloDriverConfig>({
playground: false,
GraphQLModule.forRoot<YogaDriverConfig>({
context: ({ req }) => ({ req }),
driver: ApolloDriver,
driver: YogaDriver,
autoSchemaFile: true,
resolvers: { JSON: GraphQLJSON },
plugins: [ApolloServerPluginLandingPageLocalDefault()],
formatError: (error: GraphQLError) => {
error.extensions.stacktrace = undefined;
return error;
conditionalSchema: async (request) => {
try {
// Get the SchemaGenerationService from the AppModule
const service = AppModule.moduleRef.get(SchemaGenerationService, {
strict: false,
});
// Get the JwtAuthStrategy from the AppModule
const jwtStrategy = AppModule.moduleRef.get(JwtAuthStrategy, {
strict: false,
});
// Get the EnvironmentService from the AppModule
const environmentService = AppModule.moduleRef.get(
EnvironmentService,
{
strict: false,
},
);
// Extract JWT from the request
const token = ExtractJwt.fromAuthHeaderAsBearerToken()(request.req);
// If there is no token, return an empty schema
if (!token) {
return new GraphQLSchema({});
}
// Verify and decode JWT
const decoded = verify(
token,
environmentService.getAccessTokenSecret(),
);
// Validate JWT
const { workspace } = await jwtStrategy.validate(
decoded as JwtPayload,
);
const conditionalSchema = await service.generateSchema(workspace.id);
return conditionalSchema;
} catch (error) {
if (error instanceof TokenExpiredError) {
throw new GraphQLError('Unauthenticated', {
extensions: {
code: 'UNAUTHENTICATED',
},
});
}
throw error;
}
},
csrfPrevention: false,
resolvers: { JSON: GraphQLJSON },
plugins: [],
}),
PrismaModule,
HealthModule,
@ -43,4 +98,10 @@ import { TenantModule } from './tenant/tenant.module';
],
providers: [AppService],
})
export class AppModule {}
export class AppModule {
static moduleRef: ModuleRef;
constructor(private moduleRef: ModuleRef) {
AppModule.moduleRef = this.moduleRef;
}
}

View File

@ -8,6 +8,7 @@ import { PersonService } from 'src/core/person/person.service';
import { CompanyService } from 'src/core/company/company.service';
import { PipelineProgressService } from 'src/core/pipeline/services/pipeline-progress.service';
import { ViewService } from 'src/core/view/services/view.service';
import { DataSourceService } from 'src/tenant/metadata/data-source/data-source.service';
import { WorkspaceService } from './workspace.service';
@ -46,6 +47,10 @@ describe('WorkspaceService', () => {
provide: ViewService,
useValue: {},
},
{
provide: DataSourceService,
useValue: {},
},
],
}).compile();

View File

@ -11,6 +11,7 @@ import { PipelineService } from 'src/core/pipeline/services/pipeline.service';
import { ViewService } from 'src/core/view/services/view.service';
import { PrismaService } from 'src/database/prisma.service';
import { assert } from 'src/utils/assert';
import { DataSourceService } from 'src/tenant/metadata/data-source/data-source.service';
@Injectable()
export class WorkspaceService {
@ -22,6 +23,7 @@ export class WorkspaceService {
private readonly pipelineStageService: PipelineStageService,
private readonly pipelineProgressService: PipelineProgressService,
private readonly viewService: ViewService,
private readonly dataSourceService: DataSourceService,
) {}
// Find
@ -63,6 +65,9 @@ export class WorkspaceService {
},
});
// Create workspace schema
await this.dataSourceService.createWorkspaceSchema(workspace.id);
// Create default companies
const companies = await this.companyService.createDefaultCompanies({
workspaceId: workspace.id,

View File

@ -5,6 +5,7 @@ import { PipelineModule } from 'src/core/pipeline/pipeline.module';
import { CompanyModule } from 'src/core/company/company.module';
import { PersonModule } from 'src/core/person/person.module';
import { ViewModule } from 'src/core/view/view.module';
import { DataSourceModule } from 'src/tenant/metadata/data-source/data-source.module';
import { WorkspaceService } from './services/workspace.service';
import { WorkspaceMemberService } from './services/workspace-member.service';
@ -12,7 +13,13 @@ import { WorkspaceMemberResolver } from './resolvers/workspace-member.resolver';
import { WorkspaceResolver } from './resolvers/workspace.resolver';
@Module({
imports: [PipelineModule, CompanyModule, PersonModule, ViewModule],
imports: [
PipelineModule,
CompanyModule,
PersonModule,
ViewModule,
DataSourceModule,
],
providers: [
WorkspaceService,
FileUploadService,

View File

@ -17,9 +17,7 @@ export class JwtAuthGuard extends AuthGuard(['jwt']) {
}
getRequest(context: ExecutionContext) {
const request = getRequest(context);
return request;
return getRequest(context);
}
handleRequest(err: any, user: any, info: any) {

View File

@ -0,0 +1,12 @@
import { Module } from '@nestjs/common';
import { DataSourceModule } from 'src/tenant/metadata/data-source/data-source.module';
import { EntityResolverService } from './entity-resolver.service';
@Module({
imports: [DataSourceModule],
providers: [EntityResolverService],
exports: [EntityResolverService],
})
export class EntityResolverModule {}

View File

@ -0,0 +1,27 @@
import { Test, TestingModule } from '@nestjs/testing';
import { DataSourceService } from 'src/tenant/metadata/data-source/data-source.service';
import { EntityResolverService } from './entity-resolver.service';
describe('EntityResolverService', () => {
let service: EntityResolverService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
EntityResolverService,
{
provide: DataSourceService,
useValue: {},
},
],
}).compile();
service = module.get<EntityResolverService>(EntityResolverService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,111 @@
import { BadRequestException, Injectable } from '@nestjs/common';
import { GraphQLResolveInfo } from 'graphql';
import graphqlFields from 'graphql-fields';
import { DataSourceService } from 'src/tenant/metadata/data-source/data-source.service';
import { convertFieldsToGraphQL } from './entity-resolver.util';
@Injectable()
export class EntityResolverService {
constructor(private readonly dataSourceService: DataSourceService) {}
async findAll(
entityName: string,
tableName: string,
workspaceId: string,
info: GraphQLResolveInfo,
fieldAliases: Record<string, string>,
) {
const workspaceDataSource =
await this.dataSourceService.connectToWorkspaceDataSource(workspaceId);
const graphqlQuery = await this.prepareGrapQLQuery(
workspaceId,
info,
fieldAliases,
);
/* TODO: This is a temporary solution to set the schema before each raw query.
getSchemaName is used to avoid a call to metadata.data_source table,
this won't work when we won't be able to dynamically recompute the schema name from its workspace_id only (remote schemas for example)
*/
await workspaceDataSource?.query(`
SET search_path TO ${this.dataSourceService.getSchemaName(workspaceId)};
`);
const graphqlResult = await workspaceDataSource?.query(`
SELECT graphql.resolve($$
{
${entityName}Collection: ${tableName}Collection {
${graphqlQuery}
}
}
$$);
`);
const result =
graphqlResult?.[0]?.resolve?.data?.[`${entityName}Collection`];
if (!result) {
throw new BadRequestException('Malformed result from GraphQL query');
}
return result;
}
async findOne(
entityName: string,
tableName: string,
args: { id: string },
workspaceId: string,
info: GraphQLResolveInfo,
fieldAliases: Record<string, string>,
) {
const workspaceDataSource =
await this.dataSourceService.connectToWorkspaceDataSource(workspaceId);
const graphqlQuery = await this.prepareGrapQLQuery(
workspaceId,
info,
fieldAliases,
);
await workspaceDataSource?.query(`
SET search_path TO ${this.dataSourceService.getSchemaName(workspaceId)};
`);
const graphqlResult = await workspaceDataSource?.query(`
SELECT graphql.resolve($$
{
${entityName}Collection: : ${tableName}Collection(filter: { id: { eq: "${args.id}" } }) {
${graphqlQuery}
}
}
$$);
`);
const result =
graphqlResult?.[0]?.resolve?.data?.[`${entityName}Collection`];
if (!result) {
return null;
}
return result;
}
private async prepareGrapQLQuery(
workspaceId: string,
info: GraphQLResolveInfo,
fieldAliases: Record<string, string>,
): Promise<string> {
// Extract requested fields from GraphQL resolve info
const fields = graphqlFields(info);
await this.dataSourceService.createWorkspaceSchema(workspaceId);
const graphqlQuery = convertFieldsToGraphQL(fields, fieldAliases);
return graphqlQuery;
}
}

View File

@ -0,0 +1,23 @@
import isEmpty from 'lodash.isempty';
export const convertFieldsToGraphQL = (
fields: any,
fieldAliases: Record<string, string>,
acc = '',
) => {
for (const [key, value] of Object.entries(fields)) {
if (value && !isEmpty(value)) {
acc += `${key} {\n`;
acc = convertFieldsToGraphQL(value, fieldAliases, acc);
acc += `}\n`;
} else {
if (fieldAliases[key]) {
acc += `${key}: ${fieldAliases[key]}\n`;
} else {
acc += `${key}\n`;
}
}
}
return acc;
};

View File

@ -28,10 +28,17 @@ export class DataSourceMetadataService {
});
}
getDataSourcesMetadataFromWorkspaceId(workspaceId: string) {
async getDataSourcesMetadataFromWorkspaceId(workspaceId: string) {
return this.dataSourceMetadataRepository.find({
where: { workspaceId },
order: { createdAt: 'DESC' },
});
}
async getLastDataSourceMetadataFromWorkspaceIdOrFail(workspaceId: string) {
return this.dataSourceMetadataRepository.findOneOrFail({
where: { workspaceId },
order: { createdAt: 'DESC' },
});
}
}

View File

@ -1,9 +1,4 @@
import {
Injectable,
NotFoundException,
OnModuleDestroy,
OnModuleInit,
} from '@nestjs/common';
import { Injectable, OnModuleDestroy, OnModuleInit } from '@nestjs/common';
import { DataSource, QueryRunner, Table } from 'typeorm';
@ -37,15 +32,14 @@ export class DataSourceService implements OnModuleInit, OnModuleDestroy {
* @param workspaceId
* @returns Promise<void>
*/
public async createWorkspaceSchema(workspaceId: string): Promise<void> {
public async createWorkspaceSchema(workspaceId: string): Promise<string> {
const schemaName = this.getSchemaName(workspaceId);
const queryRunner = this.mainDataSource.createQueryRunner();
const schemaAlreadyExists = await queryRunner.hasSchema(schemaName);
if (schemaAlreadyExists) {
throw new Error(
`Schema ${schemaName} already exists for workspace ${workspaceId}`,
);
return schemaName;
}
await queryRunner.createSchema(schemaName, true);
@ -56,6 +50,8 @@ export class DataSourceService implements OnModuleInit, OnModuleDestroy {
workspaceId,
schemaName,
);
return schemaName;
}
private async createMigrationTable(
@ -105,20 +101,12 @@ export class DataSourceService implements OnModuleInit, OnModuleDestroy {
return cachedDataSource;
}
const dataSourcesMetadata =
await this.dataSourceMetadataService.getDataSourcesMetadataFromWorkspaceId(
workspaceId,
);
if (dataSourcesMetadata.length === 0) {
throw new NotFoundException(
`We can't find any data source for this workspace id (${workspaceId}).`,
);
}
// We only want the first one for now, we will handle multiple data sources later with remote datasources.
// However, we will need to differentiate the data sources because we won't run migrations on remote data sources for example.
const dataSourceMetadata = dataSourcesMetadata[0];
const dataSourceMetadata =
await this.dataSourceMetadataService.getLastDataSourceMetadataFromWorkspaceIdOrFail(
workspaceId,
);
const schema = dataSourceMetadata.schema;
// Probably not needed as we will ask for the schema name OR store public by default if it's remote
@ -128,11 +116,6 @@ export class DataSourceService implements OnModuleInit, OnModuleDestroy {
);
}
const entities =
await this.entitySchemaGeneratorService.getTypeORMEntitiesByDataSourceId(
dataSourceMetadata.id,
);
const workspaceDataSource = new DataSource({
// TODO: We should use later dataSourceMetadata.type and use a switch case condition to create the right data source
url: dataSourceMetadata.url ?? this.environmentService.getPGDatabaseUrl(),
@ -141,15 +124,17 @@ export class DataSourceService implements OnModuleInit, OnModuleDestroy {
schema,
entities: {
TenantMigration,
...entities,
},
});
await workspaceDataSource.initialize();
// Set search path to workspace schema for raw queries
await workspaceDataSource?.query(`SET search_path TO ${schema};`);
this.dataSources.set(workspaceId, workspaceDataSource);
return this.dataSources.get(workspaceId);
return workspaceDataSource;
}
/**

View File

@ -31,7 +31,6 @@ export const sanitizeColumnName = (columnName: string): string =>
export const convertFieldTypeToPostgresType = (fieldType: string): string => {
switch (fieldType) {
case 'text':
return 'text';
case 'url':
return 'text';
case 'number':

View File

@ -27,9 +27,15 @@ export class FieldMetadata {
@Column({ nullable: false, name: 'target_column_name' })
targetColumnName: string;
@Column('text', { nullable: true, array: true })
enums: string[];
@Column({ default: false, name: 'is_custom' })
isCustom: boolean;
@Column({ nullable: true, default: true, name: 'is_nullable' })
isNullable: boolean;
@Column({ nullable: false, name: 'workspace_id' })
workspaceId: string;

View File

@ -41,6 +41,8 @@ export class MetadataController {
entities.push(...dataSourceEntities);
}
this.dataSourceService.createWorkspaceSchema(workspace.id);
await this.migrationGenerator.executeMigrationFromPendingMigrations(
workspace.id,
);

View File

@ -58,11 +58,6 @@ export class MigrationGeneratorService {
);
});
await queryRunner.release();
// We want to destroy all connections to the workspace data source and invalidate the cache
// so that the next request will create a new connection and get the latest entities
await this.dataSourceService.disconnectFromWorkspaceDataSource(workspaceId);
return flattenedPendingMigrations;
}

View File

@ -0,0 +1,61 @@
import { MigrationInterface, QueryRunner } from 'typeorm';
export class AlterFieldMetadataTable1695717691800
implements MigrationInterface
{
name = 'AlterFieldMetadataTable1695717691800';
public async up(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`ALTER TABLE "metadata"."field_metadata" ADD "enums" text array`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."field_metadata" ADD "is_nullable" boolean DEFAULT true`,
);
await queryRunner.query(
`ALTER TYPE "metadata"."data_source_metadata_type_enum" RENAME TO "data_source_metadata_type_enum_old"`,
);
await queryRunner.query(
`CREATE TYPE "metadata"."data_source_metadata_type_enum" AS ENUM('postgres')`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" TYPE "metadata"."data_source_metadata_type_enum" USING "type"::"text"::"metadata"."data_source_metadata_type_enum"`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" SET DEFAULT 'postgres'`,
);
await queryRunner.query(
`DROP TYPE "metadata"."data_source_metadata_type_enum_old"`,
);
}
public async down(queryRunner: QueryRunner): Promise<void> {
await queryRunner.query(
`CREATE TYPE "metadata"."data_source_metadata_type_enum_old" AS ENUM('postgres', 'mysql')`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" DROP DEFAULT`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" TYPE "metadata"."data_source_metadata_type_enum_old" USING "type"::"text"::"metadata"."data_source_metadata_type_enum_old"`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."data_source_metadata" ALTER COLUMN "type" SET DEFAULT 'postgres'`,
);
await queryRunner.query(
`DROP TYPE "metadata"."data_source_metadata_type_enum"`,
);
await queryRunner.query(
`ALTER TYPE "metadata"."data_source_metadata_type_enum_old" RENAME TO "data_source_metadata_type_enum"`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "is_nullable"`,
);
await queryRunner.query(
`ALTER TABLE "metadata"."field_metadata" DROP COLUMN "enums"`,
);
}
}

View File

@ -1,7 +1,9 @@
import { Test, TestingModule } from '@nestjs/testing';
import { TenantMigrationService } from './tenant-migration.service';
import { DataSourceService } from 'src/tenant/metadata/data-source/data-source.service';
import { TenantMigrationService } from './tenant-migration.service';
describe('TenantMigrationService', () => {
let service: TenantMigrationService;

View File

@ -0,0 +1,24 @@
import { GraphQLList, GraphQLNonNull, GraphQLObjectType } from 'graphql';
import { PageInfoType } from './page-info.graphql-type';
/**
* Generate a GraphQL connection type based on the EdgeType.
* @param EdgeType Edge type to be used in the connection.
* @returns GraphQL connection type.
*/
export const generateConnectionType = <T extends GraphQLObjectType>(
EdgeType: T,
): GraphQLObjectType<any, any> => {
return new GraphQLObjectType({
name: `${EdgeType.name.slice(0, -4)}Connection`, // Removing 'Edge' from the name
fields: {
edges: {
type: new GraphQLList(EdgeType),
},
pageInfo: {
type: new GraphQLNonNull(PageInfoType),
},
},
});
};

View File

@ -0,0 +1,22 @@
import { GraphQLNonNull, GraphQLObjectType, GraphQLString } from 'graphql';
/**
* Generate a GraphQL edge type based on the ObjectType.
* @param ObjectType Object type to be used in the Edge.
* @returns GraphQL edge type.
*/
export const generateEdgeType = <T extends GraphQLObjectType>(
ObjectType: T,
): GraphQLObjectType<any, any> => {
return new GraphQLObjectType({
name: `${ObjectType.name}Edge`,
fields: {
node: {
type: ObjectType,
},
cursor: {
type: new GraphQLNonNull(GraphQLString),
},
},
});
};

View File

@ -0,0 +1,99 @@
import {
GraphQLBoolean,
GraphQLEnumType,
GraphQLID,
GraphQLInt,
GraphQLNonNull,
GraphQLObjectType,
GraphQLString,
} from 'graphql';
import { FieldMetadata } from 'src/tenant/metadata/field-metadata/field-metadata.entity';
import { ObjectMetadata } from 'src/tenant/metadata/object-metadata/object-metadata.entity';
import { pascalCase } from 'src/utils/pascal-case';
/**
* Map the column type from field-metadata to its corresponding GraphQL type.
* @param columnType Type of the column in the database.
*/
const mapColumnTypeToGraphQLType = (column: FieldMetadata): any => {
switch (column.type) {
case 'uuid':
return GraphQLID;
case 'text':
case 'url':
case 'date':
return GraphQLString;
case 'boolean':
return GraphQLBoolean;
case 'number':
return GraphQLInt;
case 'enum': {
if (column.enums && column.enums.length > 0) {
const enumName = `${pascalCase(column.objectId)}${pascalCase(
column.displayName,
)}Enum`;
return new GraphQLEnumType({
name: enumName,
values: Object.fromEntries(
column.enums.map((value) => [value, { value }]),
),
});
}
}
default:
return GraphQLString;
}
};
/**
* Generate a GraphQL object type based on the name and columns.
* @param name Name for the GraphQL object.
* @param columns Array of FieldMetadata columns.
*/
export const generateObjectType = <TSource = any, TContext = any>(
name: string,
columns: FieldMetadata[],
): GraphQLObjectType<TSource, TContext> => {
const fields: Record<string, any> = {
// Default fields
id: { type: new GraphQLNonNull(GraphQLID) },
createdAt: { type: new GraphQLNonNull(GraphQLString) },
updatedAt: { type: new GraphQLNonNull(GraphQLString) },
};
columns.forEach((column) => {
let graphqlType = mapColumnTypeToGraphQLType(column);
if (!column.isNullable) {
graphqlType = new GraphQLNonNull(graphqlType);
}
fields[column.displayName] = {
type: graphqlType,
description: column.targetColumnName,
};
});
return new GraphQLObjectType({
name: pascalCase(name),
fields,
});
};
/**
* Generate multiple GraphQL object types based on an array of object metadata.
* @param objectMetadata Array of ObjectMetadata.
*/
export const generateObjectTypes = (objectMetadata: ObjectMetadata[]) => {
const objectTypes: Record<string, GraphQLObjectType> = {};
for (const object of objectMetadata) {
const ObjectType = generateObjectType(object.displayName, object.fields);
objectTypes[object.displayName] = ObjectType;
}
return objectTypes;
};

View File

@ -0,0 +1,19 @@
import {
GraphQLBoolean,
GraphQLNonNull,
GraphQLObjectType,
GraphQLString,
} from 'graphql';
/**
* GraphQL PageInfo type.
*/
export const PageInfoType = new GraphQLObjectType({
name: 'PageInfo',
fields: {
startCursor: { type: GraphQLString },
endCursor: { type: GraphQLString },
hasNextPage: { type: new GraphQLNonNull(GraphQLBoolean) },
hasPreviousPage: { type: new GraphQLNonNull(GraphQLBoolean) },
},
});

View File

@ -0,0 +1,21 @@
import { Module } from '@nestjs/common';
import { EntityResolverModule } from 'src/tenant/entity-resolver/entity-resolver.module';
import { JwtAuthGuard } from 'src/guards/jwt.auth.guard';
import { DataSourceMetadataModule } from 'src/tenant/metadata/data-source-metadata/data-source-metadata.module';
import { EntitySchemaGeneratorModule } from 'src/tenant/metadata/entity-schema-generator/entity-schema-generator.module';
import { ObjectMetadataModule } from 'src/tenant/metadata/object-metadata/object-metadata.module';
import { SchemaGenerationService } from './schema-generation.service';
@Module({
imports: [
EntityResolverModule,
DataSourceMetadataModule,
EntitySchemaGeneratorModule,
ObjectMetadataModule,
],
providers: [SchemaGenerationService, JwtAuthGuard],
exports: [SchemaGenerationService],
})
export class SchemaGenerationModule {}

View File

@ -0,0 +1,37 @@
import { Test, TestingModule } from '@nestjs/testing';
import { DataSourceMetadataService } from 'src/tenant/metadata/data-source-metadata/data-source-metadata.service';
import { ObjectMetadataService } from 'src/tenant/metadata/object-metadata/object-metadata.service';
import { EntityResolverService } from 'src/tenant/entity-resolver/entity-resolver.service';
import { SchemaGenerationService } from './schema-generation.service';
describe('SchemaGenerationService', () => {
let service: SchemaGenerationService;
beforeEach(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
SchemaGenerationService,
{
provide: DataSourceMetadataService,
useValue: {},
},
{
provide: ObjectMetadataService,
useValue: {},
},
{
provide: EntityResolverService,
useValue: {},
},
],
}).compile();
service = module.get<SchemaGenerationService>(SchemaGenerationService);
});
it('should be defined', () => {
expect(service).toBeDefined();
});
});

View File

@ -0,0 +1,150 @@
import { Injectable, InternalServerErrorException } from '@nestjs/common';
import {
GraphQLID,
GraphQLNonNull,
GraphQLObjectType,
GraphQLResolveInfo,
GraphQLSchema,
} from 'graphql';
import { EntityResolverService } from 'src/tenant/entity-resolver/entity-resolver.service';
import { DataSourceMetadataService } from 'src/tenant/metadata/data-source-metadata/data-source-metadata.service';
import { pascalCase } from 'src/utils/pascal-case';
import { ObjectMetadataService } from 'src/tenant/metadata/object-metadata/object-metadata.service';
import { ObjectMetadata } from 'src/tenant/metadata/object-metadata/object-metadata.entity';
import { generateEdgeType } from './graphql-types/edge.graphql-type';
import { generateConnectionType } from './graphql-types/connection.graphql-type';
import { generateObjectTypes } from './graphql-types/object.graphql-type';
@Injectable()
export class SchemaGenerationService {
constructor(
private readonly dataSourceMetadataService: DataSourceMetadataService,
private readonly objectMetadataService: ObjectMetadataService,
private readonly entityResolverService: EntityResolverService,
) {}
private generateQueryFieldForEntity(
entityName: string,
tableName: string,
ObjectType: GraphQLObjectType,
objectDefinition: ObjectMetadata,
workspaceId: string,
) {
const fieldAliases =
objectDefinition?.fields.reduce(
(acc, field) => ({
...acc,
[field.displayName]: field.targetColumnName,
}),
{},
) || {};
const EdgeType = generateEdgeType(ObjectType);
const ConnectionType = generateConnectionType(EdgeType);
return {
[`findAll${pascalCase(entityName)}`]: {
type: ConnectionType,
resolve: async (root, args, context, info: GraphQLResolveInfo) => {
return this.entityResolverService.findAll(
entityName,
tableName,
workspaceId,
info,
fieldAliases,
);
},
},
[`findOne${pascalCase(entityName)}`]: {
type: ObjectType,
args: {
id: { type: new GraphQLNonNull(GraphQLID) },
},
resolve: (root, args, context, info) => {
return this.entityResolverService.findOne(
entityName,
tableName,
args,
workspaceId,
info,
fieldAliases,
);
},
},
};
}
private generateQueryType(
ObjectTypes: Record<string, GraphQLObjectType>,
objectMetadata: ObjectMetadata[],
workspaceId: string,
): GraphQLObjectType {
const fields: any = {};
for (const [entityName, ObjectType] of Object.entries(ObjectTypes)) {
const objectDefinition = objectMetadata.find(
(object) => object.displayName === entityName,
);
const tableName = objectDefinition?.targetTableName ?? '';
if (!objectDefinition) {
throw new InternalServerErrorException('Object definition not found');
}
Object.assign(
fields,
this.generateQueryFieldForEntity(
entityName,
tableName,
ObjectType,
objectDefinition,
workspaceId,
),
);
}
return new GraphQLObjectType({
name: 'Query',
fields,
});
}
async generateSchema(
workspaceId: string | undefined,
): Promise<GraphQLSchema> {
if (!workspaceId) {
return new GraphQLSchema({});
}
const dataSourcesMetadata =
await this.dataSourceMetadataService.getDataSourcesMetadataFromWorkspaceId(
workspaceId,
);
// Can'f find any data sources for this workspace
if (!dataSourcesMetadata || dataSourcesMetadata.length === 0) {
return new GraphQLSchema({});
}
const dataSourceMetadata = dataSourcesMetadata[0];
const objectMetadata =
await this.objectMetadataService.getObjectMetadataFromDataSourceId(
dataSourceMetadata.id,
);
const ObjectTypes = generateObjectTypes(objectMetadata);
const QueryType = this.generateQueryType(
ObjectTypes,
objectMetadata,
workspaceId,
);
return new GraphQLSchema({
query: QueryType,
});
}
}

View File

@ -2,8 +2,9 @@ import { Module } from '@nestjs/common';
import { MetadataModule } from './metadata/metadata.module';
import { UniversalModule } from './universal/universal.module';
import { SchemaGenerationModule } from './schema-generation/schema-generation.module';
@Module({
imports: [MetadataModule, UniversalModule],
imports: [MetadataModule, UniversalModule, SchemaGenerationModule],
})
export class TenantModule {}

View File

@ -1,5 +1,5 @@
import { Type } from '@nestjs/common';
import { ArgsType, Directive, Field, ObjectType } from '@nestjs/graphql';
import { ArgsType, Field, ObjectType } from '@nestjs/graphql';
import { IsNumber, IsOptional, IsString } from 'class-validator';
@ -50,7 +50,6 @@ export function Paginated<T>(classRef: Type<T>): Type<IConnection<T>> {
public cursor!: ConnectionCursor;
@Field(() => classRef, { nullable: true })
@Directive(`@cacheControl(inheritMaxAge: true)`)
public node!: T;
}
@ -59,11 +58,9 @@ export function Paginated<T>(classRef: Type<T>): Type<IConnection<T>> {
public name = `${classRef.name}Connection`;
@Field(() => [Edge], { nullable: true })
@Directive(`@cacheControl(inheritMaxAge: true)`)
public edges!: IEdge<T>[];
@Field(() => PageInfo, { nullable: true })
@Directive(`@cacheControl(inheritMaxAge: true)`)
public pageInfo!: IPageInfo;
@Field()

View File

@ -0,0 +1,32 @@
import isObject from 'lodash.isobject';
import lodashCamelCase from 'lodash.camelcase';
import { PascalCase, PascalCasedPropertiesDeep } from 'type-fest';
export const capitalizeFirstLetter = (str: string) => {
return str.charAt(0).toUpperCase() + str.slice(1);
};
export const pascalCase = <T>(text: T) =>
capitalizeFirstLetter(
lodashCamelCase(text as unknown as string),
) as PascalCase<T>;
export const pascalCaseDeep = <T>(value: T): PascalCasedPropertiesDeep<T> => {
// Check if it's an array
if (Array.isArray(value)) {
return value.map(pascalCaseDeep) as PascalCasedPropertiesDeep<T>;
}
// Check if it's an object
if (isObject(value)) {
const result: Record<string, any> = {};
for (const key in value) {
result[pascalCase(key)] = pascalCaseDeep(value[key]);
}
return result as PascalCasedPropertiesDeep<T>;
}
return value as PascalCasedPropertiesDeep<T>;
};

View File

@ -160,8 +160,7 @@ describe('CompanyResolver (e2e)', () => {
const error = errors?.[0];
expect(error).toBeDefined();
expect(error.extensions.code).toBe('FORBIDDEN');
expect(error.extensions.originalError.statusCode).toBe(403);
expect(error.message).toBe('Forbidden resource');
});
});
@ -235,8 +234,7 @@ describe('CompanyResolver (e2e)', () => {
const error = errors?.[0];
expect(error).toBeDefined();
expect(error.extensions.code).toBe('FORBIDDEN');
expect(error.extensions.originalError.statusCode).toBe(403);
expect(error.message).toBe('Forbidden resource');
});
});
@ -291,8 +289,7 @@ describe('CompanyResolver (e2e)', () => {
const error = errors?.[0];
expect(error).toBeDefined();
expect(error.extensions.code).toBe('FORBIDDEN');
expect(error.extensions.originalError.statusCode).toBe(403);
expect(error.message).toBe('Forbidden resource');
});
});
});

View File

@ -1282,6 +1282,21 @@
dependencies:
"@jridgewell/trace-mapping" "0.3.9"
"@envelop/core@^4.0.0":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@envelop/core/-/core-4.0.1.tgz#0f3eb33a396e9f4527d7fa8079055ec151eb73aa"
integrity sha512-uBLI7ql3hZopz7vMi9UDAb9HWzKw4STKiqg4QT+lb+tu5ZNaeuJ4fom2rrmgITz38B85QZOhZrGyVrlJXxfDzw==
dependencies:
"@envelop/types" "4.0.1"
tslib "^2.5.0"
"@envelop/types@4.0.1":
version "4.0.1"
resolved "https://registry.yarnpkg.com/@envelop/types/-/types-4.0.1.tgz#145690d8266a003cdb06dd58fa1236e3c80050a9"
integrity sha512-ULo27/doEsP7uUhm2iTnElx13qTO6I5FKvmLoX41cpfuw8x6e0NUFknoqhEsLzAbgz8xVS5mjwcxGCXh4lDYzg==
dependencies:
tslib "^2.5.0"
"@eslint-community/eslint-utils@^4.2.0":
version "4.4.0"
resolved "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz"
@ -1314,6 +1329,17 @@
resolved "https://registry.npmjs.org/@eslint/js/-/js-8.42.0.tgz"
integrity sha512-6SWlXpWU5AvId8Ac7zjzmIOqMOba/JWY8XZ4A7q7Gn1Vlfg/SFFIlrtHXt9nPn4op9ZPAkl91Jao+QQv3r/ukw==
"@graphql-tools/executor@^1.0.0":
version "1.2.0"
resolved "https://registry.yarnpkg.com/@graphql-tools/executor/-/executor-1.2.0.tgz#6c45f4add765769d9820c4c4405b76957ba39c79"
integrity sha512-SKlIcMA71Dha5JnEWlw4XxcaJ+YupuXg0QCZgl2TOLFz4SkGCwU/geAsJvUJFwK2RbVLpQv/UMq67lOaBuwDtg==
dependencies:
"@graphql-tools/utils" "^10.0.0"
"@graphql-typed-document-node/core" "3.2.0"
"@repeaterjs/repeater" "^3.0.4"
tslib "^2.4.0"
value-or-promise "^1.0.12"
"@graphql-tools/merge@8.3.1":
version "8.3.1"
resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.3.1.tgz"
@ -1348,7 +1374,7 @@
fast-json-stable-stringify "^2.1.0"
tslib "^2.4.0"
"@graphql-tools/schema@10.0.0":
"@graphql-tools/schema@10.0.0", "@graphql-tools/schema@^10.0.0":
version "10.0.0"
resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-10.0.0.tgz"
integrity sha512-kf3qOXMFcMs2f/S8Y3A8fm/2w+GaHAkfr3Gnhh2LOug/JgpY/ywgFVxO3jOeSpSEdoYcDKLcXVjMigNbY4AdQg==
@ -1378,10 +1404,10 @@
tslib "^2.4.0"
value-or-promise "^1.0.12"
"@graphql-tools/utils@10.0.0":
version "10.0.0"
resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.0.tgz"
integrity sha512-ndBPc6zgR+eGU/jHLpuojrs61kYN3Z89JyMLwK3GCRkPv4EQn9EOr1UWqF1JO0iM+/jAVHY0mvfUxyrFFN9DUQ==
"@graphql-tools/utils@10.0.1", "@graphql-tools/utils@^10.0.0":
version "10.0.1"
resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.1.tgz"
integrity sha512-i1FozbDGHgdsFA47V/JvQZ0FE8NAy0Eiz7HGCJO2MkNdZAKNnwei66gOq0JWYVFztwpwbVQ09GkKhq7Kjcq5Cw==
dependencies:
"@graphql-typed-document-node/core" "^3.1.1"
tslib "^2.4.0"
@ -1393,14 +1419,6 @@
dependencies:
tslib "^2.4.0"
"@graphql-tools/utils@^10.0.0":
version "10.0.1"
resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-10.0.1.tgz"
integrity sha512-i1FozbDGHgdsFA47V/JvQZ0FE8NAy0Eiz7HGCJO2MkNdZAKNnwei66gOq0JWYVFztwpwbVQ09GkKhq7Kjcq5Cw==
dependencies:
"@graphql-typed-document-node/core" "^3.1.1"
tslib "^2.4.0"
"@graphql-tools/utils@^9.2.1":
version "9.2.1"
resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-9.2.1.tgz"
@ -1409,11 +1427,41 @@
"@graphql-typed-document-node/core" "^3.1.1"
tslib "^2.4.0"
"@graphql-typed-document-node/core@^3.1.1":
"@graphql-typed-document-node/core@3.2.0", "@graphql-typed-document-node/core@^3.1.1":
version "3.2.0"
resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.2.0.tgz"
integrity sha512-mB9oAsNCm9aM3/SOv4YtBMqZbYj10R7dkq8byBqxGY/ncFwhf2oQzMV+LCRlWoDSEBJ3COiR1yeDvMtsoOsuFQ==
"@graphql-yoga/logger@^1.0.0":
version "1.0.0"
resolved "https://registry.yarnpkg.com/@graphql-yoga/logger/-/logger-1.0.0.tgz#0fba12edd8c4b0b9c0f0a74b0d101f1646c3780e"
integrity sha512-JYoxwnPggH2BfO+dWlWZkDeFhyFZqaTRGLvFhy+Pjp2UxitEW6nDrw+pEDw/K9tJwMjIFMmTT9VfTqrnESmBHg==
dependencies:
tslib "^2.5.2"
"@graphql-yoga/nestjs@^2.1.0":
version "2.1.0"
resolved "https://registry.yarnpkg.com/@graphql-yoga/nestjs/-/nestjs-2.1.0.tgz#e429906f84a391a9d7bcfd4661987425896938b2"
integrity sha512-LaKdPJrLSG8QLNm8Xp7f6xWO8tGLfHZyPrNKNpqClvk4X9DByraeMHArwEyP1YDScFbsIEKkLxTaM7JoLkLkpg==
"@graphql-yoga/subscription@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@graphql-yoga/subscription/-/subscription-4.0.0.tgz#2bf5844ce8aeff46332650ad642218250201dcc5"
integrity sha512-0qsN/BPPZNMoC2CZ8i+P6PgiJyHh1H35aKDt37qARBDaIOKDQuvEOq7+4txUKElcmXi7DYFo109FkhSQoEajrg==
dependencies:
"@graphql-yoga/typed-event-target" "^2.0.0"
"@repeaterjs/repeater" "^3.0.4"
"@whatwg-node/events" "^0.1.0"
tslib "^2.5.2"
"@graphql-yoga/typed-event-target@^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/@graphql-yoga/typed-event-target/-/typed-event-target-2.0.0.tgz#41809fc8c101c27c61a5427d74e0d0ce824044db"
integrity sha512-oA/VGxGmaSDym1glOHrltw43qZsFwLLjBwvh57B79UKX/vo3+UQcRgOyE44c5RP7DCYjkrC2tuArZmb6jCzysw==
dependencies:
"@repeaterjs/repeater" "^3.0.4"
tslib "^2.5.2"
"@humanwhocodes/config-array@^0.11.10":
version "0.11.10"
resolved "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.10.tgz"
@ -1794,23 +1842,23 @@
path-to-regexp "3.2.0"
tslib "2.5.3"
"@nestjs/graphql@^11.0.6":
version "11.0.6"
resolved "https://registry.npmjs.org/@nestjs/graphql/-/graphql-11.0.6.tgz"
integrity sha512-EGE4fhLHrQLPAAgER+AwIa034IM9FNJto3xg49cZmI6c7s1yo6pLw/JULCn2jE2hUDbg5Q3IORZMMG8EUiUyMw==
"@nestjs/graphql@^12.0.8":
version "12.0.8"
resolved "https://registry.yarnpkg.com/@nestjs/graphql/-/graphql-12.0.8.tgz#15143b76dfb5fa4dc880d68a1bf2f7159ea077b6"
integrity sha512-odYDHUdLOMCxjC5VSEmF/23r8cY40N1KCwBkWaCmI1IF76Ffe3srWRDv8HS9tcai9eSmOeSWuyLBEg2OSru0cQ==
dependencies:
"@graphql-tools/merge" "9.0.0"
"@graphql-tools/schema" "10.0.0"
"@graphql-tools/utils" "10.0.0"
"@nestjs/mapped-types" "1.2.2"
"@graphql-tools/utils" "10.0.1"
"@nestjs/mapped-types" "2.0.2"
chokidar "3.5.3"
fast-glob "3.2.12"
graphql-tag "2.12.6"
graphql-ws "5.13.1"
graphql-ws "5.14.0"
lodash "4.17.21"
normalize-path "3.0.0"
subscriptions-transport-ws "0.11.0"
tslib "2.5.2"
tslib "2.6.0"
uuid "9.0.0"
ws "8.13.0"
@ -1822,10 +1870,10 @@
"@types/jsonwebtoken" "9.0.1"
jsonwebtoken "9.0.0"
"@nestjs/mapped-types@1.2.2":
version "1.2.2"
resolved "https://registry.npmjs.org/@nestjs/mapped-types/-/mapped-types-1.2.2.tgz"
integrity sha512-3dHxLXs3M0GPiriAcCFFJQHoDFUuzTD5w6JDhE7TyfT89YKpe6tcCCIqOZWdXmt9AZjjK30RkHRSFF+QEnWFQg==
"@nestjs/mapped-types@2.0.2":
version "2.0.2"
resolved "https://registry.yarnpkg.com/@nestjs/mapped-types/-/mapped-types-2.0.2.tgz#c8a090a8d22145b85ed977414c158534210f2e4f"
integrity sha512-V0izw6tWs6fTp9+KiiPUbGHWALy563Frn8X6Bm87ANLRuE46iuBMD5acKBDP5lKL/75QFvrzSJT7HkCbB0jTpg==
"@nestjs/passport@^9.0.3":
version "9.0.3"
@ -2158,6 +2206,11 @@
resolved "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz"
integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==
"@repeaterjs/repeater@^3.0.4":
version "3.0.4"
resolved "https://registry.yarnpkg.com/@repeaterjs/repeater/-/repeater-3.0.4.tgz#a04d63f4d1bf5540a41b01a921c9a7fddc3bd1ca"
integrity sha512-AW8PKd6iX3vAZ0vA43nOUOnbq/X5ihgU+mSXXqunMkeQADGiqw/PY0JNeYtD5sr0PAy51YPgAPbDoeapv9r8WA==
"@sentry-internal/tracing@7.66.0":
version "7.66.0"
resolved "https://registry.yarnpkg.com/@sentry-internal/tracing/-/tracing-7.66.0.tgz#45ea607917d55a5bcaa3229341387ff6ed9b3a2b"
@ -2834,6 +2887,13 @@
dependencies:
"@types/node" "*"
"@types/graphql-fields@^1.3.6":
version "1.3.6"
resolved "https://registry.yarnpkg.com/@types/graphql-fields/-/graphql-fields-1.3.6.tgz#fc73326082f142ac63394f8167d272c21604f74e"
integrity sha512-tLiskj9g5ftYgiVxq8VlvBEYDRow23IwY/LMnhljXfWvBhi+LIo9YEMa6AACjgIa3rG+edS9xh3v6Rja2UPbDg==
dependencies:
graphql "*"
"@types/graphql-upload@^8.0.12":
version "8.0.12"
resolved "https://registry.yarnpkg.com/@types/graphql-upload/-/graphql-upload-8.0.12.tgz#224738b8885bad8d50fb690b67bbe10bbcdef032"
@ -2938,6 +2998,13 @@
dependencies:
"@types/lodash" "*"
"@types/lodash.isempty@^4.4.7":
version "4.4.7"
resolved "https://registry.yarnpkg.com/@types/lodash.isempty/-/lodash.isempty-4.4.7.tgz#b1015d1adba560daf560ad04f294848939e75317"
integrity sha512-YOzlpoIn9jrfHzjIukKnu9Le3tmi+0PhUdOt2rMpJW/4J6jX7s0HeBatXdh9QckLga8qt4EKBxVIEqtEq6pzLg==
dependencies:
"@types/lodash" "*"
"@types/lodash.isobject@^3.0.7":
version "3.0.7"
resolved "https://registry.yarnpkg.com/@types/lodash.isobject/-/lodash.isobject-3.0.7.tgz#8a37beea56512f0ae86f8d48ea01e2ea9b79c185"
@ -3384,6 +3451,38 @@
"@webassemblyjs/ast" "1.11.6"
"@xtuc/long" "4.2.2"
"@whatwg-node/events@^0.1.0":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@whatwg-node/events/-/events-0.1.1.tgz#0ca718508249419587e130da26d40e29d99b5356"
integrity sha512-AyQEn5hIPV7Ze+xFoXVU3QTHXVbWPrzaOkxtENMPMuNL6VVHrp4hHfDt9nrQpjO7BgvuM95dMtkycX5M/DZR3w==
"@whatwg-node/fetch@^0.9.10", "@whatwg-node/fetch@^0.9.7":
version "0.9.12"
resolved "https://registry.yarnpkg.com/@whatwg-node/fetch/-/fetch-0.9.12.tgz#3b20ac8f286a2196003976b6f1452c2c513fad00"
integrity sha512-zNUkPJNfM1v9Jhy3Vmi2a7lQxaNIDTSiAb1NKO5eMsSdo05XoddBEj/CHj1xu4IOMU68VerDvuBVwzPjxBl12g==
dependencies:
"@whatwg-node/node-fetch" "^0.4.17"
urlpattern-polyfill "^9.0.0"
"@whatwg-node/node-fetch@^0.4.17":
version "0.4.18"
resolved "https://registry.yarnpkg.com/@whatwg-node/node-fetch/-/node-fetch-0.4.18.tgz#ff15beb1ecd03eb7428286435674c8bd583f159a"
integrity sha512-zdey6buMKCqDVDq+tMqcjopO75Fb6iLqWo+g6cWwN5kiwctEHtVcbws2lJUFhCbo+TLZeH6bMDRUXEo5bkPtcQ==
dependencies:
"@whatwg-node/events" "^0.1.0"
busboy "^1.6.0"
fast-querystring "^1.1.1"
fast-url-parser "^1.1.3"
tslib "^2.3.1"
"@whatwg-node/server@^0.9.1":
version "0.9.14"
resolved "https://registry.yarnpkg.com/@whatwg-node/server/-/server-0.9.14.tgz#54c47b50c370e46fabdcbbed06be3d84686b8a91"
integrity sha512-I8TT0NoCP+xThLBuGlU6dgq5wpExkphNMo2geZwQW0vAmEPtc3MNMZMIYqg5GyNmpv5Nf7fnxb8tVOIHbDvuDA==
dependencies:
"@whatwg-node/fetch" "^0.9.10"
tslib "^2.3.1"
"@xtuc/ieee754@^1.2.0":
version "1.2.0"
resolved "https://registry.npmjs.org/@xtuc/ieee754/-/ieee754-1.2.0.tgz"
@ -3394,6 +3493,11 @@
resolved "https://registry.npmjs.org/@xtuc/long/-/long-4.2.2.tgz"
integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ==
"@yarnpkg/lockfile@^1.1.0":
version "1.1.0"
resolved "https://registry.yarnpkg.com/@yarnpkg/lockfile/-/lockfile-1.1.0.tgz#e77a97fbd345b76d83245edcd17d393b1b41fb31"
integrity sha512-GpSwvyXOcOOlV70vbnzjj4fW5xW/FdUF6nQEt1ENy7m4ZCczi1+/buVUPAqmGfqznsORNFzUMjctTIp8a9tuCQ==
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
@ -3794,6 +3898,11 @@ asynckit@^0.4.0:
resolved "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz"
integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==
at-least-node@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/at-least-node/-/at-least-node-1.0.0.tgz#602cd4b46e844ad4effc92a8011a3c46e0238dc2"
integrity sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==
available-typed-arrays@^1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7"
@ -4061,7 +4170,7 @@ busboy@^0.3.1:
dependencies:
dicer "0.3.0"
busboy@^1.0.0:
busboy@^1.0.0, busboy@^1.6.0:
version "1.6.0"
resolved "https://registry.npmjs.org/busboy/-/busboy-1.6.0.tgz"
integrity sha512-8SFQbg/0hQ9xy3UNTB0YEnsNBbWfhf7RtnzpL7TkBiTBRfrQ9Fxcnz7VJsleJpyp6rVLvXiuORqjlHi5q+PYuA==
@ -4176,7 +4285,7 @@ chrome-trace-event@^1.0.2:
resolved "https://registry.npmjs.org/chrome-trace-event/-/chrome-trace-event-1.0.3.tgz"
integrity sha512-p3KULyQg4S7NIHixdwbGX+nFHkoBiA4YQmyWtjb8XngSKV124nJmRysgAeujbUVb15vh+RvFUfCPqU7rXk+hZg==
ci-info@3.8.0, ci-info@^3.2.0:
ci-info@3.8.0, ci-info@^3.2.0, ci-info@^3.7.0:
version "3.8.0"
resolved "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz"
integrity sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==
@ -4684,6 +4793,11 @@ dotenv@^16.0.3:
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-16.3.1.tgz#369034de7d7e5b120972693352a3bf112172cc3e"
integrity sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==
dset@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.2.tgz#89c436ca6450398396dc6538ea00abc0c54cd45a"
integrity sha512-g/M9sqy3oHe477Ar4voQxWtaPIFw1jTdKZuomOjhCcBx9nHUNn0pu6NopuFFrTh/TRZIKEj+76vLWFu9BNKk+Q==
ecdsa-sig-formatter@1.0.11:
version "1.0.11"
resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz"
@ -5130,6 +5244,11 @@ external-editor@^3.0.3:
iconv-lite "^0.4.24"
tmp "^0.0.33"
fast-decode-uri-component@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/fast-decode-uri-component/-/fast-decode-uri-component-1.0.1.tgz#46f8b6c22b30ff7a81357d4f59abfae938202543"
integrity sha512-WKgKWg5eUxvRZGwW8FvfbaH7AXSh2cL+3j5fMGzUMCxWBJ3dV3a7Wz8y2f/uQ0e3B6WmodD3oS54jTQ9HVTIIg==
fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
version "3.1.3"
resolved "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz"
@ -5161,11 +5280,25 @@ fast-levenshtein@^2.0.6:
resolved "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz"
integrity sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==
fast-querystring@^1.1.1:
version "1.1.2"
resolved "https://registry.yarnpkg.com/fast-querystring/-/fast-querystring-1.1.2.tgz#a6d24937b4fc6f791b4ee31dcb6f53aeafb89f53"
integrity sha512-g6KuKWmFXc0fID8WWH0jit4g0AGBoJhCkJMb1RmbsSEUNvQ+ZC8D6CUZ+GtF8nMzSPXnhiePyyqqipzNNEnHjg==
dependencies:
fast-decode-uri-component "^1.0.1"
fast-safe-stringify@2.1.1, fast-safe-stringify@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.1.1.tgz"
integrity sha512-W+KJc2dmILlPplD/H4K9l9LcAHAfPtP6BY84uVLXQ6Evcz9Lcg33Y2z1IVblT6xdY54PXYVHEv+0Wpq8Io6zkA==
fast-url-parser@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/fast-url-parser/-/fast-url-parser-1.1.3.tgz#f4af3ea9f34d8a271cf58ad2b3759f431f0b318d"
integrity sha512-5jOCVXADYNuRkKFzNJ0dCCewsZiYo0dz8QNYljkOpFC6r2U4OBmKtvm/Tsuh4w1YYdDqDb31a8TVhBJ2OJKdqQ==
dependencies:
punycode "^1.3.2"
fast-write-atomic@0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/fast-write-atomic/-/fast-write-atomic-0.2.1.tgz"
@ -5274,6 +5407,13 @@ find-up@^4.0.0, find-up@^4.1.0:
locate-path "^5.0.0"
path-exists "^4.0.0"
find-yarn-workspace-root@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/find-yarn-workspace-root/-/find-yarn-workspace-root-2.0.0.tgz#f47fb8d239c900eb78179aa81b66673eac88f7bd"
integrity sha512-1IMnbjt4KzsQfnhnzNd8wUEgXZ44IzZaZmnLYx7D5FZlaHt2gW20Cri8Q+E/t5tIj4+epTBub+2Zxu/vNILzqQ==
dependencies:
micromatch "^4.0.2"
flat-cache@^3.0.4:
version "3.0.4"
resolved "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz"
@ -5398,6 +5538,16 @@ fs-extra@^10.0.0:
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-extra@^9.0.0:
version "9.1.0"
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-9.1.0.tgz#5954460c764a8da2094ba3554bf839e6b9a7c86d"
integrity sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==
dependencies:
at-least-node "^1.0.0"
graceful-fs "^4.2.0"
jsonfile "^6.0.1"
universalify "^2.0.0"
fs-jetpack@5.1.0:
version "5.1.0"
resolved "https://registry.npmjs.org/fs-jetpack/-/fs-jetpack-5.1.0.tgz"
@ -5614,7 +5764,7 @@ gopd@^1.0.1:
dependencies:
get-intrinsic "^1.1.3"
graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.11, graceful-fs@^4.2.4, graceful-fs@^4.2.9:
version "4.2.11"
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz"
integrity sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==
@ -5629,6 +5779,11 @@ graphemer@^1.4.0:
resolved "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz"
integrity sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==
graphql-fields@^2.0.3:
version "2.0.3"
resolved "https://registry.yarnpkg.com/graphql-fields/-/graphql-fields-2.0.3.tgz#5e68dff7afbb202be4f4f40623e983b22c96ab8f"
integrity sha512-x3VE5lUcR4XCOxPIqaO4CE+bTK8u6gVouOdpQX9+EKHr+scqtK5Pp/l8nIGqIpN1TUlkKE6jDCCycm/WtLRAwA==
graphql-parse-resolve-info@^4.13.0:
version "4.13.0"
resolved "https://registry.npmjs.org/graphql-parse-resolve-info/-/graphql-parse-resolve-info-4.13.0.tgz"
@ -5659,15 +5814,32 @@ graphql-upload@^13.0.0:
http-errors "^1.8.1"
object-path "^0.11.8"
graphql-ws@5.13.1:
version "5.13.1"
resolved "https://registry.npmjs.org/graphql-ws/-/graphql-ws-5.13.1.tgz"
integrity sha512-eiX7ES/ZQr0q7hSM5UBOEIFfaAUmAY9/CSDyAnsETuybByU7l/v46drRg9DQoTvVABEHp3QnrvwgTRMhqy7zxQ==
graphql-ws@5.14.0:
version "5.14.0"
resolved "https://registry.yarnpkg.com/graphql-ws/-/graphql-ws-5.14.0.tgz#766f249f3974fc2c48fae0d1fb20c2c4c79cd591"
integrity sha512-itrUTQZP/TgswR4GSSYuwWUzrE/w5GhbwM2GX3ic2U7aw33jgEsayfIlvaj7/GcIvZgNMzsPTrE5hqPuFUiE5g==
"graphql@0.13.1 - 16", graphql@^16.7.1:
version "16.7.1"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.7.1.tgz#11475b74a7bff2aefd4691df52a0eca0abd9b642"
integrity sha512-DRYR9tf+UGU0KOsMcKAlXeFfX89UiiIZ0dRU3mR0yJfu6OjZqUcp68NnFLnqQU5RexygFoDy1EW+ccOYcPfmHg==
graphql-yoga@^4.0.4:
version "4.0.4"
resolved "https://registry.yarnpkg.com/graphql-yoga/-/graphql-yoga-4.0.4.tgz#00f388b6c38560ad4e5662c66cd2671befb58229"
integrity sha512-MvCLhFecYNIKuxAZisPjpIL9lxRYbpgPSNKENDO/8CV3oiFlsLJHZb5dp2sVAeLafXHeZ9TgkijLthUBc1+Jag==
dependencies:
"@envelop/core" "^4.0.0"
"@graphql-tools/executor" "^1.0.0"
"@graphql-tools/schema" "^10.0.0"
"@graphql-tools/utils" "^10.0.0"
"@graphql-yoga/logger" "^1.0.0"
"@graphql-yoga/subscription" "^4.0.0"
"@whatwg-node/fetch" "^0.9.7"
"@whatwg-node/server" "^0.9.1"
dset "^3.1.1"
lru-cache "^10.0.0"
tslib "^2.5.2"
graphql@*, "graphql@0.13.1 - 16", graphql@16.8.0, graphql@^16.8.0:
version "16.8.0"
resolved "https://registry.yarnpkg.com/graphql/-/graphql-16.8.0.tgz#374478b7f27b2dc6153c8f42c1b80157f79d79d4"
integrity sha512-0oKGaR+y3qcS5mCu1vb7KG+a89vjn06C7Ihq/dDl3jA+A8B3TKomvi3CiEcVLJQGalbu8F52LxkOym7U5sSfbg==
has-bigints@^1.0.1, has-bigints@^1.0.2:
version "1.0.2"
@ -6613,6 +6785,13 @@ json-stable-stringify-without-jsonify@^1.0.1:
resolved "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz"
integrity sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==
json-stable-stringify@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.2.tgz#e06f23128e0bbe342dc996ed5a19e28b57b580e0"
integrity sha512-eunSSaEnxV12z+Z73y/j5N37/In40GK4GmsSy+tEHJMxknvqnA7/djeYtAgW0GsWHUfg+847WJjKaEylk2y09g==
dependencies:
jsonify "^0.0.1"
json5@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/json5/-/json5-1.0.2.tgz#63d98d60f21b313b77c4d6da18bfa69d80e1d593"
@ -6639,6 +6818,11 @@ jsonfile@^6.0.1:
optionalDependencies:
graceful-fs "^4.1.6"
jsonify@^0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/jsonify/-/jsonify-0.0.1.tgz#2aa3111dae3d34a0f151c63f3a45d995d9420978"
integrity sha512-2/Ki0GcmuqSrgFyelQq9M05y7PS0mEwuIzrf3f1fPqkVDVRvZrPZtVSMHxdgo8Aq0sxAOb/cr2aqqA3LeWHVPg==
jsonwebtoken@9.0.0, jsonwebtoken@^9.0.0:
version "9.0.0"
resolved "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.0.tgz"
@ -6666,6 +6850,13 @@ jws@^3.2.2:
jwa "^1.4.1"
safe-buffer "^5.0.1"
klaw-sync@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/klaw-sync/-/klaw-sync-6.0.0.tgz#1fd2cfd56ebb6250181114f0a581167099c2b28c"
integrity sha512-nIeuVSzdCCs6TDPTqI8w1Yre34sSq7AkZ4B3sfOBbI2CgVSB4Du4aLQijFU2+lhAFCwt9+42Hel6lQNIv6AntQ==
dependencies:
graceful-fs "^4.1.11"
kleur@4.1.5:
version "4.1.5"
resolved "https://registry.npmjs.org/kleur/-/kleur-4.1.5.tgz"
@ -6745,6 +6936,11 @@ lodash.flatten@^4.4.0:
resolved "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz"
integrity sha512-C5N2Z3DgnnKr0LOpv/hKCgKdb7ZZwafIrsesve6lmzvZIRZRGaZ/l6Q8+2W7NaT+ZwO3fFlSCzCzrDCFdJfZ4g==
lodash.isempty@^4.4.0:
version "4.4.0"
resolved "https://registry.yarnpkg.com/lodash.isempty/-/lodash.isempty-4.4.0.tgz#6f86cbedd8be4ec987be9aaf33c9684db1b31e7e"
integrity sha512-oKMuF3xEeqDltrGMfDxAPGIVMSSRv8tbRSODbrs4KGsRRLEhrW8N8Rd4DRgB2+621hY8A8XwwrTVhXWpxFvMzg==
lodash.isobject@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/lodash.isobject/-/lodash.isobject-3.0.2.tgz#3c8fb8d5b5bf4bf90ae06e14f2a530a4ed935e1d"
@ -6818,6 +7014,11 @@ long@^4.0.0:
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-7.13.1.tgz"
integrity sha512-CHqbAq7NFlW3RSnoWXLJBxCWaZVBrfa9UEHId2M3AW8iEBurbqduNexEUCGc3SHc6iCYXNJCDi903LajSVAEPQ==
lru-cache@^10.0.0:
version "10.0.1"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-10.0.1.tgz#0a3be479df549cca0e5d693ac402ff19537a6b7a"
integrity sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==
lru-cache@^5.1.1:
version "5.1.1"
resolved "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz"
@ -6910,7 +7111,7 @@ methods@^1.1.2, methods@~1.1.2:
resolved "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz"
integrity sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==
micromatch@^4.0.0, micromatch@^4.0.4:
micromatch@^4.0.0, micromatch@^4.0.2, micromatch@^4.0.4:
version "4.0.5"
resolved "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz"
integrity sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==
@ -7277,7 +7478,7 @@ onetime@^5.1.0, onetime@^5.1.2:
dependencies:
mimic-fn "^2.1.0"
open@7.4.2:
open@7.4.2, open@^7.4.2:
version "7.4.2"
resolved "https://registry.npmjs.org/open/-/open-7.4.2.tgz"
integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q==
@ -7481,6 +7682,27 @@ passport@^0.6.0:
pause "0.0.1"
utils-merge "^1.0.1"
patch-package@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/patch-package/-/patch-package-8.0.0.tgz#d191e2f1b6e06a4624a0116bcb88edd6714ede61"
integrity sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==
dependencies:
"@yarnpkg/lockfile" "^1.1.0"
chalk "^4.1.2"
ci-info "^3.7.0"
cross-spawn "^7.0.3"
find-yarn-workspace-root "^2.0.0"
fs-extra "^9.0.0"
json-stable-stringify "^1.0.2"
klaw-sync "^6.0.0"
minimist "^1.2.6"
open "^7.4.2"
rimraf "^2.6.3"
semver "^7.5.3"
slash "^2.0.0"
tmp "^0.0.33"
yaml "^2.2.2"
path-browserify@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz"
@ -7651,6 +7873,11 @@ postgres-interval@^1.1.0:
dependencies:
xtend "^4.0.0"
postinstall-postinstall@^2.1.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/postinstall-postinstall/-/postinstall-postinstall-2.1.0.tgz#4f7f77441ef539d1512c40bd04c71b06a4704ca3"
integrity sha512-7hQX6ZlZXIoRiWNrbMQaLzUUfH+sSx39u8EJ9HYuDc1kLo9IXKWjM5RSquZN1ad5GnH8CGFM78fsAAQi3OKEEQ==
prebuild-install@^7.1.1:
version "7.1.1"
resolved "https://registry.yarnpkg.com/prebuild-install/-/prebuild-install-7.1.1.tgz#de97d5b34a70a0c81334fd24641f2a1702352e45"
@ -7772,6 +7999,11 @@ pump@^3.0.0:
end-of-stream "^1.1.0"
once "^1.3.1"
punycode@^1.3.2:
version "1.4.1"
resolved "https://registry.yarnpkg.com/punycode/-/punycode-1.4.1.tgz#c0d5a63b2718800ad8e1eb0fa5269c84dd41845e"
integrity sha512-jmYNElW7yvO7TV33CjSmvSiE2yco3bV2czu/OzDKdMNVZQWfxCblURLhf+47syQRBntjfLdd/H0egrzIG+oaFQ==
punycode@^2.1.0:
version "2.3.0"
resolved "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz"
@ -8021,6 +8253,13 @@ rimraf@4.4.1:
dependencies:
glob "^9.2.0"
rimraf@^2.6.3:
version "2.7.1"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-2.7.1.tgz#35797f13a7fdadc566142c29d4f07ccad483e3ec"
integrity sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==
dependencies:
glob "^7.1.3"
run-async@^2.4.0:
version "2.4.1"
resolved "https://registry.npmjs.org/run-async/-/run-async-2.4.1.tgz"
@ -8107,6 +8346,13 @@ semver@^7.5.0:
dependencies:
lru-cache "^6.0.0"
semver@^7.5.3:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
send@0.18.0:
version "0.18.0"
resolved "https://registry.npmjs.org/send/-/send-0.18.0.tgz"
@ -8236,6 +8482,11 @@ sisteransi@^1.0.5:
resolved "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz"
integrity sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==
slash@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/slash/-/slash-2.0.0.tgz#de552851a1759df3a8f206535442f5ec4ddeab44"
integrity sha512-ZYKh3Wh2z1PpEXWr0MpSBZ0V6mZHAQfYevttO11c51CaWjGTaadiKZ+wVt1PbMlDV5qhMFslpZCemhwOK7C89A==
slash@^3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz"
@ -8836,17 +9087,17 @@ tslib@2.5.3, tslib@^2.0.1, tslib@^2.1.0, tslib@^2.4.0:
resolved "https://registry.npmjs.org/tslib/-/tslib-2.5.3.tgz"
integrity sha512-mSxlJJwl3BMEQCUNnxXBU9jP4JBktcEGhURcPR6VQVlnP0FdDEsIaz0C35dXNGLyRfrATNofF0F5p2KPxQgB+w==
tslib@2.6.0, tslib@^2.3.1, tslib@^2.5.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==
tslib@^1.11.1, tslib@^1.8.1:
version "1.14.1"
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.3.1, tslib@^2.5.0:
version "2.6.0"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.0.tgz#b295854684dbda164e181d259a22cd779dcd7bc3"
integrity sha512-7At1WUettjcSRHXCyYtTselblcHl9PJFFVKiCAy/bY97+BPZXSQ2wbq0P9s8tK2G7dFQfNnlJnPAiArVBVBsfA==
"tslib@^2.4.1 || ^1.9.3":
"tslib@^2.4.1 || ^1.9.3", tslib@^2.5.2:
version "2.6.2"
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae"
integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==
@ -9039,6 +9290,11 @@ uri-js@^4.2.2:
dependencies:
punycode "^2.1.0"
urlpattern-polyfill@^9.0.0:
version "9.0.0"
resolved "https://registry.yarnpkg.com/urlpattern-polyfill/-/urlpattern-polyfill-9.0.0.tgz#bc7e386bb12fd7898b58d1509df21d3c29ab3460"
integrity sha512-WHN8KDQblxd32odxeIgo83rdVDE2bvdkb86it7bMhYZwWKJz0+O0RK/eZiHYnM+zgt/U7hAHOlCQGfjjvSkw2g==
util-deprecate@^1.0.1, util-deprecate@~1.0.1:
version "1.0.2"
resolved "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz"
@ -9306,6 +9562,11 @@ yaml@^1.10.0:
resolved "https://registry.npmjs.org/yaml/-/yaml-1.10.2.tgz"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==
yaml@^2.2.2:
version "2.3.2"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.3.2.tgz#f522db4313c671a0ca963a75670f1c12ea909144"
integrity sha512-N/lyzTPaJasoDmfV7YTrYCI0G/3ivm/9wdG0aHuheKowWQwGTsK0Eoiw6utmzAnI6pkJa0DUVygvp3spqqEKXg==
yargs-parser@21.1.1, yargs-parser@^21.0.1, yargs-parser@^21.1.1:
version "21.1.1"
resolved "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz"

396
yarn.lock Normal file
View File

@ -0,0 +1,396 @@
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
# yarn lockfile v1
"@mapbox/node-pre-gyp@^1.0.11":
version "1.0.11"
resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz#417db42b7f5323d79e93b34a6d7a2a12c0df43fa"
integrity sha512-Yhlar6v9WQgUp/He7BdgzOz8lqMQ8sU+jkCq7Wx8Myc5YFJLbEe7lgui/V7G1qB1DJykHSGwreceSaD60Y0PUQ==
dependencies:
detect-libc "^2.0.0"
https-proxy-agent "^5.0.0"
make-dir "^3.1.0"
node-fetch "^2.6.7"
nopt "^5.0.0"
npmlog "^5.0.1"
rimraf "^3.0.2"
semver "^7.3.5"
tar "^6.1.11"
abbrev@1:
version "1.1.1"
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8"
integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==
agent-base@6:
version "6.0.2"
resolved "https://registry.yarnpkg.com/agent-base/-/agent-base-6.0.2.tgz#49fff58577cfee3f37176feab4c22e00f86d7f77"
integrity sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==
dependencies:
debug "4"
ansi-regex@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.1.tgz#082cb2c89c9fe8659a311a53bd6a4dc5301db304"
integrity sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==
"aproba@^1.0.3 || ^2.0.0":
version "2.0.0"
resolved "https://registry.yarnpkg.com/aproba/-/aproba-2.0.0.tgz#52520b8ae5b569215b354efc0caa3fe1e45a8adc"
integrity sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==
are-we-there-yet@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz#372e0e7bd279d8e94c653aaa1f67200884bf3e1c"
integrity sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==
dependencies:
delegates "^1.0.0"
readable-stream "^3.6.0"
balanced-match@^1.0.0:
version "1.0.2"
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee"
integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==
bcrypt@^5.1.1:
version "5.1.1"
resolved "https://registry.yarnpkg.com/bcrypt/-/bcrypt-5.1.1.tgz#0f732c6dcb4e12e5b70a25e326a72965879ba6e2"
integrity sha512-AGBHOG5hPYZ5Xl9KXzU5iKq9516yEmvCKDg3ecP5kX2aB6UqTeXZxk2ELnDgDm6BQSMlLt9rDB4LoSMx0rYwww==
dependencies:
"@mapbox/node-pre-gyp" "^1.0.11"
node-addon-api "^5.0.0"
brace-expansion@^1.1.7:
version "1.1.11"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd"
integrity sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==
dependencies:
balanced-match "^1.0.0"
concat-map "0.0.1"
chownr@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/chownr/-/chownr-2.0.0.tgz#15bfbe53d2eab4cf70f18a8cd68ebe5b3cb1dece"
integrity sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==
color-support@^1.1.2:
version "1.1.3"
resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2"
integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==
concat-map@0.0.1:
version "0.0.1"
resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b"
integrity sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==
console-control-strings@^1.0.0, console-control-strings@^1.1.0:
version "1.1.0"
resolved "https://registry.yarnpkg.com/console-control-strings/-/console-control-strings-1.1.0.tgz#3d7cf4464db6446ea644bf4b39507f9851008e8e"
integrity sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==
debug@4:
version "4.3.4"
resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865"
integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==
dependencies:
ms "2.1.2"
delegates@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a"
integrity sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==
detect-libc@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.2.tgz#8ccf2ba9315350e1241b88d0ac3b0e1fbd99605d"
integrity sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw==
emoji-regex@^8.0.0:
version "8.0.0"
resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-8.0.0.tgz#e818fd69ce5ccfcb404594f842963bf53164cc37"
integrity sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==
fs-minipass@^2.0.0:
version "2.1.0"
resolved "https://registry.yarnpkg.com/fs-minipass/-/fs-minipass-2.1.0.tgz#7f5036fdbf12c63c169190cbe4199c852271f9fb"
integrity sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==
dependencies:
minipass "^3.0.0"
fs.realpath@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/fs.realpath/-/fs.realpath-1.0.0.tgz#1504ad2523158caa40db4a2787cb01411994ea4f"
integrity sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==
gauge@^3.0.0:
version "3.0.2"
resolved "https://registry.yarnpkg.com/gauge/-/gauge-3.0.2.tgz#03bf4441c044383908bcfa0656ad91803259b395"
integrity sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==
dependencies:
aproba "^1.0.3 || ^2.0.0"
color-support "^1.1.2"
console-control-strings "^1.0.0"
has-unicode "^2.0.1"
object-assign "^4.1.1"
signal-exit "^3.0.0"
string-width "^4.2.3"
strip-ansi "^6.0.1"
wide-align "^1.1.2"
glob@^7.1.3:
version "7.2.3"
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b"
integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==
dependencies:
fs.realpath "^1.0.0"
inflight "^1.0.4"
inherits "2"
minimatch "^3.1.1"
once "^1.3.0"
path-is-absolute "^1.0.0"
has-unicode@^2.0.1:
version "2.0.1"
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
integrity sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==
https-proxy-agent@^5.0.0:
version "5.0.1"
resolved "https://registry.yarnpkg.com/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz#c59ef224a04fe8b754f3db0063a25ea30d0005d6"
integrity sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==
dependencies:
agent-base "6"
debug "4"
inflight@^1.0.4:
version "1.0.6"
resolved "https://registry.yarnpkg.com/inflight/-/inflight-1.0.6.tgz#49bd6331d7d02d0c09bc910a1075ba8165b56df9"
integrity sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==
dependencies:
once "^1.3.0"
wrappy "1"
inherits@2, 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==
is-fullwidth-code-point@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz#f116f8064fe90b3f7844a38997c0b75051269f1d"
integrity sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==
lru-cache@^6.0.0:
version "6.0.0"
resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-6.0.0.tgz#6d6fe6570ebd96aaf90fcad1dafa3b2566db3a94"
integrity sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==
dependencies:
yallist "^4.0.0"
make-dir@^3.1.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f"
integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==
dependencies:
semver "^6.0.0"
minimatch@^3.1.1:
version "3.1.2"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
dependencies:
brace-expansion "^1.1.7"
minipass@^3.0.0:
version "3.3.6"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-3.3.6.tgz#7bba384db3a1520d18c9c0e5251c3444e95dd94a"
integrity sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==
dependencies:
yallist "^4.0.0"
minipass@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/minipass/-/minipass-5.0.0.tgz#3e9788ffb90b694a5d0ec94479a45b5d8738133d"
integrity sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==
minizlib@^2.1.1:
version "2.1.2"
resolved "https://registry.yarnpkg.com/minizlib/-/minizlib-2.1.2.tgz#e90d3466ba209b932451508a11ce3d3632145931"
integrity sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==
dependencies:
minipass "^3.0.0"
yallist "^4.0.0"
mkdirp@^1.0.3:
version "1.0.4"
resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-1.0.4.tgz#3eb5ed62622756d79a5f0e2a221dfebad75c2f7e"
integrity sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==
ms@2.1.2:
version "2.1.2"
resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.2.tgz#d09d1f357b443f493382a8eb3ccd183872ae6009"
integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==
node-addon-api@^5.0.0:
version "5.1.0"
resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-5.1.0.tgz#49da1ca055e109a23d537e9de43c09cca21eb762"
integrity sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==
node-fetch@^2.6.7:
version "2.7.0"
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.7.0.tgz#d0f0fa6e3e2dc1d27efcd8ad99d550bda94d187d"
integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==
dependencies:
whatwg-url "^5.0.0"
nopt@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88"
integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==
dependencies:
abbrev "1"
npmlog@^5.0.1:
version "5.0.1"
resolved "https://registry.yarnpkg.com/npmlog/-/npmlog-5.0.1.tgz#f06678e80e29419ad67ab964e0fa69959c1eb8b0"
integrity sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==
dependencies:
are-we-there-yet "^2.0.0"
console-control-strings "^1.1.0"
gauge "^3.0.0"
set-blocking "^2.0.0"
object-assign@^4.1.1:
version "4.1.1"
resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==
once@^1.3.0:
version "1.4.0"
resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1"
integrity sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==
dependencies:
wrappy "1"
path-is-absolute@^1.0.0:
version "1.0.1"
resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f"
integrity sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==
readable-stream@^3.6.0:
version "3.6.2"
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-3.6.2.tgz#56a9b36ea965c00c5a93ef31eb111a0f11056967"
integrity sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==
dependencies:
inherits "^2.0.3"
string_decoder "^1.1.1"
util-deprecate "^1.0.1"
rimraf@^3.0.2:
version "3.0.2"
resolved "https://registry.yarnpkg.com/rimraf/-/rimraf-3.0.2.tgz#f1a5402ba6220ad52cc1282bac1ae3aa49fd061a"
integrity sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==
dependencies:
glob "^7.1.3"
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==
semver@^6.0.0:
version "6.3.1"
resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.1.tgz#556d2ef8689146e46dcea4bfdd095f3434dffcb4"
integrity sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==
semver@^7.3.5:
version "7.5.4"
resolved "https://registry.yarnpkg.com/semver/-/semver-7.5.4.tgz#483986ec4ed38e1c6c48c34894a9182dbff68a6e"
integrity sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==
dependencies:
lru-cache "^6.0.0"
set-blocking@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7"
integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==
signal-exit@^3.0.0:
version "3.0.7"
resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.7.tgz#a9a1767f8af84155114eaabd73f99273c8f59ad9"
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.2.3:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
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.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
tar@^6.1.11:
version "6.2.0"
resolved "https://registry.yarnpkg.com/tar/-/tar-6.2.0.tgz#b14ce49a79cb1cd23bc9b016302dea5474493f73"
integrity sha512-/Wo7DcT0u5HUV486xg675HtjNd3BXZ6xDbzsCUZPt5iw8bTQ63bP0Raut3mvro9u+CUyq7YQd8Cx55fsZXxqLQ==
dependencies:
chownr "^2.0.0"
fs-minipass "^2.0.0"
minipass "^5.0.0"
minizlib "^2.1.1"
mkdirp "^1.0.3"
yallist "^4.0.0"
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
integrity sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==
util-deprecate@^1.0.1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
integrity sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
webidl-conversions@^3.0.0:
version "3.0.1"
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
integrity sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==
whatwg-url@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
integrity sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==
dependencies:
tr46 "~0.0.3"
webidl-conversions "^3.0.0"
wide-align@^1.1.2:
version "1.1.5"
resolved "https://registry.yarnpkg.com/wide-align/-/wide-align-1.1.5.tgz#df1d4c206854369ecf3c9a4898f1b23fbd9d15d3"
integrity sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==
dependencies:
string-width "^1.0.2 || 2 || 3 || 4"
wrappy@1:
version "1.0.2"
resolved "https://registry.yarnpkg.com/wrappy/-/wrappy-1.0.2.tgz#b5243d8f3ec1aa35f1364605bc0d1036e30ab69f"
integrity sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==
yallist@^4.0.0:
version "4.0.0"
resolved "https://registry.yarnpkg.com/yallist/-/yallist-4.0.0.tgz#9bb92790d9c0effec63be73519e11a35019a3a72"
integrity sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==