Compare commits

...

28 Commits

Author SHA1 Message Date
dependabot[bot]
1f93f3b262 chore(deps): bump express from 4.18.2 to 4.20.0
Bumps [express](https://github.com/expressjs/express) from 4.18.2 to 4.20.0.
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/master/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.18.2...4.20.0)

---
updated-dependencies:
- dependency-name: express
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2024-09-30 12:15:32 +00:00
Ömer Faruk Aydın
894afe8f92 Merge pull request #2104 from automatisch/remove-gql
refactor: remove whole graphql implementation
2024-09-30 15:14:14 +03:00
Ali BARIN
115394ac8c test(helpers/authentication): bring back applicable tests 2024-09-30 12:04:58 +00:00
Ali BARIN
de42eda65f refactor(helpers/authentication): remove obsolete arguments in isAuthenticated 2024-09-30 12:04:37 +00:00
Ali BARIN
0b7591edce refactor: remove whole graphql implementation 2024-09-30 12:02:25 +00:00
Ömer Faruk Aydın
a043a044ca Merge pull request #2099 from automatisch/aut-1262
feat: use REST API endpoints to create and reconnect connections
2024-09-30 13:33:52 +03:00
Ali BARIN
b1f2727beb test(create-dynamic-data): use 422 for error response 2024-09-30 08:25:56 +00:00
Ali BARIN
d6e78a48a0 feat(AddAppConnection): show meaningful error messages only 2024-09-30 08:25:56 +00:00
Ali BARIN
589fe0f5f3 feat(useAuthenticateApp): remove redundant graphql implementation 2024-09-30 08:25:56 +00:00
Ali BARIN
244eeeb816 feat(useAuthenticateApp): centralize invalidating queries 2024-09-30 08:25:56 +00:00
kasia.oczkowska
4d5fc50f1a feat: refactor verify connection mutation with the REST API endpoint 2024-09-30 08:25:54 +00:00
Ali BARIN
dc0273148c chore: remove redundant reset connection mutation 2024-09-30 08:25:09 +00:00
Ali BARIN
5e6f4bfb88 feat(useAuthenticateApp): use REST API endpoint to reset connection 2024-09-30 08:24:03 +00:00
Ali BARIN
bc0e18d074 chore: remove redundant update connection mutation 2024-09-30 08:24:02 +00:00
Ali BARIN
24d09fda4c feat(useAuthenticateApp): use REST API endpoint to update connection 2024-09-30 08:23:29 +00:00
Ali BARIN
5e20ac07d1 chore(add-authentication-steps): remove unnecessary id arguments 2024-09-30 08:23:29 +00:00
Ali BARIN
37c78e6bbd chore: remove redundant generate auth url mutation 2024-09-30 08:23:26 +00:00
Ali BARIN
7dcfb1081b feat(useAuthenticateApp): use REST API endpoint to create auth url 2024-09-30 08:23:00 +00:00
Ali BARIN
01407cf040 Merge pull request #2106 from automatisch/AUT-1265
feat: refactor update-current-user mutation with the REST API endpoint
2024-09-30 10:11:22 +02:00
Jakub P.
eb814c2fb0 test: add reset password tests 2024-09-27 23:16:16 +02:00
kasia.oczkowska
d3caadf4af feat: refactor update-current-user mutation with the REST API endpoint 2024-09-27 13:42:58 +01:00
Ali BARIN
5769c82fca Merge pull request #2098 from automatisch/aut-1258
feat(useAuthenticateApp): use REST API endpoint to create connection
2024-09-25 10:03:57 +02:00
Ali BARIN
3754783ce2 Merge pull request #2103 from automatisch/AUT-1279
test: add change own user data test
2024-09-24 21:25:41 +02:00
Jakub P.
55ebe2cc0b test: add change own user data test 2024-09-24 18:42:30 +02:00
Ali BARIN
23c1a42163 chore: remove redundant create connection mutation 2024-09-24 08:31:26 +00:00
Ali BARIN
c879ffa768 feat(useAuthenticateApp): use REST API endpoint to create connection 2024-09-24 08:30:53 +00:00
Ali BARIN
32f7bbfbab chore: remove redundant execute flow mutation 2024-09-24 10:23:33 +02:00
Ali BARIN
84d5b3b158 feat: use REST API endpoint to test step 2024-09-24 10:23:33 +02:00
67 changed files with 839 additions and 1806 deletions

View File

@@ -23,8 +23,6 @@
"dependencies": {
"@bull-board/express": "^3.10.1",
"@casl/ability": "^6.5.0",
"@graphql-tools/graphql-file-loader": "^7.3.4",
"@graphql-tools/load": "^7.5.2",
"@node-saml/passport-saml": "^4.0.4",
"@rudderstack/rudder-sdk-node": "^1.1.2",
"@sentry/node": "^7.42.0",
@@ -38,14 +36,10 @@
"crypto-js": "^4.1.1",
"debug": "~2.6.9",
"dotenv": "^10.0.0",
"express": "~4.18.2",
"express": "~4.20.0",
"express-async-errors": "^3.1.1",
"express-basic-auth": "^1.2.1",
"express-graphql": "^0.12.0",
"fast-xml-parser": "^4.0.11",
"graphql-middleware": "^6.1.15",
"graphql-shield": "^7.5.0",
"graphql-tools": "^8.2.0",
"handlebars": "^4.7.7",
"http-errors": "~1.6.3",
"http-proxy-agent": "^7.0.0",

View File

@@ -169,7 +169,7 @@ describe('POST /api/v1/steps/:stepId/dynamic-data', () => {
dynamicDataKey: 'listRepos',
parameters: {},
})
.expect(200);
.expect(422);
expect(response.body.errors).toEqual(errors);
});

View File

@@ -1,20 +0,0 @@
// Converted mutations
import executeFlow from './mutations/execute-flow.js';
import verifyConnection from './mutations/verify-connection.js';
import updateCurrentUser from './mutations/update-current-user.js';
import generateAuthUrl from './mutations/generate-auth-url.js';
import createConnection from './mutations/create-connection.js';
import resetConnection from './mutations/reset-connection.js';
import updateConnection from './mutations/update-connection.js';
const mutationResolvers = {
createConnection,
executeFlow,
generateAuthUrl,
resetConnection,
updateConnection,
updateCurrentUser,
verifyConnection,
};
export default mutationResolvers;

View File

@@ -1,48 +0,0 @@
import App from '../../models/app.js';
import AppConfig from '../../models/app-config.js';
const createConnection = async (_parent, params, context) => {
context.currentUser.can('create', 'Connection');
const { key, appAuthClientId } = params.input;
const app = await App.findOneByKey(key);
const appConfig = await AppConfig.query().findOne({ key });
let formattedData = params.input.formattedData;
if (appConfig) {
if (appConfig.disabled)
throw new Error(
'This application has been disabled for new connections!'
);
if (!appConfig.allowCustomConnection && formattedData)
throw new Error(`Custom connections cannot be created for ${app.name}!`);
if (appConfig.shared && !formattedData) {
const authClient = await appConfig
.$relatedQuery('appAuthClients')
.findById(appAuthClientId)
.where({
active: true,
})
.throwIfNotFound();
formattedData = authClient.formattedAuthDefaults;
}
}
const createdConnection = await context.currentUser
.$relatedQuery('connections')
.insert({
key,
appAuthClientId,
formattedData,
verified: false,
});
return createdConnection;
};
export default createConnection;

View File

@@ -1,16 +0,0 @@
import AppAuthClient from '../../models/app-auth-client';
const deleteAppAuthClient = async (_parent, params, context) => {
context.currentUser.can('delete', 'App');
await AppAuthClient.query()
.delete()
.findOne({
id: params.input.id,
})
.throwIfNotFound();
return;
};
export default deleteAppAuthClient;

View File

@@ -1,28 +0,0 @@
import testRun from '../../services/test-run.js';
import Step from '../../models/step.js';
const executeFlow = async (_parent, params, context) => {
const conditions = context.currentUser.can('update', 'Flow');
const isCreator = conditions.isCreator;
const allSteps = Step.query();
const userSteps = context.currentUser.$relatedQuery('steps');
const baseQuery = isCreator ? userSteps : allSteps;
const { stepId } = params.input;
const untilStep = await baseQuery.clone().findById(stepId).throwIfNotFound();
const { executionStep } = await testRun({ stepId });
if (executionStep.isFailed) {
throw new Error(JSON.stringify(executionStep.errorDetails));
}
await untilStep.$query().patch({
status: 'completed',
});
return { data: executionStep.dataOut, step: untilStep };
};
export default executeFlow;

View File

@@ -1,30 +0,0 @@
import globalVariable from '../../helpers/global-variable.js';
import App from '../../models/app.js';
const generateAuthUrl = async (_parent, params, context) => {
context.currentUser.can('create', 'Connection');
const connection = await context.currentUser
.$relatedQuery('connections')
.findOne({
id: params.input.id,
})
.throwIfNotFound();
if (!connection.formattedData) {
return null;
}
const authInstance = (
await import(`../../apps/${connection.key}/auth/index.js`)
).default;
const app = await App.findOneByKey(connection.key);
const $ = await globalVariable({ connection, app });
await authInstance.generateAuthUrl($);
return connection.formattedData;
};
export default generateAuthUrl;

View File

@@ -1,22 +0,0 @@
const resetConnection = async (_parent, params, context) => {
context.currentUser.can('create', 'Connection');
let connection = await context.currentUser
.$relatedQuery('connections')
.findOne({
id: params.input.id,
})
.throwIfNotFound();
if (!connection.formattedData) {
return null;
}
connection = await connection.$query().patchAndFetch({
formattedData: { screenName: connection.formattedData.screenName },
});
return connection;
};
export default resetConnection;

View File

@@ -1,33 +0,0 @@
import AppAuthClient from '../../models/app-auth-client.js';
const updateConnection = async (_parent, params, context) => {
context.currentUser.can('create', 'Connection');
let connection = await context.currentUser
.$relatedQuery('connections')
.findOne({
id: params.input.id,
})
.throwIfNotFound();
let formattedData = params.input.formattedData;
if (params.input.appAuthClientId) {
const appAuthClient = await AppAuthClient.query()
.findById(params.input.appAuthClientId)
.throwIfNotFound();
formattedData = appAuthClient.formattedAuthDefaults;
}
connection = await connection.$query().patchAndFetch({
formattedData: {
...connection.formattedData,
...formattedData,
},
});
return connection;
};
export default updateConnection;

View File

@@ -1,11 +0,0 @@
const updateCurrentUser = async (_parent, params, context) => {
const user = await context.currentUser.$query().patchAndFetch({
email: params.input.email,
password: params.input.password,
fullName: params.input.fullName,
});
return user;
};
export default updateCurrentUser;

View File

@@ -1,18 +0,0 @@
const updateFlow = async (_parent, params, context) => {
context.currentUser.can('update', 'Flow');
let flow = await context.currentUser
.$relatedQuery('flows')
.findOne({
id: params.input.id,
})
.throwIfNotFound();
flow = await flow.$query().patchAndFetch({
name: params.input.name,
});
return flow;
};
export default updateFlow;

View File

@@ -1,62 +0,0 @@
import Role from '../../models/role.js';
import Permission from '../../models/permission.js';
import permissionCatalog from '../../helpers/permission-catalog.ee.js';
const updateRole = async (_parent, params, context) => {
context.currentUser.can('update', 'Role');
const { id, name, description, permissions } = params.input;
const role = await Role.query().findById(id).throwIfNotFound();
try {
const updatedRole = await Role.transaction(async (trx) => {
await role.$relatedQuery('permissions', trx).delete();
if (permissions?.length) {
const sanitizedPermissions = permissions
.filter((permission) => {
const { action, subject, conditions } = permission;
const relevantAction = permissionCatalog.actions.find(
(actionCatalogItem) => actionCatalogItem.key === action
);
const validSubject = relevantAction.subjects.includes(subject);
const validConditions = conditions.every((condition) => {
return !!permissionCatalog.conditions.find(
(conditionCatalogItem) => conditionCatalogItem.key === condition
);
});
return validSubject && validConditions;
})
.map((permission) => ({
...permission,
roleId: role.id,
}));
await Permission.query().insert(sanitizedPermissions);
}
await role.$query(trx).patch({
name,
description,
});
return await Role.query(trx)
.leftJoinRelated({
permissions: true,
})
.withGraphFetched({
permissions: true,
})
.findById(id);
});
return updatedRole;
} catch (err) {
throw new Error('The role could not be updated!');
}
};
export default updateRole;

View File

@@ -1,29 +0,0 @@
import App from '../../models/app.js';
import globalVariable from '../../helpers/global-variable.js';
const verifyConnection = async (_parent, params, context) => {
context.currentUser.can('create', 'Connection');
let connection = await context.currentUser
.$relatedQuery('connections')
.findOne({
id: params.input.id,
})
.throwIfNotFound();
const app = await App.findOneByKey(connection.key);
const $ = await globalVariable({ connection, app });
await app.auth.verifyCredentials($);
connection = await connection.$query().patchAndFetch({
verified: true,
draft: false,
});
return {
...connection,
app,
};
};
export default verifyConnection;

View File

@@ -1,7 +0,0 @@
import mutationResolvers from './mutation-resolvers.js';
const resolvers = {
Mutation: mutationResolvers,
};
export default resolvers;

View File

