feat(cli): run migrations and app in start command (#284)
* feat: perform pending migrations in start command * feat: log error when DB connection is refused * feat: log error when Redis connection is refused * refactor: fix type errors * fix: correct server and copy graphql schema * fix: differentiate migrations by env * chore: remove dev executable * chore: fix typo in default postgresUsername * fix: copy json files into dist folder * chore(cli): add dev script * chore: pull non-dev logs to info level * feat(cli): run app in start command * fix(backend): remove default count in Connection * fix(cli): remove .eslintrc usage in lint script * refactor: remove disableMigrationsListValidation * refactor: make Step optional in ExecutionStep * refactor: make Flow optional in Step
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
import User from '../../src/models/user';
|
import User from '../../src/models/user';
|
||||||
import '../../src/config/database';
|
import '../../src/config/orm';
|
||||||
import logger from '../../src/helpers/logger';
|
import logger from '../../src/helpers/logger';
|
||||||
|
|
||||||
const userParams = {
|
const userParams = {
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
import appConfig from './src/config/app';
|
import appConfig from './src/config/app';
|
||||||
|
|
||||||
|
const fileExtension = appConfig.isDev ? 'ts' : 'js';
|
||||||
|
|
||||||
const knexConfig = {
|
const knexConfig = {
|
||||||
client: 'pg',
|
client: 'pg',
|
||||||
connection: {
|
connection: {
|
||||||
@@ -13,6 +15,8 @@ const knexConfig = {
|
|||||||
pool: { min: 0, max: 20 },
|
pool: { min: 0, max: 20 },
|
||||||
migrations: {
|
migrations: {
|
||||||
directory: __dirname + '/src/db/migrations',
|
directory: __dirname + '/src/db/migrations',
|
||||||
|
extension: fileExtension,
|
||||||
|
loadExtensions: [`.${fileExtension}`],
|
||||||
},
|
},
|
||||||
seeds: {
|
seeds: {
|
||||||
directory: __dirname + '/src/db/seeds',
|
directory: __dirname + '/src/db/seeds',
|
||||||
|
@@ -3,10 +3,10 @@
|
|||||||
"version": "0.1.0",
|
"version": "0.1.0",
|
||||||
"description": "> TODO: description",
|
"description": "> TODO: description",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/app.ts",
|
"dev": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/server.ts",
|
||||||
"worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts",
|
"worker": "nodemon --watch 'src/**/*.ts' --exec 'ts-node' src/worker.ts",
|
||||||
"build": "tsc",
|
"build": "tsc && yarn copy-statics",
|
||||||
"start": "node dist/src/app.js",
|
"start": "node dist/src/server.js",
|
||||||
"test": "ava",
|
"test": "ava",
|
||||||
"lint": "eslint . --ignore-path ../../.eslintignore",
|
"lint": "eslint . --ignore-path ../../.eslintignore",
|
||||||
"db:create": "ts-node ./bin/database/create.ts",
|
"db:create": "ts-node ./bin/database/create.ts",
|
||||||
@@ -14,7 +14,8 @@
|
|||||||
"db:drop": "ts-node ./bin/database/drop.ts",
|
"db:drop": "ts-node ./bin/database/drop.ts",
|
||||||
"db:migration:create": "knex migrate:make",
|
"db:migration:create": "knex migrate:make",
|
||||||
"db:rollback": "knex migrate:rollback",
|
"db:rollback": "knex migrate:rollback",
|
||||||
"db:migrate": "knex migrate:latest"
|
"db:migrate": "knex migrate:latest",
|
||||||
|
"copy-statics": "copyfiles src/**/*.{graphql,json} dist"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@automatisch/web": "0.1.0",
|
"@automatisch/web": "0.1.0",
|
||||||
@@ -27,6 +28,7 @@
|
|||||||
"axios": "0.24.0",
|
"axios": "0.24.0",
|
||||||
"bcrypt": "^5.0.1",
|
"bcrypt": "^5.0.1",
|
||||||
"bullmq": "^1.76.1",
|
"bullmq": "^1.76.1",
|
||||||
|
"copyfiles": "^2.4.1",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"crypto-js": "^4.1.1",
|
"crypto-js": "^4.1.1",
|
||||||
"debug": "~2.6.9",
|
"debug": "~2.6.9",
|
||||||
@@ -60,7 +62,7 @@
|
|||||||
}
|
}
|
||||||
],
|
],
|
||||||
"homepage": "https://github.com/automatisch/automatisch#readme",
|
"homepage": "https://github.com/automatisch/automatisch#readme",
|
||||||
"main": "src/app.ts",
|
"main": "dist/src/app",
|
||||||
"directories": {
|
"directories": {
|
||||||
"src": "src",
|
"src": "src",
|
||||||
"test": "__tests__"
|
"test": "__tests__"
|
||||||
@@ -78,6 +80,7 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@automatisch/types": "0.1.0",
|
"@automatisch/types": "0.1.0",
|
||||||
"@types/bcrypt": "^5.0.0",
|
"@types/bcrypt": "^5.0.0",
|
||||||
|
"@types/bull": "^3.15.8",
|
||||||
"@types/cors": "^2.8.12",
|
"@types/cors": "^2.8.12",
|
||||||
"@types/crypto-js": "^4.0.2",
|
"@types/crypto-js": "^4.0.2",
|
||||||
"@types/express": "^4.17.13",
|
"@types/express": "^4.17.13",
|
||||||
|
@@ -4,12 +4,11 @@ import express, { Request, Response, NextFunction } from 'express';
|
|||||||
import cors from 'cors';
|
import cors from 'cors';
|
||||||
import corsOptions from './config/cors-options';
|
import corsOptions from './config/cors-options';
|
||||||
import graphQLInstance from './helpers/graphql-instance';
|
import graphQLInstance from './helpers/graphql-instance';
|
||||||
import logger from './helpers/logger';
|
|
||||||
import morgan from './helpers/morgan';
|
import morgan from './helpers/morgan';
|
||||||
import appAssetsHandler from './helpers/app-assets-handler';
|
import appAssetsHandler from './helpers/app-assets-handler';
|
||||||
import webUIHandler from './helpers/web-ui-handler';
|
import webUIHandler from './helpers/web-ui-handler';
|
||||||
import errorHandler from './helpers/error-handler';
|
import errorHandler from './helpers/error-handler';
|
||||||
import './config/database';
|
import './config/orm';
|
||||||
import {
|
import {
|
||||||
createBullBoardHandler,
|
createBullBoardHandler,
|
||||||
serverAdapter,
|
serverAdapter,
|
||||||
@@ -21,7 +20,6 @@ if (appConfig.appEnv === 'development') {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const app = express();
|
const app = express();
|
||||||
const port = appConfig.port;
|
|
||||||
|
|
||||||
if (appConfig.appEnv === 'development') {
|
if (appConfig.appEnv === 'development') {
|
||||||
injectBullBoardHandler(app, serverAdapter);
|
injectBullBoardHandler(app, serverAdapter);
|
||||||
@@ -44,6 +42,4 @@ app.use(function (req: Request, res: Response, next: NextFunction) {
|
|||||||
|
|
||||||
app.use(errorHandler);
|
app.use(errorHandler);
|
||||||
|
|
||||||
app.listen(port, () => {
|
export default app;
|
||||||
logger.info(`Server is listening on ${port}`);
|
|
||||||
});
|
|
||||||
|
@@ -5,15 +5,16 @@ type AppConfig = {
|
|||||||
host: string;
|
host: string;
|
||||||
protocol: string;
|
protocol: string;
|
||||||
port: string;
|
port: string;
|
||||||
webAppUrl?: string;
|
webAppUrl: string;
|
||||||
appEnv: string;
|
appEnv: string;
|
||||||
|
isDev: boolean;
|
||||||
postgresDatabase: string;
|
postgresDatabase: string;
|
||||||
postgresPort: number;
|
postgresPort: number;
|
||||||
postgresHost: string;
|
postgresHost: string;
|
||||||
postgresUsername: string;
|
postgresUsername: string;
|
||||||
postgresPassword: string;
|
postgresPassword?: string;
|
||||||
postgresEnableSsl: boolean;
|
postgresEnableSsl: boolean;
|
||||||
baseUrl?: string;
|
baseUrl: string;
|
||||||
encryptionKey: string;
|
encryptionKey: string;
|
||||||
appSecretKey: string;
|
appSecretKey: string;
|
||||||
serveWebAppSeparately: boolean;
|
serveWebAppSeparately: boolean;
|
||||||
@@ -21,37 +22,44 @@ type AppConfig = {
|
|||||||
redisPort: number;
|
redisPort: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const host = process.env.HOST || 'localhost';
|
||||||
|
const protocol = process.env.PROTOCOL || 'http';
|
||||||
|
const port = process.env.PORT || '3000';
|
||||||
|
const serveWebAppSeparately = process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false;
|
||||||
|
|
||||||
|
let webAppUrl = `${protocol}://${host}:${port}`;
|
||||||
|
if (serveWebAppSeparately) {
|
||||||
|
webAppUrl = process.env.WEB_APP_URL || 'http://localhost:3001';
|
||||||
|
}
|
||||||
|
|
||||||
|
const baseUrl = `${protocol}://${host}:${port}`;
|
||||||
|
|
||||||
|
const appEnv = process.env.APP_ENV || 'development';
|
||||||
|
|
||||||
const appConfig: AppConfig = {
|
const appConfig: AppConfig = {
|
||||||
host: process.env.HOST || 'localhost',
|
host,
|
||||||
protocol: process.env.PROTOCOL || 'http',
|
protocol,
|
||||||
port: process.env.PORT || '3000',
|
port,
|
||||||
appEnv: process.env.APP_ENV || 'development',
|
appEnv: appEnv,
|
||||||
|
isDev: appEnv === 'development',
|
||||||
postgresDatabase: process.env.POSTGRES_DATABASE || 'automatisch_development',
|
postgresDatabase: process.env.POSTGRES_DATABASE || 'automatisch_development',
|
||||||
postgresPort: parseInt(process.env.POSTGRES_PORT) || 5432,
|
postgresPort: parseInt(process.env.POSTGRES_PORT|| '5432'),
|
||||||
postgresHost: process.env.POSTGRES_HOST || 'localhost',
|
postgresHost: process.env.POSTGRES_HOST || 'localhost',
|
||||||
postgresUsername:
|
postgresUsername:
|
||||||
process.env.POSTGRES_USERNAME || 'automatish_development_user',
|
process.env.POSTGRES_USERNAME || 'automatisch_development_user',
|
||||||
postgresPassword: process.env.POSTGRES_PASSWORD,
|
postgresPassword: process.env.POSTGRES_PASSWORD,
|
||||||
postgresEnableSsl: process.env.POSTGRES_ENABLE_SSL === 'true' ? true : false,
|
postgresEnableSsl: process.env.POSTGRES_ENABLE_SSL === 'true' ? true : false,
|
||||||
encryptionKey: process.env.ENCRYPTION_KEY,
|
encryptionKey: process.env.ENCRYPTION_KEY || '',
|
||||||
appSecretKey: process.env.APP_SECRET_KEY,
|
appSecretKey: process.env.APP_SECRET_KEY || '',
|
||||||
serveWebAppSeparately:
|
serveWebAppSeparately,
|
||||||
process.env.SERVE_WEB_APP_SEPARATELY === 'true' ? true : false,
|
|
||||||
redisHost: process.env.REDIS_HOST || '127.0.0.1',
|
redisHost: process.env.REDIS_HOST || '127.0.0.1',
|
||||||
redisPort: parseInt(process.env.REDIS_PORT) || 6379,
|
redisPort: parseInt(process.env.REDIS_PORT || '6379'),
|
||||||
|
baseUrl,
|
||||||
|
webAppUrl,
|
||||||
};
|
};
|
||||||
|
|
||||||
if (appConfig.serveWebAppSeparately) {
|
|
||||||
appConfig.webAppUrl = process.env.WEB_APP_URL || 'http://localhost:3001';
|
|
||||||
} else {
|
|
||||||
appConfig.webAppUrl = `${appConfig.protocol}://${appConfig.host}:${appConfig.port}`;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!appConfig.encryptionKey) {
|
if (!appConfig.encryptionKey) {
|
||||||
throw new Error('ENCRYPTION_KEY environment variable needs to be set!');
|
throw new Error('ENCRYPTION_KEY environment variable needs to be set!');
|
||||||
}
|
}
|
||||||
|
|
||||||
const baseUrl = `${appConfig.protocol}://${appConfig.host}:${appConfig.port}`;
|
|
||||||
appConfig.baseUrl = baseUrl;
|
|
||||||
|
|
||||||
export default appConfig;
|
export default appConfig;
|
||||||
|
@@ -1,6 +1,19 @@
|
|||||||
import { Model } from 'objection';
|
import process from 'process';
|
||||||
import knexInstance from 'knex';
|
import knex from 'knex';
|
||||||
|
import type { Knex } from 'knex';
|
||||||
import knexConfig from '../../knexfile';
|
import knexConfig from '../../knexfile';
|
||||||
|
import logger from '../helpers/logger';
|
||||||
|
|
||||||
const knex = knexInstance(knexConfig)
|
const knexInstance: Knex = knex(knexConfig);
|
||||||
Model.knex(knex)
|
|
||||||
|
const CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||||
|
|
||||||
|
knexInstance.raw('SELECT 1')
|
||||||
|
.catch((err) => {
|
||||||
|
if (err.code === CONNECTION_REFUSED) {
|
||||||
|
logger.error('Make sure you have installed PostgreSQL and it is running.', err);
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
export default knexInstance;
|
||||||
|
4
packages/backend/src/config/orm.ts
Normal file
4
packages/backend/src/config/orm.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
import { Model } from 'objection';
|
||||||
|
import database from './database';
|
||||||
|
|
||||||
|
Model.knex(database)
|
@@ -3,6 +3,7 @@ import appConfig from './app';
|
|||||||
const redisConfig = {
|
const redisConfig = {
|
||||||
host: appConfig.redisHost,
|
host: appConfig.redisHost,
|
||||||
port: appConfig.redisPort,
|
port: appConfig.redisPort,
|
||||||
|
enableOfflineQueue: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default redisConfig;
|
export default redisConfig;
|
||||||
|
@@ -22,6 +22,8 @@ const createAuthData = async (
|
|||||||
const appClass = (await import(`../../apps/${connection.key}`)).default;
|
const appClass = (await import(`../../apps/${connection.key}`)).default;
|
||||||
const appData = App.findOneByKey(connection.key);
|
const appData = App.findOneByKey(connection.key);
|
||||||
|
|
||||||
|
if (!connection.formattedData) { return null; }
|
||||||
|
|
||||||
const appInstance = new appClass(appData, {
|
const appInstance = new appClass(appData, {
|
||||||
consumerKey: connection.formattedData.consumerKey,
|
consumerKey: connection.formattedData.consumerKey,
|
||||||
consumerSecret: connection.formattedData.consumerSecret,
|
consumerSecret: connection.formattedData.consumerSecret,
|
||||||
|
@@ -19,6 +19,8 @@ const deleteStep = async (
|
|||||||
})
|
})
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
if (!step) return;
|
||||||
|
|
||||||
await step.$query().delete();
|
await step.$query().delete();
|
||||||
|
|
||||||
const nextSteps = await step.flow
|
const nextSteps = await step.flow
|
||||||
|
@@ -18,6 +18,8 @@ const resetConnection = async (
|
|||||||
})
|
})
|
||||||
.throwIfNotFound();
|
.throwIfNotFound();
|
||||||
|
|
||||||
|
if (!connection.formattedData) { return null; }
|
||||||
|
|
||||||
connection = await connection.$query().patchAndFetch({
|
connection = await connection.$query().patchAndFetch({
|
||||||
formattedData: { screenName: connection.formattedData.screenName },
|
formattedData: { screenName: connection.formattedData.screenName },
|
||||||
});
|
});
|
||||||
|
@@ -26,10 +26,13 @@ const getConnectedApps = async (
|
|||||||
.filter((app: IApp) => connectionKeys.includes(app.key))
|
.filter((app: IApp) => connectionKeys.includes(app.key))
|
||||||
.map((app: IApp) => {
|
.map((app: IApp) => {
|
||||||
const connection = connections.find(
|
const connection = connections.find(
|
||||||
(connection: IConnection) => connection.key === app.key
|
(connection) => (connection as IConnection).key === app.key
|
||||||
);
|
);
|
||||||
|
|
||||||
app.connectionCount = connection.count;
|
if (connection) {
|
||||||
|
app.connectionCount = connection.count;
|
||||||
|
}
|
||||||
|
|
||||||
return app;
|
return app;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@@ -12,8 +12,12 @@ const getData = async (_parent: unknown, params: Params, context: Context) => {
|
|||||||
.withGraphFetched('connection')
|
.withGraphFetched('connection')
|
||||||
.findById(params.stepId);
|
.findById(params.stepId);
|
||||||
|
|
||||||
|
if (!step) return null;
|
||||||
|
|
||||||
const connection = step.connection;
|
const connection = step.connection;
|
||||||
|
|
||||||
|
if (!connection || !step.appKey) return null;
|
||||||
|
|
||||||
const appData = App.findOneByKey(step.appKey);
|
const appData = App.findOneByKey(step.appKey);
|
||||||
const AppClass = (await import(`../../apps/${step.appKey}`)).default;
|
const AppClass = (await import(`../../apps/${step.appKey}`)).default;
|
||||||
|
|
||||||
|
@@ -10,7 +10,7 @@ const levels = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const level = () => {
|
const level = () => {
|
||||||
return appConfig.appEnv === 'development' ? 'debug' : 'warn'
|
return appConfig.appEnv === 'development' ? 'debug' : 'info'
|
||||||
}
|
}
|
||||||
|
|
||||||
const colors = {
|
const colors = {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { dirname, join } from 'path';
|
import { dirname, join } from 'path';
|
||||||
|
import { IApp } from '@automatisch/types';
|
||||||
import appInfoConverter from '../helpers/app-info-converter';
|
import appInfoConverter from '../helpers/app-info-converter';
|
||||||
|
|
||||||
class App {
|
class App {
|
||||||
@@ -7,7 +8,7 @@ class App {
|
|||||||
static folderPath = join(dirname(this.backendPath), 'apps');
|
static folderPath = join(dirname(this.backendPath), 'apps');
|
||||||
static list = fs.readdirSync(this.folderPath);
|
static list = fs.readdirSync(this.folderPath);
|
||||||
|
|
||||||
static findAll(name?: string): object[] {
|
static findAll(name?: string): IApp[] {
|
||||||
if (!name) return this.list.map((name) => this.findOneByName(name));
|
if (!name) return this.list.map((name) => this.findOneByName(name));
|
||||||
|
|
||||||
return this.list
|
return this.list
|
||||||
@@ -15,7 +16,7 @@ class App {
|
|||||||
.map((name) => this.findOneByName(name));
|
.map((name) => this.findOneByName(name));
|
||||||
}
|
}
|
||||||
|
|
||||||
static findOneByName(name: string): object {
|
static findOneByName(name: string): IApp {
|
||||||
const rawAppData = fs.readFileSync(
|
const rawAppData = fs.readFileSync(
|
||||||
this.folderPath + `/${name}/info.json`,
|
this.folderPath + `/${name}/info.json`,
|
||||||
'utf-8'
|
'utf-8'
|
||||||
@@ -23,7 +24,7 @@ class App {
|
|||||||
return appInfoConverter(rawAppData);
|
return appInfoConverter(rawAppData);
|
||||||
}
|
}
|
||||||
|
|
||||||
static findOneByKey(key: string): object {
|
static findOneByKey(key: string): IApp {
|
||||||
const rawAppData = fs.readFileSync(
|
const rawAppData = fs.readFileSync(
|
||||||
this.folderPath + `/${key}/info.json`,
|
this.folderPath + `/${key}/info.json`,
|
||||||
'utf-8'
|
'utf-8'
|
||||||
|
@@ -9,10 +9,10 @@ import { IJSONObject } from '@automatisch/types';
|
|||||||
class Connection extends Base {
|
class Connection extends Base {
|
||||||
id!: string;
|
id!: string;
|
||||||
key!: string;
|
key!: string;
|
||||||
data: string;
|
data = '';
|
||||||
formattedData!: IJSONObject;
|
formattedData?: IJSONObject;
|
||||||
userId!: string;
|
userId!: string;
|
||||||
verified: boolean;
|
verified = false;
|
||||||
count: number;
|
count: number;
|
||||||
|
|
||||||
static tableName = 'connections';
|
static tableName = 'connections';
|
||||||
@@ -50,7 +50,7 @@ class Connection extends Base {
|
|||||||
appConfig.encryptionKey
|
appConfig.encryptionKey
|
||||||
).toString();
|
).toString();
|
||||||
|
|
||||||
delete this['formattedData'];
|
delete this.formattedData;
|
||||||
}
|
}
|
||||||
|
|
||||||
decryptData(): void {
|
decryptData(): void {
|
||||||
|
@@ -8,7 +8,7 @@ class ExecutionStep extends Base {
|
|||||||
stepId!: string;
|
stepId!: string;
|
||||||
dataIn!: Record<string, unknown>;
|
dataIn!: Record<string, unknown>;
|
||||||
dataOut!: Record<string, unknown>;
|
dataOut!: Record<string, unknown>;
|
||||||
status: string;
|
status = 'failure';
|
||||||
step: Step;
|
step: Step;
|
||||||
|
|
||||||
static tableName = 'execution_steps';
|
static tableName = 'execution_steps';
|
||||||
|
@@ -5,8 +5,8 @@ import ExecutionStep from './execution-step';
|
|||||||
class Execution extends Base {
|
class Execution extends Base {
|
||||||
id!: string;
|
id!: string;
|
||||||
flowId!: string;
|
flowId!: string;
|
||||||
testRun: boolean;
|
testRun = false;
|
||||||
executionSteps: ExecutionStep[];
|
executionSteps: ExecutionStep[] = [];
|
||||||
|
|
||||||
static tableName = 'executions';
|
static tableName = 'executions';
|
||||||
|
|
||||||
|
@@ -6,9 +6,9 @@ import Execution from './execution';
|
|||||||
|
|
||||||
class Flow extends Base {
|
class Flow extends Base {
|
||||||
id!: string;
|
id!: string;
|
||||||
name: string;
|
name!: string;
|
||||||
userId!: string;
|
userId!: string;
|
||||||
active: boolean;
|
active = false;
|
||||||
steps?: [Step];
|
steps?: [Step];
|
||||||
|
|
||||||
static tableName = 'flows';
|
static tableName = 'flows';
|
||||||
|
@@ -7,15 +7,15 @@ import type { IStep } from '@automatisch/types';
|
|||||||
class Step extends Base {
|
class Step extends Base {
|
||||||
id!: string;
|
id!: string;
|
||||||
flowId!: string;
|
flowId!: string;
|
||||||
key: string;
|
key?: string;
|
||||||
appKey: string;
|
appKey?: string;
|
||||||
type!: IStep["type"];
|
type!: IStep["type"];
|
||||||
connectionId?: string;
|
connectionId?: string;
|
||||||
status: string;
|
status = 'incomplete';
|
||||||
position: number;
|
position!: number;
|
||||||
parameters: Record<string, unknown>;
|
parameters: Record<string, unknown> = {};
|
||||||
connection?: Connection;
|
connection?: Connection;
|
||||||
flow?: Flow;
|
flow: Flow;
|
||||||
executionSteps?: [ExecutionStep];
|
executionSteps?: [ExecutionStep];
|
||||||
|
|
||||||
static tableName = 'steps';
|
static tableName = 'steps';
|
||||||
|
@@ -1,11 +1,22 @@
|
|||||||
|
import process from 'process';
|
||||||
import { Queue, QueueScheduler } from 'bullmq';
|
import { Queue, QueueScheduler } from 'bullmq';
|
||||||
import redisConfig from '../config/redis';
|
import redisConfig from '../config/redis';
|
||||||
|
import logger from '../helpers/logger';
|
||||||
|
|
||||||
|
const CONNECTION_REFUSED = 'ECONNREFUSED';
|
||||||
|
|
||||||
const redisConnection = {
|
const redisConnection = {
|
||||||
connection: redisConfig,
|
connection: redisConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
new QueueScheduler('processor', redisConnection);
|
|
||||||
const processorQueue = new Queue('processor', redisConnection);
|
const processorQueue = new Queue('processor', redisConnection);
|
||||||
|
new QueueScheduler('processor', redisConnection);
|
||||||
|
|
||||||
|
processorQueue.on('error', (err) => {
|
||||||
|
if ((err as any).code === CONNECTION_REFUSED) {
|
||||||
|
logger.error('Make sure you have installed Redis and it is running.', err);
|
||||||
|
process.exit();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
export default processorQueue;
|
export default processorQueue;
|
||||||
|
9
packages/backend/src/server.ts
Normal file
9
packages/backend/src/server.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import app from './app';
|
||||||
|
import appConfig from './config/app';
|
||||||
|
import logger from './helpers/logger';
|
||||||
|
|
||||||
|
const port = appConfig.port;
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
logger.info(`Server is listening on ${port}`);
|
||||||
|
});
|
@@ -14,8 +14,8 @@ type ProcessorOptions = {
|
|||||||
|
|
||||||
class Processor {
|
class Processor {
|
||||||
flow: Flow;
|
flow: Flow;
|
||||||
untilStep: Step;
|
untilStep?: Step;
|
||||||
testRun: boolean;
|
testRun?: boolean;
|
||||||
|
|
||||||
static variableRegExp = /({{step\..+\..+}})/g;
|
static variableRegExp = /({{step\..+\..+}})/g;
|
||||||
|
|
||||||
@@ -32,7 +32,8 @@ class Processor {
|
|||||||
.orderBy('position', 'asc');
|
.orderBy('position', 'asc');
|
||||||
|
|
||||||
const triggerStep = steps.find((step) => step.type === 'trigger');
|
const triggerStep = steps.find((step) => step.type === 'trigger');
|
||||||
let initialTriggerData = await this.getInitialTriggerData(triggerStep);
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
||||||
|
let initialTriggerData = await this.getInitialTriggerData(triggerStep!);
|
||||||
|
|
||||||
if (this.testRun) {
|
if (this.testRun) {
|
||||||
initialTriggerData = [initialTriggerData[0]];
|
initialTriggerData = [initialTriggerData[0]];
|
||||||
@@ -53,6 +54,8 @@ class Processor {
|
|||||||
let fetchedActionData = {};
|
let fetchedActionData = {};
|
||||||
|
|
||||||
for await (const step of steps) {
|
for await (const step of steps) {
|
||||||
|
if (!step.appKey) continue;
|
||||||
|
|
||||||
const appData = App.findOneByKey(step.appKey);
|
const appData = App.findOneByKey(step.appKey);
|
||||||
|
|
||||||
const {
|
const {
|
||||||
@@ -74,11 +77,11 @@ class Processor {
|
|||||||
|
|
||||||
const appInstance = new AppClass(
|
const appInstance = new AppClass(
|
||||||
appData,
|
appData,
|
||||||
connection.formattedData,
|
connection?.formattedData,
|
||||||
computedParameters
|
computedParameters
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!isTrigger) {
|
if (!isTrigger && key) {
|
||||||
const command = appInstance.actions[key];
|
const command = appInstance.actions[key];
|
||||||
fetchedActionData = await command.run();
|
fetchedActionData = await command.run();
|
||||||
}
|
}
|
||||||
@@ -107,17 +110,21 @@ class Processor {
|
|||||||
.orderBy('created_at', 'desc')
|
.orderBy('created_at', 'desc')
|
||||||
.first();
|
.first();
|
||||||
|
|
||||||
return lastExecutionStepFromFirstExecution.dataOut;
|
return lastExecutionStepFromFirstExecution?.dataOut;
|
||||||
}
|
}
|
||||||
|
|
||||||
async getInitialTriggerData(step: Step) {
|
async getInitialTriggerData(step: Step) {
|
||||||
|
if (!step.appKey) return null;
|
||||||
|
|
||||||
const appData = App.findOneByKey(step.appKey);
|
const appData = App.findOneByKey(step.appKey);
|
||||||
const { appKey, connection, key, parameters: rawParameters = {} } = step;
|
const { appKey, connection, key, parameters: rawParameters = {} } = step;
|
||||||
|
|
||||||
|
if (!key) return null;
|
||||||
|
|
||||||
const AppClass = (await import(`../apps/${appKey}`)).default;
|
const AppClass = (await import(`../apps/${appKey}`)).default;
|
||||||
const appInstance = new AppClass(
|
const appInstance = new AppClass(
|
||||||
appData,
|
appData,
|
||||||
connection.formattedData,
|
connection?.formattedData,
|
||||||
rawParameters
|
rawParameters
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -149,30 +156,34 @@ class Processor {
|
|||||||
executionSteps: ExecutionSteps
|
executionSteps: ExecutionSteps
|
||||||
): Step['parameters'] {
|
): Step['parameters'] {
|
||||||
const entries = Object.entries(parameters);
|
const entries = Object.entries(parameters);
|
||||||
return entries.reduce((result, [key, value]: [string, string]) => {
|
return entries.reduce((result, [key, value]: [string, unknown]) => {
|
||||||
const parts = value.split(Processor.variableRegExp);
|
if (typeof value === 'string') {
|
||||||
|
const parts = value.split(Processor.variableRegExp);
|
||||||
|
|
||||||
const computedValue = parts
|
const computedValue = parts
|
||||||
.map((part: string) => {
|
.map((part: string) => {
|
||||||
const isVariable = part.match(Processor.variableRegExp);
|
const isVariable = part.match(Processor.variableRegExp);
|
||||||
if (isVariable) {
|
if (isVariable) {
|
||||||
const stepIdAndKeyPath = part.replace(/{{step.|}}/g, '') as string;
|
const stepIdAndKeyPath = part.replace(/{{step.|}}/g, '') as string;
|
||||||
const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.');
|
const [stepId, ...keyPaths] = stepIdAndKeyPath.split('.');
|
||||||
const keyPath = keyPaths.join('.');
|
const keyPath = keyPaths.join('.');
|
||||||
const executionStep = executionSteps[stepId.toString() as string];
|
const executionStep = executionSteps[stepId.toString() as string];
|
||||||
const data = executionStep?.dataOut;
|
const data = executionStep?.dataOut;
|
||||||
const dataValue = get(data, keyPath);
|
const dataValue = get(data, keyPath);
|
||||||
return dataValue;
|
return dataValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return part;
|
return part;
|
||||||
})
|
})
|
||||||
.join('');
|
.join('');
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...result,
|
...result,
|
||||||
[key]: computedValue,
|
[key]: computedValue,
|
||||||
};
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}, {});
|
}, {});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,10 +1,9 @@
|
|||||||
import type { IApp } from '@automatisch/types';
|
import type { IApp, IJSONObject } from '@automatisch/types';
|
||||||
import JSONObject from './json-object';
|
|
||||||
|
|
||||||
export default interface AuthenticationInterface {
|
export default interface AuthenticationInterface {
|
||||||
appData: IApp;
|
appData: IApp;
|
||||||
connectionData: IJSONObject;
|
connectionData: IJSONObject;
|
||||||
client: unknown;
|
client: unknown;
|
||||||
verifyCredentials(): Promise<JSONObject>;
|
verifyCredentials(): Promise<IJSONObject>;
|
||||||
isStillVerified(): Promise<boolean>;
|
isStillVerified(): Promise<boolean>;
|
||||||
}
|
}
|
||||||
|
@@ -1,2 +1,2 @@
|
|||||||
import './config/database';
|
import './config/orm';
|
||||||
import './workers/processor';
|
import './workers/processor';
|
||||||
|
@@ -1,19 +1,22 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "commonjs",
|
|
||||||
"esModuleInterop": true,
|
|
||||||
"target": "es6",
|
|
||||||
"noImplicitAny": true,
|
|
||||||
"moduleResolution": "node",
|
|
||||||
"sourceMap": true,
|
|
||||||
"outDir": "dist",
|
|
||||||
"baseUrl": ".",
|
"baseUrl": ".",
|
||||||
|
"declaration": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"lib": ["es2021"],
|
||||||
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
|
"noImplicitAny": true,
|
||||||
|
"outDir": "dist",
|
||||||
"paths": {
|
"paths": {
|
||||||
"*": [
|
"*": [
|
||||||
"node_modules/*",
|
"node_modules/*",
|
||||||
"src/types/*"
|
"src/types/*"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"target": "es2021",
|
||||||
"typeRoots": [
|
"typeRoots": [
|
||||||
"node_modules/@types",
|
"node_modules/@types",
|
||||||
"./src/types",
|
"./src/types",
|
||||||
@@ -21,6 +24,12 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"include": [
|
"include": [
|
||||||
"src/**/*"
|
"src/**/*",
|
||||||
]
|
"bin/**/*"
|
||||||
|
],
|
||||||
|
"ts-node": {
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,17 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
const oclif = require('@oclif/core')
|
|
||||||
|
|
||||||
const path = require('path')
|
|
||||||
const project = path.join(__dirname, '..', 'tsconfig.json')
|
|
||||||
|
|
||||||
// In dev mode -> use ts-node and dev plugins
|
|
||||||
process.env.NODE_ENV = 'development'
|
|
||||||
|
|
||||||
require('ts-node').register({project})
|
|
||||||
|
|
||||||
// In dev mode, always show stack traces
|
|
||||||
oclif.settings.debug = true;
|
|
||||||
|
|
||||||
// Start the CLI
|
|
||||||
oclif.run().then(oclif.flush).catch(oclif.Errors.handle)
|
|
@@ -1,3 +0,0 @@
|
|||||||
@echo off
|
|
||||||
|
|
||||||
node "%~dp0\dev" %*
|
|
@@ -24,16 +24,19 @@
|
|||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "shx rm -rf dist && tsc -b",
|
"build": "shx rm -rf dist && tsc -b",
|
||||||
"lint": "eslint . --ext .ts --config .eslintrc",
|
"dev": "nodemon --watch 'src/**/*.ts' --exec 'shx rm -rf dist && tsc -b' --ext 'ts'",
|
||||||
|
"lint": "eslint . --ext .ts --ignore-path ../../.eslintignore",
|
||||||
"postpack": "shx rm -f oclif.manifest.json",
|
"postpack": "shx rm -f oclif.manifest.json",
|
||||||
"posttest": "yarn lint",
|
"posttest": "yarn lint",
|
||||||
"prepack": "yarn build && oclif manifest && oclif readme",
|
"prepack": "yarn build && oclif manifest && oclif readme",
|
||||||
"version": "oclif readme && git add README.md"
|
"version": "oclif readme && git add README.md"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@automatisch/backend": "^0.1.0",
|
||||||
"@oclif/core": "^1",
|
"@oclif/core": "^1",
|
||||||
"@oclif/plugin-help": "^5",
|
"@oclif/plugin-help": "^5",
|
||||||
"@oclif/plugin-plugins": "^2.0.1"
|
"@oclif/plugin-plugins": "^2.0.1",
|
||||||
|
"dotenv": "^10.0.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@oclif/test": "^2",
|
"@oclif/test": "^2",
|
||||||
@@ -46,7 +49,7 @@
|
|||||||
"shx": "^0.3.3",
|
"shx": "^0.3.3",
|
||||||
"ts-node": "^10.2.1",
|
"ts-node": "^10.2.1",
|
||||||
"tslib": "^2.3.1",
|
"tslib": "^2.3.1",
|
||||||
"typescript": "^4.4.3"
|
"typescript": "^4.6.3"
|
||||||
},
|
},
|
||||||
"oclif": {
|
"oclif": {
|
||||||
"bin": "automatisch",
|
"bin": "automatisch",
|
||||||
|
@@ -1,9 +1,56 @@
|
|||||||
import { Command } from '@oclif/core';
|
import { Command, Flags } from '@oclif/core';
|
||||||
|
import * as dotenv from 'dotenv';
|
||||||
|
|
||||||
export default class Start extends Command {
|
export default class Start extends Command {
|
||||||
static description = 'Say hello world';
|
static description = 'Run automatisch';
|
||||||
|
|
||||||
|
static flags = {
|
||||||
|
env: Flags.string({
|
||||||
|
multiple: true,
|
||||||
|
char: 'e',
|
||||||
|
}),
|
||||||
|
'env-file': Flags.string(),
|
||||||
|
}
|
||||||
|
|
||||||
|
async prepareEnvVars(): Promise<void> {
|
||||||
|
const { flags } = await this.parse(Start);
|
||||||
|
|
||||||
|
if (flags['env-file']) {
|
||||||
|
dotenv.config({ path: flags['env-file'] });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flags.env) {
|
||||||
|
for (const env of flags.env) {
|
||||||
|
const [key, value] = env.split('=');
|
||||||
|
process.env[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
delete process.env.SERVE_WEB_APP_SEPARATELY;
|
||||||
|
}
|
||||||
|
|
||||||
|
async runMigrationsIfNeeded(): Promise<void> {
|
||||||
|
const database = (await import('@automatisch/backend/dist/src/config/database')).default;
|
||||||
|
const migrator = database.migrate;
|
||||||
|
|
||||||
|
const [, pendingMigrations] = await migrator.list();
|
||||||
|
const pendingMigrationsCount = pendingMigrations.length;
|
||||||
|
const needsToMigrate = pendingMigrationsCount > 0;
|
||||||
|
|
||||||
|
if (needsToMigrate) {
|
||||||
|
await migrator.latest();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async runApp(): Promise<void> {
|
||||||
|
await import('@automatisch/backend/dist/src/server');
|
||||||
|
}
|
||||||
|
|
||||||
async run(): Promise<void> {
|
async run(): Promise<void> {
|
||||||
this.log('hello world from start script');
|
await this.prepareEnvVars();
|
||||||
|
|
||||||
|
await this.runMigrationsIfNeeded();
|
||||||
|
|
||||||
|
await this.runApp();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -1,12 +1,17 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"declaration": true,
|
"declaration": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
"importHelpers": true,
|
"importHelpers": true,
|
||||||
|
"lib": ["es2021"],
|
||||||
"module": "commonjs",
|
"module": "commonjs",
|
||||||
|
"moduleResolution": "node",
|
||||||
"outDir": "dist",
|
"outDir": "dist",
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
|
"skipLibCheck": true,
|
||||||
"strict": true,
|
"strict": true,
|
||||||
"target": "es2019",
|
"target": "es2021",
|
||||||
|
"traceResolution": false,
|
||||||
"typeRoots": ["node_modules/@types", "./src/types"]
|
"typeRoots": ["node_modules/@types", "./src/types"]
|
||||||
},
|
},
|
||||||
"include": ["src/**/*"]
|
"include": ["src/**/*"]
|
||||||
|
@@ -35,7 +35,7 @@
|
|||||||
"slate": "^0.72.8",
|
"slate": "^0.72.8",
|
||||||
"slate-history": "^0.66.0",
|
"slate-history": "^0.66.0",
|
||||||
"slate-react": "^0.72.9",
|
"slate-react": "^0.72.9",
|
||||||
"typescript": "^4.1.2",
|
"typescript": "^4.6.3",
|
||||||
"web-vitals": "^1.0.1",
|
"web-vitals": "^1.0.1",
|
||||||
"yup": "^0.32.11"
|
"yup": "^0.32.11"
|
||||||
},
|
},
|
||||||
|
86
yarn.lock
86
yarn.lock
@@ -4198,6 +4198,14 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/bull@^3.15.8":
|
||||||
|
version "3.15.8"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/bull/-/bull-3.15.8.tgz#ae2139f94490d740b37c8da5d828ce75dd82ce7c"
|
||||||
|
integrity sha512-8DbSPMSsZH5PWPnGEkAZLYgJEH4ghHJNKF7LB6Wr5R0/v6g+Vs+JoaA7kcvLtHE936xg2WpFPkaoaJgExOmKDw==
|
||||||
|
dependencies:
|
||||||
|
"@types/ioredis" "*"
|
||||||
|
"@types/redis" "^2.8.0"
|
||||||
|
|
||||||
"@types/chai@*":
|
"@types/chai@*":
|
||||||
version "4.3.0"
|
version "4.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc"
|
resolved "https://registry.yarnpkg.com/@types/chai/-/chai-4.3.0.tgz#23509ebc1fa32f1b4d50d6a66c4032d5b8eaabdc"
|
||||||
@@ -4338,6 +4346,13 @@
|
|||||||
dependencies:
|
dependencies:
|
||||||
"@types/node" "*"
|
"@types/node" "*"
|
||||||
|
|
||||||
|
"@types/ioredis@*":
|
||||||
|
version "4.28.10"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/ioredis/-/ioredis-4.28.10.tgz#40ceb157a4141088d1394bb87c98ed09a75a06ff"
|
||||||
|
integrity sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/is-hotkey@^0.1.1":
|
"@types/is-hotkey@^0.1.1":
|
||||||
version "0.1.7"
|
version "0.1.7"
|
||||||
resolved "https://registry.yarnpkg.com/@types/is-hotkey/-/is-hotkey-0.1.7.tgz#30ec6d4234895230b576728ef77e70a52962f3b3"
|
resolved "https://registry.yarnpkg.com/@types/is-hotkey/-/is-hotkey-0.1.7.tgz#30ec6d4234895230b576728ef77e70a52962f3b3"
|
||||||
@@ -4593,6 +4608,13 @@
|
|||||||
"@types/scheduler" "*"
|
"@types/scheduler" "*"
|
||||||
csstype "^3.0.2"
|
csstype "^3.0.2"
|
||||||
|
|
||||||
|
"@types/redis@^2.8.0":
|
||||||
|
version "2.8.32"
|
||||||
|
resolved "https://registry.yarnpkg.com/@types/redis/-/redis-2.8.32.tgz#1d3430219afbee10f8cfa389dad2571a05ecfb11"
|
||||||
|
integrity sha512-7jkMKxcGq9p242exlbsVzuJb57KqHRhNl4dHoQu2Y5v9bCAbtIXXH0R3HleSQW4CTOqpHIYUW3t6tpUj4BVQ+w==
|
||||||
|
dependencies:
|
||||||
|
"@types/node" "*"
|
||||||
|
|
||||||
"@types/resolve@1.17.1":
|
"@types/resolve@1.17.1":
|
||||||
version "1.17.1"
|
version "1.17.1"
|
||||||
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
|
resolved "https://registry.yarnpkg.com/@types/resolve/-/resolve-1.17.1.tgz#3afd6ad8967c77e4376c598a82ddd58f46ec45d6"
|
||||||
@@ -7120,6 +7142,19 @@ copy-webpack-plugin@^9.0.1:
|
|||||||
schema-utils "^3.1.1"
|
schema-utils "^3.1.1"
|
||||||
serialize-javascript "^6.0.0"
|
serialize-javascript "^6.0.0"
|
||||||
|
|
||||||
|
copyfiles@^2.4.1:
|
||||||
|
version "2.4.1"
|
||||||
|
resolved "https://registry.yarnpkg.com/copyfiles/-/copyfiles-2.4.1.tgz#d2dcff60aaad1015f09d0b66e7f0f1c5cd3c5da5"
|
||||||
|
integrity sha512-fereAvAvxDrQDOXybk3Qu3dPbOoKoysFMWtkY3mv5BsL8//OSZVL5DCLYqgRfY5cWirgRzlC+WSrxp6Bo3eNZg==
|
||||||
|
dependencies:
|
||||||
|
glob "^7.0.5"
|
||||||
|
minimatch "^3.0.3"
|
||||||
|
mkdirp "^1.0.4"
|
||||||
|
noms "0.0.0"
|
||||||
|
through2 "^2.0.1"
|
||||||
|
untildify "^4.0.0"
|
||||||
|
yargs "^16.1.0"
|
||||||
|
|
||||||
core-js-compat@^3.20.0, core-js-compat@^3.20.2:
|
core-js-compat@^3.20.0, core-js-compat@^3.20.2:
|
||||||
version "3.20.3"
|
version "3.20.3"
|
||||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6"
|
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.3.tgz#d71f85f94eb5e4bea3407412e549daa083d23bd6"
|
||||||
@@ -9710,7 +9745,7 @@ glob-to-regexp@^0.4.1:
|
|||||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
||||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||||
|
|
||||||
glob@^7.0.0, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
glob@^7.0.0, glob@^7.0.5, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@^7.2.0:
|
||||||
version "7.2.0"
|
version "7.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023"
|
||||||
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==
|
||||||
@@ -10529,7 +10564,7 @@ inflight@^1.0.4:
|
|||||||
once "^1.3.0"
|
once "^1.3.0"
|
||||||
wrappy "1"
|
wrappy "1"
|
||||||
|
|
||||||
inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.3:
|
inherits@2, inherits@2.0.4, inherits@^2.0.0, inherits@^2.0.1, inherits@^2.0.3, inherits@^2.0.4, inherits@~2.0.1, inherits@~2.0.3:
|
||||||
version "2.0.4"
|
version "2.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.4.tgz#0fa2c64f932917c3433a0ded55363aae37416b7c"
|
||||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||||
@@ -12786,6 +12821,13 @@ minimatch@3.0.4, minimatch@^3.0.4:
|
|||||||
dependencies:
|
dependencies:
|
||||||
brace-expansion "^1.1.7"
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
|
minimatch@^3.0.3:
|
||||||
|
version "3.1.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-3.1.2.tgz#19cd194bfd3e428f049a70817c038d89ab4be35b"
|
||||||
|
integrity sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==
|
||||||
|
dependencies:
|
||||||
|
brace-expansion "^1.1.7"
|
||||||
|
|
||||||
minimatch@^5.0.0:
|
minimatch@^5.0.0:
|
||||||
version "5.0.1"
|
version "5.0.1"
|
||||||
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
|
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-5.0.1.tgz#fb9022f7528125187c92bd9e9b6366be1cf3415b"
|
||||||
@@ -13205,6 +13247,14 @@ nodemon@^2.0.13:
|
|||||||
undefsafe "^2.0.5"
|
undefsafe "^2.0.5"
|
||||||
update-notifier "^5.1.0"
|
update-notifier "^5.1.0"
|
||||||
|
|
||||||
|
noms@0.0.0:
|
||||||
|
version "0.0.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/noms/-/noms-0.0.0.tgz#da8ebd9f3af9d6760919b27d9cdc8092a7332859"
|
||||||
|
integrity sha1-2o69nzr51nYJGbJ9nNyAkqczKFk=
|
||||||
|
dependencies:
|
||||||
|
inherits "^2.0.1"
|
||||||
|
readable-stream "~1.0.31"
|
||||||
|
|
||||||
nopt@^4.0.1:
|
nopt@^4.0.1:
|
||||||
version "4.0.3"
|
version "4.0.3"
|
||||||
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
|
resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.3.tgz#a375cad9d02fd921278d954c2254d5aa57e15e48"
|
||||||
@@ -15803,6 +15853,16 @@ readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable
|
|||||||
string_decoder "~1.1.1"
|
string_decoder "~1.1.1"
|
||||||
util-deprecate "~1.0.1"
|
util-deprecate "~1.0.1"
|
||||||
|
|
||||||
|
readable-stream@~1.0.31:
|
||||||
|
version "1.0.34"
|
||||||
|
resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c"
|
||||||
|
integrity sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=
|
||||||
|
dependencies:
|
||||||
|
core-util-is "~1.0.0"
|
||||||
|
inherits "~2.0.1"
|
||||||
|
isarray "0.0.1"
|
||||||
|
string_decoder "~0.10.x"
|
||||||
|
|
||||||
readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0:
|
readdir-scoped-modules@^1.0.0, readdir-scoped-modules@^1.1.0:
|
||||||
version "1.1.0"
|
version "1.1.0"
|
||||||
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309"
|
resolved "https://registry.yarnpkg.com/readdir-scoped-modules/-/readdir-scoped-modules-1.1.0.tgz#8d45407b4f870a0dcaebc0e28670d18e74514309"
|
||||||
@@ -17223,6 +17283,11 @@ string_decoder@^1.1.1:
|
|||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "~5.2.0"
|
safe-buffer "~5.2.0"
|
||||||
|
|
||||||
|
string_decoder@~0.10.x:
|
||||||
|
version "0.10.31"
|
||||||
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94"
|
||||||
|
integrity sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=
|
||||||
|
|
||||||
string_decoder@~1.1.1:
|
string_decoder@~1.1.1:
|
||||||
version "1.1.1"
|
version "1.1.1"
|
||||||
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.1.1.tgz#9cf1611ba62685d7030ae9e4ba34149c3af03fc8"
|
||||||
@@ -17694,7 +17759,7 @@ throat@^6.0.1:
|
|||||||
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
|
resolved "https://registry.yarnpkg.com/throat/-/throat-6.0.1.tgz#d514fedad95740c12c2d7fc70ea863eb51ade375"
|
||||||
integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
|
integrity sha512-8hmiGIJMDlwjg7dlJ4yKGLK8EsYqKgPWbG3b4wjJddKNwc7N7Dpn08Df4szr/sZdMVeOstrdYSsqzX6BYbcB+w==
|
||||||
|
|
||||||
through2@^2.0.0:
|
through2@^2.0.0, through2@^2.0.1:
|
||||||
version "2.0.5"
|
version "2.0.5"
|
||||||
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd"
|
||||||
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==
|
||||||
@@ -18108,15 +18173,10 @@ typedarray@^0.0.6:
|
|||||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||||
|
|
||||||
typescript@^4.1.2:
|
typescript@^4.6.3:
|
||||||
version "4.5.4"
|
version "4.6.3"
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.4.tgz#a17d3a0263bf5c8723b9c52f43c5084edf13c2e8"
|
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.6.3.tgz#eefeafa6afdd31d725584c67a0eaba80f6fc6c6c"
|
||||||
integrity sha512-VgYs2A2QIRuGphtzFV7aQJduJ2gyfTljngLzjpfW9FoYZF6xuw1W0vW9ghCKLfcWrCFxK81CSGRAvS1pn4fIUg==
|
integrity sha512-yNIatDa5iaofVozS/uQJEl3JRWLKKGJKh6Yaiv0GLGSuhpFJe7P3SbHZ8/yjAHRQwKRoA6YZqlfjXWmVzoVSMw==
|
||||||
|
|
||||||
typescript@^4.4.3:
|
|
||||||
version "4.5.5"
|
|
||||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-4.5.5.tgz#d8c953832d28924a9e3d37c73d729c846c5896f3"
|
|
||||||
integrity sha512-TCTIul70LyWe6IJWT8QSYeA54WQe8EjQFU4wY52Fasj5UKx88LNYKCgBEHcOMOrFF1rKGbD8v/xcNWVUq9SymA==
|
|
||||||
|
|
||||||
ua-parser-js@^0.7.30:
|
ua-parser-js@^0.7.30:
|
||||||
version "0.7.31"
|
version "0.7.31"
|
||||||
@@ -19351,7 +19411,7 @@ yargs-parser@^20.2.2, yargs-parser@^20.2.3:
|
|||||||
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
|
||||||
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
integrity sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==
|
||||||
|
|
||||||
yargs@^16.2.0:
|
yargs@^16.1.0, yargs@^16.2.0:
|
||||||
version "16.2.0"
|
version "16.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
resolved "https://registry.yarnpkg.com/yargs/-/yargs-16.2.0.tgz#1c82bf0f6b6a66eafce7ef30e376f49a12477f66"
|
||||||
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
integrity sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==
|
||||||
|
Reference in New Issue
Block a user