diff --git a/packages/@n8n/benchmark/.eslintrc.js b/packages/@n8n/benchmark/.eslintrc.js new file mode 100644 index 0000000000..99c277f212 --- /dev/null +++ b/packages/@n8n/benchmark/.eslintrc.js @@ -0,0 +1,30 @@ +const sharedOptions = require('@n8n_io/eslint-config/shared'); + +/** + * @type {import('@types/eslint').ESLint.ConfigData} + */ +module.exports = { + extends: ['@n8n_io/eslint-config/node'], + + ...sharedOptions(__dirname), + + parserOptions: { + project: './tsconfig.json', + }, + + ignorePatterns: ['scenarios/**'], + + rules: { + 'n8n-local-rules/no-plain-errors': 'off', + complexity: 'error', + }, + + overrides: [ + { + files: ['./src/commands/*.ts'], + rules: { + 'import/no-default-export': 'off', + }, + }, + ], +}; diff --git a/packages/@n8n/benchmark/package.json b/packages/@n8n/benchmark/package.json index 3a7afb50a3..2a3ad1c214 100644 --- a/packages/@n8n/benchmark/package.json +++ b/packages/@n8n/benchmark/package.json @@ -5,6 +5,8 @@ "main": "dist/index", "scripts": { "build": "tsc -p tsconfig.build.json && tsc-alias -p tsconfig.build.json", + "lint": "eslint .", + "lintfix": "eslint . --fix", "start": "./bin/n8n-benchmark", "test": "echo \"Error: no test specified\" && exit 1", "typecheck": "tsc --noEmit", diff --git a/packages/@n8n/benchmark/src/n8nApiClient/authenticatedN8nApiClient.ts b/packages/@n8n/benchmark/src/n8nApiClient/authenticatedN8nApiClient.ts index 93cd767347..b7ebc6800d 100644 --- a/packages/@n8n/benchmark/src/n8nApiClient/authenticatedN8nApiClient.ts +++ b/packages/@n8n/benchmark/src/n8nApiClient/authenticatedN8nApiClient.ts @@ -1,6 +1,6 @@ import { strict as assert } from 'node:assert'; +import type { AxiosRequestConfig } from 'axios'; import { N8nApiClient } from './n8nApiClient'; -import { AxiosRequestConfig } from 'axios'; export class AuthenticatedN8nApiClient extends N8nApiClient { constructor( diff --git a/packages/@n8n/benchmark/src/n8nApiClient/n8nApiClient.ts b/packages/@n8n/benchmark/src/n8nApiClient/n8nApiClient.ts index 86ca52aff8..b1ec2d5162 100644 --- a/packages/@n8n/benchmark/src/n8nApiClient/n8nApiClient.ts +++ b/packages/@n8n/benchmark/src/n8nApiClient/n8nApiClient.ts @@ -1,4 +1,5 @@ -import axios, { AxiosError, AxiosRequestConfig } from 'axios'; +import type { AxiosError, AxiosRequestConfig } from 'axios'; +import axios from 'axios'; export class N8nApiClient { constructor(public readonly apiBaseUrl: string) {} @@ -11,7 +12,7 @@ export class N8nApiClient { while (Date.now() - START_TIME < TIMEOUT_MS) { try { - const response = await axios.request({ + const response = await axios.request<{ status: 'ok' }>({ url: `${this.apiBaseUrl}/${HEALTH_ENDPOINT}`, method: 'GET', }); @@ -72,7 +73,7 @@ export class N8nApiClient { return `${this.apiBaseUrl}/rest${endpoint}`; } - private delay(ms: number): Promise { - return new Promise((resolve) => setTimeout(resolve, ms)); + private async delay(ms: number): Promise { + return await new Promise((resolve) => setTimeout(resolve, ms)); } } diff --git a/packages/@n8n/benchmark/src/n8nApiClient/workflowsApiClient.ts b/packages/@n8n/benchmark/src/n8nApiClient/workflowsApiClient.ts index 18f2ecbcda..825303a111 100644 --- a/packages/@n8n/benchmark/src/n8nApiClient/workflowsApiClient.ts +++ b/packages/@n8n/benchmark/src/n8nApiClient/workflowsApiClient.ts @@ -1,5 +1,5 @@ -import { Workflow } from '@/n8nApiClient/n8nApiClient.types'; -import { AuthenticatedN8nApiClient } from './authenticatedN8nApiClient'; +import type { AuthenticatedN8nApiClient } from './authenticatedN8nApiClient'; +import type { Workflow } from '@/n8nApiClient/n8nApiClient.types'; export class WorkflowApiClient { constructor(private readonly apiClient: AuthenticatedN8nApiClient) {} diff --git a/packages/@n8n/benchmark/src/scenario/scenarioDataLoader.ts b/packages/@n8n/benchmark/src/scenario/scenarioDataLoader.ts index 43638a2e00..d59857cc62 100644 --- a/packages/@n8n/benchmark/src/scenario/scenarioDataLoader.ts +++ b/packages/@n8n/benchmark/src/scenario/scenarioDataLoader.ts @@ -1,7 +1,7 @@ -import fs from 'node:fs'; -import path from 'node:path'; -import { Scenario } from '@/types/scenario'; -import { Workflow } from '@/n8nApiClient/n8nApiClient.types'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import type { Scenario } from '@/types/scenario'; +import type { Workflow } from '@/n8nApiClient/n8nApiClient.types'; /** * Loads scenario data files from FS @@ -25,11 +25,10 @@ export class ScenarioDataFileLoader { const fileContent = fs.readFileSync(workflowFilePath, 'utf8'); try { - return JSON.parse(fileContent); + return JSON.parse(fileContent) as Workflow; } catch (error) { - throw new Error( - `Failed to parse workflow file ${workflowFilePath}: ${error instanceof Error ? error.message : error}`, - ); + const e = error as Error; + throw new Error(`Failed to parse workflow file ${workflowFilePath}: ${e.message}`); } } } diff --git a/packages/@n8n/benchmark/src/scenario/scenarioLoader.ts b/packages/@n8n/benchmark/src/scenario/scenarioLoader.ts index 475ce495ac..13cc52daf0 100644 --- a/packages/@n8n/benchmark/src/scenario/scenarioLoader.ts +++ b/packages/@n8n/benchmark/src/scenario/scenarioLoader.ts @@ -48,7 +48,11 @@ export class ScenarioLoader { private loadAndValidateScenarioManifest( scenarioManifestPath: string, ): [ScenarioManifest, null] | [null, string[]] { - const scenario = JSON.parse(fs.readFileSync(scenarioManifestPath, 'utf8')); + const [scenario, error] = this.loadScenarioManifest(scenarioManifestPath); + if (!scenario) { + return [null, [error]]; + } + const validationErrors: string[] = []; if (!scenario.name) { @@ -61,6 +65,21 @@ export class ScenarioLoader { return validationErrors.length === 0 ? [scenario, null] : [null, validationErrors]; } + private loadScenarioManifest( + scenarioManifestPath: string, + ): [ScenarioManifest, null] | [null, string] { + try { + const scenario = JSON.parse( + fs.readFileSync(scenarioManifestPath, 'utf8'), + ) as ScenarioManifest; + + return [scenario, null]; + } catch (error) { + const message = error instanceof Error ? error.message : JSON.stringify(error); + return [null, `Failed to parse manifest ${scenarioManifestPath}: ${message}`]; + } + } + private formScenarioId(scenarioPath: string): string { return createHash('sha256').update(scenarioPath).digest('hex'); } diff --git a/packages/@n8n/benchmark/src/testExecution/k6Executor.ts b/packages/@n8n/benchmark/src/testExecution/k6Executor.ts index 49386993a5..176f1c15bb 100644 --- a/packages/@n8n/benchmark/src/testExecution/k6Executor.ts +++ b/packages/@n8n/benchmark/src/testExecution/k6Executor.ts @@ -1,5 +1,5 @@ import { $, which } from 'zx'; -import { Scenario } from '@/types/scenario'; +import type { Scenario } from '@/types/scenario'; /** * Executes test scenarios using k6 @@ -24,7 +24,7 @@ export class K6Executor { })`${k6ExecutablePath} run --quiet --stage ${stage} ${scenario.scriptPath}`; for await (const chunk of processPromise.stdout) { - console.log(chunk.toString()); + console.log((chunk as Buffer).toString()); } } diff --git a/packages/@n8n/benchmark/src/testExecution/scenarioDataImporter.ts b/packages/@n8n/benchmark/src/testExecution/scenarioDataImporter.ts index 1c1ec3777e..25fc4cf875 100644 --- a/packages/@n8n/benchmark/src/testExecution/scenarioDataImporter.ts +++ b/packages/@n8n/benchmark/src/testExecution/scenarioDataImporter.ts @@ -1,5 +1,5 @@ -import { AuthenticatedN8nApiClient } from '@/n8nApiClient/authenticatedN8nApiClient'; -import { Workflow } from '@/n8nApiClient/n8nApiClient.types'; +import type { AuthenticatedN8nApiClient } from '@/n8nApiClient/authenticatedN8nApiClient'; +import type { Workflow } from '@/n8nApiClient/n8nApiClient.types'; import { WorkflowApiClient } from '@/n8nApiClient/workflowsApiClient'; /** diff --git a/packages/@n8n/benchmark/src/testExecution/scenarioRunner.ts b/packages/@n8n/benchmark/src/testExecution/scenarioRunner.ts index 83e1077680..e9a9dfc396 100644 --- a/packages/@n8n/benchmark/src/testExecution/scenarioRunner.ts +++ b/packages/@n8n/benchmark/src/testExecution/scenarioRunner.ts @@ -1,7 +1,7 @@ -import { Scenario } from '@/types/scenario'; -import { N8nApiClient } from '@/n8nApiClient/n8nApiClient'; -import { ScenarioDataFileLoader } from '@/scenario/scenarioDataLoader'; -import { K6Executor } from './k6Executor'; +import type { K6Executor } from './k6Executor'; +import type { Scenario } from '@/types/scenario'; +import type { N8nApiClient } from '@/n8nApiClient/n8nApiClient'; +import type { ScenarioDataFileLoader } from '@/scenario/scenarioDataLoader'; import { ScenarioDataImporter } from '@/testExecution/scenarioDataImporter'; import { AuthenticatedN8nApiClient } from '@/n8nApiClient/authenticatedN8nApiClient';