@@ -1,382 +0,0 @@
type Query {
placeholderQuery(name: String): Boolean
}
type Mutation {
createConnection(input: CreateConnectionInput): Connection
executeFlow(input: ExecuteFlowInput): executeFlowType
generateAuthUrl(input: GenerateAuthUrlInput): AuthLink
resetConnection(input: ResetConnectionInput): Connection
updateConnection(input: UpdateConnectionInput): Connection
updateCurrentUser(input: UpdateCurrentUserInput): User
verifyConnection(input: VerifyConnectionInput): Connection
}
"""
Exposes a URL that specifies the behaviour of this scalar.
"""
directive @specifiedBy(
"""
The URL that specifies the behaviour of this scalar.
"""
url: String!
) on SCALAR
type Trigger {
name: String
key: String
description: String
showWebhookUrl: Boolean
pollInterval: Int
type: String
substeps: [Substep]
}
type Action {
name: String
key: String
description: String
substeps: [Substep]
}
type Substep {
key: String
name: String
arguments: [SubstepArgument]
}
type SubstepArgument {
label: String
key: String
type: String
description: String
required: Boolean
variables: Boolean
options: [SubstepArgumentOption]
source: SubstepArgumentSource
additionalFields: SubstepArgumentAdditionalFields
dependsOn: [String]
fields: [SubstepArgument]
value: JSONObject
}
type SubstepArgumentOption {
label: String
value: JSONObject
}
type SubstepArgumentSource {
type: String
name: String
arguments: [SubstepArgumentSourceArgument]
}
type SubstepArgumentSourceArgument {
name: String
value: String
}
type SubstepArgumentAdditionalFields {
type: String
name: String
arguments: [SubstepArgumentAdditionalFieldsArgument]
}
type SubstepArgumentAdditionalFieldsArgument {
name: String
value: String
}
type App {
name: String
key: String
connectionCount: Int
flowCount: Int
iconUrl: String
docUrl: String
authDocUrl: String
primaryColor: String
supportsConnections: Boolean
auth: AppAuth
triggers: [Trigger]
actions: [Action]
connections: [Connection]
}
type AppAuth {
fields: [Field]
authenticationSteps: [AuthenticationStep]
sharedAuthenticationSteps: [AuthenticationStep]
reconnectionSteps: [ReconnectionStep]
sharedReconnectionSteps: [ReconnectionStep]
}
enum ArgumentEnumType {
integer
string
}
type AuthenticationStep {
type: String
name: String
arguments: [AuthenticationStepArgument]
}
type AuthenticationStepArgument {
name: String
value: String
type: ArgumentEnumType
properties: [AuthenticationStepProperty]
}
type AuthenticationStepProperty {
name: String
value: String
}
type AuthLink {
url: String
}
type Connection {
id: String
key: String
reconnectable: Boolean
appAuthClientId: String
formattedData: ConnectionData
verified: Boolean
app: App
createdAt: String
flowCount: Int
}
type ConnectionData {
screenName: String
}
type executeFlowType {
data: JSONObject
step: Step
}
type ExecutionStep {
id: String
executionId: String
stepId: String
step: Step
status: String
dataIn: JSONObject
dataOut: JSONObject
errorDetails: JSONObject
createdAt: String
updatedAt: String
}
type Field {
key: String
label: String
type: String
required: Boolean
readOnly: Boolean
value: String
placeholder: String
description: String
docUrl: String
clickToCopy: Boolean
options: [SubstepArgumentOption]
}
enum FlowStatus {
paused
published
draft
}
type Flow {
id: String
name: String
active: Boolean
steps: [Step]
createdAt: String
updatedAt: String
status: FlowStatus
}
type SamlAuthProvidersRoleMapping {
id: String
samlAuthProviderId: String
roleId: String
remoteRoleName: String
}
input CreateConnectionInput {
key: String!
appAuthClientId: String
formattedData: JSONObject
}
input GenerateAuthUrlInput {
id: String!
}
input UpdateConnectionInput {
id: String!
formattedData: JSONObject
appAuthClientId: String
}
input ResetConnectionInput {
id: String!
}
input VerifyConnectionInput {
id: String!
}
input ExecuteFlowInput {
stepId: String!
}
input UserRoleInput {
id: String
}
input UpdateCurrentUserInput {
email: String
password: String
fullName: String
}
"""
The `JSONObject` scalar type represents JSON objects as specified by [ECMA-404](http://www.ecma-international.org/publications/files/ECMA-ST/ECMA-404.pdf).
"""
scalar JSONObject
input PreviousStepInput {
id: String
}
type ReconnectionStep {
type: String
name: String
arguments: [ReconnectionStepArgument]
}
type ReconnectionStepArgument {
name: String
value: String
type: ArgumentEnumType
properties: [ReconnectionStepProperty]
}
type ReconnectionStepProperty {
name: String
value: String
}
type Step {
id: String
previousStepId: String
key: String
appKey: String
iconUrl: String
webhookUrl: String
type: StepEnumType
parameters: JSONObject
connection: Connection
flow: Flow
position: Int
status: String
executionSteps: [ExecutionStep]
}
input StepConnectionInput {
id: String
}
enum StepEnumType {
trigger
action
}
input StepFlowInput {
id: String
}
input StepInput {
id: String
previousStepId: String
key: String
appKey: String
connection: StepConnectionInput
flow: StepFlowInput
parameters: JSONObject
previousStep: PreviousStepInput
}
type User {
id: String
fullName: String
email: String
role: Role
permissions: [Permission]
createdAt: String
updatedAt: String
}
type Role {
id: String
name: String
key: String
description: String
isAdmin: Boolean
permissions: [Permission]
}
type PageInfo {
currentPage: Int!
totalPages: Int!
}
type ExecutionStepEdge {
node: ExecutionStep
}
type ExecutionStepConnection {
edges: [ExecutionStepEdge]
pageInfo: PageInfo
}
type License {
id: String
name: String
expireAt: String
verified: Boolean
}
type Permission {
id: String
action: String
subject: String
conditions: [String]
}
type Action {
label: String
key: String
subjects: [String]
}
type Condition {
key: String
label: String
}
type Subject {
label: String
key: String
}
schema {
query: Query
mutation: Mutation
}

View File

@@ -27,12 +27,7 @@ const authenticationStepsWithoutAuthUrl = [
{
type: 'mutation',
name: 'verifyConnection',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
],
arguments: [],
},
];
@@ -54,12 +49,7 @@ const authenticationStepsWithAuthUrl = [
{
type: 'mutation',
name: 'generateAuthUrl',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
],
arguments: [],
},
{
type: 'openWithPopup',
@@ -75,10 +65,6 @@ const authenticationStepsWithAuthUrl = [
type: 'mutation',
name: 'updateConnection',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
{
name: 'formattedData',
value: '{openAuthPopup.all}',
@@ -88,12 +74,7 @@ const authenticationStepsWithAuthUrl = [
{
type: 'mutation',
name: 'verifyConnection',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
],
arguments: [],
},
];
@@ -115,12 +96,7 @@ const sharedAuthenticationStepsWithAuthUrl = [
{
type: 'mutation',
name: 'generateAuthUrl',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
],
arguments: [],
},
{
type: 'openWithPopup',
@@ -136,10 +112,6 @@ const sharedAuthenticationStepsWithAuthUrl = [
type: 'mutation',
name: 'updateConnection',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
{
name: 'formattedData',
value: '{openAuthPopup.all}',
@@ -149,12 +121,7 @@ const sharedAuthenticationStepsWithAuthUrl = [
{
type: 'mutation',
name: 'verifyConnection',
arguments: [
{
name: 'id',
value: '{createConnection.id}',
},
],
arguments: [],
},
];

View File

@@ -1,54 +1,23 @@
import cloneDeep from 'lodash/cloneDeep.js';
const connectionIdArgument = {
name: 'id',
value: '{connection.id}',
};
const resetConnectionStep = {
type: 'mutation',
name: 'resetConnection',
arguments: [connectionIdArgument],
arguments: [],
};
function replaceCreateConnection(string) {
return string.replace('{createConnection.id}', '{connection.id}');
}
function removeAppKeyArgument(args) {
return args.filter((argument) => argument.name !== 'key');
}
function addConnectionId(step) {
step.arguments = step.arguments.map((argument) => {
if (typeof argument.value === 'string') {
argument.value = replaceCreateConnection(argument.value);
}
if (argument.properties) {
argument.properties = argument.properties.map((property) => {
return {
name: property.name,
value: replaceCreateConnection(property.value),
};
});
}
return argument;
});
return step;
}
function replaceCreateConnectionsWithUpdate(steps) {
const updatedSteps = cloneDeep(steps);
return updatedSteps.map((step) => {
const updatedStep = addConnectionId(step);
const updatedStep = { ...step };
if (step.name === 'createConnection') {
updatedStep.name = 'updateConnection';
updatedStep.arguments = removeAppKeyArgument(updatedStep.arguments);
updatedStep.arguments.unshift(connectionIdArgument);
return updatedStep;
}

View File

@@ -1,8 +1,7 @@
import { rule, shield } from 'graphql-shield';
import User from '../models/user.js';
import AccessToken from '../models/access-token.js';
export const isAuthenticated = async (_parent, _args, req) => {
export const isAuthenticated = async (req) => {
const token = req.headers['authorization'];
if (token == null) return false;
@@ -41,25 +40,9 @@ export const isAuthenticated = async (_parent, _args, req) => {
};
export const authenticateUser = async (request, response, next) => {
if (await isAuthenticated(null, null, request)) {
if (await isAuthenticated(request)) {
next();
} else {
return response.status(401).end();
}
};
const isAuthenticatedRule = rule()(isAuthenticated);
export const authenticationRules = {
Mutation: {
'*': isAuthenticatedRule,
},
};
const authenticationOptions = {
allowExternalErrors: true,
};
const authentication = shield(authenticationRules, authenticationOptions);
export default authentication;

View File

@@ -1,18 +1,17 @@
import { describe, it, expect } from 'vitest';
import { allow } from 'graphql-shield';
import { isAuthenticated, authenticationRules } from './authentication.js';
import { isAuthenticated } from './authentication.js';
import { createUser } from '../../test/factories/user.js';
import createAuthTokenByUserId from '../helpers/create-auth-token-by-user-id.js';
describe('isAuthenticated', () => {
it('should return false if no token is provided', async () => {
const req = { headers: {} };
expect(await isAuthenticated(null, null, req)).toBe(false);
expect(await isAuthenticated(req)).toBe(false);
});
it('should return false if token is invalid', async () => {
const req = { headers: { authorization: 'invalidToken' } };
expect(await isAuthenticated(null, null, req)).toBe(false);
expect(await isAuthenticated(req)).toBe(false);
});
it('should return true if token is valid and there is a user', async () => {
@@ -20,7 +19,7 @@ describe('isAuthenticated', () => {
const token = await createAuthTokenByUserId(user.id);
const req = { headers: { authorization: token } };
expect(await isAuthenticated(null, null, req)).toBe(true);
expect(await isAuthenticated(req)).toBe(true);
});
it('should return false if token is valid and but there is no user', async () => {
@@ -29,46 +28,6 @@ describe('isAuthenticated', () => {
await user.$query().delete();
const req = { headers: { authorization: token } };
expect(await isAuthenticated(null, null, req)).toBe(false);
});
});
describe('authentication rules', () => {
const getQueryAndMutationNames = (rules) => {
const queries = Object.keys(rules.Query || {});
const mutations = Object.keys(rules.Mutation || {});
return { queries, mutations };
};
const { queries, mutations } = getQueryAndMutationNames(authenticationRules);
if (queries.length) {
describe('for queries', () => {
queries.forEach((query) => {
it(`should apply correct rule for query: ${query}`, () => {
const ruleApplied = authenticationRules.Query[query];
if (query === '*') {
expect(ruleApplied.func).toBe(isAuthenticated);
} else {
expect(ruleApplied).toEqual(allow);
}
});
});
});
}
describe('for mutations', () => {
mutations.forEach((mutation) => {
it(`should apply correct rule for mutation: ${mutation}`, () => {
const ruleApplied = authenticationRules.Mutation[mutation];
if (mutation === '*') {
expect(ruleApplied.func).toBe(isAuthenticated);
} else {
expect(ruleApplied).toBe(allow);
}
});
});
expect(await isAuthenticated(req)).toBe(false);
});
});

View File

@@ -50,7 +50,7 @@ const errorHandler = (error, request, response, next) => {
},
};
response.status(200).json(httpErrorPayload);
response.status(422).json(httpErrorPayload);
}
if (error instanceof NotAuthorizedError) {

View File

@@ -1,53 +0,0 @@
import path, { join } from 'path';
import { fileURLToPath } from 'url';
import { graphqlHTTP } from 'express-graphql';
import { loadSchemaSync } from '@graphql-tools/load';
import { GraphQLFileLoader } from '@graphql-tools/graphql-file-loader';
import { addResolversToSchema } from '@graphql-tools/schema';
import { applyMiddleware } from 'graphql-middleware';
import appConfig from '../config/app.js';
import logger from './logger.js';
import authentication from './authentication.js';
import * as Sentry from './sentry.ee.js';
import resolvers from '../graphql/resolvers.js';
import HttpError from '../errors/http.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const schema = loadSchemaSync(join(__dirname, '../graphql/schema.graphql'), {
loaders: [new GraphQLFileLoader()],
});
const schemaWithResolvers = addResolversToSchema({
schema,
resolvers,
});
const graphQLInstance = graphqlHTTP({
schema: applyMiddleware(
schemaWithResolvers,
authentication.generate(schemaWithResolvers)
),
graphiql: appConfig.isDev,
customFormatErrorFn: (error) => {
logger.error(error.path + ' : ' + error.message + '\n' + error.stack);
if (error.originalError instanceof HttpError) {
delete error.originalError.response;
}
Sentry.captureException(error, {
tags: { graphql: true },
extra: {
source: error.source?.body,
positions: error.positions,
path: error.path,
},
});
return error;
},
});
export default graphQLInstance;

View File

@@ -6,18 +6,8 @@ const stream = {
logger.http(message.substring(0, message.lastIndexOf('\n'))),
};
const registerGraphQLToken = () => {
morgan.token('graphql-query', (req) => {
if (req.body.query) {
return `\n GraphQL ${req.body.query}`;
}
});
};
registerGraphQLToken();
const morganMiddleware = morgan(
':method :url :status :res[content-length] - :response-time ms :graphql-query',
':method :url :status :res[content-length] - :response-time ms',
{ stream }
);

View File

@@ -17,7 +17,6 @@ export function init(app) {
integrations: [
app && new Sentry.Integrations.Http({ tracing: true }),
app && new Tracing.Integrations.Express({ app }),
app && new Tracing.Integrations.GraphQL(),
].filter(Boolean),
tracesSampleRate: 1.0,
});

View File

@@ -1,5 +1,4 @@
import { Router } from 'express';
import graphQLInstance from '../helpers/graphql-instance.js';
import webhooksRouter from './webhooks.js';
import paddleRouter from './paddle.ee.js';
import healthcheckRouter from './healthcheck.js';
@@ -23,7 +22,6 @@ import installationUsersRouter from './api/v1/installation/users.js';
const router = Router();
router.use('/graphql', graphQLInstance);
router.use('/webhooks', webhooksRouter);
router.use('/paddle', paddleRouter);
router.use('/healthcheck', healthcheckRouter);

View File

@@ -8,6 +8,7 @@ export class AuthenticatedPage extends BasePage {
super(page);
this.profileMenuButton = this.page.getByTestId('profile-menu-button');
this.logoutMenuItem = this.page.getByTestId('logout-item');
this.adminMenuItem = this.page.getByRole('menuitem', { name: 'Admin' });
this.userInterfaceDrawerItem = this.page.getByTestId(
'user-interface-drawer-link'
@@ -18,4 +19,9 @@ export class AuthenticatedPage extends BasePage {
this.typographyLogo = this.page.getByTestId('typography-logo');
this.customLogo = this.page.getByTestId('custom-logo');
}
async logout() {
await this.profileMenuButton.click();
await this.logoutMenuItem.click();
}
}

View File

@@ -35,7 +35,7 @@ exports.test = test.extend({
userInterfacePage: async ({ page }, use) => {
await use(new UserInterfacePage(page));
},
...adminFixtures
...adminFixtures,
});
exports.publicTest = test.extend({
@@ -49,21 +49,18 @@ exports.publicTest = test.extend({
await use(loginPage);
},
acceptInvitationPage: async ({ page }, use) => {
const acceptInvitationPage = new AcceptInvitation(page);
await use(acceptInvitationPage);
},
adminSetupPage: async ({ page }, use) => {
const adminSetupPage = new AdminSetupPage(page);
await use(adminSetupPage);
},
adminCreateUserPage: async ({page}, use) => {
adminCreateUserPage: async ({ page }, use) => {
const adminCreateUserPage = new AdminCreateUserPage(page);
await use(adminCreateUserPage);
}
},
});
expect.extend({

View File

@@ -0,0 +1,23 @@
const { AuthenticatedPage } = require('./authenticated-page');
export class MyProfilePage extends AuthenticatedPage {
constructor(page) {
super(page);
this.fullName = this.page.locator('[name="fullName"]');
this.email = this.page.locator('[name="email"]');
this.currentPassword = this.page.locator('[name="currentPassword"]');
this.newPassword = this.page.locator('[name="password"]');
this.passwordConfirmation = this.page.locator('[name="confirmPassword"]');
this.updateProfileButton = this.page.getByTestId('update-profile-button');
this.updatePasswordButton = this.page.getByTestId('update-password-button');
this.settingsMenuItem = this.page.getByRole('menuitem', {
name: 'Settings',
});
}
async navigateTo() {
await this.profileMenuButton.click();
await this.settingsMenuItem.click();
}
}

View File

@@ -0,0 +1,187 @@
const { publicTest, expect } = require('../../fixtures/index');
const { AdminUsersPage } = require('../../fixtures/admin/users-page');
const { MyProfilePage } = require('../../fixtures/my-profile-page');
const { LoginPage } = require('../../fixtures/login-page');
publicTest.describe('My Profile', () => {
let testUser;
publicTest.beforeEach(
async ({ acceptInvitationPage, adminCreateUserPage, loginPage, page }) => {
let acceptInvitationLink;
adminCreateUserPage.seed(
Math.ceil(Math.random() * Number.MAX_SAFE_INTEGER)
);
testUser = adminCreateUserPage.generateUser();
const adminUsersPage = new AdminUsersPage(page);
const myProfilePage = new MyProfilePage(page);
await publicTest.step('login as Admin', async () => {
await loginPage.login();
await expect(loginPage.page).toHaveURL('/flows');
});
await publicTest.step('create new user', async () => {
await adminUsersPage.navigateTo();
await adminUsersPage.createUserButton.click();
await adminCreateUserPage.fullNameInput.fill(testUser.fullName);
await adminCreateUserPage.emailInput.fill(testUser.email);
await adminCreateUserPage.roleInput.click();
await adminCreateUserPage.page
.getByRole('option', { name: 'Admin' })
.click();
await adminCreateUserPage.createButton.click();
const snackbar = await adminUsersPage.getSnackbarData(
'snackbar-create-user-success'
);
await expect(snackbar.variant).toBe('success');
});
await publicTest.step('copy invitation link', async () => {
const invitationMessage =
await adminCreateUserPage.acceptInvitationLink;
acceptInvitationLink = await invitationMessage.getAttribute('href');
});
await publicTest.step('logout', async () => {
await myProfilePage.logout();
});
await publicTest.step('accept invitation', async () => {
await page.goto(acceptInvitationLink);
await acceptInvitationPage.acceptInvitation(LoginPage.defaultPassword);
});
await publicTest.step('login as new Admin', async () => {
await loginPage.login(testUser.email, LoginPage.defaultPassword);
await expect(loginPage.loginButton).not.toBeVisible();
await expect(page).toHaveURL('/flows');
});
}
);
publicTest('user should be able to change own data', async ({ page }) => {
const myProfilePage = new MyProfilePage(page);
await publicTest.step('change own data', async () => {
await myProfilePage.navigateTo();
await myProfilePage.fullName.fill('abecadło');
await myProfilePage.email.fill('a' + testUser.email);
await myProfilePage.updateProfileButton.click();
});
await publicTest.step('verify changed data', async () => {
await expect(myProfilePage.fullName).toHaveValue('abecadło');
await expect(myProfilePage.email).toHaveValue('a' + testUser.email);
await page.reload();
await expect(myProfilePage.fullName).toHaveValue('abecadło');
await expect(myProfilePage.email).toHaveValue('a' + testUser.email);
});
});
publicTest(
'user should not be able to change email to already existing one',
async ({ page }) => {
const myProfilePage = new MyProfilePage(page);
await publicTest.step('change email to existing one', async () => {
await myProfilePage.navigateTo();
await myProfilePage.email.fill(LoginPage.defaultEmail);
await myProfilePage.updateProfileButton.click();
});
await publicTest.step('verify error message', async () => {
const snackbar = await myProfilePage.getSnackbarData(
'snackbar-update-profile-settings-error'
);
await expect(snackbar.variant).toBe('error');
});
}
);
publicTest(
'user should be able to change own password',
async ({ loginPage, page }) => {
const myProfilePage = new MyProfilePage(page);
await publicTest.step('change own password', async () => {
await myProfilePage.navigateTo();
await myProfilePage.currentPassword.fill(LoginPage.defaultPassword);
await myProfilePage.newPassword.fill(
LoginPage.defaultPassword + LoginPage.defaultPassword
);
await myProfilePage.passwordConfirmation.fill(
LoginPage.defaultPassword + LoginPage.defaultPassword
);
await myProfilePage.updatePasswordButton.click();
});
await publicTest.step('logout', async () => {
await myProfilePage.logout();
});
await publicTest.step('login with new credentials', async () => {
await loginPage.login(
testUser.email,
LoginPage.defaultPassword + LoginPage.defaultPassword
);
await expect(loginPage.loginButton).not.toBeVisible();
await expect(page).toHaveURL('/flows');
});
await publicTest.step('verify if user is the same', async () => {
await myProfilePage.navigateTo();
await expect(myProfilePage.email).toHaveValue(testUser.email);
});
}
);
publicTest(
'user should not be able to change own password if current one is incorrect',
async ({ loginPage, page }) => {
const myProfilePage = new MyProfilePage(page);
await publicTest.step('change own password', async () => {
await myProfilePage.navigateTo();
await myProfilePage.currentPassword.fill('wrongpassword');
await myProfilePage.newPassword.fill(
LoginPage.defaultPassword + LoginPage.defaultPassword
);
await myProfilePage.passwordConfirmation.fill(
LoginPage.defaultPassword + LoginPage.defaultPassword
);
await myProfilePage.updatePasswordButton.click();
});
await publicTest.step('verify error message', async () => {
const snackbar = await myProfilePage.getSnackbarData(
'snackbar-update-password-error'
);
await expect(snackbar.variant).toBe('error');
});
await publicTest.step('logout', async () => {
await myProfilePage.logout();
});
await publicTest.step('login with old credentials', async () => {
await loginPage.login(testUser.email, LoginPage.defaultPassword);
await expect(loginPage.loginButton).not.toBeVisible();
await expect(page).toHaveURL('/flows');
});
await publicTest.step('verify if user is the same', async () => {
await myProfilePage.navigateTo();
await expect(myProfilePage.email).toHaveValue(testUser.email);
});
}
);
});

View File

@@ -4,7 +4,6 @@
"license": "See LICENSE file",
"description": "The open source Zapier alternative. Build workflow automation without spending time and money.",
"dependencies": {
"@apollo/client": "^3.6.9",
"@casl/ability": "^6.5.0",
"@casl/react": "^3.1.0",
"@dagrejs/dagre": "^1.1.2",
@@ -21,7 +20,6 @@
"@testing-library/user-event": "^12.1.10",
"clipboard-copy": "^4.0.1",
"compare-versions": "^4.1.3",
"graphql": "^15.6.0",
"lodash": "^4.17.21",
"luxon": "^2.3.1",
"mui-color-input": "^2.0.0",

View File

@@ -86,7 +86,6 @@ export default function ResetPasswordForm() {
: ''
}
/>
<TextField
label={formatMessage(
'acceptInvitationForm.confirmPasswordFieldLabel',
@@ -111,7 +110,7 @@ export default function ResetPasswordForm() {
{acceptInvitation.isError && (
<Alert
data-test='accept-invitation-form-error'
data-test="accept-invitation-form-error"
severity="error"
sx={{ mt: 1, fontWeight: 500 }}
>

View File

@@ -6,11 +6,11 @@ import Menu from '@mui/material/Menu';
import { Link } from 'react-router-dom';
import Can from 'components/Can';
import apolloClient from 'graphql/client';
import * as URLS from 'config/urls';
import useAuthentication from 'hooks/useAuthentication';
import useFormatMessage from 'hooks/useFormatMessage';
import useRevokeAccessToken from 'hooks/useRevokeAccessToken';
function AccountDropdownMenu(props) {
const formatMessage = useFormatMessage();
const authentication = useAuthentication();
@@ -23,7 +23,6 @@ function AccountDropdownMenu(props) {
await revokeAccessTokenMutation.mutateAsync();
authentication.removeToken();
await apolloClient.clearStore();
onClose();
navigate(URLS.LOGIN);
};

View File

@@ -26,7 +26,8 @@ function AddAppConnection(props) {
const navigate = useNavigate();
const [searchParams] = useSearchParams();
const formatMessage = useFormatMessage();
const [error, setError] = React.useState(null);
const [errorMessage, setErrorMessage] = React.useState(null);
const [errorDetails, setErrorDetails] = React.useState(null);
const [inProgress, setInProgress] = React.useState(false);
const hasConnection = Boolean(connectionId);
const useShared = searchParams.get('shared') === 'true';
@@ -76,7 +77,8 @@ function AddAppConnection(props) {
async (data) => {
if (!authenticate) return;
setInProgress(true);
setError(null);
setErrorMessage(null);
setErrorDetails(null);
try {
const response = await authenticate({
fields: data,
@@ -85,21 +87,19 @@ function AddAppConnection(props) {
await queryClient.invalidateQueries({
queryKey: ['apps', key, 'connections'],
});
onClose(response);
} catch (err) {
const error = err;
console.log(error);
if (error.message) {
setError(error);
} else {
setError(error.graphQLErrors?.[0]);
}
setErrorMessage(error.message);
setErrorDetails(error?.response?.data?.errors);
} finally {
setInProgress(false);
}
},
[authenticate],
[authenticate, key, onClose, queryClient],
);
if (useShared)
@@ -134,16 +134,16 @@ function AddAppConnection(props) {
</Alert>
)}
{error && (
{(errorMessage || errorDetails) && (
<Alert
data-test="add-connection-error"
severity="error"
sx={{ mt: 1, fontWeight: 500, wordBreak: 'break-all' }}
>
{error.message}
{error.details && (
{!errorDetails && errorMessage}
{errorDetails && (
<pre style={{ whiteSpace: 'pre-wrap' }}>
{JSON.stringify(error.details, null, 2)}
{JSON.stringify(errorDetails, null, 2)}
</pre>
)}
</Alert>

View File

@@ -7,7 +7,6 @@ import DialogContent from '@mui/material/DialogContent';
import DialogContentText from '@mui/material/DialogContentText';
import DialogTitle from '@mui/material/DialogTitle';
import CircularProgress from '@mui/material/CircularProgress';
import { ApolloError } from '@apollo/client';
import { FieldPropType } from 'propTypes/propTypes';
import useFormatMessage from 'hooks/useFormatMessage';
@@ -89,7 +88,9 @@ function AdminApplicationAuthClientDialog(props) {
}
AdminApplicationAuthClientDialog.propTypes = {
error: PropTypes.instanceOf(ApolloError),
error: PropTypes.shape({
message: PropTypes.string,
}),
onClose: PropTypes.func.isRequired,
title: PropTypes.string.isRequired,
loading: PropTypes.bool.isRequired,

View File

@@ -1,34 +0,0 @@
import { ApolloProvider as BaseApolloProvider } from '@apollo/client';
import * as React from 'react';
import { mutateAndGetClient } from 'graphql/client';
import useAuthentication from 'hooks/useAuthentication';
import useEnqueueSnackbar from 'hooks/useEnqueueSnackbar';
const ApolloProvider = (props) => {
const enqueueSnackbar = useEnqueueSnackbar();
const authentication = useAuthentication();
const onError = React.useCallback(
(message) => {
enqueueSnackbar(message, {
variant: 'error',
SnackbarProps: {
'data-test': 'snackbar-error',
},
});
},
[enqueueSnackbar],
);
const client = React.useMemo(() => {
return mutateAndGetClient({
onError,
token: authentication.token,
});
}, [onError, authentication]);
return <BaseApolloProvider client={client} {...props} />;
};
export default ApolloProvider;

View File

@@ -4,7 +4,6 @@ import { useNavigate } from 'react-router-dom';
import * as URLS from 'config/urls';
import ConfirmationDialog from 'components/ConfirmationDialog';
import apolloClient from 'graphql/client';
import useAuthentication from 'hooks/useAuthentication';
import useFormatMessage from 'hooks/useFormatMessage';
import useCurrentUser from 'hooks/useCurrentUser';
@@ -27,8 +26,6 @@ function DeleteAccountDialog(props) {
authentication.removeToken();
await apolloClient.clearStore();
navigate(URLS.LOGIN);
}, [deleteCurrentUser, authentication, navigate]);

View File

@@ -1,5 +1,4 @@
import { useEffect, useCallback, createContext, useRef, useState } from 'react';
import { useMutation } from '@apollo/client';
import { useQueryClient } from '@tanstack/react-query';
import { FlowPropType } from 'propTypes/propTypes';
import ReactFlow, { useNodesState, useEdgesState } from 'reactflow';

View File

@@ -1,6 +1,5 @@
import * as React from 'react';
import { useNavigate } from 'react-router-dom';
import { useMutation } from '@apollo/client';
import Paper from '@mui/material/Paper';
import Typography from '@mui/material/Typography';
import LoadingButton from '@mui/lab/LoadingButton';

View File

@@ -1,6 +1,5 @@
import PropTypes from 'prop-types';
import * as React from 'react';
import { useMutation } from '@apollo/client';
import Box from '@mui/material/Box';
import Collapse from '@mui/material/Collapse';
import ListItem from '@mui/material/ListItem';
@@ -10,37 +9,19 @@ import LoadingButton from '@mui/lab/LoadingButton';
import { EditorContext } from 'contexts/Editor';
import useFormatMessage from 'hooks/useFormatMessage';
import { EXECUTE_FLOW } from 'graphql/mutations/execute-flow';
import useTestStep from 'hooks/useTestStep';
import JSONViewer from 'components/JSONViewer';
import WebhookUrlInfo from 'components/WebhookUrlInfo';
import FlowSubstepTitle from 'components/FlowSubstepTitle';
import { useQueryClient } from '@tanstack/react-query';
import { StepPropType, SubstepPropType } from 'propTypes/propTypes';
function serializeErrors(graphQLErrors) {
return graphQLErrors?.map((error) => {
try {
return {
...error,
message: (
<pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>
{JSON.stringify(JSON.parse(error.message), null, 2)}
</pre>
),
};
} catch {
return error;
}
});
}
function TestSubstep(props) {
const {
substep,
expanded = false,
onExpand,
onCollapse,
onSubmit,
onContinue,
step,
showWebhookUrl = false,
@@ -48,15 +29,21 @@ function TestSubstep(props) {
} = props;
const formatMessage = useFormatMessage();
const editorContext = React.useContext(EditorContext);
const [executeFlow, { data, error, loading, called, reset }] = useMutation(
EXECUTE_FLOW,
{
context: { autoSnackbar: false },
},
);
const response = data?.executeFlow?.data;
const isCompleted = !error && called && !loading;
const hasNoOutput = !response && isCompleted;
const {
mutateAsync: testStep,
isPending: isTestStepPending,
data,
isSuccess: isCompleted,
reset,
} = useTestStep(step.id);
const loading = isTestStepPending;
const lastExecutionStep = data?.data.lastExecutionStep;
const dataOut = lastExecutionStep?.dataOut;
const errorDetails = lastExecutionStep?.errorDetails;
const hasError = errorDetails && Object.values(errorDetails).length > 0;
const hasNoOutput = !hasError && isCompleted && !dataOut;
const hasOutput =
!hasError && isCompleted && dataOut && Object.values(dataOut).length > 0;
const { name } = substep;
const queryClient = useQueryClient();
@@ -75,18 +62,12 @@ function TestSubstep(props) {
return;
}
await executeFlow({
variables: {
input: {
stepId: step.id,
},
},
});
await testStep();
await queryClient.invalidateQueries({
queryKey: ['flows', flowId],
});
}, [onSubmit, onContinue, isCompleted, queryClient, flowId]);
}, [testStep, onContinue, isCompleted, queryClient, flowId]);
const onToggle = expanded ? onCollapse : onExpand;
@@ -102,14 +83,14 @@ function TestSubstep(props) {
alignItems: 'flex-start',
}}
>
{!!error?.graphQLErrors?.length && (
{hasError && (
<Alert
severity="error"
sx={{ mb: 2, fontWeight: 500, width: '100%' }}
>
{serializeErrors(error.graphQLErrors).map((error, i) => (
<div key={i}>{error.message}</div>
))}
<pre style={{ margin: 0, whiteSpace: 'pre-wrap' }}>
{JSON.stringify(errorDetails, null, 2)}
</pre>
</Alert>
)}
@@ -133,12 +114,12 @@ function TestSubstep(props) {
</Alert>
)}
{response && (
{hasOutput && (
<Box
sx={{ maxHeight: 400, overflowY: 'auto', width: '100%' }}
data-test="flow-test-substep-output"
>
<JSONViewer data={response} />
<JSONViewer data={dataOut} />
</Box>
)}

View File

@@ -16,7 +16,6 @@ const computeUrl = (url, backendUrl) => {
const config = {
baseUrl: process.env.REACT_APP_BASE_URL,
graphqlUrl: computeUrl('/graphql', backendUrl),
restApiUrl: computeUrl('/api', backendUrl),
supportEmailAddress: 'support@automatisch.io',
};

View File

@@ -1,42 +0,0 @@
import { InMemoryCache } from '@apollo/client';
const cache = new InMemoryCache({
typePolicies: {
App: {
keyFields: ['key'],
},
Mutation: {
mutationType: true,
fields: {
verifyConnection: {
merge(existing, verifiedConnection, { readField, cache }) {
const appKey = readField('key', verifiedConnection);
const appCacheId = cache.identify({
__typename: 'App',
key: appKey,
});
cache.modify({
id: appCacheId,
fields: {
connections: (existingConnections) => {
const existingConnectionIndex = existingConnections.findIndex(
(connection) => {
return connection.__ref === verifiedConnection.__ref;
}
);
const connectionExists = existingConnectionIndex !== -1;
// newly created and verified connection
if (!connectionExists) {
return [verifiedConnection, ...existingConnections];
}
return existingConnections;
},
},
});
return verifiedConnection;
},
},
},
},
},
});
export default cache;

View File

@@ -1,31 +0,0 @@
import { ApolloClient } from '@apollo/client';
import cache from './cache';
import createLink from './link';
import appConfig from 'config/app';
const client = new ApolloClient({
cache,
link: createLink({ uri: appConfig.graphqlUrl }),
defaultOptions: {
watchQuery: {
fetchPolicy: 'cache-and-network',
},
},
});
export function mutateAndGetClient(options) {
const { onError, token } = options;
const link = createLink({
uri: appConfig.graphqlUrl,
token,
onError,
});
client.setLink(link);
return client;
}
export default client;

View File

@@ -1,46 +0,0 @@
import { HttpLink, from } from '@apollo/client';
import { onError } from '@apollo/client/link/error';
import { setItem } from 'helpers/storage';
import * as URLS from 'config/urls';
const createHttpLink = (options) => {
const { uri, token } = options;
const headers = {
authorization: token,
};
return new HttpLink({ uri, headers });
};
const NOT_AUTHORISED = 'Not Authorised!';
const createErrorLink = (callback) =>
onError(({ graphQLErrors, networkError, operation }) => {
const context = operation.getContext();
const autoSnackbar = context.autoSnackbar ?? true;
if (graphQLErrors)
graphQLErrors.forEach(({ message, locations, path }) => {
if (autoSnackbar) {
callback?.(message);
}
console.error(
`[GraphQL error]: Message: ${message}, Location: ${locations}, Path: ${path}`,
);
if (message === NOT_AUTHORISED) {
setItem('token', '');
if (window.location.pathname !== URLS.LOGIN) {
window.location.href = URLS.LOGIN;
}
}
});
if (networkError) {
if (autoSnackbar) {
callback?.(networkError.toString());
}
console.error(`[Network error]: ${networkError}`);
}
});
const noop = () => {};
const createLink = (options) => {
const { uri, onError = noop, token } = options;
const httpOptions = { uri, token };
return from([createErrorLink(onError), createHttpLink(httpOptions)]);
};
export default createLink;

View File

@@ -1,13 +0,0 @@
import { gql } from '@apollo/client';
export const CREATE_CONNECTION = gql`
mutation CreateConnection($input: CreateConnectionInput) {
createConnection(input: $input) {
id
key
verified
formattedData {
screenName
}
}
}
`;

View File

@@ -1,12 +0,0 @@
import { gql } from '@apollo/client';
export const EXECUTE_FLOW = gql`
mutation ExecuteFlow($input: ExecuteFlowInput) {
executeFlow(input: $input) {
step {
id
status
}
data
}
}
`;

View File

@@ -1,8 +0,0 @@
import { gql } from '@apollo/client';
export const GENERATE_AUTH_URL = gql`
mutation generateAuthUrl($input: GenerateAuthUrlInput) {
generateAuthUrl(input: $input) {
url
}
}
`;

View File

@@ -1,13 +0,0 @@
import { CREATE_CONNECTION } from './create-connection';
import { UPDATE_CONNECTION } from './update-connection';
import { VERIFY_CONNECTION } from './verify-connection';
import { RESET_CONNECTION } from './reset-connection';
import { GENERATE_AUTH_URL } from './generate-auth-url';
const mutations = {
createConnection: CREATE_CONNECTION,
updateConnection: UPDATE_CONNECTION,
verifyConnection: VERIFY_CONNECTION,
resetConnection: RESET_CONNECTION,
generateAuthUrl: GENERATE_AUTH_URL,
};
export default mutations;

View File

@@ -1,8 +0,0 @@
import { gql } from '@apollo/client';
export const RESET_CONNECTION = gql`
mutation ResetConnection($input: ResetConnectionInput) {
resetConnection(input: $input) {
id
}
}
`;

View File

@@ -1,13 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_CONNECTION = gql`
mutation UpdateConnection($input: UpdateConnectionInput) {
updateConnection(input: $input) {
id
key
verified
formattedData {
screenName
}
}
}
`;

View File

@@ -1,10 +0,0 @@
import { gql } from '@apollo/client';
export const UPDATE_CURRENT_USER = gql`
mutation UpdateCurrentUser($input: UpdateCurrentUserInput) {
updateCurrentUser(input: $input) {
id
fullName
email
}
}
`;

View File

@@ -1,17 +0,0 @@
import { gql } from '@apollo/client';
export const VERIFY_CONNECTION = gql`
mutation VerifyConnection($input: VerifyConnectionInput) {
verifyConnection(input: $input) {
id
key
verified
formattedData {
screenName
}
createdAt
app {
key
}
}
}
`;

View File

@@ -1,32 +0,0 @@
const makeEmptyData = () => {
return {
edges: [],
pageInfo: {
currentPage: 1,
totalPages: 1,
},
};
};
function offsetLimitPagination(keyArgs = false) {
return {
keyArgs,
merge(existing, incoming, { args }) {
if (!existing) {
existing = makeEmptyData();
}
if (!incoming || incoming === null) return existing;
const existingEdges = existing?.edges || [];
const incomingEdges = incoming.edges || [];
if (args) {
const newEdges = [...existingEdges, ...incomingEdges];
return {
pageInfo: incoming.pageInfo,
edges: newEdges,
};
} else {
return existing;
}
},
};
}
export default offsetLimitPagination;

View File

@@ -1,69 +0,0 @@
import { gql } from '@apollo/client';
export const GET_DYNAMIC_FIELDS = gql`
query GetDynamicFields(
$stepId: String!
$key: String!
$parameters: JSONObject
) {
getDynamicFields(stepId: $stepId, key: $key, parameters: $parameters) {
label
key
type
required
description
variables
dependsOn
value
options {
label
value
}
source {
type
name
arguments {
name
value
}
}
additionalFields {
type
name
arguments {
name
value
}
}
fields {
label
key
type
required
description
variables
value
dependsOn
options {
label
value
}
source {
type
name
arguments {
name
value
}
}
additionalFields {
type
name
arguments {
name
value
}
}
}
}
}
`;

View File

@@ -1,20 +1,3 @@
import apolloClient from 'graphql/client';
import MUTATIONS from 'graphql/mutations';
export const processMutation = async (mutationName, variables) => {
const mutation = MUTATIONS[mutationName];
const mutationResponse = await apolloClient.mutate({
mutation,
variables: { input: variables },
context: {
autoSnackbar: false,
},
});
const responseData = mutationResponse.data[mutationName];
return responseData;
};
const parseUrlSearchParams = (event) => {
const searchParams = new URLSearchParams(event.data.payload.search);
const hashParams = new URLSearchParams(event.data.payload.hash.substring(1));

View File

@@ -1,27 +1,35 @@
import template from 'lodash/template';
const interpolate = /{([\s\S]+?)}/g;
const computeAuthStepVariables = (variableSchema, aggregatedData) => {
const variables = {};
for (const variable of variableSchema) {
if (variable.properties) {
variables[variable.name] = computeAuthStepVariables(
variable.properties,
aggregatedData
aggregatedData,
);
continue;
}
if (variable.value) {
if (variable.value.endsWith('.all}')) {
const key = variable.value.replace('{', '').replace('.all}', '');
variables[variable.name] = aggregatedData[key];
continue;
}
const computedVariable = template(variable.value, { interpolate })(
aggregatedData
aggregatedData,
);
variables[variable.name] = computedVariable;
}
}
return variables;
};
export default computeAuthStepVariables;

View File

@@ -1,13 +1,18 @@
import * as React from 'react';
import { useQueryClient } from '@tanstack/react-query';
import {
processMutation,
processOpenWithPopup,
processPopupMessage,
} from 'helpers/authenticationSteps';
import computeAuthStepVariables from 'helpers/computeAuthStepVariables';
import useAppAuth from './useAppAuth';
import useFormatMessage from './useFormatMessage';
import useAppAuth from './useAppAuth';
import useCreateConnection from './useCreateConnection';
import useCreateConnectionAuthUrl from './useCreateConnectionAuthUrl';
import useUpdateConnection from './useUpdateConnection';
import useResetConnection from './useResetConnection';
import useVerifyConnection from './useVerifyConnection';
function getSteps(auth, hasConnection, useShared) {
if (hasConnection) {
@@ -27,10 +32,16 @@ function getSteps(auth, hasConnection, useShared) {
export default function useAuthenticateApp(payload) {
const { appKey, appAuthClientId, connectionId, useShared = false } = payload;
const { data: auth } = useAppAuth(appKey);
const queryClient = useQueryClient();
const { mutateAsync: createConnection } = useCreateConnection(appKey);
const { mutateAsync: createConnectionAuthUrl } = useCreateConnectionAuthUrl();
const { mutateAsync: updateConnection } = useUpdateConnection();
const { mutateAsync: resetConnection } = useResetConnection();
const [authenticationInProgress, setAuthenticationInProgress] =
React.useState(false);
const formatMessage = useFormatMessage();
const steps = getSteps(auth?.data, !!connectionId, useShared);
const { mutateAsync: verifyConnection } = useVerifyConnection();
const authenticate = React.useMemo(() => {
if (!steps?.length) return;
@@ -42,9 +53,7 @@ export default function useAuthenticateApp(payload) {
const response = {
key: appKey,
appAuthClientId: appAuthClientId || payload.appAuthClientId,
connection: {
id: connectionId,
},
connectionId,
fields,
};
let stepIndex = 0;
@@ -65,8 +74,32 @@ export default function useAuthenticateApp(payload) {
}
if (step.type === 'mutation') {
const stepResponse = await processMutation(step.name, variables);
response[step.name] = stepResponse;
if (step.name === 'createConnection') {
const stepResponse = await createConnection(variables);
response[step.name] = stepResponse.data;
response.connectionId = stepResponse.data.id;
} else if (step.name === 'generateAuthUrl') {
const stepResponse = await createConnectionAuthUrl(
response.connectionId,
);
response[step.name] = stepResponse.data;
} else if (step.name === 'updateConnection') {
const stepResponse = await updateConnection({
...variables,
connectionId: response.connectionId,
});
response[step.name] = stepResponse.data;
} else if (step.name === 'resetConnection') {
const stepResponse = await resetConnection(response.connectionId);
response[step.name] = stepResponse.data;
} else if (step.name === 'verifyConnection') {
const stepResponse = await verifyConnection(
response.connectionId,
);
response[step.name] = stepResponse?.data;
}
} else if (step.type === 'openWithPopup') {
const stepResponse = await processPopupMessage(popup);
response[step.name] = stepResponse;
@@ -74,17 +107,38 @@ export default function useAuthenticateApp(payload) {
} catch (err) {
console.log(err);
setAuthenticationInProgress(false);
queryClient.invalidateQueries({
queryKey: ['apps', appKey, 'connections'],
});
throw err;
}
stepIndex++;
if (stepIndex === steps.length) {
return response;
}
setAuthenticationInProgress(false);
stepIndex++;
}
await queryClient.invalidateQueries({
queryKey: ['apps', appKey, 'connections'],
});
setAuthenticationInProgress(false);
return response;
};
}, [steps, appKey, appAuthClientId, connectionId, formatMessage]);
}, [
steps,
appKey,
appAuthClientId,
connectionId,
queryClient,
formatMessage,
createConnection,
createConnectionAuthUrl,
updateConnection,
resetConnection,
verifyConnection,
]);
return {
authenticate,

View File

@@ -0,0 +1,18 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useCreateConnection(appKey) {
const query = useMutation({
mutationFn: async ({ appAuthClientId, formattedData }) => {
const { data } = await api.post(`/v1/apps/${appKey}/connections`, {
appAuthClientId,
formattedData,
});
return data;
},
});
return query;
}

View File

@@ -0,0 +1,17 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useCreateConnectionAuthUrl() {
const query = useMutation({
mutationFn: async (connectionId) => {
const { data } = await api.post(
`/v1/connections/${connectionId}/auth-url`,
);
return data;
},
});
return query;
}

View File

@@ -0,0 +1,15 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useResetConnection() {
const query = useMutation({
mutationFn: async (connectionId) => {
const { data } = await api.post(`/v1/connections/${connectionId}/reset`);
return data;
},
});
return query;
}

View File

@@ -0,0 +1,14 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useTestStep(stepId) {
const query = useMutation({
mutationFn: async () => {
const { data } = await api.post(`/v1/steps/${stepId}/test`);
return data;
},
});
return query;
}

View File

@@ -0,0 +1,18 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useUpdateConnection() {
const query = useMutation({
mutationFn: async ({ connectionId, formattedData, appAuthClientId }) => {
const { data } = await api.patch(`/v1/connections/${connectionId}`, {
formattedData,
appAuthClientId,
});
return data;
},
});
return query;
}

View File

@@ -0,0 +1,19 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useUpdateCurrentUser(userId) {
const queryClient = useQueryClient();
const query = useMutation({
mutationFn: async (payload) => {
const { data } = await api.patch(`/v1/users/${userId}`, payload);
return data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['users', 'me'] });
},
});
return query;
}

View File

@@ -0,0 +1,14 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useUpdateCurrentUserPassword(userId) {
const query = useMutation({
mutationFn: async (payload) => {
const { data } = await api.patch(`/v1/users/${userId}/password`, payload);
return data;
},
});
return query;
}

View File

@@ -0,0 +1,24 @@
import { useMutation } from '@tanstack/react-query';
import api from 'helpers/api';
export default function useVerifyConnection() {
const query = useMutation({
mutationFn: async (connectionId) => {
try {
const { data } = await api.post(
`/v1/connections/${connectionId}/verify`,
);
return data;
} catch (err) {
if (err.response.status === 500) {
throw new Error('Failed while verifying connection!');
}
throw err;
}
},
});
return query;
}

View File

@@ -3,7 +3,6 @@ import { Settings } from 'luxon';
import ThemeProvider from 'components/ThemeProvider';
import IntlProvider from 'components/IntlProvider';
import ApolloProvider from 'components/ApolloProvider';
import SnackbarProvider from 'components/SnackbarProvider';
import MetadataProvider from 'components/MetadataProvider';
import { AuthenticationProvider } from 'contexts/Authentication';
@@ -23,13 +22,11 @@ root.render(
<SnackbarProvider>
<AuthenticationProvider>
<QueryClientProvider>
<ApolloProvider>
<IntlProvider>
<ThemeProvider>
<MetadataProvider>{routes}</MetadataProvider>
</ThemeProvider>
</IntlProvider>
</ApolloProvider>
<IntlProvider>
<ThemeProvider>
<MetadataProvider>{routes}</MetadataProvider>
</ThemeProvider>
</IntlProvider>
</QueryClientProvider>
</AuthenticationProvider>
</SnackbarProvider>

View File

@@ -102,7 +102,12 @@
"profileSettings.fullName": "Full name",
"profileSettings.email": "Email",
"profileSettings.updateProfile": "Update your profile",
"profileSettings.updatePassword": "Update your password",
"profileSettings.updatedProfile": "Your profile has been updated.",
"profileSettings.updatedPassword": "Your password has been updated.",
"profileSettings.updateProfileError": "Something went wrong while updating your profile.",
"profileSettings.updatePasswordError": "Something went wrong while updating your password.",
"profileSettings.currentPassword": "Current password",
"profileSettings.newPassword": "New password",
"profileSettings.confirmNewPassword": "Confirm new password",
"profileSettings.deleteMyAccount": "Delete my account",

View File

@@ -1,4 +1,3 @@
import { useMutation } from '@apollo/client';
import { yupResolver } from '@hookform/resolvers/yup';
import Alert from '@mui/material/Alert';
import AlertTitle from '@mui/material/AlertTitle';
@@ -15,19 +14,26 @@ import DeleteAccountDialog from 'components/DeleteAccountDialog/index.ee';
import Form from 'components/Form';
import PageTitle from 'components/PageTitle';
import TextField from 'components/TextField';
import { UPDATE_CURRENT_USER } from 'graphql/mutations/update-current-user';
import useCurrentUser from 'hooks/useCurrentUser';
import useFormatMessage from 'hooks/useFormatMessage';
import { useQueryClient } from '@tanstack/react-query';
import useUpdateCurrentUser from 'hooks/useUpdateCurrentUser';
import useUpdateCurrentUserPassword from 'hooks/useUpdateCurrentUserPassword';
const validationSchema = yup
const validationSchemaProfile = yup
.object({
fullName: yup.string().required(),
email: yup.string().email().required(),
password: yup.string(),
fullName: yup.string().required('Full name is required'),
email: yup.string().email().required('Email is required'),
})
.required();
const validationSchemaPassword = yup
.object({
currentPassword: yup.string().required('Current password is required'),
password: yup.string().required('New password is required'),
confirmPassword: yup
.string()
.oneOf([yup.ref('password')], 'Passwords must match'),
.oneOf([yup.ref('password')], 'Passwords must match')
.required('Confirm password is required'),
})
.required();
@@ -37,6 +43,24 @@ const StyledForm = styled(Form)`
flex-direction: column;
`;
const getErrorMessage = (error) => {
const errors = error?.response?.data?.errors || {};
const errorMessages = Object.entries(errors)
.map(([key, messages]) => {
if (Array.isArray(messages) && messages.length) {
return `${key} ${messages.join(', ')}`;
}
if (typeof messages === 'string') {
return `${key} ${messages}`;
}
return '';
})
.filter((message) => !!message)
.join(' ');
return errorMessages;
};
function ProfileSettings() {
const [showDeleteAccountConfirmation, setShowDeleteAccountConfirmation] =
React.useState(false);
@@ -44,42 +68,65 @@ function ProfileSettings() {
const { data } = useCurrentUser();
const currentUser = data?.data;
const formatMessage = useFormatMessage();
const [updateCurrentUser] = useMutation(UPDATE_CURRENT_USER);
const queryClient = useQueryClient();
const { mutateAsync: updateCurrentUser } = useUpdateCurrentUser(
currentUser?.id,
);
const { mutateAsync: updateCurrentUserPassword } =
useUpdateCurrentUserPassword(currentUser?.id);
const handleProfileSettingsUpdate = async (data) => {
const { fullName, password, email } = data;
const mutationInput = {
fullName,
email,
};
try {
const { fullName, email } = data;
if (password) {
mutationInput.password = password;
}
await updateCurrentUser({ fullName, email });
await updateCurrentUser({
variables: {
input: mutationInput,
},
optimisticResponse: {
updateCurrentUser: {
__typename: 'User',
id: currentUser.id,
fullName,
email,
enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), {
variant: 'success',
SnackbarProps: {
'data-test': 'snackbar-update-profile-settings-success',
},
},
});
});
} catch (error) {
enqueueSnackbar(
getErrorMessage(error) ||
formatMessage('profileSettings.updateProfileError'),
{
variant: 'error',
SnackbarProps: {
'data-test': 'snackbar-update-profile-settings-error',
},
},
);
}
};
await queryClient.invalidateQueries({ queryKey: ['users', 'me'] });
const handlePasswordUpdate = async (data) => {
try {
const { password, currentPassword } = data;
enqueueSnackbar(formatMessage('profileSettings.updatedProfile'), {
variant: 'success',
SnackbarProps: {
'data-test': 'snackbar-update-profile-settings-success',
},
});
await updateCurrentUserPassword({
currentPassword,
password,
});
enqueueSnackbar(formatMessage('profileSettings.updatedPassword'), {
variant: 'success',
SnackbarProps: {
'data-test': 'snackbar-update-password-success',
},
});
} catch (error) {
enqueueSnackbar(
getErrorMessage(error) ||
formatMessage('profileSettings.updatePasswordError'),
{
variant: 'error',
SnackbarProps: {
'data-test': 'snackbar-update-password-error',
},
},
);
}
};
return (
@@ -93,11 +140,9 @@ function ProfileSettings() {
<StyledForm
defaultValues={{
...currentUser,
password: '',
confirmPassword: '',
}}
onSubmit={handleProfileSettingsUpdate}
resolver={yupResolver(validationSchema)}
resolver={yupResolver(validationSchemaProfile)}
mode="onChange"
sx={{ mb: 2 }}
render={({
@@ -117,8 +162,8 @@ function ProfileSettings() {
margin="dense"
error={touchedFields.fullName && !!errors?.fullName}
helperText={errors?.fullName?.message || ' '}
required
/>
<TextField
fullWidth
name="email"
@@ -126,6 +171,57 @@ function ProfileSettings() {
margin="dense"
error={touchedFields.email && !!errors?.email}
helperText={errors?.email?.message || ' '}
required
/>
<Button
variant="contained"
type="submit"
disabled={!isDirty || !isValid || isSubmitting}
data-test="update-profile-button"
>
{formatMessage('profileSettings.updateProfile')}
</Button>
</>
)}
/>
</Grid>
<Grid item xs={12} justifyContent="flex-end" sx={{ pt: 3 }}>
<StyledForm
defaultValues={{
currentPassword: '',
password: '',
confirmPassword: '',
}}
onSubmit={handlePasswordUpdate}
resolver={yupResolver(validationSchemaPassword)}
mode="onChange"
sx={{ mb: 2 }}
render={({
formState: {
errors,
touchedFields,
isDirty,
isValid,
isSubmitting,
},
}) => (
<>
<TextField
fullWidth
name="currentPassword"
label={formatMessage('profileSettings.currentPassword')}
margin="dense"
type="password"
error={
touchedFields.currentPassword && !!errors?.currentPassword
}
helperText={
(touchedFields.currentPassword &&
errors?.currentPassword?.message) ||
' '
}
required
/>
<TextField
@@ -138,8 +234,8 @@ function ProfileSettings() {
helperText={
(touchedFields.password && errors?.password?.message) || ' '
}
required
/>
<TextField
fullWidth
name="confirmPassword"
@@ -154,14 +250,15 @@ function ProfileSettings() {
errors?.confirmPassword?.message) ||
' '
}
required
/>
<Button
variant="contained"
type="submit"
disabled={!isDirty || !isValid || isSubmitting}
data-test="update-password-button"
>
{formatMessage('profileSettings.updateProfile')}
{formatMessage('profileSettings.updatePassword')}
</Button>
</>
)}

575
yarn.lock
View File

@@ -134,42 +134,6 @@
jsonpointer "^5.0.0"
leven "^3.1.0"
"@apollo/client@^3.6.9":
version "3.6.9"
resolved "https://registry.npmjs.org/@apollo/client/-/client-3.6.9.tgz"
integrity sha512-Y1yu8qa2YeaCUBVuw08x8NHenFi0sw2I3KCu7Kw9mDSu86HmmtHJkCAifKVrN2iPgDTW/BbP3EpSV8/EQCcxZA==
dependencies:
"@graphql-typed-document-node/core" "^3.1.1"
"@wry/context" "^0.6.0"
"@wry/equality" "^0.5.0"
"@wry/trie" "^0.3.0"
graphql-tag "^2.12.6"
hoist-non-react-statics "^3.3.2"
optimism "^0.16.1"
prop-types "^15.7.2"
symbol-observable "^4.0.0"
ts-invariant "^0.10.3"
tslib "^2.3.0"
zen-observable-ts "^1.2.5"
"@apollo/client@~3.2.5 || ~3.3.0 || ~3.4.0":
version "3.4.17"
resolved "https://registry.npmjs.org/@apollo/client/-/client-3.4.17.tgz"
integrity sha512-MDt2rwMX1GqodiVEKJqmDmAz8xr0qJmq5PdWeIt0yDaT4GOkKYWZiWkyfhfv3raTk8PyJvbsNG9q2CqmUrlGfg==
dependencies:
"@graphql-typed-document-node/core" "^3.0.0"
"@wry/context" "^0.6.0"
"@wry/equality" "^0.5.0"
"@wry/trie" "^0.3.0"
graphql-tag "^2.12.3"
hoist-non-react-statics "^3.3.2"
optimism "^0.16.1"
prop-types "^15.7.2"
symbol-observable "^4.0.0"
ts-invariant "^0.9.0"
tslib "^2.3.0"
zen-observable-ts "~1.1.0"
"@babel/code-frame@^7.0.0", "@babel/code-frame@^7.10.4", "@babel/code-frame@^7.12.13", "@babel/code-frame@^7.16.0", "@babel/code-frame@^7.16.7", "@babel/code-frame@^7.8.3":
version "7.16.7"
resolved "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.16.7.tgz"
@@ -1320,13 +1284,6 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.10.5":
version "7.17.2"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.2.tgz"
integrity sha512-hzeyJyMA1YGdJTuWU0e/j4wKXrU4OMFvY2MSlaI9B7VQb0r5cxTE3EAIS2Q7Tn2RIcDkRvTA/v2JsAEhxe99uw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.17.8":
version "7.17.9"
resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.17.9.tgz"
@@ -1871,89 +1828,6 @@
resolved "https://registry.npmjs.org/@gar/promisify/-/promisify-1.1.2.tgz"
integrity sha512-82cpyJyKRoQoRi+14ibCeGPu0CwypgtBAdBhq1WfvagpCZNKqwXbKwXllYSMG91DhmG4jt9gN8eP6lGOtozuaw==
"@graphql-tools/batch-execute@^8.3.2":
version "8.3.2"
resolved "https://registry.npmjs.org/@graphql-tools/batch-execute/-/batch-execute-8.3.2.tgz"
integrity sha512-ICWqM+MvEkIPHm18Q0cmkvm134zeQMomBKmTRxyxMNhL/ouz6Nqld52/brSlaHnzA3fczupeRJzZ0YatruGBcQ==
dependencies:
"@graphql-tools/utils" "^8.6.2"
dataloader "2.0.0"
tslib "~2.3.0"
value-or-promise "1.0.11"
"@graphql-tools/delegate@^8.5.1":
version "8.5.1"
resolved "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-8.5.1.tgz"
integrity sha512-/YPmVxitt57F8sH50pnfXASzOOjEfaUDkX48eF5q6f16+JBncej2zeu+Zm2c68q8MbIxhPlEGfpd0QZeqTvAxw==
dependencies:
"@graphql-tools/batch-execute" "^8.3.2"
"@graphql-tools/schema" "^8.3.2"
"@graphql-tools/utils" "^8.6.2"
dataloader "2.0.0"
graphql-executor "0.0.18"
tslib "~2.3.0"
value-or-promise "1.0.11"
"@graphql-tools/graphql-file-loader@^7.3.4":
version "7.3.4"
resolved "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-7.3.4.tgz"
integrity sha512-Q0/YtDq0APR6syRclsQMNguWKRlchd8nFTOpLhfc7Xeiy21VhEEi4Ik+quRySfb7ubDfJGhiUq4MQW43FhWJvg==
dependencies:
"@graphql-tools/import" "^6.6.6"
"@graphql-tools/utils" "^8.6.2"
globby "^11.0.3"
tslib "~2.3.0"
unixify "^1.0.0"
"@graphql-tools/import@^6.6.6":
version "6.6.6"
resolved "https://registry.npmjs.org/@graphql-tools/import/-/import-6.6.6.tgz"
integrity sha512-a0aVajxqu1MsL8EwavA44Osw20lBOIhq8IM2ZIHFPP62cPAcOB26P+Sq57DHMsSyX5YQ0ab9XPM2o4e1dQhs0w==
dependencies:
"@graphql-tools/utils" "8.6.2"
resolve-from "5.0.0"
tslib "~2.3.0"
"@graphql-tools/load@^7.5.2":
version "7.5.2"
resolved "https://registry.npmjs.org/@graphql-tools/load/-/load-7.5.2.tgz"
integrity sha512-URPqVP77mYxdZxT895DzrWf2C23S3yC/oAmXD4D4YlxR5eVVH/fxu0aZR78WcEKF331fWSiFwWy9j7BZWvkj7g==
dependencies:
"@graphql-tools/schema" "8.3.2"
"@graphql-tools/utils" "^8.6.2"
p-limit "3.1.0"
tslib "~2.3.0"
"@graphql-tools/merge@^8.2.3":
version "8.2.3"
resolved "https://registry.npmjs.org/@graphql-tools/merge/-/merge-8.2.3.tgz"
integrity sha512-XCSmL6/Xg8259OTWNp69B57CPWiVL69kB7pposFrufG/zaAlI9BS68dgzrxmmSqZV5ZHU4r/6Tbf6fwnEJGiSw==
dependencies:
"@graphql-tools/utils" "^8.6.2"
tslib "~2.3.0"
"@graphql-tools/schema@8.3.2", "@graphql-tools/schema@^8.2.0", "@graphql-tools/schema@^8.3.2":
version "8.3.2"
resolved "https://registry.npmjs.org/@graphql-tools/schema/-/schema-8.3.2.tgz"
integrity sha512-77feSmIuHdoxMXRbRyxE8rEziKesd/AcqKV6fmxe7Zt+PgIQITxNDew2XJJg7qFTMNM43W77Ia6njUSBxNOkwg==
dependencies:
"@graphql-tools/merge" "^8.2.3"
"@graphql-tools/utils" "^8.6.2"
tslib "~2.3.0"
value-or-promise "1.0.11"
"@graphql-tools/utils@8.6.2", "@graphql-tools/utils@^8.6.2":
version "8.6.2"
resolved "https://registry.npmjs.org/@graphql-tools/utils/-/utils-8.6.2.tgz"
integrity sha512-x1DG0cJgpJtImUlNE780B/dfp8pxvVxOD6UeykFH5rHes26S4kGokbgU8F1IgrJ1vAPm/OVBHtd2kicTsPfwdA==
dependencies:
tslib "~2.3.0"
"@graphql-typed-document-node/core@^3.0.0", "@graphql-typed-document-node/core@^3.1.1":
version "3.1.1"
resolved "https://registry.npmjs.org/@graphql-typed-document-node/core/-/core-3.1.1.tgz"
integrity sha512-NQ17ii0rK1b34VZonlmT2QMJFI70m0TRwbknO/ihlbatXyaktDhN/98vBiUU6kNBPljqGqyIrl2T4nY2RpFANg==
"@hookform/resolvers@^2.8.8":
version "2.8.8"
resolved "https://registry.npmjs.org/@hookform/resolvers/-/resolvers-2.8.8.tgz"
@@ -4538,16 +4412,6 @@
dependencies:
"@types/yargs-parser" "*"
"@types/yup@0.29.11":
version "0.29.11"
resolved "https://registry.npmjs.org/@types/yup/-/yup-0.29.11.tgz"
integrity sha512-9cwk3c87qQKZrT251EDoibiYRILjCmxBvvcb4meofCmx1vdnNcR9gyildy5vOHASpOKMsn42CugxUvcwK5eu1g==
"@types/zen-observable@0.8.3":
version "0.8.3"
resolved "https://registry.npmjs.org/@types/zen-observable/-/zen-observable-0.8.3.tgz"
integrity sha512-fbF6oTd4sGGy0xjHPKAt+eS2CrxJ3+6gQ3FGcBoIJR2TLAyCkCyI8JqZNy+FeON0AhVgNJoUumVoZQjBFUqHkw==
"@typescript-eslint/eslint-plugin@^5.5.0":
version "5.10.0"
resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.10.0.tgz"
@@ -5088,27 +4952,6 @@
"@webassemblyjs/ast" "1.11.1"
"@xtuc/long" "4.2.2"
"@wry/context@^0.6.0":
version "0.6.1"
resolved "https://registry.npmjs.org/@wry/context/-/context-0.6.1.tgz"
integrity sha512-LOmVnY1iTU2D8tv4Xf6MVMZZ+juIJ87Kt/plMijjN20NMAXGmH4u8bS1t0uT74cZ5gwpocYueV58YwyI8y+GKw==
dependencies:
tslib "^2.3.0"
"@wry/equality@^0.5.0":
version "0.5.2"
resolved "https://registry.npmjs.org/@wry/equality/-/equality-0.5.2.tgz"
integrity sha512-oVMxbUXL48EV/C0/M7gLVsoK6qRHPS85x8zECofEZOVvxGmIPLA9o5Z27cc2PoAyZz1S2VoM2A7FLAnpfGlneA==
dependencies:
tslib "^2.3.0"
"@wry/trie@^0.3.0":
version "0.3.1"
resolved "https://registry.npmjs.org/@wry/trie/-/trie-0.3.1.tgz"
integrity sha512-WwB53ikYudh9pIorgxrkHKrQZcCqNM/Q/bDzZBffEaGUKGuHrRb3zZUT9Sh2qw9yogC7SsdRmQ1ER0pqvd3bfw==
dependencies:
tslib "^2.3.0"
"@xmldom/xmldom@0.8.7":
version "0.8.7"
resolved "https://registry.npmjs.org/@xmldom/xmldom/-/xmldom-0.8.7.tgz"
@@ -5152,7 +4995,7 @@ abbrev@^2.0.0:
resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-2.0.0.tgz#cf59829b8b4f03f89dda2771cb7f3653828c89bf"
integrity sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==
accepts@^1.3.7, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8:
version "1.3.8"
resolved "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz"
integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==
@@ -5917,21 +5760,21 @@ body-parser@1.19.2:
raw-body "2.4.3"
type-is "~1.6.18"
body-parser@1.20.1:
version "1.20.1"
resolved "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz"
integrity sha512-jWi7abTbYwajOytWCQc37VulmWiRae5RyTpaCyDcS5/lMdtwSz5lOpDE67srw/HYe35f1z3fDQw+3txg7gNtWw==
body-parser@1.20.3:
version "1.20.3"
resolved "https://registry.yarnpkg.com/body-parser/-/body-parser-1.20.3.tgz#1953431221c6fb5cd63c4b36d53fab0928e548c6"
integrity sha512-7rAxByjUMqQ3/bHJy7D6OGXvx/MMc4IqBn/X0fcM1QUcAItpZrBEYhWGem+tzXH90c+G01ypMcYJBO9Y30203g==
dependencies:
bytes "3.1.2"
content-type "~1.0.4"
content-type "~1.0.5"
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
http-errors "2.0.0"
iconv-lite "0.4.24"
on-finished "2.4.1"
qs "6.11.0"
raw-body "2.5.1"
qs "6.13.0"
raw-body "2.5.2"
type-is "~1.6.18"
unpipe "1.0.0"
@@ -6110,11 +5953,6 @@ bytes@3.1.0:
resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.1.0.tgz#f6cf7933a360e0588fa9fde85651cdc7f805d1f6"
integrity sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==
bytes@3.1.1:
version "3.1.1"
resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.1.tgz"
integrity sha512-dWe4nWO/ruEOY7HkUJ5gFt1DCFV9zPRoJr8pV0/ASQermOZjtq8jMjOprC0Kd10GLN+l7xaUPvxzJFWtxGu8Fg==
bytes@3.1.2:
version "3.1.2"
resolved "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz"
@@ -6188,6 +6026,17 @@ call-bind@^1.0.0, call-bind@^1.0.2:
function-bind "^1.1.1"
get-intrinsic "^1.0.2"
call-bind@^1.0.7:
version "1.0.7"
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9"
integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==
dependencies:
es-define-property "^1.0.0"
es-errors "^1.3.0"
function-bind "^1.1.2"
get-intrinsic "^1.2.4"
set-function-length "^1.2.1"
callsites@^3.0.0:
version "3.1.0"
resolved "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz"
@@ -6718,11 +6567,16 @@ content-disposition@0.5.4:
dependencies:
safe-buffer "5.2.1"
content-type@1.0.4, content-type@^1.0.4, content-type@~1.0.4:
content-type@1.0.4:
version "1.0.4"
resolved "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz"
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
content-type@~1.0.4, content-type@~1.0.5:
version "1.0.5"
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
conventional-changelog-angular@^5.0.12:
version "5.0.13"
resolved "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-5.0.13.tgz"
@@ -6822,10 +6676,10 @@ cookie@0.4.2, cookie@^0.4.1:
resolved "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz"
integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==
cookie@0.5.0:
version "0.5.0"
resolved "https://registry.npmjs.org/cookie/-/cookie-0.5.0.tgz"
integrity sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==
cookie@0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.6.0.tgz#2798b04b071b0ecbff0dbb62a505a8efa4e19051"
integrity sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==
cookiejar@^2.1.4:
version "2.1.4"
@@ -7248,11 +7102,6 @@ data-urls@^2.0.0:
whatwg-mimetype "^2.3.0"
whatwg-url "^8.0.0"
dataloader@2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/dataloader/-/dataloader-2.0.0.tgz"
integrity sha512-YzhyDAwA4TaQIhM5go+vCLmU0UikghC/t9DTQYZR2M/UvZ1MdOhPezSDZcjj9uqQJOMqjLcpWtyW2iNINdlatQ==
dateformat@^3.0.0:
version "3.0.3"
resolved "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz"
@@ -7384,6 +7233,15 @@ defer-to-connect@^1.0.1:
resolved "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz"
integrity sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==
define-data-property@^1.1.4:
version "1.1.4"
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e"
integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==
dependencies:
es-define-property "^1.0.0"
es-errors "^1.3.0"
gopd "^1.0.1"
define-lazy-prop@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz"
@@ -7806,6 +7664,11 @@ encodeurl@~1.0.2:
resolved "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz"
integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=
encodeurl@~2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-2.0.0.tgz#7b8ea898077d7e409d3ac45474ea38eaf0857a58"
integrity sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==
encoding@^0.1.12, encoding@^0.1.13:
version "0.1.13"
resolved "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz"
@@ -7893,6 +7756,18 @@ es-abstract@^1.17.2, es-abstract@^1.19.0, es-abstract@^1.19.1:
string.prototype.trimstart "^1.0.4"
unbox-primitive "^1.0.1"
es-define-property@^1.0.0:
version "1.0.0"
resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845"
integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==
dependencies:
get-intrinsic "^1.2.4"
es-errors@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f"
integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==
es-module-lexer@^0.9.0:
version "0.9.3"
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz"
@@ -8563,17 +8438,7 @@ express-basic-auth@^1.2.1:
dependencies:
basic-auth "^2.0.1"
express-graphql@^0.12.0:
version "0.12.0"
resolved "https://registry.npmjs.org/express-graphql/-/express-graphql-0.12.0.tgz"
integrity sha512-DwYaJQy0amdy3pgNtiTDuGGM2BLdj+YO2SgbKoLliCfuHv3VVTt7vNG/ZqK2hRYjtYHE2t2KB705EU94mE64zg==
dependencies:
accepts "^1.3.7"
content-type "^1.0.4"
http-errors "1.8.0"
raw-body "^2.4.1"
express@4.17.3, express@^4.17.1:
express@4.17.3:
version "4.17.3"
resolved "https://registry.npmjs.org/express/-/express-4.17.3.tgz"
integrity sha512-yuSQpz5I+Ch7gFrPCk4/c+dIBKlQUxtgwqzph132bsT6qhuzss6I8cLJQz7B3rFblzd6wtcI0ZbGltH/C4LjUg==
@@ -8609,37 +8474,37 @@ express@4.17.3, express@^4.17.1:
utils-merge "1.0.1"
vary "~1.1.2"
express@~4.18.2:
version "4.18.2"
resolved "https://registry.npmjs.org/express/-/express-4.18.2.tgz"
integrity sha512-5/PsL6iGPdfQ/lKM1UuielYgv3BUoJfz1aUwU9vHZ+J7gyvwdQXFEBIEIaxeGf0GIcreATNyBExtalisDbuMqQ==
express@^4.17.1, express@~4.20.0:
version "4.20.0"
resolved "https://registry.yarnpkg.com/express/-/express-4.20.0.tgz#f1d08e591fcec770c07be4767af8eb9bcfd67c48"
integrity sha512-pLdae7I6QqShF5PnNTCVn4hI91Dx0Grkn2+IAsMTgMIKuQVte2dN9PeGSSAME2FR8anOhVA62QDIUaWVfEXVLw==
dependencies:
accepts "~1.3.8"
array-flatten "1.1.1"
body-parser "1.20.1"
body-parser "1.20.3"
content-disposition "0.5.4"
content-type "~1.0.4"
cookie "0.5.0"
cookie "0.6.0"
cookie-signature "1.0.6"
debug "2.6.9"
depd "2.0.0"
encodeurl "~1.0.2"
encodeurl "~2.0.0"
escape-html "~1.0.3"
etag "~1.8.1"
finalhandler "1.2.0"
fresh "0.5.2"
http-errors "2.0.0"
merge-descriptors "1.0.1"
merge-descriptors "1.0.3"
methods "~1.1.2"
on-finished "2.4.1"
parseurl "~1.3.3"
path-to-regexp "0.1.7"
path-to-regexp "0.1.10"
proxy-addr "~2.0.7"
qs "6.11.0"
range-parser "~1.2.1"
safe-buffer "5.2.1"
send "0.18.0"
serve-static "1.15.0"
send "0.19.0"
serve-static "1.16.0"
setprototypeof "1.2.0"
statuses "2.0.1"
type-is "~1.6.18"
@@ -9037,6 +8902,11 @@ function-bind@^1.1.1:
resolved "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz"
integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
function-bind@^1.1.2:
version "1.1.2"
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
functional-red-black-tree@^1.0.1:
version "1.0.1"
resolved "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz"
@@ -9104,6 +8974,17 @@ get-intrinsic@^1.1.0, get-intrinsic@^1.1.1:
has "^1.0.3"
has-symbols "^1.0.1"
get-intrinsic@^1.1.3, get-intrinsic@^1.2.4:
version "1.2.4"
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd"
integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==
dependencies:
es-errors "^1.3.0"
function-bind "^1.1.2"
has-proto "^1.0.1"
has-symbols "^1.0.3"
hasown "^2.0.0"
get-own-enumerable-property-symbols@^3.0.0:
version "3.0.2"
resolved "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.2.tgz"
@@ -9315,7 +9196,7 @@ globals@^13.6.0, globals@^13.9.0:
dependencies:
type-fest "^0.20.2"
globby@^11.0.1, globby@^11.0.2, globby@^11.0.3, globby@^11.0.4, globby@^11.1.0:
globby@^11.0.1, globby@^11.0.2, globby@^11.0.4, globby@^11.1.0:
version "11.1.0"
resolved "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz"
integrity sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==
@@ -9332,6 +9213,13 @@ goober@^2.0.33:
resolved "https://registry.yarnpkg.com/goober/-/goober-2.1.13.tgz#e3c06d5578486212a76c9eba860cbc3232ff6d7c"
integrity sha512-jFj3BQeleOoy7t93E9rZ2de+ScC4lQICLwiAQmKMg9F6roKGaLSHoCDYKkWlSafg138jejvq/mTdvmnwDQgqoQ==
gopd@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.0.1.tgz#29ff76de69dac7489b7c0918a5788e56477c332c"
integrity sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==
dependencies:
get-intrinsic "^1.1.3"
got@^9.6.0:
version "9.6.0"
resolved "https://registry.npmjs.org/got/-/got-9.6.0.tgz"
@@ -9354,50 +9242,6 @@ graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6
resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.9.tgz"
integrity sha512-NtNxqUcXgpW2iMrfqSfR73Glt39K+BLwWsPs94yR63v45T0Wbej7eRmL5cWfwEgqXnmjQp3zaJTshdRW/qC2ZQ==
graphql-executor@0.0.18:
version "0.0.18"
resolved "https://registry.npmjs.org/graphql-executor/-/graphql-executor-0.0.18.tgz"
integrity sha512-upUSl7tfZCZ5dWG1XkOvpG70Yk3duZKcCoi/uJso4WxJVT6KIrcK4nZ4+2X/hzx46pL8wAukgYHY6iNmocRN+g==
graphql-middleware@^6.1.15:
version "6.1.15"
resolved "https://registry.npmjs.org/graphql-middleware/-/graphql-middleware-6.1.15.tgz"
integrity sha512-JiLuIM48EE3QLcr79K0VCCHqMt6c23esLlkZv2Nr9a/yHnv6eU9DKV9eXARl+wV9m4LkT9ZCg4cIamIa9vPidQ==
dependencies:
"@graphql-tools/delegate" "^8.5.1"
"@graphql-tools/schema" "^8.3.2"
graphql-shield@^7.5.0:
version "7.5.0"
resolved "https://registry.npmjs.org/graphql-shield/-/graphql-shield-7.5.0.tgz"
integrity sha512-T1A6OreOe/dHDk/1Qg3AHCrKLmTkDJ3fPFGYpSOmUbYXyDnjubK4J5ab5FjHdKHK5fWQRZNTvA0SrBObYsyfaw==
dependencies:
"@types/yup" "0.29.11"
object-hash "^2.0.3"
yup "^0.31.0"
graphql-tag@^2.12.3, graphql-tag@^2.12.6:
version "2.12.6"
resolved "https://registry.npmjs.org/graphql-tag/-/graphql-tag-2.12.6.tgz"
integrity sha512-FdSNcu2QQcWnM2VNvSCCDCVS5PpPqpzgFT8+GXzqJuoDd0CBncxCY278u4mhRO7tMgo2JjgJA5aZ+nWSQ/Z+xg==
dependencies:
tslib "^2.1.0"
graphql-tools@^8.2.0:
version "8.2.0"
resolved "https://registry.npmjs.org/graphql-tools/-/graphql-tools-8.2.0.tgz"
integrity sha512-9axT/0exEzVCk+vMPykOPannlrA4VQNo6nuWgh25IJ5arPf92OKxvjSHAbm7dQIFmcWxE0hVvyD2rWHjDqZCgQ==
dependencies:
"@graphql-tools/schema" "^8.2.0"
tslib "~2.3.0"
optionalDependencies:
"@apollo/client" "~3.2.5 || ~3.3.0 || ~3.4.0"
graphql@^15.6.0:
version "15.8.0"
resolved "https://registry.npmjs.org/graphql/-/graphql-15.8.0.tgz"
integrity sha512-5gghUc24tP9HRznNpV2+FIoq3xKkj5dTQqf4v0CpdPbFVwFkWoxOM+o+2OC9ZSvjEMTjfmG9QT+gcvggTwW1zw==
gzip-size@^6.0.0:
version "6.0.0"
resolved "https://registry.npmjs.org/gzip-size/-/gzip-size-6.0.0.tgz"
@@ -9460,6 +9304,18 @@ has-flag@^4.0.0:
resolved "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz"
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
has-property-descriptors@^1.0.2:
version "1.0.2"
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854"
integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==
dependencies:
es-define-property "^1.0.0"
has-proto@^1.0.1:
version "1.0.3"
resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd"
integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==
has-symbols@^1.0.1, has-symbols@^1.0.2:
version "1.0.2"
resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz"
@@ -9494,6 +9350,13 @@ has@^1.0.3:
dependencies:
function-bind "^1.1.1"
hasown@^2.0.0:
version "2.0.2"
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003"
integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==
dependencies:
function-bind "^1.1.2"
he@1.2.0, he@^1.2.0:
version "1.2.0"
resolved "https://registry.npmjs.org/he/-/he-1.2.0.tgz"
@@ -9617,17 +9480,6 @@ http-errors@1.7.3:
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@1.8.0:
version "1.8.0"
resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.0.tgz"
integrity sha512-4I8r0C5JDhT5VkvI47QktDW75rNlGVsUf/8hzjCC/wkWI/jdTRmBb9aI7erSG82r1bjKY3F6k28WnsVxB1C73A==
dependencies:
depd "~1.1.2"
inherits "2.0.4"
setprototypeof "1.2.0"
statuses ">= 1.5.0 < 2"
toidentifier "1.0.0"
http-errors@1.8.1:
version "1.8.1"
resolved "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz"
@@ -11239,7 +11091,7 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash-es@^4.17.11, lodash-es@^4.17.21:
lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
@@ -11605,6 +11457,11 @@ merge-descriptors@1.0.1:
resolved "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz"
integrity sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=
merge-descriptors@1.0.3:
version "1.0.3"
resolved "https://registry.yarnpkg.com/merge-descriptors/-/merge-descriptors-1.0.3.tgz#d80319a65f3c7935351e5cfdac8f9318504dbed5"
integrity sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==
merge-stream@^2.0.0:
version "2.0.0"
resolved "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz"
@@ -12244,13 +12101,6 @@ normalize-package-data@^3.0.0, normalize-package-data@^3.0.2:
semver "^7.3.4"
validate-npm-package-license "^3.0.1"
normalize-path@^2.1.1:
version "2.1.1"
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz"
integrity sha1-GrKLVW4Zg2Oowab35vogE3/mrtk=
dependencies:
remove-trailing-separator "^1.0.1"
normalize-path@^3.0.0, normalize-path@~3.0.0:
version "3.0.0"
resolved "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz"
@@ -12440,7 +12290,7 @@ object-assign@^4, object-assign@^4.1.0, object-assign@^4.1.1:
resolved "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz"
integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
object-hash@^2.0.3, object-hash@^2.2.0:
object-hash@^2.2.0:
version "2.2.0"
resolved "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz"
integrity sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==
@@ -12450,6 +12300,11 @@ object-inspect@^1.11.0:
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.0.tgz"
integrity sha512-Ho2z80bVIvJloH+YzRmpZVQe87+qASmBUKZDWgx9cu+KDrX2ZDH/3tMy+gXbZETVGs2M8YdxObOh7XAtim9Y0g==
object-inspect@^1.13.1:
version "1.13.2"
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.2.tgz#dea0088467fb991e67af4058147a24824a3043ff"
integrity sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==
object-inspect@^1.9.0:
version "1.12.2"
resolved "https://registry.npmjs.org/object-inspect/-/object-inspect-1.12.2.tgz"
@@ -12591,14 +12446,6 @@ open@^8.0.9, open@^8.4.0:
is-docker "^2.1.1"
is-wsl "^2.2.0"
optimism@^0.16.1:
version "0.16.1"
resolved "https://registry.npmjs.org/optimism/-/optimism-0.16.1.tgz"
integrity sha512-64i+Uw3otrndfq5kaoGNoY7pvOhSsjFEN4bdEFh80MWVk/dbgJfMv7VFDeCT8LxNAlEVhQmdVEbfE7X2nWNIIg==
dependencies:
"@wry/context" "^0.6.0"
"@wry/trie" "^0.3.0"
optionator@^0.8.1:
version "0.8.3"
resolved "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz"
@@ -12651,13 +12498,6 @@ p-finally@^1.0.0:
resolved "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz"
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
p-limit@3.1.0, p-limit@^3.0.2:
version "3.1.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
dependencies:
yocto-queue "^0.1.0"
p-limit@^1.1.0:
version "1.3.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz"
@@ -12672,6 +12512,13 @@ p-limit@^2.0.0, p-limit@^2.2.0:
dependencies:
p-try "^2.0.0"
p-limit@^3.0.2:
version "3.1.0"
resolved "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz"
integrity sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==
dependencies:
yocto-queue "^0.1.0"
p-limit@^5.0.0:
version "5.0.0"
resolved "https://registry.yarnpkg.com/p-limit/-/p-limit-5.0.0.tgz#6946d5b7140b649b7a33a027d89b4c625b3a5985"
@@ -12937,6 +12784,11 @@ path-scurry@^1.10.2:
lru-cache "^10.2.0"
minipass "^5.0.0 || ^6.0.2 || ^7.0.0"
path-to-regexp@0.1.10:
version "0.1.10"
resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.10.tgz#67e9108c5c0551b9e5326064387de4763c4d5f8b"
integrity sha512-7lf7qcQidTku0Gu3YDPc8DJ1q7OOucfa/BSsIwjuh56VU7katFvuM8hULfkwB3Fns/rsVF7PwPKVw1sl5KQS9w==
path-to-regexp@0.1.7:
version "0.1.7"
resolved "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz"
@@ -13939,13 +13791,20 @@ q@^1.1.2, q@^1.5.1:
resolved "https://registry.npmjs.org/q/-/q-1.5.1.tgz"
integrity sha1-fjL3W0E4EpHQRhHxvxQQmsAGUdc=
qs@6.11.0, qs@^6.11.0, qs@^6.9.4:
qs@6.11.0:
version "6.11.0"
resolved "https://registry.npmjs.org/qs/-/qs-6.11.0.tgz"
integrity sha512-MvjoMCJwEarSbUYk5O+nmoSzSutSsTwF85zcHPQ9OrlFoZOYIjaqBAJIqIXjptyD5vThxGq52Xu/MaJzRkIk4Q==
dependencies:
side-channel "^1.0.4"
qs@6.13.0, qs@^6.11.0, qs@^6.9.4:
version "6.13.0"
resolved "https://registry.yarnpkg.com/qs/-/qs-6.13.0.tgz#6ca3bd58439f7e245655798997787b0d88a51906"
integrity sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==
dependencies:
side-channel "^1.0.6"
qs@6.9.7:
version "6.9.7"
resolved "https://registry.npmjs.org/qs/-/qs-6.9.7.tgz"
@@ -14020,27 +13879,7 @@ raw-body@2.4.3:
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@2.5.1:
version "2.5.1"
resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.5.1.tgz"
integrity sha512-qqJBtEyVgS0ZmPGdCFPWJ3FreoqvG4MVQln/kCgF7Olq95IbOp0/BWyMwbdtn4VTvkM8Y7khCQ2Xgk/tcrCXig==
dependencies:
bytes "3.1.2"
http-errors "2.0.0"
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@^2.4.1:
version "2.4.2"
resolved "https://registry.npmjs.org/raw-body/-/raw-body-2.4.2.tgz"
integrity sha512-RPMAFUJP19WIet/99ngh6Iv8fzAbqum4Li7AD6DtGaW2RpMB/11xDoalPiJMTbu6I3hkbMVkATvZrqb9EEqeeQ==
dependencies:
bytes "3.1.1"
http-errors "1.8.1"
iconv-lite "0.4.24"
unpipe "1.0.0"
raw-body@^2.5.2:
raw-body@2.5.2, raw-body@^2.5.2:
version "2.5.2"
resolved "https://registry.yarnpkg.com/raw-body/-/raw-body-2.5.2.tgz#99febd83b90e08975087e8f1f9419a149366b68a"
integrity sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==
@@ -14565,11 +14404,6 @@ relateurl@^0.2.7:
resolved "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz"
integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk=
remove-trailing-separator@^1.0.1:
version "1.1.0"
resolved "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz"
integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8=
remove-trailing-slash@^0.1.1:
version "0.1.1"
resolved "https://registry.npmjs.org/remove-trailing-slash/-/remove-trailing-slash-0.1.1.tgz"
@@ -14634,16 +14468,16 @@ resolve-cwd@^3.0.0:
dependencies:
resolve-from "^5.0.0"
resolve-from@5.0.0, resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-from@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz"
integrity sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==
resolve-from@^5.0.0:
version "5.0.0"
resolved "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz"
integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==
resolve-url-loader@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/resolve-url-loader/-/resolve-url-loader-4.0.0.tgz"
@@ -14993,6 +14827,25 @@ send@0.18.0:
range-parser "~1.2.1"
statuses "2.0.1"
send@0.19.0:
version "0.19.0"
resolved "https://registry.yarnpkg.com/send/-/send-0.19.0.tgz#bbc5a388c8ea6c048967049dbeac0e4a3f09d7f8"
integrity sha512-dW41u5VfLXu8SJh5bwRmyYUbAoSB3c9uQh6L8h/KtsFREPWpbX1lrljJo186Jc4nmci/sGUZ9a0a0J2zgfq2hw==
dependencies:
debug "2.6.9"
depd "2.0.0"
destroy "1.2.0"
encodeurl "~1.0.2"
escape-html "~1.0.3"
etag "~1.8.1"
fresh "0.5.2"
http-errors "2.0.0"
mime "1.6.0"
ms "2.1.3"
on-finished "2.4.1"
range-parser "~1.2.1"
statuses "2.0.1"
serialize-javascript@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-4.0.0.tgz"
@@ -15030,10 +14883,10 @@ serve-static@1.14.2:
parseurl "~1.3.3"
send "0.17.2"
serve-static@1.15.0:
version "1.15.0"
resolved "https://registry.npmjs.org/serve-static/-/serve-static-1.15.0.tgz"
integrity sha512-XGuRDNjXUijsUL0vl6nSD7cwURuzEgglbOaFuZM9g3kwDXOWVTck0jLzjPzGD+TazWbboZYu52/9/XPdUgne9g==
serve-static@1.16.0:
version "1.16.0"
resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.16.0.tgz#2bf4ed49f8af311b519c46f272bf6ac3baf38a92"
integrity sha512-pDLK8zwl2eKaYrs8mrPZBJua4hMplRWJ1tIFksVC3FtBEBnl8dxgeHtsaMS8DhS9i4fLObaon6ABoc4/hQGdPA==
dependencies:
encodeurl "~1.0.2"
escape-html "~1.0.3"
@@ -15045,6 +14898,18 @@ set-blocking@^2.0.0, set-blocking@~2.0.0:
resolved "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz"
integrity sha1-BF+XgtARrppoA93TgrJDkrPYkPc=
set-function-length@^1.2.1:
version "1.2.2"
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449"
integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==
dependencies:
define-data-property "^1.1.4"
es-errors "^1.3.0"
function-bind "^1.1.2"
get-intrinsic "^1.2.4"
gopd "^1.0.1"
has-property-descriptors "^1.0.2"
setprototypeof@1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.0.tgz"
@@ -15109,6 +14974,16 @@ side-channel@^1.0.4:
get-intrinsic "^1.0.2"
object-inspect "^1.9.0"
side-channel@^1.0.6:
version "1.0.6"
resolved "https://registry.yarnpkg.com/side-channel/-/side-channel-1.0.6.tgz#abd25fb7cd24baf45466406b1096b7831c9215f2"
integrity sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==
dependencies:
call-bind "^1.0.7"
es-errors "^1.3.0"
get-intrinsic "^1.2.4"
object-inspect "^1.13.1"
siginfo@^2.0.0:
version "2.0.0"
resolved "https://registry.yarnpkg.com/siginfo/-/siginfo-2.0.0.tgz#32e76c70b79724e3bb567cb9d543eb858ccfaf30"
@@ -15842,11 +15717,6 @@ svgo@^2.7.0:
picocolors "^1.0.0"
stable "^0.1.8"
symbol-observable@^4.0.0:
version "4.0.0"
resolved "https://registry.npmjs.org/symbol-observable/-/symbol-observable-4.0.0.tgz"
integrity sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==
symbol-tree@^3.2.4:
version "3.2.4"
resolved "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz"
@@ -16207,20 +16077,6 @@ ts-api-utils@^1.0.1:
resolved "https://registry.yarnpkg.com/ts-api-utils/-/ts-api-utils-1.2.1.tgz#f716c7e027494629485b21c0df6180f4d08f5e8b"
integrity sha512-RIYA36cJn2WiH9Hy77hdF9r7oEwxAtB/TS9/S4Qd90Ap4z5FSiin5zEiTL44OII1Y3IIlEvxwxFUVgrHSZ/UpA==
ts-invariant@^0.10.3:
version "0.10.3"
resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.10.3.tgz"
integrity sha512-uivwYcQaxAucv1CzRp2n/QdYPo4ILf9VXgH19zEIjFx2EJufV16P0JtJVpYHy89DItG6Kwj2oIUjrcK5au+4tQ==
dependencies:
tslib "^2.1.0"
ts-invariant@^0.9.0:
version "0.9.4"
resolved "https://registry.npmjs.org/ts-invariant/-/ts-invariant-0.9.4.tgz"
integrity sha512-63jtX/ZSwnUNi/WhXjnK8kz4cHHpYS60AnmA6ixz17l7E12a5puCWFlNpkne5Rl0J8TBPVHpGjsj4fxs8ObVLQ==
dependencies:
tslib "^2.1.0"
tsconfig-paths@^3.12.0:
version "3.12.0"
resolved "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.12.0.tgz"
@@ -16236,7 +16092,7 @@ tslib@^1.8.1, tslib@^1.9.0, tslib@^1.9.3:
resolved "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz"
integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==
tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0, tslib@^2.3.0, tslib@~2.3.0:
tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0:
version "2.3.1"
resolved "https://registry.npmjs.org/tslib/-/tslib-2.3.1.tgz"
integrity sha512-77EbyPPpMz+FRFRuAFlWMtmgUWGe9UOG2Z25NqCwiIjRhOf5iKGuzSe5P2w1laq+FkRy4p+PCuVkJSGkzTEKVw==
@@ -16442,13 +16298,6 @@ universalify@^2.0.0:
resolved "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz"
integrity sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==
unixify@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/unixify/-/unixify-1.0.0.tgz"
integrity sha1-OmQcjC/7zk2mg6XHDwOkYpQMIJA=
dependencies:
normalize-path "^2.1.1"
unpipe@1.0.0, unpipe@~1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz"
@@ -16589,11 +16438,6 @@ validate-npm-package-name@^3.0.0:
dependencies:
builtins "^1.0.3"
value-or-promise@1.0.11:
version "1.0.11"
resolved "https://registry.npmjs.org/value-or-promise/-/value-or-promise-1.0.11.tgz"
integrity sha512-41BrgH+dIbCFXClcSapVs5M6GkENd3gQOJpEfPDNa71LsUGMXDL0jMWpI/Rh7WhX+Aalfz2TTS3Zt5pUsbnhLg==
vary@^1, vary@~1.1.2:
version "1.1.2"
resolved "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz"
@@ -17436,17 +17280,6 @@ yocto-queue@^1.0.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-1.0.0.tgz#7f816433fb2cbc511ec8bf7d263c3b58a1a3c251"
integrity sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==
yup@^0.31.0:
version "0.31.1"
resolved "https://registry.npmjs.org/yup/-/yup-0.31.1.tgz"
integrity sha512-Lf6648jDYOWR75IlWkVfwesPyW6oj+50NpxlKvsQlpPsB8eI+ndI7b4S1VrwbmeV9hIZDu1MzrlIL4W+gK1jPw==
dependencies:
"@babel/runtime" "^7.10.5"
lodash "^4.17.20"
lodash-es "^4.17.11"
property-expr "^2.0.4"
toposort "^2.0.2"
yup@^0.32.11:
version "0.32.11"
resolved "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz"
@@ -17460,26 +17293,6 @@ yup@^0.32.11:
property-expr "^2.0.4"
toposort "^2.0.2"
zen-observable-ts@^1.2.5:
version "1.2.5"
resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.2.5.tgz"
integrity sha512-QZWQekv6iB72Naeake9hS1KxHlotfRpe+WGNbNx5/ta+R3DNjVO2bswf63gXlWDcs+EMd7XY8HfVQyP1X6T4Zg==
dependencies:
zen-observable "0.8.15"
zen-observable-ts@~1.1.0:
version "1.1.0"
resolved "https://registry.npmjs.org/zen-observable-ts/-/zen-observable-ts-1.1.0.tgz"
integrity sha512-1h4zlLSqI2cRLPJUHJFL8bCWHhkpuXkF+dbGkRaWjgDIG26DmzyshUMrdV/rL3UnR+mhaX4fRq8LPouq0MYYIA==
dependencies:
"@types/zen-observable" "0.8.3"
zen-observable "0.8.15"
zen-observable@0.8.15:
version "0.8.15"
resolved "https://registry.npmjs.org/zen-observable/-/zen-observable-0.8.15.tgz"
integrity sha512-PQ2PC7R9rslx84ndNBZB/Dkv8V8fZEpk83RLgXtYd0fwUgEjseMn1Dgajh2x6S8QbZAFa9p2qVCEuYZNgve0dQ==
zustand@^4.4.1:
version "4.5.2"
resolved "https://registry.yarnpkg.com/zustand/-/zustand-4.5.2.tgz#fddbe7cac1e71d45413b3682cdb47b48034c3848"