From 99dc56c7a193cbbb7ba42f5d95374f0490639347 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 30 Jul 2024 10:20:21 +0200 Subject: [PATCH 01/50] refactor(core): Make instance role clearer (no-changelog) (#10188) --- packages/cli/src/License.ts | 2 +- packages/cli/src/commands/start.ts | 7 ++----- packages/cli/src/config/schema.ts | 11 ++++++----- .../__tests__/execution-recovery.service.test.ts | 4 ++-- .../cli/src/executions/execution-recovery.service.ts | 2 +- packages/cli/src/services/orchestration.service.ts | 9 +++------ .../services/orchestration/main/MultiMainSetup.ee.ts | 12 ++++++------ packages/cli/src/services/pruning.service.ts | 2 +- .../test/unit/services/orchestration.service.test.ts | 4 ++-- 9 files changed, 24 insertions(+), 29 deletions(-) diff --git a/packages/cli/src/License.ts b/packages/cli/src/License.ts index 25abf0ecd6829..9a2740ce7cbb7 100644 --- a/packages/cli/src/License.ts +++ b/packages/cli/src/License.ts @@ -55,7 +55,7 @@ export class License { * This ensures the mains do not cause a 429 (too many requests) on license init. */ if (config.getEnv('multiMainSetup.enabled')) { - return autoRenewEnabled && config.getEnv('multiMainSetup.instanceType') === 'leader'; + return autoRenewEnabled && config.getEnv('instanceRole') === 'leader'; } return autoRenewEnabled; diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index 2e88c62e06194..c35344326e8e7 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -184,10 +184,7 @@ export class Start extends BaseCommand { await this.initOrchestration(); this.logger.debug('Orchestration init complete'); - if ( - !config.getEnv('license.autoRenewEnabled') && - config.getEnv('multiMainSetup.instanceType') === 'leader' - ) { + if (!config.getEnv('license.autoRenewEnabled') && config.getEnv('instanceRole') === 'leader') { this.logger.warn( 'Automatic license renewal is disabled. The license will not renew automatically, and access to licensed features may be lost!', ); @@ -211,7 +208,7 @@ export class Start extends BaseCommand { async initOrchestration() { if (config.getEnv('executions.mode') === 'regular') { - config.set('multiMainSetup.instanceType', 'leader'); + config.set('instanceRole', 'leader'); return; } diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index 1e6d00aed7699..0b689b8f4cfbe 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -883,12 +883,13 @@ export const schema = { }, }, + instanceRole: { + doc: 'Always `leader` in single-main setup. `leader` or `follower` in multi-main setup.', + format: ['unset', 'leader', 'follower'] as const, + default: 'unset', // only until Start.initOrchestration + }, + multiMainSetup: { - instanceType: { - doc: 'Type of instance in multi-main setup', - format: ['unset', 'leader', 'follower'] as const, - default: 'unset', // only until first leader key check - }, enabled: { doc: 'Whether to enable multi-main setup for queue mode (license required)', format: Boolean, diff --git a/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts b/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts index c1ad38538e337..7e33c6ac74a8a 100644 --- a/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts +++ b/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts @@ -48,7 +48,7 @@ describe('ExecutionRecoveryService', () => { }); beforeEach(() => { - config.set('multiMainSetup.instanceType', 'leader'); + config.set('instanceRole', 'leader'); }); afterEach(async () => { @@ -130,7 +130,7 @@ describe('ExecutionRecoveryService', () => { /** * Arrange */ - config.set('multiMainSetup.instanceType', 'follower'); + config.set('instanceRole', 'follower'); // @ts-expect-error Private method const amendSpy = jest.spyOn(executionRecoveryService, 'amend'); const messages = setupMessages('123', 'Some workflow'); diff --git a/packages/cli/src/executions/execution-recovery.service.ts b/packages/cli/src/executions/execution-recovery.service.ts index 4b4d90de54ad4..05431a6d9c9d5 100644 --- a/packages/cli/src/executions/execution-recovery.service.ts +++ b/packages/cli/src/executions/execution-recovery.service.ts @@ -332,7 +332,7 @@ export class ExecutionRecoveryService { private shouldScheduleQueueRecovery() { return ( config.getEnv('executions.mode') === 'queue' && - config.getEnv('multiMainSetup.instanceType') === 'leader' && + config.getEnv('instanceRole') === 'leader' && !this.isShuttingDown ); } diff --git a/packages/cli/src/services/orchestration.service.ts b/packages/cli/src/services/orchestration.service.ts index 3d74f187acced..bfc1d140fc6fa 100644 --- a/packages/cli/src/services/orchestration.service.ts +++ b/packages/cli/src/services/orchestration.service.ts @@ -43,15 +43,12 @@ export class OrchestrationService { return config.getEnv('redis.queueModeId'); } - /** - * Whether this instance is the leader in a multi-main setup. Always `false` in single-main setup. - */ get isLeader() { - return config.getEnv('multiMainSetup.instanceType') === 'leader'; + return config.getEnv('instanceRole') === 'leader'; } get isFollower() { - return config.getEnv('multiMainSetup.instanceType') !== 'leader'; + return config.getEnv('instanceRole') !== 'leader'; } sanityCheck() { @@ -66,7 +63,7 @@ export class OrchestrationService { if (this.isMultiMainSetupEnabled) { await this.multiMainSetup.init(); } else { - config.set('multiMainSetup.instanceType', 'leader'); + config.set('instanceRole', 'leader'); } this.isInitialized = true; diff --git a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts index 19cd14c4a87b9..3c8c052da70ae 100644 --- a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts +++ b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts @@ -45,7 +45,7 @@ export class MultiMainSetup extends EventEmitter { async shutdown() { clearInterval(this.leaderCheckInterval); - const isLeader = config.getEnv('multiMainSetup.instanceType') === 'leader'; + const isLeader = config.getEnv('instanceRole') === 'leader'; if (isLeader) await this.redisPublisher.clear(this.leaderKey); } @@ -64,8 +64,8 @@ export class MultiMainSetup extends EventEmitter { if (leaderId && leaderId !== this.instanceId) { this.logger.debug(`[Instance ID ${this.instanceId}] Leader is other instance "${leaderId}"`); - if (config.getEnv('multiMainSetup.instanceType') === 'leader') { - config.set('multiMainSetup.instanceType', 'follower'); + if (config.getEnv('instanceRole') === 'leader') { + config.set('instanceRole', 'follower'); this.emit('leader-stepdown'); // lost leadership - stop triggers, pollers, pruning, wait-tracking, queue recovery @@ -80,7 +80,7 @@ export class MultiMainSetup extends EventEmitter { `[Instance ID ${this.instanceId}] Leadership vacant, attempting to become leader...`, ); - config.set('multiMainSetup.instanceType', 'follower'); + config.set('instanceRole', 'follower'); /** * Lost leadership - stop triggers, pollers, pruning, wait tracking, license renewal, queue recovery @@ -101,7 +101,7 @@ export class MultiMainSetup extends EventEmitter { if (keySetSuccessfully) { this.logger.debug(`[Instance ID ${this.instanceId}] Leader is now this instance`); - config.set('multiMainSetup.instanceType', 'leader'); + config.set('instanceRole', 'leader'); await this.redisPublisher.setExpiration(this.leaderKey, this.leaderKeyTtl); @@ -110,7 +110,7 @@ export class MultiMainSetup extends EventEmitter { */ this.emit('leader-takeover'); } else { - config.set('multiMainSetup.instanceType', 'follower'); + config.set('instanceRole', 'follower'); } } diff --git a/packages/cli/src/services/pruning.service.ts b/packages/cli/src/services/pruning.service.ts index daa0883e1f861..78c0aad03750c 100644 --- a/packages/cli/src/services/pruning.service.ts +++ b/packages/cli/src/services/pruning.service.ts @@ -56,7 +56,7 @@ export class PruningService { if ( config.getEnv('multiMainSetup.enabled') && config.getEnv('generic.instanceType') === 'main' && - config.getEnv('multiMainSetup.instanceType') === 'follower' + config.getEnv('instanceRole') === 'follower' ) { return false; } diff --git a/packages/cli/test/unit/services/orchestration.service.test.ts b/packages/cli/test/unit/services/orchestration.service.test.ts index 43aba8d4843c7..0cdd73f605963 100644 --- a/packages/cli/test/unit/services/orchestration.service.test.ts +++ b/packages/cli/test/unit/services/orchestration.service.test.ts @@ -146,7 +146,7 @@ describe('Orchestration Service', () => { describe('shouldAddWebhooks', () => { beforeEach(() => { - config.set('multiMainSetup.instanceType', 'leader'); + config.set('instanceRole', 'leader'); }); test('should return true for init', () => { // We want to ensure that webhooks are populated on init @@ -169,7 +169,7 @@ describe('Orchestration Service', () => { }); test('should return false for update or activate when not leader', () => { - config.set('multiMainSetup.instanceType', 'follower'); + config.set('instanceRole', 'follower'); const modes = ['update', 'activate'] as WorkflowActivateMode[]; for (const mode of modes) { const result = os.shouldAddWebhooks(mode); From 1fca3af335366b932e39da3fccea0b76a37d1c21 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Tue, 30 Jul 2024 13:23:01 +0200 Subject: [PATCH 02/50] refactor(core): Use type-safe event emitters (no-changelog) (#10234) --- packages/cli/src/TypedEmitter.ts | 48 ++++++++++++++ packages/cli/src/WebhookHelpers.ts | 9 ++- .../cli/src/WorkflowExecuteAdditionalData.ts | 18 +++-- .../concurrency-control.service.ts | 6 +- .../cli/src/concurrency/concurrency-queue.ts | 16 ++--- packages/cli/src/eventbus/event.service.ts | 14 +--- packages/cli/src/push/index.ts | 9 ++- .../cli/src/services/cache/cache.service.ts | 16 ++--- .../cli/src/services/cache/cache.types.ts | 2 - .../orchestration/main/MultiMainSetup.ee.ts | 9 ++- .../services/workflow-statistics.service.ts | 65 +++++++++---------- .../workflow-statistics.service.test.ts | 1 + 12 files changed, 124 insertions(+), 89 deletions(-) create mode 100644 packages/cli/src/TypedEmitter.ts diff --git a/packages/cli/src/TypedEmitter.ts b/packages/cli/src/TypedEmitter.ts new file mode 100644 index 0000000000000..176aa9584c907 --- /dev/null +++ b/packages/cli/src/TypedEmitter.ts @@ -0,0 +1,48 @@ +import { EventEmitter } from 'node:events'; +import debounce from 'lodash/debounce'; + +type Payloads = { + [E in keyof ListenerMap]: unknown; +}; + +type Listener = (payload: Payload) => void; + +export class TypedEmitter> extends EventEmitter { + private debounceWait = 300; // milliseconds + + override on( + eventName: EventName, + listener: Listener, + ) { + return super.on(eventName, listener); + } + + override once( + eventName: EventName, + listener: Listener, + ) { + return super.once(eventName, listener); + } + + override off( + eventName: EventName, + listener: Listener, + ) { + return super.off(eventName, listener); + } + + override emit( + eventName: EventName, + payload?: ListenerMap[EventName], + ): boolean { + return super.emit(eventName, payload); + } + + protected debouncedEmit = debounce( + ( + eventName: EventName, + payload?: ListenerMap[EventName], + ) => super.emit(eventName, payload), + this.debounceWait, + ); +} diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index 95e8825e1a840..9342a97cda69c 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -360,11 +360,10 @@ export async function executeWebhook( NodeExecuteFunctions, executionMode, ); - Container.get(WorkflowStatisticsService).emit( - 'nodeFetchedData', - workflow.id, - workflowStartNode, - ); + Container.get(WorkflowStatisticsService).emit('nodeFetchedData', { + workflowId: workflow.id, + node: workflowStartNode, + }); } catch (err) { // Send error response to webhook caller const errorMessage = 'Workflow Webhook Error: Workflow could not be started!'; diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index e5a17de7ec26d..754cfea693c3d 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -525,17 +525,16 @@ function hookFunctionsSave(): IWorkflowExecuteHooks { ); } } finally { - workflowStatisticsService.emit( - 'workflowExecutionCompleted', - this.workflowData, + workflowStatisticsService.emit('workflowExecutionCompleted', { + workflowData: this.workflowData, fullRunData, - ); + }); } }, ], nodeFetchedData: [ async (workflowId: string, node: INode) => { - workflowStatisticsService.emit('nodeFetchedData', workflowId, node); + workflowStatisticsService.emit('nodeFetchedData', { workflowId, node }); }, ], }; @@ -636,11 +635,10 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { this.retryOf, ); } finally { - workflowStatisticsService.emit( - 'workflowExecutionCompleted', - this.workflowData, + workflowStatisticsService.emit('workflowExecutionCompleted', { + workflowData: this.workflowData, fullRunData, - ); + }); } }, async function (this: WorkflowHooks, runData: IRun): Promise { @@ -676,7 +674,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { ], nodeFetchedData: [ async (workflowId: string, node: INode) => { - workflowStatisticsService.emit('nodeFetchedData', workflowId, node); + workflowStatisticsService.emit('nodeFetchedData', { workflowId, node }); }, ], }; diff --git a/packages/cli/src/concurrency/concurrency-control.service.ts b/packages/cli/src/concurrency/concurrency-control.service.ts index cf249c51722eb..c562448379fef 100644 --- a/packages/cli/src/concurrency/concurrency-control.service.ts +++ b/packages/cli/src/concurrency/concurrency-control.service.ts @@ -53,7 +53,7 @@ export class ConcurrencyControlService { this.isEnabled = true; - this.productionQueue.on('concurrency-check', ({ capacity }: { capacity: number }) => { + this.productionQueue.on('concurrency-check', ({ capacity }) => { if (this.shouldReport(capacity)) { void this.telemetry.track('User hit concurrency limit', { threshold: CLOUD_TEMP_PRODUCTION_LIMIT - capacity, @@ -61,12 +61,12 @@ export class ConcurrencyControlService { } }); - this.productionQueue.on('execution-throttled', ({ executionId }: { executionId: string }) => { + this.productionQueue.on('execution-throttled', ({ executionId }) => { this.log('Execution throttled', { executionId }); this.eventService.emit('execution-throttled', { executionId }); }); - this.productionQueue.on('execution-released', async (executionId: string) => { + this.productionQueue.on('execution-released', async (executionId) => { this.log('Execution released', { executionId }); await this.executionRepository.resetStartedAt(executionId); }); diff --git a/packages/cli/src/concurrency/concurrency-queue.ts b/packages/cli/src/concurrency/concurrency-queue.ts index 90c62d2efc21e..1b578b5a8ef1d 100644 --- a/packages/cli/src/concurrency/concurrency-queue.ts +++ b/packages/cli/src/concurrency/concurrency-queue.ts @@ -1,9 +1,14 @@ import { Service } from 'typedi'; -import { EventEmitter } from 'node:events'; -import debounce from 'lodash/debounce'; +import { TypedEmitter } from '@/TypedEmitter'; + +type ConcurrencyEvents = { + 'execution-throttled': { executionId: string }; + 'execution-released': string; + 'concurrency-check': { capacity: number }; +}; @Service() -export class ConcurrencyQueue extends EventEmitter { +export class ConcurrencyQueue extends TypedEmitter { private readonly queue: Array<{ executionId: string; resolve: () => void; @@ -63,9 +68,4 @@ export class ConcurrencyQueue extends EventEmitter { resolve(); } - - private debouncedEmit = debounce( - (event: string, payload: object) => this.emit(event, payload), - 300, - ); } diff --git a/packages/cli/src/eventbus/event.service.ts b/packages/cli/src/eventbus/event.service.ts index 2df51af22cad1..2b16ff06ab40c 100644 --- a/packages/cli/src/eventbus/event.service.ts +++ b/packages/cli/src/eventbus/event.service.ts @@ -1,16 +1,6 @@ -import { EventEmitter } from 'node:events'; import { Service } from 'typedi'; +import { TypedEmitter } from '@/TypedEmitter'; import type { Event } from './event.types'; @Service() -export class EventService extends EventEmitter { - emit(eventName: K, arg?: Event[K]) { - super.emit(eventName, arg); - return true; - } - - on(eventName: K, handler: (arg: Event[K]) => void) { - super.on(eventName, handler); - return this; - } -} +export class EventService extends TypedEmitter {} diff --git a/packages/cli/src/push/index.ts b/packages/cli/src/push/index.ts index 647ab40acf0c5..a946348430133 100644 --- a/packages/cli/src/push/index.ts +++ b/packages/cli/src/push/index.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'events'; import { ServerResponse } from 'http'; import type { Server } from 'http'; import type { Socket } from 'net'; @@ -17,6 +16,11 @@ import { OrchestrationService } from '@/services/orchestration.service'; import { SSEPush } from './sse.push'; import { WebSocketPush } from './websocket.push'; import type { PushResponse, SSEPushRequest, WebSocketPushRequest } from './types'; +import { TypedEmitter } from '@/TypedEmitter'; + +type PushEvents = { + editorUiConnected: string; +}; const useWebSockets = config.getEnv('push.backend') === 'websocket'; @@ -28,7 +32,7 @@ const useWebSockets = config.getEnv('push.backend') === 'websocket'; * @emits message when a message is received from a client */ @Service() -export class Push extends EventEmitter { +export class Push extends TypedEmitter { private backend = useWebSockets ? Container.get(WebSocketPush) : Container.get(SSEPush); constructor(private readonly orchestrationService: OrchestrationService) { @@ -37,7 +41,6 @@ export class Push extends EventEmitter { handleRequest(req: SSEPushRequest | WebSocketPushRequest, res: PushResponse) { const { - user, ws, query: { pushRef }, } = req; diff --git a/packages/cli/src/services/cache/cache.service.ts b/packages/cli/src/services/cache/cache.service.ts index 8e9a4dc95c009..75dad03b49d26 100644 --- a/packages/cli/src/services/cache/cache.service.ts +++ b/packages/cli/src/services/cache/cache.service.ts @@ -1,5 +1,3 @@ -import EventEmitter from 'node:events'; - import Container, { Service } from 'typedi'; import { caching } from 'cache-manager'; import { ApplicationError, jsonStringify } from 'n8n-workflow'; @@ -10,14 +8,20 @@ import { MalformedRefreshValueError } from '@/errors/cache-errors/malformed-refr import type { TaggedRedisCache, TaggedMemoryCache, - CacheEvent, MaybeHash, Hash, } from '@/services/cache/cache.types'; import { TIME } from '@/constants'; +import { TypedEmitter } from '@/TypedEmitter'; + +type CacheEvents = { + 'metrics.cache.hit': never; + 'metrics.cache.miss': never; + 'metrics.cache.update': never; +}; @Service() -export class CacheService extends EventEmitter { +export class CacheService extends TypedEmitter { private cache: TaggedRedisCache | TaggedMemoryCache; async init() { @@ -66,10 +70,6 @@ export class CacheService extends EventEmitter { await this.cache.store.reset(); } - emit(event: CacheEvent, ...args: unknown[]) { - return super.emit(event, ...args); - } - isRedis() { return this.cache.kind === 'redis'; } diff --git a/packages/cli/src/services/cache/cache.types.ts b/packages/cli/src/services/cache/cache.types.ts index 4e96b8012a799..f598e22494446 100644 --- a/packages/cli/src/services/cache/cache.types.ts +++ b/packages/cli/src/services/cache/cache.types.ts @@ -8,5 +8,3 @@ export type TaggedMemoryCache = MemoryCache & { kind: 'memory' }; export type Hash = Record; export type MaybeHash = Hash | undefined; - -export type CacheEvent = `metrics.cache.${'hit' | 'miss' | 'update'}`; diff --git a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts index 3c8c052da70ae..cabcf5996bc08 100644 --- a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts +++ b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts @@ -1,4 +1,3 @@ -import { EventEmitter } from 'node:events'; import config from '@/config'; import { Service } from 'typedi'; import { TIME } from '@/constants'; @@ -6,9 +5,15 @@ import { ErrorReporterProxy as EventReporter } from 'n8n-workflow'; import { Logger } from '@/Logger'; import { RedisServicePubSubPublisher } from '@/services/redis/RedisServicePubSubPublisher'; import { RedisClientService } from '@/services/redis/redis-client.service'; +import { TypedEmitter } from '@/TypedEmitter'; + +type MultiMainEvents = { + 'leader-stepdown': never; + 'leader-takeover': never; +}; @Service() -export class MultiMainSetup extends EventEmitter { +export class MultiMainSetup extends TypedEmitter { constructor( private readonly logger: Logger, private readonly redisPublisher: RedisServicePubSubPublisher, diff --git a/packages/cli/src/services/workflow-statistics.service.ts b/packages/cli/src/services/workflow-statistics.service.ts index 516732add87b4..9311ef885dfbd 100644 --- a/packages/cli/src/services/workflow-statistics.service.ts +++ b/packages/cli/src/services/workflow-statistics.service.ts @@ -1,29 +1,48 @@ -import { EventEmitter } from 'events'; -import { Container, Service } from 'typedi'; +import { Service } from 'typedi'; import type { INode, IRun, IWorkflowBase } from 'n8n-workflow'; import { StatisticsNames } from '@db/entities/WorkflowStatistics'; import { WorkflowStatisticsRepository } from '@db/repositories/workflowStatistics.repository'; import { UserService } from '@/services/user.service'; import { Logger } from '@/Logger'; import { OwnershipService } from './ownership.service'; +import { TypedEmitter } from '@/TypedEmitter'; + +type WorkflowStatisticsEvents = { + nodeFetchedData: { workflowId: string; node: INode }; + workflowExecutionCompleted: { workflowData: IWorkflowBase; fullRunData: IRun }; + 'telemetry.onFirstProductionWorkflowSuccess': { + project_id: string; + workflow_id: string; + user_id: string; + }; + 'telemetry.onFirstWorkflowDataLoad': { + user_id: string; + project_id: string; + workflow_id: string; + node_type: string; + node_id: string; + }; +}; @Service() -export class WorkflowStatisticsService extends EventEmitter { +export class WorkflowStatisticsService extends TypedEmitter { constructor( private readonly logger: Logger, private readonly repository: WorkflowStatisticsRepository, private readonly ownershipService: OwnershipService, + private readonly userService: UserService, ) { super({ captureRejections: true }); if ('SKIP_STATISTICS_EVENTS' in process.env) return; this.on( 'nodeFetchedData', - async (workflowId, node) => await this.nodeFetchedData(workflowId, node), + async ({ workflowId, node }) => await this.nodeFetchedData(workflowId, node), ); this.on( 'workflowExecutionCompleted', - async (workflowData, runData) => await this.workflowExecutionCompleted(workflowData, runData), + async ({ workflowData, fullRunData }) => + await this.workflowExecutionCompleted(workflowData, fullRunData), ); } @@ -49,18 +68,18 @@ export class WorkflowStatisticsService extends EventEmitter { const upsertResult = await this.repository.upsertWorkflowStatistics(name, workflowId); if (name === StatisticsNames.productionSuccess && upsertResult === 'insert') { - const project = await Container.get(OwnershipService).getWorkflowProjectCached(workflowId); + const project = await this.ownershipService.getWorkflowProjectCached(workflowId); if (project.type === 'personal') { - const owner = await Container.get(OwnershipService).getProjectOwnerCached(project.id); + const owner = await this.ownershipService.getProjectOwnerCached(project.id); const metrics = { project_id: project.id, workflow_id: workflowId, - user_id: owner?.id, + user_id: owner!.id, }; if (owner && !owner.settings?.userActivated) { - await Container.get(UserService).updateSettings(owner.id, { + await this.userService.updateSettings(owner.id, { firstSuccessfulWorkflowId: workflowId, userActivated: true, userActivatedAt: runData.startedAt.getTime(), @@ -90,7 +109,7 @@ export class WorkflowStatisticsService extends EventEmitter { const owner = await this.ownershipService.getProjectOwnerCached(project.id); let metrics = { - user_id: owner?.id, + user_id: owner!.id, project_id: project.id, workflow_id: workflowId, node_type: node.type, @@ -111,29 +130,3 @@ export class WorkflowStatisticsService extends EventEmitter { this.emit('telemetry.onFirstWorkflowDataLoad', metrics); } } - -export declare interface WorkflowStatisticsService { - on( - event: 'nodeFetchedData', - listener: (workflowId: string | undefined | null, node: INode) => void, - ): this; - on( - event: 'workflowExecutionCompleted', - listener: (workflowData: IWorkflowBase, runData: IRun) => void, - ): this; - on( - event: 'telemetry.onFirstProductionWorkflowSuccess', - listener: (metrics: { user_id: string; workflow_id: string }) => void, - ): this; - on( - event: 'telemetry.onFirstWorkflowDataLoad', - listener: (metrics: { - user_id: string; - workflow_id: string; - node_type: string; - node_id: string; - credential_type?: string; - credential_id?: string; - }) => void, - ): this; -} diff --git a/packages/cli/test/unit/services/workflow-statistics.service.test.ts b/packages/cli/test/unit/services/workflow-statistics.service.test.ts index da1338f8eb764..9bd9864bb0446 100644 --- a/packages/cli/test/unit/services/workflow-statistics.service.test.ts +++ b/packages/cli/test/unit/services/workflow-statistics.service.test.ts @@ -48,6 +48,7 @@ describe('WorkflowStatisticsService', () => { mock(), new WorkflowStatisticsRepository(dataSource, globalConfig), ownershipService, + userService, ); const onFirstProductionWorkflowSuccess = jest.fn(); From a2d08846d0b9ff342a54d0ba6ec283aaa3a0b7aa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Tue, 30 Jul 2024 13:27:15 +0200 Subject: [PATCH 03/50] ci: Setup pnpm catalog to centralize depedency versions (no-changelog) (#9974) --- cypress/package.json | 6 +- package.json | 13 +- packages/@n8n/chat/package.json | 10 +- packages/@n8n/client-oauth2/package.json | 2 +- packages/@n8n/config/package.json | 2 +- packages/@n8n/nodes-langchain/package.json | 8 +- packages/cli/package.json | 24 +- packages/core/package.json | 24 +- packages/design-system/package.json | 9 +- packages/editor-ui/package.json | 17 +- packages/node-dev/package.json | 4 +- packages/nodes-base/package.json | 24 +- packages/workflow/package.json | 16 +- pnpm-lock.yaml | 631 +++++++++++---------- pnpm-workspace.yaml | 26 + 15 files changed, 446 insertions(+), 370 deletions(-) diff --git a/cypress/package.json b/cypress/package.json index 7740b5483f01b..0e1ce734c02cf 100644 --- a/cypress/package.json +++ b/cypress/package.json @@ -14,7 +14,7 @@ "start": "cd ..; pnpm start" }, "devDependencies": { - "@types/lodash": "^4.14.195", + "@types/lodash": "catalog:", "eslint-plugin-cypress": "^3.3.0", "n8n-workflow": "workspace:*" }, @@ -24,8 +24,8 @@ "cypress": "^13.11.0", "cypress-otp": "^1.0.3", "cypress-real-events": "^1.12.0", - "lodash": "4.17.21", - "nanoid": "3.3.6", + "lodash": "catalog:", + "nanoid": "catalog:", "start-server-and-test": "^2.0.3" } } diff --git a/package.json b/package.json index 59bae47e99169..0f32e0ea56dde 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,10 @@ "version": "1.52.0", "private": true, "engines": { - "node": ">=18.10", - "pnpm": ">=9.1" + "node": ">=20.15", + "pnpm": ">=9.5" }, - "packageManager": "pnpm@9.1.4", + "packageManager": "pnpm@9.6.0", "scripts": { "preinstall": "node scripts/block-npm-install.js", "build": "turbo run build", @@ -40,7 +40,6 @@ "@n8n_io/eslint-config": "workspace:*", "@types/jest": "^29.5.3", "@types/supertest": "^6.0.2", - "@vitest/coverage-v8": "^1.6.0", "jest": "^29.6.2", "jest-environment-jsdom": "^29.6.2", "jest-expect-message": "^1.1.3", @@ -56,11 +55,7 @@ "tsc-alias": "^1.8.7", "tsc-watch": "^6.0.4", "turbo": "2.0.6", - "typescript": "*", - "vite": "^5.2.12", - "vitest": "^1.6.0", - "vitest-mock-extended": "^1.3.1", - "vue-tsc": "^2.0.19" + "typescript": "*" }, "pnpm": { "onlyBuiltDependencies": [ diff --git a/packages/@n8n/chat/package.json b/packages/@n8n/chat/package.json index 25b9212b3eb79..4f425b6fa8237 100644 --- a/packages/@n8n/chat/package.json +++ b/packages/@n8n/chat/package.json @@ -38,16 +38,20 @@ "@vueuse/core": "^10.11.0", "highlight.js": "^11.8.0", "markdown-it-link-attributes": "^4.0.1", - "uuid": "^8.3.2", - "vue": "^3.4.21", + "uuid": "catalog:", + "vue": "catalog:frontend", "vue-markdown-render": "^2.1.1" }, "devDependencies": { "@iconify-json/mdi": "^1.1.54", "@n8n/storybook": "workspace:*", "@types/markdown-it": "^12.2.3", + "@vitest/coverage-v8": "catalog:frontend", "unplugin-icons": "^0.19.0", - "vite-plugin-dts": "^3.9.1" + "vite": "catalog:frontend", + "vitest": "catalog:frontend", + "vite-plugin-dts": "^3.9.1", + "vue-tsc": "catalog:frontend" }, "files": [ "README.md", diff --git a/packages/@n8n/client-oauth2/package.json b/packages/@n8n/client-oauth2/package.json index 38ce65e8eedce..83b858a2e9fa0 100644 --- a/packages/@n8n/client-oauth2/package.json +++ b/packages/@n8n/client-oauth2/package.json @@ -20,6 +20,6 @@ "dist/**/*" ], "dependencies": { - "axios": "1.6.7" + "axios": "catalog:" } } diff --git a/packages/@n8n/config/package.json b/packages/@n8n/config/package.json index d992cc5aee077..66332540fa2e6 100644 --- a/packages/@n8n/config/package.json +++ b/packages/@n8n/config/package.json @@ -21,6 +21,6 @@ ], "dependencies": { "reflect-metadata": "0.2.2", - "typedi": "0.10.0" + "typedi": "catalog:" } } diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index 9c10d94c495d9..a52670e3e3faf 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -121,7 +121,7 @@ ] }, "devDependencies": { - "@types/basic-auth": "^1.1.3", + "@types/basic-auth": "catalog:", "@types/cheerio": "^0.22.15", "@types/html-to-text": "^9.0.1", "@types/json-schema": "^7.0.15", @@ -159,18 +159,18 @@ "@supabase/supabase-js": "2.43.4", "@types/pg": "^8.11.3", "@xata.io/client": "0.28.4", - "basic-auth": "2.0.1", + "basic-auth": "catalog:", "cheerio": "1.0.0-rc.12", "cohere-ai": "7.10.1", "d3-dsv": "2.0.0", "epub2": "3.0.2", - "form-data": "4.0.0", + "form-data": "catalog:", "generate-schema": "2.6.0", "html-to-text": "9.0.5", "jsdom": "^23.0.1", "json-schema-to-zod": "2.1.0", "langchain": "0.2.11", - "lodash": "4.17.21", + "lodash": "catalog:", "mammoth": "1.7.2", "n8n-nodes-base": "workspace:*", "n8n-workflow": "workspace:*", diff --git a/packages/cli/package.json b/packages/cli/package.json index e69fb4fc26c52..b18513c3a20c9 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -56,12 +56,12 @@ "@types/compression": "1.0.1", "@types/convict": "^6.1.1", "@types/cookie-parser": "^1.4.2", - "@types/express": "^4.17.21", + "@types/express": "catalog:", "@types/flat": "^5.0.5", "@types/formidable": "^3.4.5", "@types/json-diff": "^1.0.0", "@types/jsonwebtoken": "^9.0.6", - "@types/lodash": "^4.14.195", + "@types/lodash": "catalog:", "@types/psl": "^1.1.0", "@types/replacestream": "^4.0.1", "@types/shelljs": "^0.8.11", @@ -69,10 +69,10 @@ "@types/superagent": "^8.1.7", "@types/swagger-ui-express": "^4.1.6", "@types/syslog-client": "^1.1.2", - "@types/uuid": "^8.3.2", + "@types/uuid": "catalog:", "@types/validator": "^13.7.0", "@types/ws": "^8.5.4", - "@types/xml2js": "^0.4.14", + "@types/xml2js": "catalog:", "@types/yamljs": "^0.2.31", "@vvo/tzdb": "^6.141.0", "chokidar": "^3.5.2", @@ -95,7 +95,7 @@ "@sentry/integrations": "7.87.0", "@sentry/node": "7.87.0", "aws4": "1.11.0", - "axios": "1.6.7", + "axios": "catalog:", "bcryptjs": "2.4.3", "bull": "4.12.1", "cache-manager": "5.2.3", @@ -115,7 +115,7 @@ "express-openapi-validator": "5.1.6", "express-prom-bundle": "6.6.0", "express-rate-limit": "7.2.0", - "fast-glob": "3.2.12", + "fast-glob": "catalog:", "flat": "5.0.2", "flatted": "3.2.7", "formidable": "3.5.1", @@ -128,14 +128,14 @@ "jsonschema": "1.4.1", "jsonwebtoken": "9.0.2", "ldapts": "4.2.6", - "lodash": "4.17.21", - "luxon": "3.3.0", + "lodash": "catalog:", + "luxon": "catalog:", "mysql2": "3.10.0", "n8n-core": "workspace:*", "n8n-editor-ui": "workspace:*", "n8n-nodes-base": "workspace:*", "n8n-workflow": "workspace:*", - "nanoid": "3.3.6", + "nanoid": "catalog:", "nodemailer": "6.9.9", "oauth-1.0a": "2.2.6", "open": "7.4.2", @@ -162,12 +162,12 @@ "sshpk": "1.17.0", "swagger-ui-express": "5.0.0", "syslog-client": "1.1.1", - "typedi": "0.10.0", - "uuid": "8.3.2", + "typedi": "catalog:", + "uuid": "catalog:", "validator": "13.7.0", "winston": "3.8.2", "ws": "8.17.1", - "xml2js": "0.6.2", + "xml2js": "catalog:", "xmllint-wasm": "3.0.1", "yamljs": "0.3.0", "zod": "3.22.4" diff --git a/packages/core/package.json b/packages/core/package.json index 0eb22acf6d5eb..17f57cc99bad1 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -28,23 +28,23 @@ "devDependencies": { "@types/aws4": "^1.5.1", "@types/concat-stream": "^2.0.0", - "@types/express": "^4.17.21", - "@types/lodash": "^4.14.195", + "@types/express": "catalog:", + "@types/lodash": "catalog:", "@types/mime-types": "^2.1.0", - "@types/uuid": "^8.3.2", - "@types/xml2js": "^0.4.14" + "@types/uuid": "catalog:", + "@types/xml2js": "catalog:" }, "dependencies": { "@n8n/client-oauth2": "workspace:*", "aws4": "1.11.0", - "axios": "1.6.7", + "axios": "catalog:", "concat-stream": "2.0.0", "cron": "3.1.7", - "fast-glob": "3.2.12", + "fast-glob": "catalog:", "file-type": "16.5.4", - "form-data": "4.0.0", - "lodash": "4.17.21", - "luxon": "^3.4.4", + "form-data": "catalog:", + "lodash": "catalog:", + "luxon": "catalog:", "mime-types": "2.1.35", "n8n-workflow": "workspace:*", "oauth-1.0a": "2.2.6", @@ -52,8 +52,8 @@ "pretty-bytes": "5.6.0", "qs": "6.11.0", "ssh2": "1.15.0", - "typedi": "0.10.0", - "uuid": "8.3.2", - "xml2js": "0.6.2" + "typedi": "catalog:", + "uuid": "catalog:", + "xml2js": "catalog:" } } diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 88b2e967d09ce..150636a64c3f0 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -27,13 +27,18 @@ "@types/markdown-it-link-attributes": "^3.0.1", "@types/sanitize-html": "^2.11.0", "@vitejs/plugin-vue": "^5.0.4", + "@vitest/coverage-v8": "catalog:frontend", "@vue/test-utils": "^2.4.3", "autoprefixer": "^10.4.19", "postcss": "^8.4.38", "sass": "^1.64.1", "tailwindcss": "^3.4.3", "unplugin-icons": "^0.19.0", - "unplugin-vue-components": "^0.27.2" + "unplugin-vue-components": "^0.27.2", + "vite": "catalog:frontend", + "vitest": "catalog:frontend", + "vitest-mock-extended": "catalog:frontend", + "vue-tsc": "catalog:frontend" }, "dependencies": { "@fortawesome/fontawesome-svg-core": "^1.2.36", @@ -45,7 +50,7 @@ "markdown-it-link-attributes": "^4.0.1", "markdown-it-task-lists": "^2.1.1", "sanitize-html": "2.12.1", - "vue": "^3.4.21", + "vue": "catalog:frontend", "vue-boring-avatars": "^1.3.0", "vue-router": "^4.2.2", "xss": "^1.0.14" diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index a4425d2504d9a..d4acc7843d1b2 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -43,7 +43,7 @@ "@vue-flow/node-resizer": "^1.4.0", "@vueuse/components": "^10.11.0", "@vueuse/core": "^10.11.0", - "axios": "1.6.7", + "axios": "catalog:", "chart.js": "^4.4.0", "codemirror-lang-html-n8n": "^1.0.0", "dateformat": "^3.0.3", @@ -55,7 +55,7 @@ "humanize-duration": "^3.27.2", "jsonpath": "^1.1.1", "lodash-es": "^4.17.21", - "luxon": "^3.3.0", + "luxon": "catalog:", "n8n-design-system": "workspace:*", "n8n-workflow": "workspace:*", "pinia": "^2.1.6", @@ -63,9 +63,9 @@ "qrcode.vue": "^3.3.4", "stream-browserify": "^3.0.0", "timeago.js": "^4.0.2", - "uuid": "^8.3.2", + "uuid": "catalog:", "v3-infinite-loading": "^1.2.2", - "vue": "^3.4.21", + "vue": "catalog:frontend", "vue-agile": "^2.0.0", "vue-chartjs": "^5.2.0", "vue-i18n": "^9.2.2", @@ -86,11 +86,16 @@ "@types/jsonpath": "^0.2.0", "@types/lodash-es": "^4.17.6", "@types/luxon": "^3.2.0", - "@types/uuid": "^8.3.2", + "@types/uuid": "catalog:", + "@vitest/coverage-v8": "catalog:frontend", "cross-env": "^7.0.3", "miragejs": "^0.1.48", "unplugin-icons": "^0.19.0", - "unplugin-vue-components": "^0.27.2" + "unplugin-vue-components": "^0.27.2", + "vite": "catalog:frontend", + "vitest": "catalog:frontend", + "vitest-mock-extended": "catalog:frontend", + "vue-tsc": "catalog:frontend" }, "peerDependencies": { "@fortawesome/fontawesome-svg-core": "*", diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 2a8333cd91c86..92db12b997737 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -40,12 +40,12 @@ "dependencies": { "@oclif/core": "4.0.7", "change-case": "^4.1.1", - "fast-glob": "^3.2.5", + "fast-glob": "catalog:", "inquirer": "^7.0.1", "n8n-core": "workspace:*", "n8n-workflow": "workspace:*", "replace-in-file": "^6.0.0", "tmp-promise": "^3.0.3", - "typedi": "^0.10.0" + "typedi": "catalog:" } } diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index ba352f63fc494..e512fb4ef89bc 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -802,15 +802,15 @@ "devDependencies": { "@types/amqplib": "^0.10.1", "@types/aws4": "^1.5.1", - "@types/basic-auth": "^1.1.3", + "@types/basic-auth": "catalog:", "@types/cheerio": "^0.22.15", "@types/eventsource": "^1.1.2", - "@types/express": "^4.17.21", + "@types/express": "catalog:", "@types/html-to-text": "^9.0.1", "@types/gm": "^1.25.0", "@types/js-nacl": "^1.3.0", "@types/jsonwebtoken": "^9.0.6", - "@types/lodash": "^4.14.195", + "@types/lodash": "catalog:", "@types/lossless-json": "^1.0.0", "@types/mailparser": "^3.4.4", "@types/mime-types": "^2.1.0", @@ -821,8 +821,8 @@ "@types/showdown": "^1.9.4", "@types/snowflake-sdk": "^1.6.20", "@types/ssh2-sftp-client": "^5.1.0", - "@types/uuid": "^8.3.2", - "@types/xml2js": "^0.4.14", + "@types/uuid": "catalog:", + "@types/xml2js": "catalog:", "eslint-plugin-n8n-nodes-base": "^1.16.0", "n8n-core": "workspace:*" }, @@ -833,7 +833,7 @@ "amqplib": "0.10.3", "alasql": "^4.4.0", "aws4": "1.11.0", - "basic-auth": "2.0.1", + "basic-auth": "catalog:", "change-case": "4.1.2", "cheerio": "1.0.0-rc.6", "chokidar": "3.5.2", @@ -842,7 +842,7 @@ "currency-codes": "2.1.0", "eventsource": "2.0.2", "html-to-text": "9.0.5", - "fast-glob": "3.2.12", + "fast-glob": "catalog:", "fflate": "0.7.4", "get-system-fonts": "2.0.2", "gm": "1.25.0", @@ -854,9 +854,9 @@ "jsonwebtoken": "9.0.2", "kafkajs": "1.16.0", "ldapts": "4.2.6", - "lodash": "4.17.21", + "lodash": "catalog:", "lossless-json": "1.0.5", - "luxon": "3.3.0", + "luxon": "catalog:", "mailparser": "3.6.7", "minifaker": "1.34.1", "moment-timezone": "0.5.37", @@ -886,9 +886,9 @@ "ssh2-sftp-client": "7.2.3", "tmp-promise": "3.0.3", "ts-ics": "^1.2.2", - "typedi": "0.10.0", - "uuid": "8.3.2", + "typedi": "catalog:", + "uuid": "catalog:", "xlsx": "https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz", - "xml2js": "0.6.2" + "xml2js": "catalog:" } } diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 96a4f31fd16fa..83de78636bf8c 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -31,31 +31,31 @@ "devDependencies": { "@langchain/core": "^0.2.18", "@types/deep-equal": "^1.0.1", - "@types/express": "^4.17.21", + "@types/express": "catalog:", "@types/jmespath": "^0.15.0", - "@types/lodash": "^4.14.195", + "@types/lodash": "catalog:", "@types/luxon": "^3.2.0", "@types/md5": "^2.3.5", - "@types/xml2js": "^0.4.14" + "@types/xml2js": "catalog:" }, "dependencies": { "@n8n/tournament": "1.0.2", "@n8n_io/riot-tmpl": "4.0.0", "ast-types": "0.15.2", - "axios": "1.6.7", + "axios": "catalog:", "callsites": "3.1.0", "deep-equal": "2.2.0", "esprima-next": "5.8.4", - "form-data": "4.0.0", + "form-data": "catalog:", "jmespath": "0.16.0", "js-base64": "3.7.2", "jssha": "3.3.1", - "lodash": "4.17.21", - "luxon": "3.3.0", + "lodash": "catalog:", + "luxon": "catalog:", "md5": "2.3.0", "recast": "0.21.5", "title-case": "3.0.3", "transliteration": "2.3.5", - "xml2js": "0.6.2" + "xml2js": "catalog:" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e97519b806455..58fd67d51719a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4,6 +4,70 @@ settings: autoInstallPeers: true excludeLinksFromLockfile: false +catalogs: + default: + '@types/basic-auth': + specifier: ^1.1.3 + version: 1.1.3 + '@types/express': + specifier: ^4.17.21 + version: 4.17.21 + '@types/lodash': + specifier: ^4.14.195 + version: 4.14.195 + '@types/uuid': + specifier: ^8.3.2 + version: 8.3.4 + '@types/xml2js': + specifier: ^0.4.14 + version: 0.4.14 + basic-auth: + specifier: 2.0.1 + version: 2.0.1 + fast-glob: + specifier: 3.2.12 + version: 3.2.12 + form-data: + specifier: 4.0.0 + version: 4.0.0 + lodash: + specifier: 4.17.21 + version: 4.17.21 + luxon: + specifier: 3.4.4 + version: 3.4.4 + nanoid: + specifier: 3.3.6 + version: 3.3.6 + typedi: + specifier: 0.10.0 + version: 0.10.0 + uuid: + specifier: 8.3.2 + version: 8.3.2 + xml2js: + specifier: 0.6.2 + version: 0.6.2 + frontend: + '@vitest/coverage-v8': + specifier: ^1.6.0 + version: 1.6.0 + vite: + specifier: ^5.2.12 + version: 5.2.12 + vitest: + specifier: ^1.6.0 + version: 1.6.0 + vitest-mock-extended: + specifier: ^1.3.1 + version: 1.3.1 + vue: + specifier: ^3.4.21 + version: 3.4.21 + vue-tsc: + specifier: ^2.0.19 + version: 2.0.19 + overrides: '@types/node': ^18.16.16 axios: 1.6.7 @@ -54,9 +118,6 @@ importers: '@types/supertest': specifier: ^6.0.2 version: 6.0.2 - '@vitest/coverage-v8': - specifier: ^1.6.0 - version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) jest: specifier: ^29.6.2 version: 29.6.2(@types/node@18.16.16) @@ -105,18 +166,6 @@ importers: typescript: specifier: ^5.5.2 version: 5.5.2 - vite: - specifier: ^5.2.12 - version: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1) - vitest: - specifier: ^1.6.0 - version: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1) - vitest-mock-extended: - specifier: ^1.3.1 - version: 1.3.1(typescript@5.5.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) - vue-tsc: - specifier: ^2.0.19 - version: 2.0.19(typescript@5.5.2) cypress: dependencies: @@ -136,17 +185,17 @@ importers: specifier: ^1.12.0 version: 1.12.0(cypress@13.11.0) lodash: - specifier: 4.17.21 + specifier: 'catalog:' version: 4.17.21 nanoid: - specifier: 3.3.6 + specifier: 'catalog:' version: 3.3.6 start-server-and-test: specifier: ^2.0.3 version: 2.0.3 devDependencies: '@types/lodash': - specifier: ^4.14.195 + specifier: 'catalog:' version: 4.14.195 eslint-plugin-cypress: specifier: ^3.3.0 @@ -167,10 +216,10 @@ importers: specifier: ^4.0.1 version: 4.0.1 uuid: - specifier: ^8.3.2 + specifier: 'catalog:' version: 8.3.2 vue: - specifier: ^3.4.21 + specifier: catalog:frontend version: 3.4.21(typescript@5.5.2) vue-markdown-render: specifier: ^2.1.1 @@ -185,12 +234,24 @@ importers: '@types/markdown-it': specifier: ^12.2.3 version: 12.2.3 + '@vitest/coverage-v8': + specifier: catalog:frontend + version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) unplugin-icons: specifier: ^0.19.0 version: 0.19.0(@vue/compiler-sfc@3.4.21)(vue-template-compiler@2.7.14) + vite: + specifier: catalog:frontend + version: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1) vite-plugin-dts: specifier: ^3.9.1 version: 3.9.1(@types/node@18.16.16)(rollup@4.18.0)(typescript@5.5.2)(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)) + vitest: + specifier: catalog:frontend + version: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1) + vue-tsc: + specifier: catalog:frontend + version: 2.0.19(typescript@5.5.2) packages/@n8n/client-oauth2: dependencies: @@ -220,7 +281,7 @@ importers: specifier: 0.2.2 version: 0.2.2 typedi: - specifier: 0.10.0 + specifier: 'catalog:' version: 0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe) packages/@n8n/imap: @@ -288,10 +349,10 @@ importers: version: 0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)) '@langchain/community': specifier: 0.2.20 - version: 0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1) + version: 0.2.20(bl2awxa6z6ereahlcpz2dlbf6q) '@langchain/core': specifier: 0.2.18 - version: 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + version: 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) '@langchain/google-genai': specifier: 0.0.23 version: 0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8) @@ -309,19 +370,19 @@ importers: version: 0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)) '@langchain/openai': specifier: 0.2.5 - version: 0.2.5(encoding@0.1.13)(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1)) + version: 0.2.5(encoding@0.1.13)(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq)) '@langchain/pinecone': specifier: 0.0.8 - version: 0.0.8(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + version: 0.0.8(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) '@langchain/qdrant': specifier: ^0.0.5 - version: 0.0.5(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))(typescript@5.5.2) + version: 0.0.5(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))(typescript@5.5.2) '@langchain/redis': specifier: 0.0.5 - version: 0.0.5(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + version: 0.0.5(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) '@langchain/textsplitters': specifier: 0.0.3 - version: 0.0.3(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + version: 0.0.3(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) '@mozilla/readability': specifier: ^0.5.0 version: 0.5.0 @@ -347,7 +408,7 @@ importers: specifier: 0.28.4 version: 0.28.4(typescript@5.5.2) basic-auth: - specifier: 2.0.1 + specifier: 'catalog:' version: 2.0.1 cheerio: specifier: 1.0.0-rc.12 @@ -362,7 +423,7 @@ importers: specifier: 3.0.2 version: 3.0.2(ts-toolbelt@9.6.0) form-data: - specifier: 4.0.0 + specifier: 'catalog:' version: 4.0.0 generate-schema: specifier: 2.6.0 @@ -378,9 +439,9 @@ importers: version: 2.1.0 langchain: specifier: 0.2.11 - version: 0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1) + version: 0.2.11(3eonfu4fvxvjb5zdrprnb3pokq) lodash: - specifier: 4.17.21 + specifier: 'catalog:' version: 4.17.21 mammoth: specifier: 1.7.2 @@ -420,7 +481,7 @@ importers: version: 3.23.0(zod@3.23.8) devDependencies: '@types/basic-auth': - specifier: ^1.1.3 + specifier: 'catalog:' version: 1.1.3 '@types/cheerio': specifier: ^0.22.15 @@ -459,7 +520,7 @@ importers: version: 8.1.4(@types/react@18.0.27)(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/addon-interactions': specifier: ^8.1.4 - version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) + version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)) '@storybook/addon-links': specifier: ^8.1.4 version: 8.1.4(react@18.2.0) @@ -471,7 +532,7 @@ importers: version: 8.1.4(@types/react@18.0.27)(encoding@0.1.13)(prettier@3.2.5)(react-dom@18.2.0(react@18.2.0))(react@18.2.0) '@storybook/test': specifier: ^8.1.4 - version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) + version: 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)) '@storybook/vue3': specifier: ^8.1.4 version: 8.1.4(encoding@0.1.13)(prettier@3.2.5)(vue@3.4.21(typescript@5.5.2)) @@ -644,7 +705,7 @@ importers: specifier: 7.2.0 version: 7.2.0(express@4.19.2) fast-glob: - specifier: 3.2.12 + specifier: 'catalog:' version: 3.2.12 flat: specifier: 5.0.2 @@ -683,11 +744,11 @@ importers: specifier: 4.2.6 version: 4.2.6 lodash: - specifier: 4.17.21 + specifier: 'catalog:' version: 4.17.21 luxon: - specifier: 3.3.0 - version: 3.3.0 + specifier: 'catalog:' + version: 3.4.4 mysql2: specifier: 3.10.0 version: 3.10.0 @@ -704,7 +765,7 @@ importers: specifier: workspace:* version: link:../workflow nanoid: - specifier: 3.3.6 + specifier: 'catalog:' version: 3.3.6 nodemailer: specifier: 6.9.9 @@ -785,10 +846,10 @@ importers: specifier: 1.1.1 version: 1.1.1 typedi: - specifier: 0.10.0 + specifier: 'catalog:' version: 0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe) uuid: - specifier: 8.3.2 + specifier: 'catalog:' version: 8.3.2 validator: specifier: 13.7.0 @@ -800,7 +861,7 @@ importers: specifier: '>=8.17.1' version: 8.17.1 xml2js: - specifier: 0.6.2 + specifier: 'catalog:' version: 0.6.2 xmllint-wasm: specifier: 3.0.1 @@ -831,7 +892,7 @@ importers: specifier: ^1.4.2 version: 1.4.3 '@types/express': - specifier: ^4.17.21 + specifier: 'catalog:' version: 4.17.21 '@types/flat': specifier: ^5.0.5 @@ -846,7 +907,7 @@ importers: specifier: ^9.0.6 version: 9.0.6 '@types/lodash': - specifier: ^4.14.195 + specifier: 'catalog:' version: 4.14.195 '@types/psl': specifier: ^1.1.0 @@ -870,7 +931,7 @@ importers: specifier: ^1.1.2 version: 1.1.2 '@types/uuid': - specifier: ^8.3.2 + specifier: 'catalog:' version: 8.3.4 '@types/validator': specifier: ^13.7.0 @@ -879,7 +940,7 @@ importers: specifier: ^8.5.4 version: 8.5.4(patch_hash=nbzuqaoyqbrfwipijj5qriqqju) '@types/xml2js': - specifier: ^0.4.14 + specifier: 'catalog:' version: 0.4.14 '@types/yamljs': specifier: ^0.2.31 @@ -918,19 +979,19 @@ importers: specifier: 3.1.7 version: 3.1.7 fast-glob: - specifier: 3.2.12 + specifier: 'catalog:' version: 3.2.12 file-type: specifier: 16.5.4 version: 16.5.4 form-data: - specifier: 4.0.0 + specifier: 'catalog:' version: 4.0.0 lodash: - specifier: 4.17.21 + specifier: 'catalog:' version: 4.17.21 luxon: - specifier: ^3.4.4 + specifier: 'catalog:' version: 3.4.4 mime-types: specifier: 2.1.35 @@ -954,13 +1015,13 @@ importers: specifier: 1.15.0 version: 1.15.0 typedi: - specifier: 0.10.0 + specifier: 'catalog:' version: 0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe) uuid: - specifier: 8.3.2 + specifier: 'catalog:' version: 8.3.2 xml2js: - specifier: 0.6.2 + specifier: 'catalog:' version: 0.6.2 devDependencies: '@types/aws4': @@ -970,19 +1031,19 @@ importers: specifier: ^2.0.0 version: 2.0.0 '@types/express': - specifier: ^4.17.21 + specifier: 'catalog:' version: 4.17.21 '@types/lodash': - specifier: ^4.14.195 + specifier: 'catalog:' version: 4.14.195 '@types/mime-types': specifier: ^2.1.0 version: 2.1.1 '@types/uuid': - specifier: ^8.3.2 + specifier: 'catalog:' version: 8.3.4 '@types/xml2js': - specifier: ^0.4.14 + specifier: 'catalog:' version: 0.4.14 packages/design-system: @@ -1015,7 +1076,7 @@ importers: specifier: 2.12.1 version: 2.12.1 vue: - specifier: ^3.4.21 + specifier: catalog:frontend version: 3.4.21(typescript@5.5.2) vue-boring-avatars: specifier: ^1.3.0 @@ -1054,6 +1115,9 @@ importers: '@vitejs/plugin-vue': specifier: ^5.0.4 version: 5.0.4(vite@5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))(vue@3.4.21(typescript@5.5.2)) + '@vitest/coverage-v8': + specifier: catalog:frontend + version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) '@vue/test-utils': specifier: ^2.4.3 version: 2.4.3(@vue/server-renderer@3.4.21(vue@3.4.21(typescript@5.5.2)))(vue@3.4.21(typescript@5.5.2)) @@ -1075,6 +1139,18 @@ importers: unplugin-vue-components: specifier: ^0.27.2 version: 0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.5.2)) + vite: + specifier: catalog:frontend + version: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1) + vitest: + specifier: catalog:frontend + version: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1) + vitest-mock-extended: + specifier: catalog:frontend + version: 1.3.1(typescript@5.5.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) + vue-tsc: + specifier: catalog:frontend + version: 2.0.19(typescript@5.5.2) packages/editor-ui: dependencies: @@ -1208,8 +1284,8 @@ importers: specifier: ^4.17.21 version: 4.17.21 luxon: - specifier: ^3.3.0 - version: 3.3.0 + specifier: 'catalog:' + version: 3.4.4 n8n-design-system: specifier: workspace:* version: link:../design-system @@ -1232,13 +1308,13 @@ importers: specifier: ^4.0.2 version: 4.0.2 uuid: - specifier: ^8.3.2 + specifier: 'catalog:' version: 8.3.2 v3-infinite-loading: specifier: ^1.2.2 version: 1.2.2 vue: - specifier: ^3.4.21 + specifier: catalog:frontend version: 3.4.21(typescript@5.5.2) vue-agile: specifier: ^2.0.0 @@ -1296,8 +1372,11 @@ importers: specifier: ^3.2.0 version: 3.2.0 '@types/uuid': - specifier: ^8.3.2 + specifier: 'catalog:' version: 8.3.4 + '@vitest/coverage-v8': + specifier: catalog:frontend + version: 1.6.0(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) cross-env: specifier: ^7.0.3 version: 7.0.3 @@ -1310,6 +1389,18 @@ importers: unplugin-vue-components: specifier: ^0.27.2 version: 0.27.3(@babel/parser@7.24.6)(rollup@4.18.0)(vue@3.4.21(typescript@5.5.2)) + vite: + specifier: catalog:frontend + version: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1) + vitest: + specifier: catalog:frontend + version: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1) + vitest-mock-extended: + specifier: catalog:frontend + version: 1.3.1(typescript@5.5.2)(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) + vue-tsc: + specifier: catalog:frontend + version: 2.0.19(typescript@5.5.2) packages/node-dev: dependencies: @@ -1320,7 +1411,7 @@ importers: specifier: ^4.1.1 version: 4.1.2 fast-glob: - specifier: ^3.2.5 + specifier: 'catalog:' version: 3.2.12 inquirer: specifier: ^7.0.1 @@ -1338,7 +1429,7 @@ importers: specifier: ^3.0.3 version: 3.0.3 typedi: - specifier: ^0.10.0 + specifier: 'catalog:' version: 0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe) devDependencies: '@types/inquirer': @@ -1366,7 +1457,7 @@ importers: specifier: 1.11.0 version: 1.11.0 basic-auth: - specifier: 2.0.1 + specifier: 'catalog:' version: 2.0.1 change-case: specifier: 4.1.2 @@ -1390,7 +1481,7 @@ importers: specifier: 2.0.2 version: 2.0.2 fast-glob: - specifier: 3.2.12 + specifier: 'catalog:' version: 3.2.12 fflate: specifier: 0.7.4 @@ -1429,14 +1520,14 @@ importers: specifier: 4.2.6 version: 4.2.6 lodash: - specifier: 4.17.21 + specifier: 'catalog:' version: 4.17.21 lossless-json: specifier: 1.0.5 version: 1.0.5 luxon: - specifier: 3.3.0 - version: 3.3.0 + specifier: 'catalog:' + version: 3.4.4 mailparser: specifier: 3.6.7 version: 3.6.7 @@ -1525,16 +1616,16 @@ importers: specifier: ^1.2.2 version: 1.2.2(date-fns@2.30.0)(lodash@4.17.21)(zod@3.23.8) typedi: - specifier: 0.10.0 + specifier: 'catalog:' version: 0.10.0(patch_hash=sk6omkefrosihg7lmqbzh7vfxe) uuid: - specifier: 8.3.2 + specifier: 'catalog:' version: 8.3.2 xlsx: specifier: https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz version: https://cdn.sheetjs.com/xlsx-0.20.2/xlsx-0.20.2.tgz xml2js: - specifier: 0.6.2 + specifier: 'catalog:' version: 0.6.2 devDependencies: '@types/amqplib': @@ -1544,7 +1635,7 @@ importers: specifier: ^1.5.1 version: 1.11.2 '@types/basic-auth': - specifier: ^1.1.3 + specifier: 'catalog:' version: 1.1.3 '@types/cheerio': specifier: ^0.22.15 @@ -1553,7 +1644,7 @@ importers: specifier: ^1.1.2 version: 1.1.9 '@types/express': - specifier: ^4.17.21 + specifier: 'catalog:' version: 4.17.21 '@types/gm': specifier: ^1.25.0 @@ -1568,7 +1659,7 @@ importers: specifier: ^9.0.6 version: 9.0.6 '@types/lodash': - specifier: ^4.14.195 + specifier: 'catalog:' version: 4.14.195 '@types/lossless-json': specifier: ^1.0.0 @@ -1601,10 +1692,10 @@ importers: specifier: ^5.1.0 version: 5.3.2 '@types/uuid': - specifier: ^8.3.2 + specifier: 'catalog:' version: 8.3.4 '@types/xml2js': - specifier: ^0.4.14 + specifier: 'catalog:' version: 0.4.14 eslint-plugin-n8n-nodes-base: specifier: ^1.16.0 @@ -1637,7 +1728,7 @@ importers: specifier: 5.8.4 version: 5.8.4 form-data: - specifier: 4.0.0 + specifier: 'catalog:' version: 4.0.0 jmespath: specifier: 0.16.0 @@ -1649,11 +1740,11 @@ importers: specifier: 3.3.1 version: 3.3.1 lodash: - specifier: 4.17.21 + specifier: 'catalog:' version: 4.17.21 luxon: - specifier: 3.3.0 - version: 3.3.0 + specifier: 'catalog:' + version: 3.4.4 md5: specifier: 2.3.0 version: 2.3.0 @@ -1667,23 +1758,23 @@ importers: specifier: 2.3.5 version: 2.3.5 xml2js: - specifier: 0.6.2 + specifier: 'catalog:' version: 0.6.2 devDependencies: '@langchain/core': specifier: ^0.2.18 - version: 0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) + version: 0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) '@types/deep-equal': specifier: ^1.0.1 version: 1.0.1 '@types/express': - specifier: ^4.17.21 + specifier: 'catalog:' version: 4.17.21 '@types/jmespath': specifier: ^0.15.0 version: 0.15.0 '@types/lodash': - specifier: ^4.14.195 + specifier: 'catalog:' version: 4.14.195 '@types/luxon': specifier: ^3.2.0 @@ -1692,7 +1783,7 @@ importers: specifier: ^2.3.5 version: 2.3.5 '@types/xml2js': - specifier: ^0.4.14 + specifier: 'catalog:' version: 0.4.14 packages: @@ -3470,15 +3561,9 @@ packages: '@jridgewell/source-map@0.3.2': resolution: {integrity: sha512-m7O9o2uR8k2ObDysZYzdfhb08VuEml5oWGiosa1VdaPZ/A6QyPkAJuwN0Q1lhULOf6B7MtQmHENS743hWtCrgw==} - '@jridgewell/sourcemap-codec@1.4.14': - resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} - '@jridgewell/sourcemap-codec@1.4.15': resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} - '@jridgewell/trace-mapping@0.3.18': - resolution: {integrity: sha512-w+niJYzMHdd7USdiH2U6869nqhD2nbfZXND5Yp93qIbEmnDNk7PD48o+YchRVpzMU7M6jVCbenTR7PA1FLQ9pA==} - '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} @@ -8828,10 +8913,6 @@ packages: isstream@0.1.2: resolution: {integrity: sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==} - istanbul-lib-coverage@3.2.0: - resolution: {integrity: sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==} - engines: {node: '>=8'} - istanbul-lib-coverage@3.2.2: resolution: {integrity: sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==} engines: {node: '>=8'} @@ -8840,10 +8921,6 @@ packages: resolution: {integrity: sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==} engines: {node: '>=8'} - istanbul-lib-report@3.0.0: - resolution: {integrity: sha512-wcdi+uAKzfiGT2abPpKZ0hSU1rGQjUQnLvtY5MpQ7QCTahD3VODhcu4wcfY1YtkGaDD5yuydOLINXsfbus9ROw==} - engines: {node: '>=8'} - istanbul-lib-report@3.0.1: resolution: {integrity: sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==} engines: {node: '>=10'} @@ -8856,10 +8933,6 @@ packages: resolution: {integrity: sha512-wHOoEsNJTVltaJp8eVkm8w+GVkVNHT2YDYo53YdzQEL2gWm1hBX5cGFR9hQJtuGLebidVX7et3+dmDZrmclduw==} engines: {node: '>=10'} - istanbul-reports@3.1.5: - resolution: {integrity: sha512-nUsEMa9pBt/NOHqbcbeJEgqIlY/K7rVWUX6Lql2orY5e9roQOthbR3vtY4zzf2orPELg80fnxxk9zUyPlgwD1w==} - engines: {node: '>=8'} - istanbul-reports@3.1.6: resolution: {integrity: sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==} engines: {node: '>=8'} @@ -9696,10 +9769,6 @@ packages: lunr@2.3.9: resolution: {integrity: sha512-zTU3DaZaF3Rt9rhN3uBMGQD3dD2/vFQqnvZCDv4dl5iOzq2IZQqTxu90r4E5J+nP70J3ilqVCrbho2eWaeW8Ow==} - luxon@3.3.0: - resolution: {integrity: sha512-An0UCfG/rSiqtAIiBPO0Y9/zAnHUZxAMiCpTd5h2smgsj7GGmcenvrvww2cqNA8/4A5ZrD1gJpHN2mIHZQF+Mg==} - engines: {node: '>=12'} - luxon@3.4.4: resolution: {integrity: sha512-zobTr7akeGHnv7eBOXcRgMeCP6+uyYsczwmeRCauvpvaAltgNyTbLH/+VaEAPUeWBT+1GuNmz4wC/6jtQzbbVA==} engines: {node: '>=12'} @@ -10628,9 +10697,6 @@ packages: resolution: {integrity: sha512-5HviZNaZcfqP95rwpv+1HDgUamezbqdSYTyzjTvwtJSnIH+3vnbmWsItli8OFEndS984VT55M3jduxZbX351gg==} engines: {node: '>=12'} - pathe@1.1.1: - resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} - pathe@1.1.2: resolution: {integrity: sha512-whLdWMYL2TwI08hn8/ZqAbrVemu0LNaNNJZX73O6qaIdCTfXutsLhMkjdENX0qhsQ9uIimo4/aQOmXkoon2nDQ==} @@ -13247,7 +13313,7 @@ snapshots: '@acuminous/bitsyntax@0.1.2': dependencies: buffer-more-ints: 1.0.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) safe-buffer: 5.1.2 transitivePeerDependencies: - supports-color @@ -14360,10 +14426,10 @@ snapshots: '@babel/helper-compilation-targets': 7.23.6 '@babel/helper-module-transforms': 7.23.3(@babel/core@7.24.0) '@babel/helpers': 7.24.0 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.6 '@babel/template': 7.24.0 '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 convert-source-map: 2.0.0 debug: 4.3.5(supports-color@8.1.1) gensync: 1.0.0-beta.2 @@ -14394,14 +14460,14 @@ snapshots: '@babel/generator@7.22.9': dependencies: - '@babel/types': 7.24.0 - '@jridgewell/gen-mapping': 0.3.2 - '@jridgewell/trace-mapping': 0.3.18 + '@babel/types': 7.24.6 + '@jridgewell/gen-mapping': 0.3.5 + '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 '@babel/generator@7.23.6': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@jridgewell/gen-mapping': 0.3.5 '@jridgewell/trace-mapping': 0.3.25 jsesc: 2.5.2 @@ -14415,7 +14481,7 @@ snapshots: '@babel/helper-annotate-as-pure@7.22.5': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-annotate-as-pure@7.24.6': dependencies: @@ -14510,7 +14576,7 @@ snapshots: '@babel/helper-function-name@7.23.0': dependencies: '@babel/template': 7.24.0 - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-function-name@7.24.6': dependencies: @@ -14519,7 +14585,7 @@ snapshots: '@babel/helper-hoist-variables@7.22.5': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-hoist-variables@7.24.6': dependencies: @@ -14527,7 +14593,7 @@ snapshots: '@babel/helper-member-expression-to-functions@7.23.0': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-member-expression-to-functions@7.24.6': dependencies: @@ -14535,7 +14601,7 @@ snapshots: '@babel/helper-module-imports@7.22.15': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-module-imports@7.24.6': dependencies: @@ -14570,7 +14636,7 @@ snapshots: '@babel/helper-optimise-call-expression@7.22.5': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-optimise-call-expression@7.24.6': dependencies: @@ -14605,7 +14671,7 @@ snapshots: '@babel/helper-simple-access@7.22.5': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-simple-access@7.24.6': dependencies: @@ -14613,7 +14679,7 @@ snapshots: '@babel/helper-skip-transparent-expression-wrappers@7.22.5': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-skip-transparent-expression-wrappers@7.24.6': dependencies: @@ -14621,7 +14687,7 @@ snapshots: '@babel/helper-split-export-declaration@7.22.6': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@babel/helper-split-export-declaration@7.24.6': dependencies: @@ -14649,7 +14715,7 @@ snapshots: dependencies: '@babel/template': 7.24.0 '@babel/traverse': 7.24.0 - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 transitivePeerDependencies: - supports-color @@ -15324,7 +15390,7 @@ snapshots: dependencies: '@babel/core': 7.24.6 '@babel/helper-plugin-utils': 7.24.6 - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 esutils: 2.0.3 '@babel/preset-typescript@7.23.3(@babel/core@7.24.6)': @@ -15362,8 +15428,8 @@ snapshots: '@babel/template@7.24.0': dependencies: '@babel/code-frame': 7.24.6 - '@babel/parser': 7.24.0 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 '@babel/template@7.24.6': dependencies: @@ -15379,8 +15445,8 @@ snapshots: '@babel/helper-function-name': 7.23.0 '@babel/helper-hoist-variables': 7.22.5 '@babel/helper-split-export-declaration': 7.22.6 - '@babel/parser': 7.24.0 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 debug: 4.3.5(supports-color@8.1.1) globals: 11.12.0 transitivePeerDependencies: @@ -15743,8 +15809,8 @@ snapshots: url-join: 4.0.1 zod: 3.23.8 optionalDependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - langchain: 0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + langchain: 0.2.11(3eonfu4fvxvjb5zdrprnb3pokq) transitivePeerDependencies: - encoding @@ -16008,18 +16074,18 @@ snapshots: '@jest/test-result': 29.6.2 '@jest/transform': 29.6.2 '@jest/types': 29.6.1 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.25 '@types/node': 18.16.16 chalk: 4.1.2 collect-v8-coverage: 1.0.1 exit: 0.1.2 glob: 7.2.3 graceful-fs: 4.2.11 - istanbul-lib-coverage: 3.2.0 + istanbul-lib-coverage: 3.2.2 istanbul-lib-instrument: 5.2.1 - istanbul-lib-report: 3.0.0 + istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 4.0.1 - istanbul-reports: 3.1.5 + istanbul-reports: 3.1.6 jest-message-util: 29.6.2 jest-util: 29.6.2 jest-worker: 29.6.2 @@ -16044,7 +16110,7 @@ snapshots: '@jest/source-map@29.6.0': dependencies: - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.25 callsites: 3.1.0 graceful-fs: 4.2.11 @@ -16066,7 +16132,7 @@ snapshots: dependencies: '@babel/core': 7.24.0 '@jest/types': 29.6.1 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.25 babel-plugin-istanbul: 6.1.1 chalk: 4.1.2 convert-source-map: 2.0.0 @@ -16095,7 +16161,7 @@ snapshots: dependencies: '@jridgewell/set-array': 1.1.2 '@jridgewell/sourcemap-codec': 1.4.15 - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.25 '@jridgewell/gen-mapping@0.3.5': dependencies: @@ -16115,15 +16181,8 @@ snapshots: '@jridgewell/trace-mapping': 0.3.25 optional: true - '@jridgewell/sourcemap-codec@1.4.14': {} - '@jridgewell/sourcemap-codec@1.4.15': {} - '@jridgewell/trace-mapping@0.3.18': - dependencies: - '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 - '@jridgewell/trace-mapping@0.3.25': dependencies: '@jridgewell/resolve-uri': 3.1.0 @@ -16169,7 +16228,7 @@ snapshots: '@kwsites/file-exists@1.1.1': dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -16178,7 +16237,7 @@ snapshots: '@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))': dependencies: '@anthropic-ai/sdk': 0.22.0(encoding@0.1.13) - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) fast-xml-parser: 4.3.5 zod: 3.23.8 zod-to-json-schema: 3.23.0(zod@3.23.8) @@ -16190,23 +16249,23 @@ snapshots: '@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))': dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) cohere-ai: 7.10.1(encoding@0.1.13) transitivePeerDependencies: - encoding - langchain - openai - ? '@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1)' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - '@langchain/openai': 0.2.5(encoding@0.1.13)(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1)) + '@langchain/community@0.2.20(bl2awxa6z6ereahlcpz2dlbf6q)': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + '@langchain/openai': 0.2.5(encoding@0.1.13)(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq)) binary-extensions: 2.2.0 expr-eval: 2.0.2 flat: 5.0.2 js-yaml: 4.1.0 - langchain: 0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1) - langsmith: 0.1.34(@langchain/core@0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + langchain: 0.2.11(3eonfu4fvxvjb5zdrprnb3pokq) + langsmith: 0.1.34(@langchain/core@0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) uuid: 10.0.0 zod: 3.23.8 zod-to-json-schema: 3.23.0(zod@3.23.8) @@ -16267,13 +16326,13 @@ snapshots: - pyodide - supports-color - ? '@langchain/core@0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))' - : dependencies: + '@langchain/core@0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))': + dependencies: ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.12 - langsmith: 0.1.39(@langchain/core@0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + langsmith: 0.1.39(@langchain/core@0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) ml-distance: 4.0.1 mustache: 4.2.0 p-queue: 6.6.2 @@ -16285,13 +16344,13 @@ snapshots: - langchain - openai - '@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0)': + '@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0)': dependencies: ansi-styles: 5.2.0 camelcase: 6.3.0 decamelize: 1.2.0 js-tiktoken: 1.0.12 - langsmith: 0.1.39(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) + langsmith: 0.1.39(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) ml-distance: 4.0.1 mustache: 4.2.0 p-queue: 6.6.2 @@ -16303,9 +16362,9 @@ snapshots: - langchain - openai - ? '@langchain/google-common@0.0.22(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8)' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/google-common@0.0.22(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8)': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) uuid: 10.0.0 zod-to-json-schema: 3.23.0(zod@3.23.8) transitivePeerDependencies: @@ -16313,10 +16372,10 @@ snapshots: - openai - zod - ? '@langchain/google-gauth@0.0.21(encoding@0.1.13)(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8)' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - '@langchain/google-common': 0.0.22(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8) + '@langchain/google-gauth@0.0.21(encoding@0.1.13)(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8)': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + '@langchain/google-common': 0.0.22(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8) google-auth-library: 8.9.0(encoding@0.1.13) transitivePeerDependencies: - encoding @@ -16328,7 +16387,7 @@ snapshots: '@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8)': dependencies: '@google/generative-ai': 0.7.1 - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) zod-to-json-schema: 3.23.0(zod@3.23.8) transitivePeerDependencies: - langchain @@ -16337,8 +16396,8 @@ snapshots: '@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8)': dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - '@langchain/google-gauth': 0.0.21(encoding@0.1.13)(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + '@langchain/google-gauth': 0.0.21(encoding@0.1.13)(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))(zod@3.23.8) transitivePeerDependencies: - encoding - langchain @@ -16348,8 +16407,8 @@ snapshots: '@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))': dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - '@langchain/openai': 0.2.5(encoding@0.1.13)(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1)) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + '@langchain/openai': 0.2.5(encoding@0.1.13)(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq)) groq-sdk: 0.3.2(encoding@0.1.13) zod: 3.23.8 zod-to-json-schema: 3.23.0(zod@3.23.8) @@ -16361,7 +16420,7 @@ snapshots: '@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))': dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) '@mistralai/mistralai': 0.4.0(encoding@0.1.13) uuid: 10.0.0 zod: 3.23.8 @@ -16373,16 +16432,16 @@ snapshots: '@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))': dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) ollama: 0.5.6 uuid: 10.0.0 transitivePeerDependencies: - langchain - openai - ? '@langchain/openai@0.2.5(encoding@0.1.13)(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/openai@0.2.5(encoding@0.1.13)(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) js-tiktoken: 1.0.12 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 @@ -16392,9 +16451,9 @@ snapshots: - langchain - supports-color - '@langchain/openai@0.2.5(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))': + '@langchain/openai@0.2.5(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))': dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) + '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) js-tiktoken: 1.0.12 openai: 4.53.0(encoding@0.1.13) zod: 3.23.8 @@ -16405,9 +16464,9 @@ snapshots: - supports-color optional: true - ? '@langchain/pinecone@0.0.8(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/pinecone@0.0.8(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) '@pinecone-database/pinecone': 3.0.0 flat: 5.0.2 uuid: 10.0.0 @@ -16415,9 +16474,9 @@ snapshots: - langchain - openai - ? '@langchain/qdrant@0.0.5(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))(typescript@5.5.2)' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/qdrant@0.0.5(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))(typescript@5.5.2)': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) '@qdrant/js-client-rest': 1.9.0(typescript@5.5.2) uuid: 9.0.1 transitivePeerDependencies: @@ -16425,25 +16484,25 @@ snapshots: - openai - typescript - ? '@langchain/redis@0.0.5(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/redis@0.0.5(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) redis: 4.6.13 transitivePeerDependencies: - langchain - openai - ? '@langchain/textsplitters@0.0.3(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13))' - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + '@langchain/textsplitters@0.0.3(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13))': + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) js-tiktoken: 1.0.12 transitivePeerDependencies: - langchain - openai - '@langchain/textsplitters@0.0.3(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0)': + '@langchain/textsplitters@0.0.3(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0)': dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) + '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) js-tiktoken: 1.0.12 transitivePeerDependencies: - langchain @@ -17712,11 +17771,11 @@ snapshots: dependencies: '@storybook/global': 5.0.0 - '@storybook/addon-interactions@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))': + '@storybook/addon-interactions@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))': dependencies: '@storybook/global': 5.0.0 '@storybook/instrumenter': 8.1.4 - '@storybook/test': 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) + '@storybook/test': 8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)) '@storybook/types': 8.1.4 polished: 4.2.2 ts-dedent: 2.2.0 @@ -18164,14 +18223,14 @@ snapshots: - prettier - supports-color - '@storybook/test@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))': + '@storybook/test@8.1.4(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))': dependencies: '@storybook/client-logger': 8.1.4 '@storybook/core-events': 8.1.4 '@storybook/instrumenter': 8.1.4 '@storybook/preview-api': 8.1.4 '@testing-library/dom': 9.3.4 - '@testing-library/jest-dom': 6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1)) + '@testing-library/jest-dom': 6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1)) '@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4) '@vitest/expect': 1.3.1 '@vitest/spy': 1.3.1 @@ -18334,7 +18393,7 @@ snapshots: jest: 29.6.2(@types/node@18.16.16) vitest: 1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1) - '@testing-library/jest-dom@6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(jsdom@23.0.1)(sass@1.64.1)(terser@5.16.1))': + '@testing-library/jest-dom@6.4.2(@jest/globals@29.6.2)(@types/jest@29.5.3)(jest@29.6.2(@types/node@18.16.16))(vitest@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1))': dependencies: '@adobe/css-tools': 4.3.2 '@babel/runtime': 7.23.6 @@ -18393,24 +18452,24 @@ snapshots: '@types/babel__core@7.20.0': dependencies: - '@babel/parser': 7.24.0 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 '@types/babel__generator': 7.6.4 '@types/babel__template': 7.4.1 '@types/babel__traverse': 7.18.2 '@types/babel__generator@7.6.4': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@types/babel__template@7.4.1': dependencies: - '@babel/parser': 7.24.0 - '@babel/types': 7.24.0 + '@babel/parser': 7.24.6 + '@babel/types': 7.24.6 '@types/babel__traverse@7.18.2': dependencies: - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@types/basic-auth@1.1.3': dependencies: @@ -18986,12 +19045,12 @@ snapshots: dependencies: '@ampproject/remapping': 2.2.1 '@bcoe/v8-coverage': 0.2.3 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 istanbul-lib-report: 3.0.1 istanbul-lib-source-maps: 5.0.4 istanbul-reports: 3.1.6 - magic-string: 0.30.8 + magic-string: 0.30.10 magicast: 0.3.3 picocolors: 1.0.0 std-env: 3.6.0 @@ -19017,12 +19076,12 @@ snapshots: dependencies: '@vitest/utils': 1.6.0 p-limit: 5.0.0 - pathe: 1.1.1 + pathe: 1.1.2 '@vitest/snapshot@1.6.0': dependencies: - magic-string: 0.30.8 - pathe: 1.1.1 + magic-string: 0.30.10 + pathe: 1.1.2 pretty-format: 29.7.0 '@vitest/spy@1.3.1': @@ -19122,15 +19181,15 @@ snapshots: '@vue/compiler-sfc@3.4.21': dependencies: - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.6 '@vue/compiler-core': 3.4.21 '@vue/compiler-dom': 3.4.21 '@vue/compiler-ssr': 3.4.21 '@vue/shared': 3.4.21 estree-walker: 2.0.2 - magic-string: 0.30.8 + magic-string: 0.30.10 postcss: 8.4.38 - source-map-js: 1.0.2 + source-map-js: 1.2.0 '@vue/compiler-ssr@3.4.21': dependencies: @@ -19182,7 +19241,7 @@ snapshots: '@vue/compiler-dom': 3.4.21 '@vue/shared': 3.4.21 computeds: 0.0.1 - minimatch: 9.0.4 + minimatch: 9.0.5 path-browserify: 1.0.1 vue-template-compiler: 2.7.14 optionalDependencies: @@ -19329,7 +19388,7 @@ snapshots: agent-base@6.0.2: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -19675,7 +19734,7 @@ snapshots: babel-plugin-jest-hoist@29.5.0: dependencies: '@babel/template': 7.24.0 - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@types/babel__core': 7.20.0 '@types/babel__traverse': 7.18.2 @@ -22066,7 +22125,7 @@ snapshots: https-proxy-agent: 5.0.1 mri: 1.2.0 node-fetch-native: 1.0.1 - pathe: 1.1.1 + pathe: 1.1.2 tar: 6.2.1 transitivePeerDependencies: - supports-color @@ -22143,7 +22202,7 @@ snapshots: dependencies: array-union: 2.1.0 dir-glob: 3.0.1 - fast-glob: 3.2.12 + fast-glob: 3.3.2 ignore: 5.2.4 merge2: 1.4.1 slash: 3.0.0 @@ -22414,7 +22473,7 @@ snapshots: http-proxy-agent@7.0.0: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -22429,14 +22488,14 @@ snapshots: https-proxy-agent@5.0.1: dependencies: agent-base: 6.0.2 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color https-proxy-agent@7.0.2: dependencies: agent-base: 7.1.0 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -22464,7 +22523,7 @@ snapshots: ics@2.40.0: dependencies: - nanoid: 3.3.6 + nanoid: 3.3.7 yup: 0.32.11 ieee754@1.2.1: {} @@ -22788,26 +22847,18 @@ snapshots: isstream@0.1.2: {} - istanbul-lib-coverage@3.2.0: {} - istanbul-lib-coverage@3.2.2: {} istanbul-lib-instrument@5.2.1: dependencies: '@babel/core': 7.24.0 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.6 '@istanbuljs/schema': 0.1.3 istanbul-lib-coverage: 3.2.2 semver: 7.6.0 transitivePeerDependencies: - supports-color - istanbul-lib-report@3.0.0: - dependencies: - istanbul-lib-coverage: 3.2.2 - make-dir: 3.1.0 - supports-color: 7.2.0 - istanbul-lib-report@3.0.1: dependencies: istanbul-lib-coverage: 3.2.2 @@ -22825,16 +22876,11 @@ snapshots: istanbul-lib-source-maps@5.0.4: dependencies: '@jridgewell/trace-mapping': 0.3.25 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) istanbul-lib-coverage: 3.2.2 transitivePeerDependencies: - supports-color - istanbul-reports@3.1.5: - dependencies: - html-escaper: 2.0.2 - istanbul-lib-report: 3.0.1 - istanbul-reports@3.1.6: dependencies: html-escaper: 2.0.2 @@ -22925,7 +22971,7 @@ snapshots: jest-validate: 29.6.2 micromatch: 4.0.5 parse-json: 5.2.0 - pretty-format: 29.6.2 + pretty-format: 29.7.0 slash: 3.0.0 strip-json-comments: 3.1.1 optionalDependencies: @@ -23014,7 +23060,7 @@ snapshots: chalk: 4.1.2 jest-diff: 29.5.0 jest-get-type: 29.4.3 - pretty-format: 29.6.2 + pretty-format: 29.7.0 jest-matcher-utils@29.6.2: dependencies: @@ -23031,7 +23077,7 @@ snapshots: chalk: 4.1.2 graceful-fs: 4.2.11 micromatch: 4.0.5 - pretty-format: 29.6.2 + pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.6 @@ -23043,7 +23089,7 @@ snapshots: chalk: 4.1.2 graceful-fs: 4.2.11 micromatch: 4.0.5 - pretty-format: 29.6.2 + pretty-format: 29.7.0 slash: 3.0.0 stack-utils: 2.0.6 @@ -23143,7 +23189,7 @@ snapshots: '@babel/generator': 7.22.9 '@babel/plugin-syntax-jsx': 7.18.6(@babel/core@7.24.0) '@babel/plugin-syntax-typescript': 7.20.0(@babel/core@7.24.0) - '@babel/types': 7.24.0 + '@babel/types': 7.24.6 '@jest/expect-utils': 29.6.2 '@jest/transform': 29.6.2 '@jest/types': 29.6.1 @@ -23157,7 +23203,7 @@ snapshots: jest-message-util: 29.6.2 jest-util: 29.6.2 natural-compare: 1.4.0 - pretty-format: 29.6.2 + pretty-format: 29.7.0 semver: 7.6.0 transitivePeerDependencies: - supports-color @@ -23187,7 +23233,7 @@ snapshots: chalk: 4.1.2 jest-get-type: 29.4.3 leven: 3.1.0 - pretty-format: 29.6.2 + pretty-format: 29.7.0 jest-watcher@29.6.2: dependencies: @@ -23278,7 +23324,7 @@ snapshots: jscodeshift@0.15.2(@babel/preset-env@7.24.6(@babel/core@7.24.6)): dependencies: '@babel/core': 7.24.6 - '@babel/parser': 7.24.0 + '@babel/parser': 7.24.6 '@babel/plugin-transform-class-properties': 7.23.3(@babel/core@7.24.6) '@babel/plugin-transform-modules-commonjs': 7.23.3(@babel/core@7.24.6) '@babel/plugin-transform-nullish-coalescing-operator': 7.23.4(@babel/core@7.24.6) @@ -23489,17 +23535,17 @@ snapshots: kuler@2.0.0: {} - ? langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1) - : dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - '@langchain/openai': 0.2.5(encoding@0.1.13)(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1)) - '@langchain/textsplitters': 0.0.3(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq): + dependencies: + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + '@langchain/openai': 0.2.5(encoding@0.1.13)(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq)) + '@langchain/textsplitters': 0.0.3(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) binary-extensions: 2.2.0 js-tiktoken: 1.0.12 js-yaml: 4.1.0 jsonpointer: 5.0.1 langchainhub: 0.0.8 - langsmith: 0.1.34(@langchain/core@0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) + langsmith: 0.1.34(@langchain/core@0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) ml-distance: 4.0.1 openapi-types: 12.1.3 p-retry: 4.6.2 @@ -23512,7 +23558,7 @@ snapshots: '@aws-sdk/credential-provider-node': 3.535.0 '@langchain/anthropic': 0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)) '@langchain/cohere': 0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)) - '@langchain/community': 0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1) + '@langchain/community': 0.2.20(bl2awxa6z6ereahlcpz2dlbf6q) '@langchain/google-genai': 0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8) '@langchain/google-vertexai': 0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8) '@langchain/groq': 0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)) @@ -23540,17 +23586,17 @@ snapshots: - openai - supports-color - langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0): + langchain@0.2.11(axios@1.6.7)(openai@4.53.0): dependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) - '@langchain/openai': 0.2.5(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0)) - '@langchain/textsplitters': 0.0.3(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) + '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) + '@langchain/openai': 0.2.5(langchain@0.2.11(axios@1.6.7)(openai@4.53.0)) + '@langchain/textsplitters': 0.0.3(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) binary-extensions: 2.2.0 js-tiktoken: 1.0.12 js-yaml: 4.1.0 jsonpointer: 5.0.1 langchainhub: 0.0.8 - langsmith: 0.1.34(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) + langsmith: 0.1.34(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) ml-distance: 4.0.1 openapi-types: 12.1.3 p-retry: 4.6.2 @@ -23560,7 +23606,6 @@ snapshots: zod-to-json-schema: 3.23.0(zod@3.23.8) optionalDependencies: axios: 1.6.7(debug@3.2.7) - jsdom: 23.0.1 transitivePeerDependencies: - encoding - openai @@ -23569,8 +23614,8 @@ snapshots: langchainhub@0.0.8: {} - ? langsmith@0.1.34(@langchain/core@0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - : dependencies: + langsmith@0.1.34(@langchain/core@0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)): + dependencies: '@types/uuid': 9.0.7 commander: 10.0.1 lodash.set: 4.3.2 @@ -23578,11 +23623,11 @@ snapshots: p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - langchain: 0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + langchain: 0.2.11(3eonfu4fvxvjb5zdrprnb3pokq) openai: 4.53.0(encoding@0.1.13) - langsmith@0.1.34(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0): + langsmith@0.1.34(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0): dependencies: '@types/uuid': 9.0.7 commander: 10.0.1 @@ -23591,24 +23636,24 @@ snapshots: p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) - langchain: 0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0) + '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) + langchain: 0.2.11(axios@1.6.7)(openai@4.53.0) openai: 4.53.0(encoding@0.1.13) optional: true - ? langsmith@0.1.39(@langchain/core@0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - : dependencies: + langsmith@0.1.39(@langchain/core@0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)))(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)): + dependencies: '@types/uuid': 9.0.7 commander: 10.0.1 p-queue: 6.6.2 p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1))(openai@4.53.0(encoding@0.1.13)) - langchain: 0.2.11(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@langchain/anthropic@0.2.9(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/cohere@0.0.10(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/community@0.2.20(@aws-sdk/client-bedrock-runtime@3.535.0)(@aws-sdk/client-s3@3.478.0)(@aws-sdk/credential-provider-node@3.535.0)(@getzep/zep-cloud@1.0.11)(@getzep/zep-js@0.9.0)(@google-ai/generativelanguage@2.5.0(encoding@0.1.13))(@google-cloud/storage@6.11.0(encoding@0.1.13))(@huggingface/inference@2.7.0)(@langchain/anthropic@0.2.9)(@langchain/cohere@0.0.10)(@langchain/google-genai@0.0.23)(@langchain/google-vertexai@0.0.21)(@langchain/groq@0.0.15)(@langchain/mistralai@0.0.27)(@langchain/ollama@0.0.2)(@mozilla/readability@0.5.0)(@pinecone-database/pinecone@3.0.0)(@qdrant/js-client-rest@1.9.0(typescript@5.5.2))(@smithy/eventstream-codec@2.2.0)(@smithy/protocol-http@3.3.0)(@smithy/signature-v4@2.2.1)(@smithy/util-utf8@2.3.0)(@supabase/postgrest-js@1.15.2)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(cohere-ai@7.10.1(encoding@0.1.13))(crypto-js@4.2.0)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(google-auth-library@9.10.0(encoding@0.1.13))(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(jsonwebtoken@9.0.2)(lodash@4.17.21)(mammoth@1.7.2)(mysql2@3.10.0)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(pg@8.11.3)(redis@4.6.12)(ws@8.17.1))(@langchain/google-genai@0.0.23(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/google-vertexai@0.0.21(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13))(zod@3.23.8))(@langchain/groq@0.0.15(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/mistralai@0.0.27(encoding@0.1.13)(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@langchain/ollama@0.0.2(langchain@0.2.11)(openai@4.53.0(encoding@0.1.13)))(@pinecone-database/pinecone@3.0.0)(@supabase/supabase-js@2.43.4)(@xata.io/client@0.28.4(typescript@5.5.2))(axios@1.6.7)(cheerio@1.0.0-rc.12)(d3-dsv@2.0.0)(encoding@0.1.13)(epub2@3.0.2(ts-toolbelt@9.6.0))(fast-xml-parser@4.3.5)(handlebars@4.7.8)(html-to-text@9.0.5)(ignore@5.2.4)(ioredis@5.3.2)(jsdom@23.0.1)(mammoth@1.7.2)(openai@4.53.0(encoding@0.1.13))(pdf-parse@1.1.1)(redis@4.6.12)(ws@8.17.1) + '@langchain/core': 0.2.18(langchain@0.2.11(3eonfu4fvxvjb5zdrprnb3pokq))(openai@4.53.0(encoding@0.1.13)) + langchain: 0.2.11(3eonfu4fvxvjb5zdrprnb3pokq) openai: 4.53.0(encoding@0.1.13) - langsmith@0.1.39(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0): + langsmith@0.1.39(@langchain/core@0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0))(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0): dependencies: '@types/uuid': 9.0.7 commander: 10.0.1 @@ -23616,8 +23661,8 @@ snapshots: p-retry: 4.6.2 uuid: 9.0.1 optionalDependencies: - '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0))(openai@4.53.0) - langchain: 0.2.11(axios@1.6.7)(jsdom@23.0.1)(openai@4.53.0) + '@langchain/core': 0.2.18(langchain@0.2.11(axios@1.6.7)(openai@4.53.0))(openai@4.53.0) + langchain: 0.2.11(axios@1.6.7)(openai@4.53.0) openai: 4.53.0(encoding@0.1.13) lazy-ass@1.6.0: {} @@ -23852,8 +23897,6 @@ snapshots: lunr@2.3.9: {} - luxon@3.3.0: {} - luxon@3.4.4: {} lz-string@1.5.0: {} @@ -24041,7 +24084,7 @@ snapshots: minifaker@1.34.1: dependencies: '@types/uuid': 8.3.4 - nanoid: 3.3.6 + nanoid: 3.3.7 uuid: 8.3.2 minimalistic-assert@1.0.1: {} @@ -24165,7 +24208,7 @@ snapshots: mlly@1.4.2: dependencies: acorn: 8.11.2 - pathe: 1.1.1 + pathe: 1.1.2 pkg-types: 1.0.3 ufo: 1.3.2 @@ -24216,7 +24259,7 @@ snapshots: mqtt-packet@9.0.0: dependencies: bl: 6.0.12 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) process-nextick-args: 2.0.1 transitivePeerDependencies: - supports-color @@ -24510,7 +24553,7 @@ snapshots: number-allocator@1.0.14: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) js-sdsl: 4.3.0 transitivePeerDependencies: - supports-color @@ -24861,8 +24904,6 @@ snapshots: path-type@5.0.0: {} - pathe@1.1.1: {} - pathe@1.1.2: {} pathval@1.1.1: {} @@ -25011,7 +25052,7 @@ snapshots: dependencies: jsonc-parser: 3.2.0 mlly: 1.4.2 - pathe: 1.1.1 + pathe: 1.1.2 pkg-types@1.1.3: dependencies: @@ -25064,9 +25105,9 @@ snapshots: postcss@8.4.31: dependencies: - nanoid: 3.3.6 + nanoid: 3.3.7 picocolors: 1.0.0 - source-map-js: 1.0.2 + source-map-js: 1.2.0 postcss@8.4.38: dependencies: @@ -26105,7 +26146,7 @@ snapshots: simple-websocket@9.1.0: dependencies: - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) queue-microtask: 1.2.3 randombytes: 2.1.0 readable-stream: 3.6.0 @@ -26510,7 +26551,7 @@ snapshots: dependencies: component-emitter: 1.3.0 cookiejar: 2.1.4 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) fast-safe-stringify: 2.1.1 form-data: 4.0.0 formidable: 3.5.1 @@ -27244,7 +27285,7 @@ snapshots: v8-to-istanbul@9.1.0: dependencies: - '@jridgewell/trace-mapping': 0.3.18 + '@jridgewell/trace-mapping': 0.3.25 '@types/istanbul-lib-coverage': 2.0.4 convert-source-map: 1.9.0 @@ -27266,8 +27307,8 @@ snapshots: vite-node@1.6.0(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1): dependencies: cac: 6.7.14 - debug: 4.3.4(supports-color@8.1.1) - pathe: 1.1.1 + debug: 4.3.5(supports-color@8.1.1) + pathe: 1.1.2 picocolors: 1.0.0 vite: 5.2.12(@types/node@18.16.16)(sass@1.64.1)(terser@5.16.1) transitivePeerDependencies: @@ -27323,11 +27364,11 @@ snapshots: '@vitest/utils': 1.6.0 acorn-walk: 8.3.2 chai: 4.3.10 - debug: 4.3.4(supports-color@8.1.1) + debug: 4.3.5(supports-color@8.1.1) execa: 8.0.1 local-pkg: 0.5.0 - magic-string: 0.30.8 - pathe: 1.1.1 + magic-string: 0.30.10 + pathe: 1.1.2 picocolors: 1.0.0 std-env: 3.6.0 strip-literal: 2.0.0 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index add43b6c008b6..2ce2d3d7dfeec 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -3,3 +3,29 @@ packages: - packages/@n8n/* - packages/@n8n_io/* - cypress + +catalog: + '@types/basic-auth': ^1.1.3 + '@types/express': ^4.17.21 + '@types/lodash': ^4.14.195 + '@types/uuid': ^8.3.2 + '@types/xml2js': ^0.4.14 + axios: 1.6.7 + basic-auth: 2.0.1 + fast-glob: 3.2.12 + form-data: 4.0.0 + lodash: 4.17.21 + luxon: 3.4.4 + nanoid: 3.3.6 + typedi: 0.10.0 + uuid: 8.3.2 + xml2js: 0.6.2 + +catalogs: + frontend: + '@vitest/coverage-v8': ^1.6.0 + vite: ^5.2.12 + vitest: ^1.6.0 + vitest-mock-extended: ^1.3.1 + vue: ^3.4.21 + vue-tsc: ^2.0.19 From d6770b5fcaec6438d677b918aaeb1669ad7424c2 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Tue, 30 Jul 2024 14:49:41 +0300 Subject: [PATCH 04/50] fix(core): Flush instance stopped event immediately (#10238) --- packages/cli/src/InternalHooks.ts | 171 +++++++----------- .../handlers/executions/executions.handler.ts | 4 +- .../v1/handlers/users/users.handler.ee.ts | 4 +- .../handlers/workflows/workflows.handler.ts | 6 +- .../email/UserManagementMailer.ts | 8 +- packages/cli/src/auth/methods/ldap.ts | 2 +- .../concurrency-control.service.ts | 2 +- .../cli/src/controllers/auth.controller.ts | 2 +- .../communityPackages.controller.ts | 2 - .../src/controllers/invitation.controller.ts | 2 +- packages/cli/src/controllers/me.controller.ts | 6 +- .../cli/src/controllers/owner.controller.ts | 2 +- .../controllers/passwordReset.controller.ts | 12 +- .../cli/src/controllers/users.controller.ts | 4 +- packages/cli/src/services/frontend.service.ts | 2 +- packages/cli/src/services/user.service.ts | 6 +- packages/cli/src/telemetry/index.ts | 118 ++++++------ .../telemetry-event-relay.service.ts | 64 ++++--- .../cli/src/workflows/workflow.service.ts | 2 +- .../cli/src/workflows/workflows.controller.ts | 4 +- packages/cli/test/unit/Telemetry.test.ts | 76 ++++---- 21 files changed, 224 insertions(+), 275 deletions(-) diff --git a/packages/cli/src/InternalHooks.ts b/packages/cli/src/InternalHooks.ts index e6418743d7b1a..dedfb46b0cb3f 100644 --- a/packages/cli/src/InternalHooks.ts +++ b/packages/cli/src/InternalHooks.ts @@ -43,13 +43,11 @@ export class InternalHooks { private readonly projectRelationRepository: ProjectRelationRepository, private readonly _eventBus: MessageEventBus, // needed until we decouple telemetry ) { - workflowStatisticsService.on( - 'telemetry.onFirstProductionWorkflowSuccess', - async (metrics) => await this.onFirstProductionWorkflowSuccess(metrics), + workflowStatisticsService.on('telemetry.onFirstProductionWorkflowSuccess', (metrics) => + this.onFirstProductionWorkflowSuccess(metrics), ); - workflowStatisticsService.on( - 'telemetry.onFirstWorkflowDataLoad', - async (metrics) => await this.onFirstWorkflowDataLoad(metrics), + workflowStatisticsService.on('telemetry.onFirstWorkflowDataLoad', (metrics) => + this.onFirstWorkflowDataLoad(metrics), ); } @@ -57,35 +55,29 @@ export class InternalHooks { await this.telemetry.init(); } - async onFrontendSettingsAPI(pushRef?: string): Promise { - return await this.telemetry.track('Session started', { session_id: pushRef }); + onFrontendSettingsAPI(pushRef?: string): void { + this.telemetry.track('Session started', { session_id: pushRef }); } - async onPersonalizationSurveySubmitted( - userId: string, - answers: Record, - ): Promise { + onPersonalizationSurveySubmitted(userId: string, answers: Record): void { const camelCaseKeys = Object.keys(answers); const personalizationSurveyData = { user_id: userId } as Record; camelCaseKeys.forEach((camelCaseKey) => { personalizationSurveyData[snakeCase(camelCaseKey)] = answers[camelCaseKey]; }); - return await this.telemetry.track( - 'User responded to personalization questions', - personalizationSurveyData, - ); + this.telemetry.track('User responded to personalization questions', personalizationSurveyData); } - async onWorkflowCreated( + onWorkflowCreated( user: User, workflow: IWorkflowBase, project: Project, publicApi: boolean, - ): Promise { + ): void { const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); - void this.telemetry.track('User created workflow', { + this.telemetry.track('User created workflow', { user_id: user.id, workflow_id: workflow.id, node_graph_string: JSON.stringify(nodeGraph), @@ -95,8 +87,8 @@ export class InternalHooks { }); } - async onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): Promise { - void this.telemetry.track('User deleted workflow', { + onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): void { + this.telemetry.track('User deleted workflow', { user_id: user.id, workflow_id: workflowId, public_api: publicApi, @@ -136,7 +128,7 @@ export class InternalHooks { (note) => note.overlapping, ).length; - void this.telemetry.track('User saved workflow', { + this.telemetry.track('User saved workflow', { user_id: user.id, workflow_id: workflow.id, node_graph_string: JSON.stringify(nodeGraph), @@ -155,7 +147,7 @@ export class InternalHooks { workflow: IWorkflowBase, runData?: IRun, userId?: string, - ): Promise { + ) { if (!workflow.id) { return; } @@ -165,8 +157,6 @@ export class InternalHooks { return; } - const promises = []; - const telemetryProperties: IExecutionTrackProperties = { workflow_id: workflow.id, is_manual: false, @@ -270,7 +260,7 @@ export class InternalHooks { node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode], }; - promises.push(this.telemetry.track('Manual node exec finished', telemetryPayload)); + this.telemetry.track('Manual node exec finished', telemetryPayload); } else { nodeGraphResult.webhookNodeNames.forEach((name: string) => { const execJson = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0] @@ -282,56 +272,52 @@ export class InternalHooks { } }); - promises.push( - this.telemetry.track('Manual workflow exec finished', manualExecEventProperties), - ); + this.telemetry.track('Manual workflow exec finished', manualExecEventProperties); } } } - void Promise.all([...promises, this.telemetry.trackWorkflowExecution(telemetryProperties)]); + this.telemetry.trackWorkflowExecution(telemetryProperties); } - async onWorkflowSharingUpdate(workflowId: string, userId: string, userList: string[]) { + onWorkflowSharingUpdate(workflowId: string, userId: string, userList: string[]) { const properties: ITelemetryTrackProperties = { workflow_id: workflowId, user_id_sharer: userId, user_id_list: userList, }; - return await this.telemetry.track('User updated workflow sharing', properties); + this.telemetry.track('User updated workflow sharing', properties); } async onN8nStop(): Promise { const timeoutPromise = new Promise((resolve) => { - setTimeout(() => { - resolve(); - }, 3000); + setTimeout(resolve, 3000); }); return await Promise.race([timeoutPromise, this.telemetry.trackN8nStop()]); } - async onUserDeletion(userDeletionData: { + onUserDeletion(userDeletionData: { user: User; telemetryData: ITelemetryUserDeletionData; publicApi: boolean; - }): Promise { - void this.telemetry.track('User deleted user', { + }) { + this.telemetry.track('User deleted user', { ...userDeletionData.telemetryData, user_id: userDeletionData.user.id, public_api: userDeletionData.publicApi, }); } - async onUserInvite(userInviteData: { + onUserInvite(userInviteData: { user: User; target_user_id: string[]; public_api: boolean; email_sent: boolean; invitee_role: string; - }): Promise { - void this.telemetry.track('User invited new user', { + }) { + this.telemetry.track('User invited new user', { user_id: userInviteData.user.id, target_user_id: userInviteData.target_user_id, public_api: userInviteData.public_api, @@ -340,7 +326,7 @@ export class InternalHooks { }); } - async onUserRoleChange(userRoleChangeData: { + onUserRoleChange(userRoleChangeData: { user: User; target_user_id: string; public_api: boolean; @@ -348,74 +334,53 @@ export class InternalHooks { }) { const { user, ...rest } = userRoleChangeData; - void this.telemetry.track('User changed role', { user_id: user.id, ...rest }); + this.telemetry.track('User changed role', { user_id: user.id, ...rest }); } - async onUserRetrievedUser(userRetrievedData: { - user_id: string; - public_api: boolean; - }): Promise { - return await this.telemetry.track('User retrieved user', userRetrievedData); + onUserRetrievedUser(userRetrievedData: { user_id: string; public_api: boolean }) { + this.telemetry.track('User retrieved user', userRetrievedData); } - async onUserRetrievedAllUsers(userRetrievedData: { - user_id: string; - public_api: boolean; - }): Promise { - return await this.telemetry.track('User retrieved all users', userRetrievedData); + onUserRetrievedAllUsers(userRetrievedData: { user_id: string; public_api: boolean }) { + this.telemetry.track('User retrieved all users', userRetrievedData); } - async onUserRetrievedExecution(userRetrievedData: { - user_id: string; - public_api: boolean; - }): Promise { - return await this.telemetry.track('User retrieved execution', userRetrievedData); + onUserRetrievedExecution(userRetrievedData: { user_id: string; public_api: boolean }) { + this.telemetry.track('User retrieved execution', userRetrievedData); } - async onUserRetrievedAllExecutions(userRetrievedData: { - user_id: string; - public_api: boolean; - }): Promise { - return await this.telemetry.track('User retrieved all executions', userRetrievedData); + onUserRetrievedAllExecutions(userRetrievedData: { user_id: string; public_api: boolean }) { + this.telemetry.track('User retrieved all executions', userRetrievedData); } - async onUserRetrievedWorkflow(userRetrievedData: { - user_id: string; - public_api: boolean; - }): Promise { - return await this.telemetry.track('User retrieved workflow', userRetrievedData); + onUserRetrievedWorkflow(userRetrievedData: { user_id: string; public_api: boolean }) { + this.telemetry.track('User retrieved workflow', userRetrievedData); } - async onUserRetrievedAllWorkflows(userRetrievedData: { - user_id: string; - public_api: boolean; - }): Promise { - return await this.telemetry.track('User retrieved all workflows', userRetrievedData); + onUserRetrievedAllWorkflows(userRetrievedData: { user_id: string; public_api: boolean }) { + this.telemetry.track('User retrieved all workflows', userRetrievedData); } - async onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }): Promise { - void this.telemetry.track('User changed personal settings', { + onUserUpdate(userUpdateData: { user: User; fields_changed: string[] }) { + this.telemetry.track('User changed personal settings', { user_id: userUpdateData.user.id, fields_changed: userUpdateData.fields_changed, }); } - async onUserInviteEmailClick(userInviteClickData: { - inviter: User; - invitee: User; - }): Promise { - void this.telemetry.track('User clicked invite link from email', { + onUserInviteEmailClick(userInviteClickData: { inviter: User; invitee: User }) { + this.telemetry.track('User clicked invite link from email', { user_id: userInviteClickData.invitee.id, }); } - async onUserPasswordResetEmailClick(userPasswordResetData: { user: User }): Promise { - void this.telemetry.track('User clicked password reset link from email', { + onUserPasswordResetEmailClick(userPasswordResetData: { user: User }) { + this.telemetry.track('User clicked password reset link from email', { user_id: userPasswordResetData.user.id, }); } - async onUserTransactionalEmail(userTransactionalEmailData: { + onUserTransactionalEmail(userTransactionalEmailData: { user_id: string; message_type: | 'Reset password' @@ -424,37 +389,34 @@ export class InternalHooks { | 'Workflow shared' | 'Credentials shared'; public_api: boolean; - }): Promise { - return await this.telemetry.track( - 'Instance sent transactional email to user', - userTransactionalEmailData, - ); + }) { + this.telemetry.track('Instance sent transactional email to user', userTransactionalEmailData); } - async onUserPasswordResetRequestClick(userPasswordResetData: { user: User }): Promise { - void this.telemetry.track('User requested password reset while logged out', { + onUserPasswordResetRequestClick(userPasswordResetData: { user: User }) { + this.telemetry.track('User requested password reset while logged out', { user_id: userPasswordResetData.user.id, }); } - async onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }): Promise { - return await this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData); + onInstanceOwnerSetup(instanceOwnerSetupData: { user_id: string }) { + this.telemetry.track('Owner finished instance setup', instanceOwnerSetupData); } - async onUserSignup( + onUserSignup( user: User, userSignupData: { user_type: AuthProviderType; was_disabled_ldap_user: boolean; }, - ): Promise { - void this.telemetry.track('User signed up', { + ) { + this.telemetry.track('User signed up', { user_id: user.id, ...userSignupData, }); } - async onEmailFailed(failedEmailData: { + onEmailFailed(failedEmailData: { user: User; message_type: | 'Reset password' @@ -463,8 +425,8 @@ export class InternalHooks { | 'Workflow shared' | 'Credentials shared'; public_api: boolean; - }): Promise { - void this.telemetry.track('Instance failed to send transactional email to user', { + }) { + this.telemetry.track('Instance failed to send transactional email to user', { user_id: failedEmailData.user.id, }); } @@ -472,21 +434,18 @@ export class InternalHooks { /* * Execution Statistics */ - async onFirstProductionWorkflowSuccess(data: { - user_id: string; - workflow_id: string; - }): Promise { - return await this.telemetry.track('Workflow first prod success', data); + onFirstProductionWorkflowSuccess(data: { user_id: string; workflow_id: string }) { + this.telemetry.track('Workflow first prod success', data); } - async onFirstWorkflowDataLoad(data: { + onFirstWorkflowDataLoad(data: { user_id: string; workflow_id: string; node_type: string; node_id: string; credential_type?: string; credential_id?: string; - }): Promise { - return await this.telemetry.track('Workflow first data fetched', data); + }) { + this.telemetry.track('Workflow first data fetched', data); } } diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts index cd936d1d1cec1..16ca8093cb325 100644 --- a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts @@ -78,7 +78,7 @@ export = { return res.status(404).json({ message: 'Not Found' }); } - void Container.get(InternalHooks).onUserRetrievedExecution({ + Container.get(InternalHooks).onUserRetrievedExecution({ user_id: req.user.id, public_api: true, }); @@ -129,7 +129,7 @@ export = { const count = await Container.get(ExecutionRepository).getExecutionsCountForPublicApi(filters); - void Container.get(InternalHooks).onUserRetrievedAllExecutions({ + Container.get(InternalHooks).onUserRetrievedAllExecutions({ user_id: req.user.id, public_api: true, }); diff --git a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts index 96b2d57239c57..0df22ec4aa9ef 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts +++ b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts @@ -33,7 +33,7 @@ export = { public_api: true, }; - void Container.get(InternalHooks).onUserRetrievedUser(telemetryData); + Container.get(InternalHooks).onUserRetrievedUser(telemetryData); return res.json(clean(user, { includeRole })); }, @@ -56,7 +56,7 @@ export = { public_api: true, }; - void Container.get(InternalHooks).onUserRetrievedAllUsers(telemetryData); + Container.get(InternalHooks).onUserRetrievedAllUsers(telemetryData); return res.json({ data: clean(users, { includeRole }), diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts index 943d53764daf3..5139cb686fd60 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -58,7 +58,7 @@ export = { ); await Container.get(ExternalHooks).run('workflow.afterCreate', [createdWorkflow]); - void Container.get(InternalHooks).onWorkflowCreated(req.user, createdWorkflow, project, true); + Container.get(InternalHooks).onWorkflowCreated(req.user, createdWorkflow, project, true); Container.get(EventService).emit('workflow-created', { workflow: createdWorkflow, user: req.user, @@ -101,7 +101,7 @@ export = { return res.status(404).json({ message: 'Not Found' }); } - void Container.get(InternalHooks).onUserRetrievedWorkflow({ + Container.get(InternalHooks).onUserRetrievedWorkflow({ user_id: req.user.id, public_api: true, }); @@ -163,7 +163,7 @@ export = { ...(!config.getEnv('workflowTagsDisabled') && { relations: ['tags'] }), }); - void Container.get(InternalHooks).onUserRetrievedAllWorkflows({ + Container.get(InternalHooks).onUserRetrievedAllWorkflows({ user_id: req.user.id, public_api: true, }); diff --git a/packages/cli/src/UserManagement/email/UserManagementMailer.ts b/packages/cli/src/UserManagement/email/UserManagementMailer.ts index 531ce24cd67c4..6d60c3b4907e1 100644 --- a/packages/cli/src/UserManagement/email/UserManagementMailer.ts +++ b/packages/cli/src/UserManagement/email/UserManagementMailer.ts @@ -112,7 +112,7 @@ export class UserManagementMailer { this.logger.info('Sent workflow shared email successfully', { sharerId: sharer.id }); - void Container.get(InternalHooks).onUserTransactionalEmail({ + Container.get(InternalHooks).onUserTransactionalEmail({ user_id: sharer.id, message_type: 'Workflow shared', public_api: false, @@ -120,7 +120,7 @@ export class UserManagementMailer { return result; } catch (e) { - void Container.get(InternalHooks).onEmailFailed({ + Container.get(InternalHooks).onEmailFailed({ user: sharer, message_type: 'Workflow shared', public_api: false, @@ -171,7 +171,7 @@ export class UserManagementMailer { this.logger.info('Sent credentials shared email successfully', { sharerId: sharer.id }); - void Container.get(InternalHooks).onUserTransactionalEmail({ + Container.get(InternalHooks).onUserTransactionalEmail({ user_id: sharer.id, message_type: 'Credentials shared', public_api: false, @@ -179,7 +179,7 @@ export class UserManagementMailer { return result; } catch (e) { - void Container.get(InternalHooks).onEmailFailed({ + Container.get(InternalHooks).onEmailFailed({ user: sharer, message_type: 'Credentials shared', public_api: false, diff --git a/packages/cli/src/auth/methods/ldap.ts b/packages/cli/src/auth/methods/ldap.ts index 09e8f38c87a03..c8946aec9321e 100644 --- a/packages/cli/src/auth/methods/ldap.ts +++ b/packages/cli/src/auth/methods/ldap.ts @@ -51,7 +51,7 @@ export const handleLdapLogin = async ( await updateLdapUserOnLocalDb(identity, ldapAttributesValues); } else { const user = await createLdapUserOnLocalDb(ldapAttributesValues, ldapId); - void Container.get(InternalHooks).onUserSignup(user, { + Container.get(InternalHooks).onUserSignup(user, { user_type: 'ldap', was_disabled_ldap_user: false, }); diff --git a/packages/cli/src/concurrency/concurrency-control.service.ts b/packages/cli/src/concurrency/concurrency-control.service.ts index c562448379fef..50c73fa668b4e 100644 --- a/packages/cli/src/concurrency/concurrency-control.service.ts +++ b/packages/cli/src/concurrency/concurrency-control.service.ts @@ -55,7 +55,7 @@ export class ConcurrencyControlService { this.productionQueue.on('concurrency-check', ({ capacity }) => { if (this.shouldReport(capacity)) { - void this.telemetry.track('User hit concurrency limit', { + this.telemetry.track('User hit concurrency limit', { threshold: CLOUD_TEMP_PRODUCTION_LIMIT - capacity, }); } diff --git a/packages/cli/src/controllers/auth.controller.ts b/packages/cli/src/controllers/auth.controller.ts index 7994221d9db6c..7711d95177e0f 100644 --- a/packages/cli/src/controllers/auth.controller.ts +++ b/packages/cli/src/controllers/auth.controller.ts @@ -179,7 +179,7 @@ export class AuthController { throw new BadRequestError('Invalid request'); } - void this.internalHooks.onUserInviteEmailClick({ inviter, invitee }); + this.internalHooks.onUserInviteEmailClick({ inviter, invitee }); this.eventService.emit('user-invite-email-click', { inviter, invitee }); const { firstName, lastName } = inviter; diff --git a/packages/cli/src/controllers/communityPackages.controller.ts b/packages/cli/src/controllers/communityPackages.controller.ts index b37d3df249ce4..1860e1df86652 100644 --- a/packages/cli/src/controllers/communityPackages.controller.ts +++ b/packages/cli/src/controllers/communityPackages.controller.ts @@ -9,7 +9,6 @@ import { Delete, Get, Middleware, Patch, Post, RestController, GlobalScope } fro import { NodeRequest } from '@/requests'; import type { InstalledPackages } from '@db/entities/InstalledPackages'; import type { CommunityPackages } from '@/Interfaces'; -import { InternalHooks } from '@/InternalHooks'; import { Push } from '@/push'; import { CommunityPackagesService } from '@/services/communityPackages.service'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; @@ -37,7 +36,6 @@ export function isNpmError(error: unknown): error is { code: number; stdout: str export class CommunityPackagesController { constructor( private readonly push: Push, - private readonly internalHooks: InternalHooks, private readonly communityPackagesService: CommunityPackagesService, private readonly eventService: EventService, ) {} diff --git a/packages/cli/src/controllers/invitation.controller.ts b/packages/cli/src/controllers/invitation.controller.ts index d89d077ae61b0..c32bf543002fd 100644 --- a/packages/cli/src/controllers/invitation.controller.ts +++ b/packages/cli/src/controllers/invitation.controller.ts @@ -168,7 +168,7 @@ export class InvitationController { this.authService.issueCookie(res, updatedUser, req.browserId); - void this.internalHooks.onUserSignup(updatedUser, { + this.internalHooks.onUserSignup(updatedUser, { user_type: 'email', was_disabled_ldap_user: false, }); diff --git a/packages/cli/src/controllers/me.controller.ts b/packages/cli/src/controllers/me.controller.ts index 62a2e101ff27d..3f9366b441260 100644 --- a/packages/cli/src/controllers/me.controller.ts +++ b/packages/cli/src/controllers/me.controller.ts @@ -101,7 +101,7 @@ export class MeController { this.authService.issueCookie(res, user, req.browserId); const fieldsChanged = Object.keys(payload); - void this.internalHooks.onUserUpdate({ user, fields_changed: fieldsChanged }); + this.internalHooks.onUserUpdate({ user, fields_changed: fieldsChanged }); this.eventService.emit('user-updated', { user, fieldsChanged }); const publicUser = await this.userService.toPublic(user); @@ -151,7 +151,7 @@ export class MeController { this.authService.issueCookie(res, updatedUser, req.browserId); - void this.internalHooks.onUserUpdate({ user: updatedUser, fields_changed: ['password'] }); + this.internalHooks.onUserUpdate({ user: updatedUser, fields_changed: ['password'] }); this.eventService.emit('user-updated', { user: updatedUser, fieldsChanged: ['password'] }); await this.externalHooks.run('user.password.update', [updatedUser.email, updatedUser.password]); @@ -186,7 +186,7 @@ export class MeController { this.logger.info('User survey updated successfully', { userId: req.user.id }); - void this.internalHooks.onPersonalizationSurveySubmitted(req.user.id, personalizationAnswers); + this.internalHooks.onPersonalizationSurveySubmitted(req.user.id, personalizationAnswers); return { success: true }; } diff --git a/packages/cli/src/controllers/owner.controller.ts b/packages/cli/src/controllers/owner.controller.ts index 6077026c90202..9b86ac41ab313 100644 --- a/packages/cli/src/controllers/owner.controller.ts +++ b/packages/cli/src/controllers/owner.controller.ts @@ -85,7 +85,7 @@ export class OwnerController { this.authService.issueCookie(res, owner, req.browserId); - void this.internalHooks.onInstanceOwnerSetup({ user_id: owner.id }); + this.internalHooks.onInstanceOwnerSetup({ user_id: owner.id }); return await this.userService.toPublic(owner, { posthog: this.postHog, withScopes: true }); } diff --git a/packages/cli/src/controllers/passwordReset.controller.ts b/packages/cli/src/controllers/passwordReset.controller.ts index c548e607fd9d4..e17a0f15efe63 100644 --- a/packages/cli/src/controllers/passwordReset.controller.ts +++ b/packages/cli/src/controllers/passwordReset.controller.ts @@ -120,7 +120,7 @@ export class PasswordResetController { domain: this.urlService.getInstanceBaseUrl(), }); } catch (error) { - void this.internalHooks.onEmailFailed({ + this.internalHooks.onEmailFailed({ user, message_type: 'Reset password', public_api: false, @@ -132,13 +132,13 @@ export class PasswordResetController { } this.logger.info('Sent password reset email successfully', { userId: user.id, email }); - void this.internalHooks.onUserTransactionalEmail({ + this.internalHooks.onUserTransactionalEmail({ user_id: id, message_type: 'Reset password', public_api: false, }); - void this.internalHooks.onUserPasswordResetRequestClick({ user }); + this.internalHooks.onUserPasswordResetRequestClick({ user }); this.eventService.emit('user-password-reset-request-click', { user }); } @@ -171,7 +171,7 @@ export class PasswordResetController { } this.logger.info('Reset-password token resolved successfully', { userId: user.id }); - void this.internalHooks.onUserPasswordResetEmailClick({ user }); + this.internalHooks.onUserPasswordResetEmailClick({ user }); this.eventService.emit('user-password-reset-email-click', { user }); } @@ -215,13 +215,13 @@ export class PasswordResetController { this.authService.issueCookie(res, user, req.browserId); - void this.internalHooks.onUserUpdate({ user, fields_changed: ['password'] }); + this.internalHooks.onUserUpdate({ user, fields_changed: ['password'] }); this.eventService.emit('user-updated', { user, fieldsChanged: ['password'] }); // if this user used to be an LDAP users const ldapIdentity = user?.authIdentities?.find((i) => i.providerType === 'ldap'); if (ldapIdentity) { - void this.internalHooks.onUserSignup(user, { + this.internalHooks.onUserSignup(user, { user_type: 'email', was_disabled_ldap_user: true, }); diff --git a/packages/cli/src/controllers/users.controller.ts b/packages/cli/src/controllers/users.controller.ts index 0fd8481ed8813..f0815aa54d405 100644 --- a/packages/cli/src/controllers/users.controller.ts +++ b/packages/cli/src/controllers/users.controller.ts @@ -253,7 +253,7 @@ export class UsersController { await trx.delete(User, { id: userToDelete.id }); }); - void this.internalHooks.onUserDeletion({ + this.internalHooks.onUserDeletion({ user: req.user, telemetryData, publicApi: false, @@ -294,7 +294,7 @@ export class UsersController { await this.userService.update(targetUser.id, { role: payload.newRoleName }); - void this.internalHooks.onUserRoleChange({ + this.internalHooks.onUserRoleChange({ user: req.user, target_user_id: targetUser.id, target_user_new_role: ['global', payload.newRoleName].join(' '), diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 918de1793518c..4ada98a9cf7d7 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -244,7 +244,7 @@ export class FrontendService { } getSettings(pushRef?: string): IN8nUISettings { - void this.internalHooks.onFrontendSettingsAPI(pushRef); + this.internalHooks.onFrontendSettingsAPI(pushRef); const restEndpoint = config.getEnv('endpoints.rest'); diff --git a/packages/cli/src/services/user.service.ts b/packages/cli/src/services/user.service.ts index cc50e09f74607..007054c6aa980 100644 --- a/packages/cli/src/services/user.service.ts +++ b/packages/cli/src/services/user.service.ts @@ -144,14 +144,14 @@ export class UserService { if (result.emailSent) { invitedUser.user.emailSent = true; delete invitedUser.user?.inviteAcceptUrl; - void Container.get(InternalHooks).onUserTransactionalEmail({ + Container.get(InternalHooks).onUserTransactionalEmail({ user_id: id, message_type: 'New user invite', public_api: false, }); } - void Container.get(InternalHooks).onUserInvite({ + Container.get(InternalHooks).onUserInvite({ user: owner, target_user_id: Object.values(toInviteUsers), public_api: false, @@ -164,7 +164,7 @@ export class UserService { }); } catch (e) { if (e instanceof Error) { - void Container.get(InternalHooks).onEmailFailed({ + Container.get(InternalHooks).onEmailFailed({ user: owner, message_type: 'New user invite', public_api: false, diff --git a/packages/cli/src/telemetry/index.ts b/packages/cli/src/telemetry/index.ts index f63caf9b2c7b6..2d762297304cc 100644 --- a/packages/cli/src/telemetry/index.ts +++ b/packages/cli/src/telemetry/index.ts @@ -88,30 +88,28 @@ export class Telemetry { ); // every 6 hours } - private async pulse(): Promise { + private async pulse() { if (!this.rudderStack) { return; } - const allPromises = Object.keys(this.executionCountsBuffer) - .filter((workflowId) => { - const data = this.executionCountsBuffer[workflowId]; - const sum = - (data.manual_error?.count ?? 0) + - (data.manual_success?.count ?? 0) + - (data.prod_error?.count ?? 0) + - (data.prod_success?.count ?? 0); - return sum > 0; - }) - .map(async (workflowId) => { - const promise = this.track('Workflow execution count', { - event_version: '2', - workflow_id: workflowId, - ...this.executionCountsBuffer[workflowId], - }); - - return await promise; + const workflowIdsToReport = Object.keys(this.executionCountsBuffer).filter((workflowId) => { + const data = this.executionCountsBuffer[workflowId]; + const sum = + (data.manual_error?.count ?? 0) + + (data.manual_success?.count ?? 0) + + (data.prod_error?.count ?? 0) + + (data.prod_success?.count ?? 0); + return sum > 0; + }); + + for (const workflowId of workflowIdsToReport) { + this.track('Workflow execution count', { + event_version: '2', + workflow_id: workflowId, + ...this.executionCountsBuffer[workflowId], }); + } this.executionCountsBuffer = {}; @@ -131,11 +129,11 @@ export class Telemetry { team_projects: (await Container.get(ProjectRepository).getProjectCounts()).team, project_role_count: await Container.get(ProjectRelationRepository).countUsersByRole(), }; - allPromises.push(this.track('pulse', pulsePacket)); - return await Promise.all(allPromises); + + this.track('pulse', pulsePacket); } - async trackWorkflowExecution(properties: IExecutionTrackProperties): Promise { + trackWorkflowExecution(properties: IExecutionTrackProperties) { if (this.rudderStack) { const execTime = new Date(); const workflowId = properties.workflow_id; @@ -164,66 +162,60 @@ export class Telemetry { properties.is_manual && properties.error_node_type?.startsWith('n8n-nodes-base') ) { - void this.track('Workflow execution errored', properties); + this.track('Workflow execution errored', properties); } } } async trackN8nStop(): Promise { clearInterval(this.pulseIntervalReference); - await this.track('User instance stopped'); - void Promise.all([this.postHog.stop(), this.rudderStack?.flush()]); + + this.track('User instance stopped'); + + await Promise.all([this.postHog.stop(), this.rudderStack?.flush()]); } - async identify(traits?: { - [key: string]: string | number | boolean | object | undefined | null; - }): Promise { + identify(traits?: { [key: string]: string | number | boolean | object | undefined | null }) { + if (!this.rudderStack) { + return; + } + const { instanceId } = this.instanceSettings; - return await new Promise((resolve) => { - if (this.rudderStack) { - this.rudderStack.identify( - { - userId: instanceId, - traits: { ...traits, instanceId }, - }, - resolve, - ); - } else { - resolve(); - } + + this.rudderStack.identify({ + userId: instanceId, + traits: { ...traits, instanceId }, }); } - async track( + track( eventName: string, properties: ITelemetryTrackProperties = {}, { withPostHog } = { withPostHog: false }, // whether to additionally track with PostHog - ): Promise { - const { instanceId } = this.instanceSettings; - return await new Promise((resolve) => { - if (this.rudderStack) { - const { user_id } = properties; - const updatedProperties = { - ...properties, - instance_id: instanceId, - version_cli: N8N_VERSION, - }; + ) { + if (!this.rudderStack) { + return; + } - const payload = { - userId: `${instanceId}${user_id ? `#${user_id}` : ''}`, - event: eventName, - properties: updatedProperties, - }; + const { instanceId } = this.instanceSettings; + const { user_id } = properties; + const updatedProperties = { + ...properties, + instance_id: instanceId, + version_cli: N8N_VERSION, + }; - if (withPostHog) { - this.postHog?.track(payload); - } + const payload = { + userId: `${instanceId}${user_id ? `#${user_id}` : ''}`, + event: eventName, + properties: updatedProperties, + }; - return this.rudderStack.track(payload, resolve); - } + if (withPostHog) { + this.postHog?.track(payload); + } - return resolve(); - }); + return this.rudderStack.track(payload); } // test helpers diff --git a/packages/cli/src/telemetry/telemetry-event-relay.service.ts b/packages/cli/src/telemetry/telemetry-event-relay.service.ts index 63c7fe4d11e74..bb1d4b8d560a2 100644 --- a/packages/cli/src/telemetry/telemetry-event-relay.service.ts +++ b/packages/cli/src/telemetry/telemetry-event-relay.service.ts @@ -104,7 +104,7 @@ export class TelemetryEventRelay { } private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) { - void this.telemetry.track('Project settings updated', { + this.telemetry.track('Project settings updated', { user_id: userId, role, // eslint-disable-next-line @typescript-eslint/no-shadow @@ -120,7 +120,7 @@ export class TelemetryEventRelay { removalType, targetProjectId, }: Event['team-project-deleted']) { - void this.telemetry.track('User deleted project', { + this.telemetry.track('User deleted project', { user_id: userId, role, project_id: projectId, @@ -130,7 +130,7 @@ export class TelemetryEventRelay { } private teamProjectCreated({ userId, role }: Event['team-project-created']) { - void this.telemetry.track('User created project', { + this.telemetry.track('User created project', { user_id: userId, role, }); @@ -142,7 +142,7 @@ export class TelemetryEventRelay { repoType, connected, }: Event['source-control-settings-updated']) { - void this.telemetry.track('User updated source control settings', { + this.telemetry.track('User updated source control settings', { branch_name: branchName, read_only_instance: readOnlyInstance, repo_type: repoType, @@ -155,7 +155,7 @@ export class TelemetryEventRelay { workflowConflicts, credConflicts, }: Event['source-control-user-started-pull-ui']) { - void this.telemetry.track('User started pull via UI', { + this.telemetry.track('User started pull via UI', { workflow_updates: workflowUpdates, workflow_conflicts: workflowConflicts, cred_conflicts: credConflicts, @@ -165,7 +165,7 @@ export class TelemetryEventRelay { private sourceControlUserFinishedPullUi({ workflowUpdates, }: Event['source-control-user-finished-pull-ui']) { - void this.telemetry.track('User finished pull via UI', { + this.telemetry.track('User finished pull via UI', { workflow_updates: workflowUpdates, }); } @@ -178,7 +178,7 @@ export class TelemetryEventRelay { workflow_updates: workflowUpdates, forced, }); - void this.telemetry.track('User pulled via API', { + this.telemetry.track('User pulled via API', { workflow_updates: workflowUpdates, forced, }); @@ -191,7 +191,7 @@ export class TelemetryEventRelay { credsEligibleWithConflicts, variablesEligible, }: Event['source-control-user-started-push-ui']) { - void this.telemetry.track('User started push via UI', { + this.telemetry.track('User started push via UI', { workflows_eligible: workflowsEligible, workflows_eligible_with_conflicts: workflowsEligibleWithConflicts, creds_eligible: credsEligible, @@ -206,7 +206,7 @@ export class TelemetryEventRelay { credsPushed, variablesPushed, }: Event['source-control-user-finished-push-ui']) { - void this.telemetry.track('User finished push via UI', { + this.telemetry.track('User finished push via UI', { workflows_eligible: workflowsEligible, workflows_pushed: workflowsPushed, creds_pushed: credsPushed, @@ -215,13 +215,13 @@ export class TelemetryEventRelay { } private licenseRenewalAttempted({ success }: Event['license-renewal-attempted']) { - void this.telemetry.track('Instance attempted to refresh license', { + this.telemetry.track('Instance attempted to refresh license', { success, }); } private variableCreated() { - void this.telemetry.track('User created variable'); + this.telemetry.track('User created variable'); } private externalSecretsProviderSettingsSaved({ @@ -231,7 +231,7 @@ export class TelemetryEventRelay { isNew, errorMessage, }: Event['external-secrets-provider-settings-saved']) { - void this.telemetry.track('User updated external secrets settings', { + this.telemetry.track('User updated external secrets settings', { user_id: userId, vault_type: vaultType, is_valid: isValid, @@ -241,7 +241,7 @@ export class TelemetryEventRelay { } private publicApiInvoked({ userId, path, method, apiVersion }: Event['public-api-invoked']) { - void this.telemetry.track('User invoked API', { + this.telemetry.track('User invoked API', { user_id: userId, path, method, @@ -252,7 +252,7 @@ export class TelemetryEventRelay { private publicApiKeyCreated(event: Event['public-api-key-created']) { const { user, publicApi } = event; - void this.telemetry.track('API key created', { + this.telemetry.track('API key created', { user_id: user.id, public_api: publicApi, }); @@ -261,7 +261,7 @@ export class TelemetryEventRelay { private publicApiKeyDeleted(event: Event['public-api-key-deleted']) { const { user, publicApi } = event; - void this.telemetry.track('API key deleted', { + this.telemetry.track('API key deleted', { user_id: user.id, public_api: publicApi, }); @@ -278,7 +278,7 @@ export class TelemetryEventRelay { packageAuthorEmail, failureReason, }: Event['community-package-installed']) { - void this.telemetry.track('cnr package install finished', { + this.telemetry.track('cnr package install finished', { user_id: user.id, input_string: inputString, package_name: packageName, @@ -300,7 +300,7 @@ export class TelemetryEventRelay { packageAuthor, packageAuthorEmail, }: Event['community-package-updated']) { - void this.telemetry.track('cnr package updated', { + this.telemetry.track('cnr package updated', { user_id: user.id, package_name: packageName, package_version_current: packageVersionCurrent, @@ -319,7 +319,7 @@ export class TelemetryEventRelay { packageAuthor, packageAuthorEmail, }: Event['community-package-deleted']) { - void this.telemetry.track('cnr package deleted', { + this.telemetry.track('cnr package deleted', { user_id: user.id, package_name: packageName, package_version: packageVersion, @@ -336,7 +336,7 @@ export class TelemetryEventRelay { projectId, projectType, }: Event['credentials-created']) { - void this.telemetry.track('User created credentials', { + this.telemetry.track('User created credentials', { user_id: user.id, credential_type: credentialType, credential_id: credentialId, @@ -353,7 +353,7 @@ export class TelemetryEventRelay { userIdsShareesAdded, shareesRemoved, }: Event['credentials-shared']) { - void this.telemetry.track('User updated cred sharing', { + this.telemetry.track('User updated cred sharing', { user_id: user.id, credential_type: credentialType, credential_id: credentialId, @@ -364,7 +364,7 @@ export class TelemetryEventRelay { } private credentialsUpdated({ user, credentialId, credentialType }: Event['credentials-updated']) { - void this.telemetry.track('User updated credentials', { + this.telemetry.track('User updated credentials', { user_id: user.id, credential_type: credentialType, credential_id: credentialId, @@ -372,7 +372,7 @@ export class TelemetryEventRelay { } private credentialsDeleted({ user, credentialId, credentialType }: Event['credentials-deleted']) { - void this.telemetry.track('User deleted credentials', { + this.telemetry.track('User deleted credentials', { user_id: user.id, credential_type: credentialType, credential_id: credentialId, @@ -385,7 +385,7 @@ export class TelemetryEventRelay { usersSynced, error, }: Event['ldap-general-sync-finished']) { - void this.telemetry.track('Ldap general sync finished', { + this.telemetry.track('Ldap general sync finished', { type, succeeded, users_synced: usersSynced, @@ -407,7 +407,7 @@ export class TelemetryEventRelay { loginLabel, loginEnabled, }: Event['ldap-settings-updated']) { - void this.telemetry.track('User updated Ldap settings', { + this.telemetry.track('User updated Ldap settings', { user_id: userId, loginIdAttribute, firstNameAttribute, @@ -424,11 +424,11 @@ export class TelemetryEventRelay { } private ldapLoginSyncFailed({ error }: Event['ldap-login-sync-failed']) { - void this.telemetry.track('Ldap login sync failed', { error }); + this.telemetry.track('Ldap login sync failed', { error }); } private loginFailedDueToLdapDisabled({ userId }: Event['login-failed-due-to-ldap-disabled']) { - void this.telemetry.track('User login failed since ldap disabled', { user_ud: userId }); + this.telemetry.track('User login failed since ldap disabled', { user_ud: userId }); } private async serverStarted() { @@ -489,12 +489,10 @@ export class TelemetryEventRelay { where: {}, }); - void Promise.all([ - this.telemetry.identify(info), - this.telemetry.track('Instance started', { - ...info, - earliest_workflow_created: firstWorkflow?.createdAt, - }), - ]); + this.telemetry.identify(info); + this.telemetry.track('Instance started', { + ...info, + earliest_workflow_created: firstWorkflow?.createdAt, + }); } } diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index cf742acecef2e..7c0cbfc242e1b 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -282,7 +282,7 @@ export class WorkflowService { await this.workflowRepository.delete(workflowId); await this.binaryDataService.deleteMany(idsForDeletion); - void Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false); + Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false); this.eventService.emit('workflow-deleted', { user, workflowId }); await this.externalHooks.run('workflow.afterDelete', [workflowId]); diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index d0ec019586279..05863edb84c04 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -179,7 +179,7 @@ export class WorkflowsController { delete savedWorkflowWithMetaData.shared; await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]); - void this.internalHooks.onWorkflowCreated(req.user, newWorkflow, project!, false); + this.internalHooks.onWorkflowCreated(req.user, newWorkflow, project!, false); this.eventService.emit('workflow-created', { user: req.user, workflow: newWorkflow }); const scopes = await this.workflowService.getWorkflowScopes(req.user, savedWorkflow.id); @@ -454,7 +454,7 @@ export class WorkflowsController { newShareeIds = toShare; }); - void this.internalHooks.onWorkflowSharingUpdate(workflowId, req.user.id, shareWithIds); + this.internalHooks.onWorkflowSharingUpdate(workflowId, req.user.id, shareWithIds); const projectsRelations = await this.projectRelationRepository.findBy({ projectId: In(newShareeIds), diff --git a/packages/cli/test/unit/Telemetry.test.ts b/packages/cli/test/unit/Telemetry.test.ts index 25f4a808675b0..e055abb1fdb74 100644 --- a/packages/cli/test/unit/Telemetry.test.ts +++ b/packages/cli/test/unit/Telemetry.test.ts @@ -14,11 +14,12 @@ describe('Telemetry', () => { let startPulseSpy: jest.SpyInstance; const spyTrack = jest.spyOn(Telemetry.prototype, 'track').mockName('track'); - const mockRudderStack: Pick = { - flush: (resolve) => resolve?.(), - identify: (data, resolve) => resolve?.(), - track: (data, resolve) => resolve?.(), - }; + const mockRudderStack = mock(); + mockRudderStack.track.mockImplementation(function (_, cb) { + cb?.(); + + return this; + }); let telemetry: Telemetry; const instanceId = 'Telemetry unit test'; @@ -26,9 +27,9 @@ describe('Telemetry', () => { const instanceSettings = mockInstance(InstanceSettings, { instanceId }); beforeAll(() => { - startPulseSpy = jest - .spyOn(Telemetry.prototype as any, 'startPulse') - .mockImplementation(() => {}); + // @ts-expect-error Spying on private method + startPulseSpy = jest.spyOn(Telemetry.prototype, 'startPulse').mockImplementation(() => {}); + jest.useFakeTimers(); jest.setSystemTime(testDateTime); config.set('diagnostics.enabled', true); @@ -49,7 +50,8 @@ describe('Telemetry', () => { await postHog.init(); telemetry = new Telemetry(mock(), postHog, mock(), instanceSettings, mock()); - (telemetry as any).rudderStack = mockRudderStack; + // @ts-expect-error Assigning to private property + telemetry.rudderStack = mockRudderStack; }); afterEach(async () => { @@ -79,30 +81,30 @@ describe('Telemetry', () => { payload.is_manual = true; payload.success = true; const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); fakeJestSystemTime('2022-01-01 12:30:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.is_manual = false; payload.success = true; const execTime2 = fakeJestSystemTime('2022-01-01 13:00:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); fakeJestSystemTime('2022-01-01 12:30:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.is_manual = true; payload.success = false; const execTime3 = fakeJestSystemTime('2022-01-01 14:00:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); fakeJestSystemTime('2022-01-01 12:30:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.is_manual = false; payload.success = false; const execTime4 = fakeJestSystemTime('2022-01-01 15:00:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); fakeJestSystemTime('2022-01-01 12:30:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); expect(spyTrack).toHaveBeenCalledTimes(0); @@ -127,9 +129,9 @@ describe('Telemetry', () => { }; const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); fakeJestSystemTime('2022-01-01 12:30:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); let execBuffer = telemetry.getCountsBuffer(); @@ -140,9 +142,9 @@ describe('Telemetry', () => { payload.error_node_type = 'n8n-nodes-base.node-type'; fakeJestSystemTime('2022-01-01 13:00:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); fakeJestSystemTime('2022-01-01 12:30:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); execBuffer = telemetry.getCountsBuffer(); @@ -163,7 +165,7 @@ describe('Telemetry', () => { // successful execution const execTime1 = fakeJestSystemTime('2022-01-01 12:00:00'); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); expect(spyTrack).toHaveBeenCalledTimes(0); @@ -179,7 +181,7 @@ describe('Telemetry', () => { payload.error_node_type = 'n8n-nodes-base.merge'; payload.workflow_id = '2'; - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); expect(spyTrack).toHaveBeenCalledTimes(0); @@ -198,12 +200,12 @@ describe('Telemetry', () => { payload.error_node_type = 'n8n-nodes-base.merge'; payload.workflow_id = '2'; - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.error_node_type = 'n8n-nodes-base.merge'; payload.workflow_id = '1'; - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); expect(spyTrack).toHaveBeenCalledTimes(0); execBuffer = telemetry.getCountsBuffer(); @@ -225,7 +227,7 @@ describe('Telemetry', () => { const execTime2 = fakeJestSystemTime('2022-01-01 12:00:00'); payload.error_node_type = 'custom-package.custom-node'; payload.success = false; - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); expect(spyTrack).toHaveBeenCalledTimes(0); @@ -249,7 +251,7 @@ describe('Telemetry', () => { payload.success = false; payload.error_node_type = 'n8n-nodes-base.merge'; payload.is_manual = true; - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); expect(spyTrack).toHaveBeenCalledTimes(1); @@ -327,27 +329,27 @@ describe('Telemetry', () => { error_node_type: 'custom-nodes-base.node-type', }; - await telemetry.trackWorkflowExecution(payload); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.is_manual = false; payload.success = true; - await telemetry.trackWorkflowExecution(payload); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.is_manual = true; payload.success = false; - await telemetry.trackWorkflowExecution(payload); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.is_manual = false; payload.success = false; - await telemetry.trackWorkflowExecution(payload); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); payload.workflow_id = '2'; - await telemetry.trackWorkflowExecution(payload); - await telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); + telemetry.trackWorkflowExecution(payload); expect(spyTrack).toHaveBeenCalledTimes(0); expect(pulseSpy).toBeCalledTimes(0); From f87854f8db360b7b870583753fcfb4af95adab8c Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Tue, 30 Jul 2024 15:43:05 +0300 Subject: [PATCH 05/50] fix(Wait Node): Authentication fix (#10236) --- packages/nodes-base/nodes/Form/utils.ts | 14 +++++---- packages/nodes-base/nodes/Wait/Wait.node.ts | 33 ++++++++++++++++++++- 2 files changed, 41 insertions(+), 6 deletions(-) diff --git a/packages/nodes-base/nodes/Form/utils.ts b/packages/nodes-base/nodes/Form/utils.ts index 1928c935bdea8..2e79c5699ce5f 100644 --- a/packages/nodes-base/nodes/Form/utils.ts +++ b/packages/nodes-base/nodes/Form/utils.ts @@ -140,8 +140,11 @@ const checkResponseModeConfiguration = (context: IWebhookFunctions) => { } }; -export async function formWebhook(context: IWebhookFunctions) { - const nodeVersion = context.getNode().typeVersion; +export async function formWebhook( + context: IWebhookFunctions, + authProperty = FORM_TRIGGER_AUTHENTICATION_PROPERTY, +) { + const node = context.getNode(); const options = context.getNodeParameter('options', {}) as { ignoreBots?: boolean; respondWithOptions?: { @@ -159,9 +162,10 @@ export async function formWebhook(context: IWebhookFunctions) { const req = context.getRequestObject(); try { - if (options.ignoreBots && isbot(req.headers['user-agent'])) + if (options.ignoreBots && isbot(req.headers['user-agent'])) { throw new WebhookAuthorizationError(403); - await validateWebhookAuthentication(context, FORM_TRIGGER_AUTHENTICATION_PROPERTY); + } + await validateWebhookAuthentication(context, authProperty); } catch (error) { if (error instanceof WebhookAuthorizationError) { res.writeHead(error.responseCode, { 'WWW-Authenticate': 'Basic realm="Webhook"' }); @@ -310,7 +314,7 @@ export async function formWebhook(context: IWebhookFunctions) { let { useWorkflowTimezone } = options; - if (useWorkflowTimezone === undefined && nodeVersion > 2) { + if (useWorkflowTimezone === undefined && node.typeVersion > 2) { useWorkflowTimezone = true; } diff --git a/packages/nodes-base/nodes/Wait/Wait.node.ts b/packages/nodes-base/nodes/Wait/Wait.node.ts index 181a208f923c3..98badea8331d8 100644 --- a/packages/nodes-base/nodes/Wait/Wait.node.ts +++ b/packages/nodes-base/nodes/Wait/Wait.node.ts @@ -237,6 +237,14 @@ export class Wait extends Webhook { inputs: ['main'], outputs: ['main'], credentials: credentialsProperty(this.authPropertyName), + hints: [ + { + message: + "When testing your workflow using the Editor UI, you can't see the rest of the execution following the Wait node. To inspect the execution results, enable Save Manual Executions in your Workflow settings so you can review the execution results there.", + location: 'outputPane', + whenToDisplay: 'beforeExecution', + }, + ], webhooks: [ { ...defaultWebhookDescription, @@ -294,6 +302,29 @@ export class Wait extends Webhook { default: 'timeInterval', description: 'Determines the waiting mode to use before the workflow continues', }, + { + displayName: 'Authentication', + name: 'incomingAuthentication', + type: 'options', + options: [ + { + name: 'Basic Auth', + value: 'basicAuth', + }, + { + name: 'None', + value: 'none', + }, + ], + default: 'none', + description: + 'If and how incoming resume-webhook-requests to $execution.resumeFormUrl should be authenticated for additional security', + displayOptions: { + show: { + resume: ['form'], + }, + }, + }, { ...authenticationProperty(this.authPropertyName), description: @@ -427,7 +458,7 @@ export class Wait extends Webhook { async webhook(context: IWebhookFunctions) { const resume = context.getNodeParameter('resume', 0) as string; - if (resume === 'form') return await formWebhook(context); + if (resume === 'form') return await formWebhook(context, this.authPropertyName); return await super.webhook(context); } From af695ebf934526d926ea87fe87df61aa73d70979 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 30 Jul 2024 14:58:07 +0200 Subject: [PATCH 06/50] feat: Support create, read, delete variables in Public API (#10241) --- .../variables/spec/paths/variables.id.yml | 16 ++ .../variables/spec/paths/variables.yml | 40 +++++ .../spec/schemas/parameters/variableId.yml | 6 + .../variables/spec/schemas/variable.yml | 17 ++ .../variables/spec/schemas/variableList.yml | 11 ++ .../handlers/variables/variables.handler.ts | 55 ++++++ packages/cli/src/PublicApi/v1/openapi.yml | 6 + .../shared/middlewares/global.middleware.ts | 10 ++ .../integration/publicApi/variables.test.ts | 167 ++++++++++++++++++ .../cli/test/integration/shared/db/users.ts | 6 +- .../test/integration/shared/db/variables.ts | 12 ++ 11 files changed, 345 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.id.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/parameters/variableId.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variable.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variableList.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/variables/variables.handler.ts create mode 100644 packages/cli/test/integration/publicApi/variables.test.ts create mode 100644 packages/cli/test/integration/shared/db/variables.ts diff --git a/packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.id.yml b/packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.id.yml new file mode 100644 index 0000000000000..79c65416b26f6 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.id.yml @@ -0,0 +1,16 @@ +delete: + x-eov-operation-id: deleteVariable + x-eov-operation-handler: v1/handlers/variables/variables.handler + tags: + - Variables + summary: Delete a variable + description: Delete a variable from your instance. + parameters: + - $ref: '../schemas/parameters/variableId.yml' + responses: + '204': + description: Operation successful. + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '404': + $ref: '../../../../shared/spec/responses/notFound.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.yml b/packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.yml new file mode 100644 index 0000000000000..7418c2fe05716 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/variables/spec/paths/variables.yml @@ -0,0 +1,40 @@ +post: + x-eov-operation-id: createVariable + x-eov-operation-handler: v1/handlers/variables/variables.handler + tags: + - Variables + summary: Create a variable + description: Create a variable in your instance. + requestBody: + description: Payload for variable to create. + content: + application/json: + schema: + $ref: '../schemas/variable.yml' + required: true + responses: + '201': + description: Operation successful. + '400': + $ref: '../../../../shared/spec/responses/badRequest.yml' + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' +get: + x-eov-operation-id: getVariables + x-eov-operation-handler: v1/handlers/variables/variables.handler + tags: + - Variables + summary: Retrieve variables + description: Retrieve variables from your instance. + parameters: + - $ref: '../../../../shared/spec/parameters/limit.yml' + - $ref: '../../../../shared/spec/parameters/cursor.yml' + responses: + '200': + description: Operation successful. + content: + application/json: + schema: + $ref: '../schemas/variableList.yml' + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/parameters/variableId.yml b/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/parameters/variableId.yml new file mode 100644 index 0000000000000..a886039d8e195 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/parameters/variableId.yml @@ -0,0 +1,6 @@ +name: id +in: path +description: The ID of the variable. +required: true +schema: + type: string diff --git a/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variable.yml b/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variable.yml new file mode 100644 index 0000000000000..319ad8d4403ae --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variable.yml @@ -0,0 +1,17 @@ +type: object +additionalProperties: false +required: + - key + - value +properties: + id: + type: string + readOnly: true + key: + type: string + value: + type: string + example: test + type: + type: string + readOnly: true diff --git a/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variableList.yml b/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variableList.yml new file mode 100644 index 0000000000000..95e66e180f426 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/variables/spec/schemas/variableList.yml @@ -0,0 +1,11 @@ +type: object +properties: + data: + type: array + items: + $ref: './variable.yml' + nextCursor: + type: string + description: Paginate through variables by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. + nullable: true + example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA diff --git a/packages/cli/src/PublicApi/v1/handlers/variables/variables.handler.ts b/packages/cli/src/PublicApi/v1/handlers/variables/variables.handler.ts new file mode 100644 index 0000000000000..3073044eac03d --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/variables/variables.handler.ts @@ -0,0 +1,55 @@ +import Container from 'typedi'; +import { VariablesRepository } from '@/databases/repositories/variables.repository'; +import { VariablesController } from '@/environments/variables/variables.controller.ee'; +import { globalScope, isLicensed, validCursor } from '../../shared/middlewares/global.middleware'; +import { encodeNextCursor } from '../../shared/services/pagination.service'; +import type { Response } from 'express'; +import type { VariablesRequest } from '@/requests'; +import type { PaginatedRequest } from '@/PublicApi/types'; + +type Create = VariablesRequest.Create; +type Delete = VariablesRequest.Delete; +type GetAll = PaginatedRequest; + +export = { + createVariable: [ + isLicensed('feat:variables'), + globalScope('variable:create'), + async (req: Create, res: Response) => { + await Container.get(VariablesController).createVariable(req); + + res.status(201).send(); + }, + ], + deleteVariable: [ + isLicensed('feat:variables'), + globalScope('variable:delete'), + async (req: Delete, res: Response) => { + await Container.get(VariablesController).deleteVariable(req); + + res.status(204).send(); + }, + ], + getVariables: [ + isLicensed('feat:variables'), + globalScope('variable:list'), + validCursor, + async (req: GetAll, res: Response) => { + const { offset = 0, limit = 100 } = req.query; + + const [variables, count] = await Container.get(VariablesRepository).findAndCount({ + skip: offset, + take: limit, + }); + + return res.json({ + data: variables, + nextCursor: encodeNextCursor({ + offset, + limit, + numberOfTotalRecords: count, + }), + }); + }, + ], +}; diff --git a/packages/cli/src/PublicApi/v1/openapi.yml b/packages/cli/src/PublicApi/v1/openapi.yml index 9d8249983511c..91eacd53767ad 100644 --- a/packages/cli/src/PublicApi/v1/openapi.yml +++ b/packages/cli/src/PublicApi/v1/openapi.yml @@ -30,6 +30,8 @@ tags: description: Operations about tags - name: SourceControl description: Operations about source control + - name: Variables + description: Operations about variables paths: /audit: @@ -64,6 +66,10 @@ paths: $ref: './handlers/users/spec/paths/users.id.yml' /source-control/pull: $ref: './handlers/sourceControl/spec/paths/sourceControl.yml' + /variables: + $ref: './handlers/variables/spec/paths/variables.yml' + /variables/{id}: + $ref: './handlers/variables/spec/paths/variables.id.yml' components: schemas: $ref: './shared/spec/schemas/_index.yml' diff --git a/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts b/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts index 6fa9bed113666..7e8c39bb91a3d 100644 --- a/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts +++ b/packages/cli/src/PublicApi/v1/shared/middlewares/global.middleware.ts @@ -9,6 +9,8 @@ import type { PaginatedRequest } from '../../../types'; import { decodeCursor } from '../services/pagination.service'; import type { Scope } from '@n8n/permissions'; import { userHasScope } from '@/permissions/checkAccess'; +import type { BooleanLicenseFeature } from '@/Interfaces'; +import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; const UNLIMITED_USERS_QUOTA = -1; @@ -86,3 +88,11 @@ export const validLicenseWithUserQuota = ( return next(); }; + +export const isLicensed = (feature: BooleanLicenseFeature) => { + return async (_: AuthenticatedRequest, res: express.Response, next: express.NextFunction) => { + if (Container.get(License).isFeatureEnabled(feature)) return next(); + + return res.status(403).json({ message: new FeatureNotLicensedError(feature).message }); + }; +}; diff --git a/packages/cli/test/integration/publicApi/variables.test.ts b/packages/cli/test/integration/publicApi/variables.test.ts new file mode 100644 index 0000000000000..b97c2467ebf63 --- /dev/null +++ b/packages/cli/test/integration/publicApi/variables.test.ts @@ -0,0 +1,167 @@ +import { setupTestServer } from '@test-integration/utils'; +import { createOwner } from '@test-integration/db/users'; +import { createVariable, getVariableOrFail } from '@test-integration/db/variables'; +import * as testDb from '../shared/testDb'; +import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; + +describe('Variables in Public API', () => { + const testServer = setupTestServer({ endpointGroups: ['publicApi'] }); + + beforeAll(async () => { + await testDb.init(); + }); + + beforeEach(async () => { + await testDb.truncate(['Variables', 'User']); + }); + + describe('GET /variables', () => { + it('if licensed, should return all variables with pagination', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:variables'); + const owner = await createOwner({ withApiKey: true }); + const variables = await Promise.all([createVariable(), createVariable(), createVariable()]); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).get('/variables'); + + /** + * Assert + */ + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('data'); + expect(response.body).toHaveProperty('nextCursor'); + expect(Array.isArray(response.body.data)).toBe(true); + expect(response.body.data.length).toBe(variables.length); + + variables.forEach(({ id, key, value }) => { + expect(response.body.data).toContainEqual(expect.objectContaining({ id, key, value })); + }); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).get('/variables'); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:variables').message, + ); + }); + }); + + describe('POST /variables', () => { + it('if licensed, should create a new variable', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:variables'); + const owner = await createOwner({ withApiKey: true }); + const variablePayload = { key: 'key', value: 'value' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .post('/variables') + .send(variablePayload); + + /** + * Assert + */ + expect(response.status).toBe(201); + await expect(getVariableOrFail(response.body.id)).resolves.toEqual( + expect.objectContaining(variablePayload), + ); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + const variablePayload = { key: 'key', value: 'value' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .post('/variables') + .send(variablePayload); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:variables').message, + ); + }); + }); + + describe('DELETE /variables/:id', () => { + it('if licensed, should delete a variable', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:variables'); + const owner = await createOwner({ withApiKey: true }); + const variable = await createVariable(); + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .delete(`/variables/${variable.id}`); + + /** + * Assert + */ + expect(response.status).toBe(204); + await expect(getVariableOrFail(variable.id)).rejects.toThrow(); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + const variable = await createVariable(); + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .delete(`/variables/${variable.id}`); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:variables').message, + ); + }); + }); +}); diff --git a/packages/cli/test/integration/shared/db/users.ts b/packages/cli/test/integration/shared/db/users.ts index aa7a3baaa726f..f125f5ccded4f 100644 --- a/packages/cli/test/integration/shared/db/users.ts +++ b/packages/cli/test/integration/shared/db/users.ts @@ -78,7 +78,11 @@ export async function createUserWithMfaEnabled( }; } -export async function createOwner() { +export async function createOwner({ withApiKey } = { withApiKey: false }) { + if (withApiKey) { + return await addApiKey(await createUser({ role: 'global:owner' })); + } + return await createUser({ role: 'global:owner' }); } diff --git a/packages/cli/test/integration/shared/db/variables.ts b/packages/cli/test/integration/shared/db/variables.ts new file mode 100644 index 0000000000000..68495da3b180f --- /dev/null +++ b/packages/cli/test/integration/shared/db/variables.ts @@ -0,0 +1,12 @@ +import { VariablesRepository } from '@/databases/repositories/variables.repository'; +import { generateNanoId } from '@/databases/utils/generators'; +import { randomString } from 'n8n-workflow'; +import Container from 'typedi'; + +export async function createVariable(key = randomString(5), value = randomString(5)) { + return await Container.get(VariablesRepository).save({ id: generateNanoId(), key, value }); +} + +export async function getVariableOrFail(id: string) { + return await Container.get(VariablesRepository).findOneOrFail({ where: { id } }); +} From 3ccb9df2f902e46f8cbb9c46c0727f29d752a773 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 30 Jul 2024 14:58:25 +0200 Subject: [PATCH 07/50] feat: Introduce Google Cloud Platform as external secrets provider (#10146) --- packages/cli/package.json | 1 + .../ExternalSecretsProviders.ee.ts | 2 + .../__tests__/azure-key-vault.test.ts | 4 +- .../__tests__/gcp-secrets-manager.test.ts | 87 +++++++++++ .../azure-key-vault/azure-key-vault.ts | 4 +- .../gcp-secrets-manager.ts | 136 ++++++++++++++++++ .../providers/gcp-secrets-manager/types.ts | 19 +++ .../src/assets/images/gcp-secrets-manager.svg | 9 ++ .../ExternalSecretsProviderImage.ee.vue | 2 + pnpm-lock.yaml | 44 +++--- 10 files changed, 283 insertions(+), 25 deletions(-) create mode 100644 packages/cli/src/ExternalSecrets/providers/__tests__/gcp-secrets-manager.test.ts create mode 100644 packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts create mode 100644 packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/types.ts create mode 100644 packages/editor-ui/src/assets/images/gcp-secrets-manager.svg diff --git a/packages/cli/package.json b/packages/cli/package.json index b18513c3a20c9..60ace14d9bce8 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -83,6 +83,7 @@ "dependencies": { "@azure/identity": "^4.3.0", "@azure/keyvault-secrets": "^4.8.0", + "@google-cloud/secret-manager": "^5.6.0", "@n8n/client-oauth2": "workspace:*", "@n8n/config": "workspace:*", "@n8n/localtunnel": "2.1.0", diff --git a/packages/cli/src/ExternalSecrets/ExternalSecretsProviders.ee.ts b/packages/cli/src/ExternalSecrets/ExternalSecretsProviders.ee.ts index 33228673c3bda..274a1d69aaa51 100644 --- a/packages/cli/src/ExternalSecrets/ExternalSecretsProviders.ee.ts +++ b/packages/cli/src/ExternalSecrets/ExternalSecretsProviders.ee.ts @@ -4,6 +4,7 @@ import { InfisicalProvider } from './providers/infisical'; import { VaultProvider } from './providers/vault'; import { AwsSecretsManager } from './providers/aws-secrets/aws-secrets-manager'; import { AzureKeyVault } from './providers/azure-key-vault/azure-key-vault'; +import { GcpSecretsManager } from './providers/gcp-secrets-manager/gcp-secrets-manager'; @Service() export class ExternalSecretsProviders { @@ -12,6 +13,7 @@ export class ExternalSecretsProviders { infisical: InfisicalProvider, vault: VaultProvider, azureKeyVault: AzureKeyVault, + gcpSecretsManager: GcpSecretsManager, }; getProvider(name: string): { new (): SecretsProvider } | null { diff --git a/packages/cli/src/ExternalSecrets/providers/__tests__/azure-key-vault.test.ts b/packages/cli/src/ExternalSecrets/providers/__tests__/azure-key-vault.test.ts index 329bfca2acd6e..8bacd6bbb9d21 100644 --- a/packages/cli/src/ExternalSecrets/providers/__tests__/azure-key-vault.test.ts +++ b/packages/cli/src/ExternalSecrets/providers/__tests__/azure-key-vault.test.ts @@ -37,7 +37,7 @@ describe('AzureKeyVault', () => { yield { name: 'secret1' }; yield { name: 'secret2' }; yield { name: 'secret3' }; // no value - yield { name: '#@&' }; // invalid name + yield { name: '#@&' }; // unsupported name }, })); @@ -65,6 +65,6 @@ describe('AzureKeyVault', () => { expect(azureKeyVault.getSecret('secret1')).toBe('value1'); expect(azureKeyVault.getSecret('secret2')).toBe('value2'); expect(azureKeyVault.getSecret('secret3')).toBeUndefined(); // no value - expect(azureKeyVault.getSecret('#@&')).toBeUndefined(); // invalid name + expect(azureKeyVault.getSecret('#@&')).toBeUndefined(); // unsupported name }); }); diff --git a/packages/cli/src/ExternalSecrets/providers/__tests__/gcp-secrets-manager.test.ts b/packages/cli/src/ExternalSecrets/providers/__tests__/gcp-secrets-manager.test.ts new file mode 100644 index 0000000000000..40673041a760c --- /dev/null +++ b/packages/cli/src/ExternalSecrets/providers/__tests__/gcp-secrets-manager.test.ts @@ -0,0 +1,87 @@ +import { mock } from 'jest-mock-extended'; +import { GcpSecretsManager } from '../gcp-secrets-manager/gcp-secrets-manager'; +import type { GcpSecretsManagerContext } from '../gcp-secrets-manager/types'; +import { SecretManagerServiceClient } from '@google-cloud/secret-manager'; +import type { google } from '@google-cloud/secret-manager/build/protos/protos'; + +jest.mock('@google-cloud/secret-manager'); + +type GcpSecretVersionResponse = google.cloud.secretmanager.v1.IAccessSecretVersionResponse; + +describe('GCP Secrets Manager', () => { + const gcpSecretsManager = new GcpSecretsManager(); + + afterEach(() => { + jest.clearAllMocks(); + }); + + it('should update cached secrets', async () => { + /** + * Arrange + */ + const PROJECT_ID = 'my-project-id'; + + const SECRETS: Record = { + secret1: 'value1', + secret2: 'value2', + secret3: '', // no value + '#@&': 'value', // unsupported name + }; + + await gcpSecretsManager.init( + mock({ + settings: { serviceAccountKey: `{ "project_id": "${PROJECT_ID}" }` }, + }), + ); + + const listSpy = jest + .spyOn(SecretManagerServiceClient.prototype, 'listSecrets') + // @ts-expect-error Partial mock + .mockResolvedValue([ + [ + { name: `projects/${PROJECT_ID}/secrets/secret1` }, + { name: `projects/${PROJECT_ID}/secrets/secret2` }, + { name: `projects/${PROJECT_ID}/secrets/secret3` }, + { name: `projects/${PROJECT_ID}/secrets/#@&` }, + ], + ]); + + const getSpy = jest + .spyOn(SecretManagerServiceClient.prototype, 'accessSecretVersion') + .mockImplementation(async ({ name }: { name: string }) => { + const secretName = name.split('/')[3]; + return [ + { payload: { data: Buffer.from(SECRETS[secretName]) } }, + ] as GcpSecretVersionResponse[]; + }); + + /** + * Act + */ + await gcpSecretsManager.connect(); + await gcpSecretsManager.update(); + + /** + * Assert + */ + expect(listSpy).toHaveBeenCalled(); + + expect(getSpy).toHaveBeenCalledWith({ + name: `projects/${PROJECT_ID}/secrets/secret1/versions/latest`, + }); + expect(getSpy).toHaveBeenCalledWith({ + name: `projects/${PROJECT_ID}/secrets/secret2/versions/latest`, + }); + expect(getSpy).toHaveBeenCalledWith({ + name: `projects/${PROJECT_ID}/secrets/secret3/versions/latest`, + }); + expect(getSpy).not.toHaveBeenCalledWith({ + name: `projects/${PROJECT_ID}/secrets/#@&/versions/latest`, + }); + + expect(gcpSecretsManager.getSecret('secret1')).toBe('value1'); + expect(gcpSecretsManager.getSecret('secret2')).toBe('value2'); + expect(gcpSecretsManager.getSecret('secret3')).toBeUndefined(); // no value + expect(gcpSecretsManager.getSecret('#@&')).toBeUndefined(); // unsupported name + }); +}); diff --git a/packages/cli/src/ExternalSecrets/providers/azure-key-vault/azure-key-vault.ts b/packages/cli/src/ExternalSecrets/providers/azure-key-vault/azure-key-vault.ts index c51e54e799ba1..7df196bdf96c4 100644 --- a/packages/cli/src/ExternalSecrets/providers/azure-key-vault/azure-key-vault.ts +++ b/packages/cli/src/ExternalSecrets/providers/azure-key-vault/azure-key-vault.ts @@ -74,7 +74,7 @@ export class AzureKeyVault implements SecretsProvider { const credential = new ClientSecretCredential(tenantId, clientId, clientSecret); this.client = new SecretClient(`https://${vaultName}.vault.azure.net/`, credential); this.state = 'connected'; - } catch (error) { + } catch { this.state = 'error'; } } @@ -86,7 +86,7 @@ export class AzureKeyVault implements SecretsProvider { await this.client.listPropertiesOfSecrets().next(); return [true]; } catch (error: unknown) { - return [false, error instanceof Error ? error.message : 'unknown error']; + return [false, error instanceof Error ? error.message : 'Unknown error']; } } diff --git a/packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts b/packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts new file mode 100644 index 0000000000000..64ed49f05cc41 --- /dev/null +++ b/packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/gcp-secrets-manager.ts @@ -0,0 +1,136 @@ +import { SecretManagerServiceClient as GcpClient } from '@google-cloud/secret-manager'; +import { DOCS_HELP_NOTICE, EXTERNAL_SECRETS_NAME_REGEX } from '@/ExternalSecrets/constants'; +import type { SecretsProvider, SecretsProviderState } from '@/Interfaces'; +import { jsonParse, type INodeProperties } from 'n8n-workflow'; +import type { + GcpSecretsManagerContext, + GcpSecretAccountKey, + RawGcpSecretAccountKey, +} from './types'; + +export class GcpSecretsManager implements SecretsProvider { + name = 'gcpSecretsManager'; + + displayName = 'GCP Secrets Manager'; + + state: SecretsProviderState = 'initializing'; + + properties: INodeProperties[] = [ + DOCS_HELP_NOTICE, + { + displayName: 'Service Account Key', + name: 'serviceAccountKey', + type: 'string', + default: '', + required: true, + typeOptions: { password: true }, + placeholder: 'e.g. { "type": "service_account", "project_id": "gcp-secrets-store", ... }', + hint: 'Content of JSON file downloaded from Google Cloud Console.', + noDataExpression: true, + }, + ]; + + private cachedSecrets: Record = {}; + + private client: GcpClient; + + private settings: GcpSecretAccountKey; + + async init(context: GcpSecretsManagerContext) { + this.settings = this.parseSecretAccountKey(context.settings.serviceAccountKey); + } + + async connect() { + const { projectId, privateKey, clientEmail } = this.settings; + + try { + this.client = new GcpClient({ + credentials: { client_email: clientEmail, private_key: privateKey }, + projectId, + }); + this.state = 'connected'; + } catch { + this.state = 'error'; + } + } + + async test(): Promise<[boolean] | [boolean, string]> { + if (!this.client) return [false, 'Failed to connect to GCP Secrets Manager']; + + try { + await this.client.initialize(); + return [true]; + } catch (error: unknown) { + return [false, error instanceof Error ? error.message : 'Unknown error']; + } + } + + async disconnect() { + // unused + } + + async update() { + const { projectId } = this.settings; + + const [rawSecretNames] = await this.client.listSecrets({ + parent: `projects/${projectId}`, + }); + + const secretNames = rawSecretNames.reduce((acc, cur) => { + if (!cur.name || !EXTERNAL_SECRETS_NAME_REGEX.test(cur.name)) return acc; + + const secretName = cur.name.split('/').pop(); + + if (secretName) acc.push(secretName); + + return acc; + }, []); + + const promises = secretNames.map(async (name) => { + const versions = await this.client.accessSecretVersion({ + name: `projects/${projectId}/secrets/${name}/versions/latest`, + }); + + if (!Array.isArray(versions) || !versions.length) return null; + + const [latestVersion] = versions; + + if (!latestVersion.payload?.data) return null; + + const value = latestVersion.payload.data.toString(); + + if (!value) return null; + + return { name, value }; + }); + + const results = await Promise.all(promises); + + this.cachedSecrets = results.reduce>((acc, cur) => { + if (cur) acc[cur.name] = cur.value; + return acc; + }, {}); + } + + getSecret(name: string) { + return this.cachedSecrets[name]; + } + + hasSecret(name: string) { + return name in this.cachedSecrets; + } + + getSecretNames() { + return Object.keys(this.cachedSecrets); + } + + private parseSecretAccountKey(privateKey: string): GcpSecretAccountKey { + const parsed = jsonParse(privateKey, { fallbackValue: {} }); + + return { + projectId: parsed?.project_id ?? '', + clientEmail: parsed?.client_email ?? '', + privateKey: parsed?.private_key ?? '', + }; + } +} diff --git a/packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/types.ts b/packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/types.ts new file mode 100644 index 0000000000000..c9611df9c4c2f --- /dev/null +++ b/packages/cli/src/ExternalSecrets/providers/gcp-secrets-manager/types.ts @@ -0,0 +1,19 @@ +import type { SecretsProviderSettings } from '@/Interfaces'; + +type JsonString = string; + +export type GcpSecretsManagerContext = SecretsProviderSettings<{ + serviceAccountKey: JsonString; +}>; + +export type RawGcpSecretAccountKey = { + project_id?: string; + private_key?: string; + client_email?: string; +}; + +export type GcpSecretAccountKey = { + projectId: string; + clientEmail: string; + privateKey: string; +}; diff --git a/packages/editor-ui/src/assets/images/gcp-secrets-manager.svg b/packages/editor-ui/src/assets/images/gcp-secrets-manager.svg new file mode 100644 index 0000000000000..a6e07c76819ad --- /dev/null +++ b/packages/editor-ui/src/assets/images/gcp-secrets-manager.svg @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/packages/editor-ui/src/components/ExternalSecretsProviderImage.ee.vue b/packages/editor-ui/src/components/ExternalSecretsProviderImage.ee.vue index dc40fed88c53c..8d8eec910917d 100644 --- a/packages/editor-ui/src/components/ExternalSecretsProviderImage.ee.vue +++ b/packages/editor-ui/src/components/ExternalSecretsProviderImage.ee.vue @@ -7,6 +7,7 @@ import doppler from '../assets/images/doppler.webp'; import vault from '../assets/images/hashicorp.webp'; import awsSecretsManager from '../assets/images/aws-secrets-manager.svg'; import azureKeyVault from '../assets/images/azure-key-vault.svg'; +import gcpSecretsManager from '../assets/images/gcp-secrets-manager.svg'; const props = defineProps<{ provider: ExternalSecretsProvider; @@ -20,6 +21,7 @@ const image = computed( vault, awsSecretsManager, azureKeyVault, + gcpSecretsManager, })[props.provider.name], ); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 58fd67d51719a..005cdc310122d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -608,6 +608,9 @@ importers: '@azure/keyvault-secrets': specifier: ^4.8.0 version: 4.8.0 + '@google-cloud/secret-manager': + specifier: ^5.6.0 + version: 5.6.0(encoding@0.1.13) '@n8n/client-oauth2': specifier: workspace:* version: link:../@n8n/client-oauth2 @@ -3360,6 +3363,10 @@ packages: resolution: {integrity: sha512-uWJJf6S2PJL7oZ4ezv16aZl9+IJqPo5GzUv1pZ3/qRiMj13p0ylEgX1+LxBpX71eEPKTwMHoJV2IBBe3EAq7Xw==} engines: {node: '>=14.0.0'} + '@google-cloud/secret-manager@5.6.0': + resolution: {integrity: sha512-0daW/OXQEVc6VQKPyJTQNyD+563I/TYQ7GCQJx4dq3lB666R9FUPvqHx9b/o/qQtZ5pfuoCbGZl3krpxgTSW8Q==} + engines: {node: '>=14.0.0'} + '@google-cloud/storage@6.11.0': resolution: {integrity: sha512-p5VX5K2zLTrMXlKdS1CiQNkKpygyn7CBFm5ZvfhVj6+7QUsjWvYx9YDMkYXdarZ6JDt4cxiu451y9QUIH82ZTw==} engines: {node: '>=12'} @@ -3376,11 +3383,6 @@ packages: resolution: {integrity: sha512-vYVqYzHicDqyKB+NQhAc54I1QWCBLCrYG6unqOIcBTHx+7x8C9lcoLj3KVJXs2VB4lUbpWY+Kk9NipcbXYWmvg==} engines: {node: '>=12.10.0'} - '@grpc/proto-loader@0.7.10': - resolution: {integrity: sha512-CAqDfoaQ8ykFd9zqBDn4k6iWT9loLAlc2ETmDFS9JCD70gDcnA4L3AFEo2iV7KyAtAAHFW9ftq1Fz+Vsgq80RQ==} - engines: {node: '>=6'} - hasBin: true - '@grpc/proto-loader@0.7.13': resolution: {integrity: sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==} engines: {node: '>=6'} @@ -15843,6 +15845,13 @@ snapshots: - encoding - supports-color + '@google-cloud/secret-manager@5.6.0(encoding@0.1.13)': + dependencies: + google-gax: 4.3.4(encoding@0.1.13) + transitivePeerDependencies: + - encoding + - supports-color + '@google-cloud/storage@6.11.0(encoding@0.1.13)': dependencies: '@google-cloud/paginator': 3.0.7 @@ -15875,13 +15884,6 @@ snapshots: '@grpc/proto-loader': 0.7.13 '@js-sdsl/ordered-map': 4.4.2 - '@grpc/proto-loader@0.7.10': - dependencies: - lodash.camelcase: 4.3.0 - long: 5.2.3 - protobufjs: 7.3.0 - yargs: 17.7.2 - '@grpc/proto-loader@0.7.13': dependencies: lodash.camelcase: 4.3.0 @@ -21305,7 +21307,7 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: @@ -21330,7 +21332,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2) eslint: 8.57.0 @@ -21350,7 +21352,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -21880,7 +21882,7 @@ snapshots: follow-redirects@1.15.6(debug@3.2.7): optionalDependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) follow-redirects@1.15.6(debug@4.3.4): optionalDependencies: @@ -22221,7 +22223,7 @@ snapshots: array-parallel: 0.1.3 array-series: 0.1.5 cross-spawn: 4.0.2 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -22255,7 +22257,7 @@ snapshots: google-gax@4.3.4(encoding@0.1.13): dependencies: '@grpc/grpc-js': 1.10.8 - '@grpc/proto-loader': 0.7.10 + '@grpc/proto-loader': 0.7.13 '@types/long': 4.0.2 abort-controller: 3.0.0 duplexify: 4.1.2 @@ -24914,7 +24916,7 @@ snapshots: pdf-parse@1.1.1: dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) node-ensure: 0.0.0 transitivePeerDependencies: - supports-color @@ -25826,7 +25828,7 @@ snapshots: rhea@1.0.24: dependencies: - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) transitivePeerDependencies: - supports-color @@ -26200,7 +26202,7 @@ snapshots: binascii: 0.0.2 bn.js: 5.2.1 browser-request: 0.3.3 - debug: 3.2.7(supports-color@5.5.0) + debug: 3.2.7(supports-color@8.1.1) expand-tilde: 2.0.2 extend: 3.0.2 fast-xml-parser: 4.2.7 From 452f52c124017e002e86c547ba42b1633b14beed Mon Sep 17 00:00:00 2001 From: jeanpaul Date: Tue, 30 Jul 2024 16:19:47 +0200 Subject: [PATCH 08/50] fix(Text Classifier Node): Use proper documentation URL and respect continueOnFail (#10216) --- .../TextClassifier/TextClassifier.node.ts | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts b/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts index 6a433eaac31a0..0033361a07396 100644 --- a/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/chains/TextClassifier/TextClassifier.node.ts @@ -46,7 +46,7 @@ export class TextClassifier implements INodeType { resources: { primaryDocumentation: [ { - url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.chainllm/', + url: 'https://docs.n8n.io/integrations/builtin/cluster-nodes/root-nodes/n8n-nodes-langchain.text-classifier/', }, ], }, @@ -203,20 +203,27 @@ export class TextClassifier implements INodeType { discard: 'If there is not a very fitting category, select none of the categories.', }[fallback]; - const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate( - `${options.systemPromptTemplate ?? SYSTEM_PROMPT_TEMPLATE} -{format_instructions} -${multiClassPrompt} -${fallbackPrompt}`, - ); - const returnData: INodeExecutionData[][] = Array.from( { length: categories.length + (fallback === 'other' ? 1 : 0) }, (_) => [], ); for (let itemIdx = 0; itemIdx < items.length; itemIdx++) { + const item = items[itemIdx]; + item.pairedItem = { item: itemIdx }; const input = this.getNodeParameter('inputText', itemIdx) as string; const inputPrompt = new HumanMessage(input); + + const systemPromptTemplateOpt = this.getNodeParameter( + 'options.systemPromptTemplate', + itemIdx, + ) as string; + const systemPromptTemplate = SystemMessagePromptTemplate.fromTemplate( + `${systemPromptTemplateOpt ?? SYSTEM_PROMPT_TEMPLATE} +{format_instructions} +${multiClassPrompt} +${fallbackPrompt}`, + ); + const messages = [ await systemPromptTemplate.format({ categories: categories.map((cat) => cat.category).join(', '), @@ -227,13 +234,27 @@ ${fallbackPrompt}`, const prompt = ChatPromptTemplate.fromMessages(messages); const chain = prompt.pipe(llm).pipe(parser).withConfig(getTracingConfig(this)); - const output = await chain.invoke(messages); - categories.forEach((cat, idx) => { - if (output[cat.category]) returnData[idx].push(items[itemIdx]); - }); - if (fallback === 'other' && output.fallback) - returnData[returnData.length - 1].push(items[itemIdx]); + try { + const output = await chain.invoke(messages); + + categories.forEach((cat, idx) => { + if (output[cat.category]) returnData[idx].push(item); + }); + if (fallback === 'other' && output.fallback) returnData[returnData.length - 1].push(item); + } catch (error) { + if (this.continueOnFail(error)) { + returnData[0].push({ + json: { error: error.message }, + pairedItem: { item: itemIdx }, + }); + + continue; + } + + throw error; + } } + return returnData; } } From d719899223907b20a17883a35e4ef637a3453532 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 30 Jul 2024 17:05:48 +0200 Subject: [PATCH 09/50] feat(core): Allow filtering workflows by project and transferring workflows in Public API (#10231) --- packages/cli/src/PublicApi/types.ts | 6 ++ .../spec/paths/workflows.id.transfer.yml | 31 ++++++++++ .../workflows/spec/paths/workflows.yml | 8 +++ .../handlers/workflows/workflows.handler.ts | 22 ++++++- packages/cli/src/PublicApi/v1/openapi.yml | 2 + .../repositories/sharedWorkflow.repository.ts | 2 +- .../integration/publicApi/workflows.test.ts | 62 +++++++++++++++++++ 7 files changed, 131 insertions(+), 2 deletions(-) create mode 100644 packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.id.transfer.yml diff --git a/packages/cli/src/PublicApi/types.ts b/packages/cli/src/PublicApi/types.ts index 2e80deb1afbf7..f58b521e04a88 100644 --- a/packages/cli/src/PublicApi/types.ts +++ b/packages/cli/src/PublicApi/types.ts @@ -72,6 +72,7 @@ export declare namespace WorkflowRequest { workflowId?: number; active: boolean; name?: string; + projectId?: string; } >; @@ -82,6 +83,11 @@ export declare namespace WorkflowRequest { type Activate = Get; type GetTags = Get; type UpdateTags = AuthenticatedRequest<{ id: string }, {}, TagEntity[]>; + type Transfer = AuthenticatedRequest< + { workflowId: string }, + {}, + { destinationProjectId: string } + >; } export declare namespace UserRequest { diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.id.transfer.yml b/packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.id.transfer.yml new file mode 100644 index 0000000000000..3997647e1dafa --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.id.transfer.yml @@ -0,0 +1,31 @@ +put: + x-eov-operation-id: transferWorkflow + x-eov-operation-handler: v1/handlers/workflows/workflows.handler + tags: + - Workflow + summary: Transfer a workflow to another project. + description: Transfer a workflow to another project. + parameters: + - $ref: '../schemas/parameters/workflowId.yml' + requestBody: + description: Destination project information for the workflow transfer. + content: + application/json: + schema: + type: object + properties: + destinationProjectId: + type: string + description: The ID of the project to transfer the workflow to. + required: + - destinationProjectId + required: true + responses: + '200': + description: Operation successful. + '400': + $ref: '../../../../shared/spec/responses/badRequest.yml' + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '404': + $ref: '../../../../shared/spec/responses/notFound.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.yml b/packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.yml index 6db149195df26..1024e36cb5948 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.yml +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/spec/paths/workflows.yml @@ -52,6 +52,14 @@ get: schema: type: string example: My Workflow + - name: projectId + in: query + required: false + explode: false + allowReserved: true + schema: + type: string + example: VmwOO9HeTEj20kxM - $ref: '../../../../shared/spec/parameters/limit.yml' - $ref: '../../../../shared/spec/parameters/cursor.yml' responses: diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts index 5139cb686fd60..ea5eebdfdbe76 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -33,6 +33,8 @@ import { TagRepository } from '@/databases/repositories/tag.repository'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; import { ProjectRepository } from '@/databases/repositories/project.repository'; import { EventService } from '@/eventbus/event.service'; +import { z } from 'zod'; +import { EnterpriseWorkflowService } from '@/workflows/workflow.service.ee'; export = { createWorkflow: [ @@ -67,6 +69,20 @@ export = { return res.json(createdWorkflow); }, ], + transferWorkflow: [ + projectScope('workflow:move', 'workflow'), + async (req: WorkflowRequest.Transfer, res: express.Response) => { + const body = z.object({ destinationProjectId: z.string() }).parse(req.body); + + await Container.get(EnterpriseWorkflowService).transferOne( + req.user, + req.params.workflowId, + body.destinationProjectId, + ); + + res.status(204).send(); + }, + ], deleteWorkflow: [ projectScope('workflow:delete', 'workflow'), async (req: WorkflowRequest.Get, res: express.Response): Promise => { @@ -112,7 +128,7 @@ export = { getWorkflows: [ validCursor, async (req: WorkflowRequest.GetAll, res: express.Response): Promise => { - const { offset = 0, limit = 100, active, tags, name } = req.query; + const { offset = 0, limit = 100, active, tags, name, projectId } = req.query; const where: FindOptionsWhere = { ...(active !== undefined && { active }), @@ -145,6 +161,10 @@ export = { workflows = workflows.filter((wf) => workflowIds.includes(wf.id)); } + if (projectId) { + workflows = workflows.filter((w) => w.projectId === projectId); + } + if (!workflows.length) { return res.status(200).json({ data: [], diff --git a/packages/cli/src/PublicApi/v1/openapi.yml b/packages/cli/src/PublicApi/v1/openapi.yml index 91eacd53767ad..fd3b286a17fb5 100644 --- a/packages/cli/src/PublicApi/v1/openapi.yml +++ b/packages/cli/src/PublicApi/v1/openapi.yml @@ -58,6 +58,8 @@ paths: $ref: './handlers/workflows/spec/paths/workflows.id.activate.yml' /workflows/{id}/deactivate: $ref: './handlers/workflows/spec/paths/workflows.id.deactivate.yml' + /workflows/{id}/transfer: + $ref: './handlers/workflows/spec/paths/workflows.id.transfer.yml' /workflows/{id}/tags: $ref: './handlers/workflows/spec/paths/workflows.id.tags.yml' /users: diff --git a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts index f8ff3523b2eab..4dc54935cb193 100644 --- a/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts +++ b/packages/cli/src/databases/repositories/sharedWorkflow.repository.ts @@ -175,7 +175,7 @@ export class SharedWorkflowRepository extends Repository { }, }); - return sharedWorkflows.map((sw) => sw.workflow); + return sharedWorkflows.map((sw) => ({ ...sw.workflow, projectId: sw.projectId })); } /** diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index 10737a30f0b12..855e69e3df12a 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -21,6 +21,8 @@ import { createTag } from '../shared/db/tags'; import { mockInstance } from '../../shared/mocking'; import type { SuperAgentTest } from '../shared/types'; import { Telemetry } from '@/telemetry'; +import { ProjectService } from '@/services/project.service'; +import { createTeamProject } from '@test-integration/db/projects'; mockInstance(Telemetry); @@ -265,6 +267,25 @@ describe('GET /workflows', () => { } }); + test('should return all user-accessible workflows filtered by `projectId`', async () => { + license.setQuota('quota:maxTeamProjects', 2); + const otherProject = await Container.get(ProjectService).createTeamProject( + 'Other project', + member, + ); + + await Promise.all([ + createWorkflow({}, member), + createWorkflow({ name: 'Other workflow' }, otherProject), + ]); + + const response = await authMemberAgent.get(`/workflows?projectId=${otherProject.id}`); + + expect(response.statusCode).toBe(200); + expect(response.body.data.length).toBe(1); + expect(response.body.data[0].name).toBe('Other workflow'); + }); + test('should return all owned workflows filtered by name', async () => { const workflowName = 'Workflow 1'; @@ -1465,3 +1486,44 @@ describe('PUT /workflows/:id/tags', () => { } }); }); + +describe('PUT /workflows/:id/transfer', () => { + test('should transfer workflow to project', async () => { + /** + * Arrange + */ + const firstProject = await createTeamProject('first-project', member); + const secondProject = await createTeamProject('secon-project', member); + const workflow = await createWorkflow({}, firstProject); + + /** + * Act + */ + const response = await authMemberAgent.put(`/workflows/${workflow.id}/transfer`).send({ + destinationProjectId: secondProject.id, + }); + + /** + * Assert + */ + expect(response.statusCode).toBe(204); + }); + + test('if no destination project, should reject', async () => { + /** + * Arrange + */ + const firstProject = await createTeamProject('first-project', member); + const workflow = await createWorkflow({}, firstProject); + + /** + * Act + */ + const response = await authMemberAgent.put(`/workflows/${workflow.id}/transfer`).send({}); + + /** + * Assert + */ + expect(response.statusCode).toBe(400); + }); +}); From 5b47c8b57b25528cd2d6f97bc6d98707d47f35bc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Tue, 30 Jul 2024 17:41:26 +0200 Subject: [PATCH 10/50] fix(editor): Fix custom API call notice (#10227) --- .../__tests__/useNodeHelpers.test.ts | 29 +++++++++++++++++++ .../src/composables/useNodeHelpers.ts | 8 +++-- 2 files changed, 34 insertions(+), 3 deletions(-) diff --git a/packages/editor-ui/src/composables/__tests__/useNodeHelpers.test.ts b/packages/editor-ui/src/composables/__tests__/useNodeHelpers.test.ts index 1969b3d5b8192..732bf51f5e677 100644 --- a/packages/editor-ui/src/composables/__tests__/useNodeHelpers.test.ts +++ b/packages/editor-ui/src/composables/__tests__/useNodeHelpers.test.ts @@ -3,6 +3,7 @@ import { createTestingPinia } from '@pinia/testing'; import { useNodeHelpers } from '@/composables/useNodeHelpers'; import { createTestNode } from '@/__tests__/mocks'; import { useWorkflowsStore } from '@/stores/workflows.store'; +import { CUSTOM_API_CALL_KEY } from '@/constants'; vi.mock('@/stores/workflows.store', () => ({ useWorkflowsStore: vi.fn(), @@ -17,6 +18,34 @@ describe('useNodeHelpers()', () => { vi.clearAllMocks(); }); + describe('isCustomApiCallSelected', () => { + test('should return `true` when resource includes `CUSTOM_API_CALL_KEY`', () => { + const nodeValues = { + parameters: { resource: CUSTOM_API_CALL_KEY }, + }; + expect(useNodeHelpers().isCustomApiCallSelected(nodeValues)).toBe(true); + }); + + test('should return `true` when operation includes `CUSTOM_API_CALL_KEY`', () => { + const nodeValues = { + parameters: { + operation: CUSTOM_API_CALL_KEY, + }, + }; + expect(useNodeHelpers().isCustomApiCallSelected(nodeValues)).toBe(true); + }); + + test('should return `false` when neither resource nor operation includes `CUSTOM_API_CALL_KEY`', () => { + const nodeValues = { + parameters: { + resource: 'users', + operation: 'get', + }, + }; + expect(useNodeHelpers().isCustomApiCallSelected(nodeValues)).toBe(false); + }); + }); + describe('getNodeInputData()', () => { it('should return an empty array when node is null', () => { const { getNodeInputData } = useNodeHelpers(); diff --git a/packages/editor-ui/src/composables/useNodeHelpers.ts b/packages/editor-ui/src/composables/useNodeHelpers.ts index 00f18041b0225..19e46339bcdd2 100644 --- a/packages/editor-ui/src/composables/useNodeHelpers.ts +++ b/packages/editor-ui/src/composables/useNodeHelpers.ts @@ -97,11 +97,13 @@ export function useNodeHelpers() { if (!isObject(parameters)) return false; - if ('resource' in parameters && 'operation' in parameters) { + if ('resource' in parameters || 'operation' in parameters) { const { resource, operation } = parameters; - if (!isString(resource) || !isString(operation)) return false; - return resource.includes(CUSTOM_API_CALL_KEY) || operation.includes(CUSTOM_API_CALL_KEY); + return ( + (isString(resource) && resource.includes(CUSTOM_API_CALL_KEY)) || + (isString(operation) && operation.includes(CUSTOM_API_CALL_KEY)) + ); } return false; From 3d23f2f333dff340ed2d48bc5fa4bd008fcfaca9 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Tue, 30 Jul 2024 21:24:01 +0300 Subject: [PATCH 11/50] test(core): Fix typing issues in tests (no-changelog) (#10244) --- package.json | 2 +- packages/cli/src/InternalHooks.ts | 3 +++ .../MessageEventBusLogWriterWorker.ts | 3 +-- packages/cli/src/middlewares/listQuery/index.ts | 10 +++++++++- packages/cli/test/integration/eventbus.ee.test.ts | 4 ++-- .../test/integration/executions.controller.test.ts | 5 ++++- .../integration/project.service.integration.test.ts | 12 ++---------- .../cli/test/integration/pruning.service.test.ts | 2 -- .../security-audit/InstanceRiskReporter.test.ts | 3 ++- packages/cli/test/integration/shared/db/workflows.ts | 3 ++- packages/cli/test/integration/shared/utils/index.ts | 5 +++-- .../workflows/workflow.service.ee.test.ts | 2 ++ ...ontroller-with-active-workflow-manager.ee.test.ts | 2 -- packages/cli/test/unit/ActiveExecutions.test.ts | 5 ++++- packages/cli/test/unit/Telemetry.test.ts | 5 ----- .../test/unit/services/orchestration.service.test.ts | 8 ++++---- .../services/workflow-statistics.service.test.ts | 4 ++-- packages/cli/tsconfig.json | 1 - 18 files changed, 41 insertions(+), 38 deletions(-) diff --git a/package.json b/package.json index 0f32e0ea56dde..3dd939d9d30fa 100644 --- a/package.json +++ b/package.json @@ -13,7 +13,7 @@ "build:backend": "turbo run build:backend", "build:frontend": "turbo run build:frontend", "build:nodes": "turbo run build:nodes", - "typecheck": "turbo --filter=!n8n typecheck", + "typecheck": "turbo typecheck", "dev": "turbo run dev --parallel --env-mode=loose --filter=!n8n-design-system --filter=!@n8n/chat", "dev:ai": "turbo run dev --parallel --env-mode=loose --filter=@n8n/nodes-langchain --filter=n8n --filter=n8n-core", "clean": "turbo run clean --parallel", diff --git a/packages/cli/src/InternalHooks.ts b/packages/cli/src/InternalHooks.ts index dedfb46b0cb3f..7912054085824 100644 --- a/packages/cli/src/InternalHooks.ts +++ b/packages/cli/src/InternalHooks.ts @@ -41,6 +41,9 @@ export class InternalHooks { private readonly sharedWorkflowRepository: SharedWorkflowRepository, workflowStatisticsService: WorkflowStatisticsService, private readonly projectRelationRepository: ProjectRelationRepository, + // Can't use @ts-expect-error because only dev time tsconfig considers this as an error, but not build time + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore - needed until we decouple telemetry private readonly _eventBus: MessageEventBus, // needed until we decouple telemetry ) { workflowStatisticsService.on('telemetry.onFirstProductionWorkflowSuccess', (metrics) => diff --git a/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriterWorker.ts b/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriterWorker.ts index 4686a1cf3c860..69d2e8ce26f73 100644 --- a/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriterWorker.ts +++ b/packages/cli/src/eventbus/MessageEventBusWriter/MessageEventBusLogWriterWorker.ts @@ -6,7 +6,6 @@ import type { MessageEventBusLogWriterOptions } from './MessageEventBusLogWriter let logFileBasePath = ''; let loggingPaused = true; let keepFiles = 10; -let fileStatTimer: NodeJS.Timer; let maxLogFileSizeInKB = 102400; function setLogFileBasePath(basePath: string) { @@ -117,7 +116,7 @@ if (!isMainThread) { if (logFileBasePath) { renameAndCreateLogs(); loggingPaused = false; - fileStatTimer = setInterval(async () => { + setInterval(async () => { await checkFileSize(buildLogFileNameWithCounter()); }, 5000); } diff --git a/packages/cli/src/middlewares/listQuery/index.ts b/packages/cli/src/middlewares/listQuery/index.ts index 0c8e2c7427c54..524dcb268e96d 100644 --- a/packages/cli/src/middlewares/listQuery/index.ts +++ b/packages/cli/src/middlewares/listQuery/index.ts @@ -1,8 +1,16 @@ import { filterListQueryMiddleware } from './filter'; import { selectListQueryMiddleware } from './select'; import { paginationListQueryMiddleware } from './pagination'; +import type { ListQuery } from '@/requests'; +import type { NextFunction, Response } from 'express'; -export const listQueryMiddleware = [ +export type ListQueryMiddleware = ( + req: ListQuery.Request, + res: Response, + next: NextFunction, +) => void; + +export const listQueryMiddleware: ListQueryMiddleware[] = [ filterListQueryMiddleware, selectListQueryMiddleware, paginationListQueryMiddleware, diff --git a/packages/cli/test/integration/eventbus.ee.test.ts b/packages/cli/test/integration/eventbus.ee.test.ts index a79c1388b39ee..f21822683b854 100644 --- a/packages/cli/test/integration/eventbus.ee.test.ts +++ b/packages/cli/test/integration/eventbus.ee.test.ts @@ -200,7 +200,7 @@ test('should anonymize audit message to syslog ', async () => { 'message', async function handler005(msg: { command: string; data: any }) { if (msg.command === 'appendMessageToLog') { - const sent = await eventBus.getEventsAll(); + await eventBus.getEventsAll(); await confirmIdInAll(testAuditMessage.id); expect(mockedSyslogClientLog).toHaveBeenCalled(); eventBus.logWriter.worker?.removeListener('message', handler005); @@ -217,7 +217,7 @@ test('should anonymize audit message to syslog ', async () => { 'message', async function handler006(msg: { command: string; data: any }) { if (msg.command === 'appendMessageToLog') { - const sent = await eventBus.getEventsAll(); + await eventBus.getEventsAll(); await confirmIdInAll(testAuditMessage.id); expect(mockedSyslogClientLog).toHaveBeenCalled(); syslogDestination.disable(); diff --git a/packages/cli/test/integration/executions.controller.test.ts b/packages/cli/test/integration/executions.controller.test.ts index ef04dde99bd81..1e6d65a4f8368 100644 --- a/packages/cli/test/integration/executions.controller.test.ts +++ b/packages/cli/test/integration/executions.controller.test.ts @@ -12,7 +12,10 @@ import { WaitTracker } from '@/WaitTracker'; import { createTeamProject, linkUserToProject } from './shared/db/projects'; mockInstance(WaitTracker); -mockInstance(ConcurrencyControlService, { isEnabled: false }); +mockInstance(ConcurrencyControlService, { + // @ts-expect-error Private property + isEnabled: false, +}); const testServer = setupTestServer({ endpointGroups: ['executions'] }); diff --git a/packages/cli/test/integration/project.service.integration.test.ts b/packages/cli/test/integration/project.service.integration.test.ts index 77d388c1617d6..7fe55ade06747 100644 --- a/packages/cli/test/integration/project.service.integration.test.ts +++ b/packages/cli/test/integration/project.service.integration.test.ts @@ -31,7 +31,6 @@ describe('ProjectService', () => { describe('when user has roles in projects where workflow is accessible', () => { it('should return roles and project IDs', async () => { const user = await createUser(); - const secondUser = await createUser(); // @TODO: Needed only to satisfy index in legacy column const firstProject = await createTeamProject('Project 1'); const secondProject = await createTeamProject('Project 2'); @@ -42,17 +41,15 @@ describe('ProjectService', () => { const workflow = await createWorkflow(); await sharedWorkflowRepository.insert({ - userId: user.id, // @TODO: Legacy column projectId: firstProject.id, workflowId: workflow.id, role: 'workflow:owner', }); await sharedWorkflowRepository.insert({ - userId: secondUser.id, // @TODO: Legacy column projectId: secondProject.id, workflowId: workflow.id, - role: 'workflow:user', + role: 'workflow:owner', }); const projectIds = await projectService.findProjectsWorkflowIsIn(workflow.id); @@ -63,9 +60,6 @@ describe('ProjectService', () => { describe('when user has no roles in projects where workflow is accessible', () => { it('should return project IDs but no roles', async () => { - const user = await createUser(); - const secondUser = await createUser(); // @TODO: Needed only to satisfy index in legacy column - const firstProject = await createTeamProject('Project 1'); const secondProject = await createTeamProject('Project 2'); @@ -74,17 +68,15 @@ describe('ProjectService', () => { const workflow = await createWorkflow(); await sharedWorkflowRepository.insert({ - userId: user.id, // @TODO: Legacy column projectId: firstProject.id, workflowId: workflow.id, role: 'workflow:owner', }); await sharedWorkflowRepository.insert({ - userId: secondUser.id, // @TODO: Legacy column projectId: secondProject.id, workflowId: workflow.id, - role: 'workflow:user', + role: 'workflow:owner', }); const projectIds = await projectService.findProjectsWorkflowIsIn(workflow.id); diff --git a/packages/cli/test/integration/pruning.service.test.ts b/packages/cli/test/integration/pruning.service.test.ts index a600b4aabdcab..09a43a5b5b123 100644 --- a/packages/cli/test/integration/pruning.service.test.ts +++ b/packages/cli/test/integration/pruning.service.test.ts @@ -98,7 +98,6 @@ describe('softDeleteOnPruningCycle()', () => { }); test.each<[ExecutionStatus, Partial]>([ - ['warning', { startedAt: now, stoppedAt: now }], ['unknown', { startedAt: now, stoppedAt: now }], ['canceled', { startedAt: now, stoppedAt: now }], ['crashed', { startedAt: now, stoppedAt: now }], @@ -191,7 +190,6 @@ describe('softDeleteOnPruningCycle()', () => { }); test.each<[ExecutionStatus, Partial]>([ - ['warning', { startedAt: yesterday, stoppedAt: yesterday }], ['unknown', { startedAt: yesterday, stoppedAt: yesterday }], ['canceled', { startedAt: yesterday, stoppedAt: yesterday }], ['crashed', { startedAt: yesterday, stoppedAt: yesterday }], diff --git a/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts b/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts index 5d359e0dba52b..5f59206426a05 100644 --- a/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts +++ b/packages/cli/test/integration/security-audit/InstanceRiskReporter.test.ts @@ -14,6 +14,7 @@ import config from '@/config'; import { generateNanoId } from '@db/utils/generators'; import { WorkflowRepository } from '@db/repositories/workflow.repository'; import Container from 'typedi'; +import { NodeConnectionType } from 'n8n-workflow'; let securityAuditService: SecurityAuditService; @@ -156,7 +157,7 @@ test('should not report webhooks validated by direct children', async () => { [ { node: 'My Node', - type: 'main', + type: NodeConnectionType.Main, index: 0, }, ], diff --git a/packages/cli/test/integration/shared/db/workflows.ts b/packages/cli/test/integration/shared/db/workflows.ts index f81ac044c3c1d..dc5490f9f3c61 100644 --- a/packages/cli/test/integration/shared/db/workflows.ts +++ b/packages/cli/test/integration/shared/db/workflows.ts @@ -9,6 +9,7 @@ import { WorkflowRepository } from '@db/repositories/workflow.repository'; import type { SharedWorkflow, WorkflowSharingRole } from '@db/entities/SharedWorkflow'; import { ProjectRepository } from '@/databases/repositories/project.repository'; import { Project } from '@/databases/entities/Project'; +import { NodeConnectionType } from 'n8n-workflow'; export async function createManyWorkflows( amount: number, @@ -157,7 +158,7 @@ export async function createWorkflowWithTrigger( position: [780, 300], }, ], - connections: { Cron: { main: [[{ node: 'Set', type: 'main', index: 0 }]] } }, + connections: { Cron: { main: [[{ node: 'Set', type: NodeConnectionType.Main, index: 0 }]] } }, ...attributes, }, user, diff --git a/packages/cli/test/integration/shared/utils/index.ts b/packages/cli/test/integration/shared/utils/index.ts index 96efb06039681..7abdb1a8e834d 100644 --- a/packages/cli/test/integration/shared/utils/index.ts +++ b/packages/cli/test/integration/shared/utils/index.ts @@ -96,9 +96,10 @@ export async function initBinaryDataService(mode: 'default' | 'filesystem' = 'de * Extract the value (token) of the auth cookie in a response. */ export function getAuthToken(response: request.Response, authCookieName = AUTH_COOKIE_NAME) { - const cookies: string[] = response.headers['set-cookie']; + const cookiesHeader = response.headers['set-cookie']; + if (!cookiesHeader) return undefined; - if (!cookies) return undefined; + const cookies = Array.isArray(cookiesHeader) ? cookiesHeader : [cookiesHeader]; const authCookie = cookies.find((c) => c.startsWith(`${authCookieName}=`)); diff --git a/packages/cli/test/integration/workflows/workflow.service.ee.test.ts b/packages/cli/test/integration/workflows/workflow.service.ee.test.ts index 55287c5f2210c..6e3b00bd58fcc 100644 --- a/packages/cli/test/integration/workflows/workflow.service.ee.test.ts +++ b/packages/cli/test/integration/workflows/workflow.service.ee.test.ts @@ -30,6 +30,8 @@ describe('EnterpriseWorkflowService', () => { Container.get(CredentialsRepository), mock(), mock(), + mock(), + mock(), ); }); diff --git a/packages/cli/test/integration/workflows/workflows.controller-with-active-workflow-manager.ee.test.ts b/packages/cli/test/integration/workflows/workflows.controller-with-active-workflow-manager.ee.test.ts index 607639b091a12..d32722c23dd86 100644 --- a/packages/cli/test/integration/workflows/workflows.controller-with-active-workflow-manager.ee.test.ts +++ b/packages/cli/test/integration/workflows/workflows.controller-with-active-workflow-manager.ee.test.ts @@ -11,7 +11,6 @@ import { Telemetry } from '@/telemetry'; mockInstance(Telemetry); let member: User; -let anotherMember: User; const testServer = utils.setupTestServer({ endpointGroups: ['workflows'], @@ -20,7 +19,6 @@ const testServer = utils.setupTestServer({ beforeAll(async () => { member = await createUser({ role: 'global:member' }); - anotherMember = await createUser({ role: 'global:member' }); await utils.initNodeTypes(); }); diff --git a/packages/cli/test/unit/ActiveExecutions.test.ts b/packages/cli/test/unit/ActiveExecutions.test.ts index b2454de87c5e1..0cc70a10a4e61 100644 --- a/packages/cli/test/unit/ActiveExecutions.test.ts +++ b/packages/cli/test/unit/ActiveExecutions.test.ts @@ -20,7 +20,10 @@ const executionRepository = mock({ createNewExecution, }); -const concurrencyControl = mockInstance(ConcurrencyControlService, { isEnabled: false }); +const concurrencyControl = mockInstance(ConcurrencyControlService, { + // @ts-expect-error Private property + isEnabled: false, +}); describe('ActiveExecutions', () => { let activeExecutions: ActiveExecutions; diff --git a/packages/cli/test/unit/Telemetry.test.ts b/packages/cli/test/unit/Telemetry.test.ts index e055abb1fdb74..af8445c814663 100644 --- a/packages/cli/test/unit/Telemetry.test.ts +++ b/packages/cli/test/unit/Telemetry.test.ts @@ -15,11 +15,6 @@ describe('Telemetry', () => { const spyTrack = jest.spyOn(Telemetry.prototype, 'track').mockName('track'); const mockRudderStack = mock(); - mockRudderStack.track.mockImplementation(function (_, cb) { - cb?.(); - - return this; - }); let telemetry: Telemetry; const instanceId = 'Telemetry unit test'; diff --git a/packages/cli/test/unit/services/orchestration.service.test.ts b/packages/cli/test/unit/services/orchestration.service.test.ts index 0cdd73f605963..75c7c06e40bb6 100644 --- a/packages/cli/test/unit/services/orchestration.service.test.ts +++ b/packages/cli/test/unit/services/orchestration.service.test.ts @@ -33,7 +33,7 @@ function setDefaultConfig() { config.set('generic.instanceType', 'main'); } -const workerRestartEventbusResponse: RedisServiceWorkerResponseObject = { +const workerRestartEventBusResponse: RedisServiceWorkerResponseObject = { senderId: 'test', workerId: 'test', command: 'restartEventBus', @@ -88,7 +88,7 @@ describe('Orchestration Service', () => { test('should handle worker responses', async () => { const response = await handleWorkerResponseMessageMain( - JSON.stringify(workerRestartEventbusResponse), + JSON.stringify(workerRestartEventBusResponse), ); expect(response.command).toEqual('restartEventBus'); }); @@ -108,7 +108,7 @@ describe('Orchestration Service', () => { test('should reject command messages from itself', async () => { const response = await handleCommandMessageMain( - JSON.stringify({ ...workerRestartEventbusResponse, senderId: queueModeId }), + JSON.stringify({ ...workerRestartEventBusResponse, senderId: queueModeId }), ); expect(response).toBeDefined(); expect(response!.command).toEqual('restartEventBus'); @@ -141,7 +141,7 @@ describe('Orchestration Service', () => { ); expect(helpers.debounceMessageReceiver).toHaveBeenCalledTimes(2); expect(res1!.payload).toBeUndefined(); - expect(res2!.payload!.result).toEqual('debounced'); + expect((res2!.payload as { result: string }).result).toEqual('debounced'); }); describe('shouldAddWebhooks', () => { diff --git a/packages/cli/test/unit/services/workflow-statistics.service.test.ts b/packages/cli/test/unit/services/workflow-statistics.service.test.ts index 9bd9864bb0446..6d9baf49ea939 100644 --- a/packages/cli/test/unit/services/workflow-statistics.service.test.ts +++ b/packages/cli/test/unit/services/workflow-statistics.service.test.ts @@ -118,7 +118,7 @@ describe('WorkflowStatisticsService', () => { }; const runData: IRun = { finished: false, - status: 'failed', + status: 'error', data: { resultData: { runData: {} } }, mode: 'internal' as WorkflowExecuteMode, startedAt: new Date(), @@ -206,7 +206,7 @@ describe('WorkflowStatisticsService', () => { test('should not send metrics for entries that already have the flag set', async () => { // Fetch data for workflow 2 which is set up to not be altered in the mocks - entityManager.insert.mockRejectedValueOnce(new QueryFailedError('', undefined, '')); + entityManager.insert.mockRejectedValueOnce(new QueryFailedError('', undefined, new Error())); const workflowId = '1'; const node = { id: 'abcde', diff --git a/packages/cli/tsconfig.json b/packages/cli/tsconfig.json index 008f1cb70d742..0efc9328610d8 100644 --- a/packages/cli/tsconfig.json +++ b/packages/cli/tsconfig.json @@ -2,7 +2,6 @@ "extends": ["../../tsconfig.json", "../../tsconfig.backend.json"], "compilerOptions": { "rootDir": ".", - "preserveSymlinks": true, "emitDecoratorMetadata": true, "experimentalDecorators": true, "baseUrl": "src", From 7e643589c67adc0218216ec4b89a95f0edfedbee Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Wed, 31 Jul 2024 09:09:54 +0200 Subject: [PATCH 12/50] fix(editor): Fix workflow execution list scrolling after filter change (#10226) --- cypress/e2e/20-workflow-executions.cy.ts | 44 +++++++++++++++++++ .../workflow/WorkflowExecutionsSidebar.vue | 3 ++ 2 files changed, 47 insertions(+) diff --git a/cypress/e2e/20-workflow-executions.cy.ts b/cypress/e2e/20-workflow-executions.cy.ts index 075923e940b4e..59f08c570b0ff 100644 --- a/cypress/e2e/20-workflow-executions.cy.ts +++ b/cypress/e2e/20-workflow-executions.cy.ts @@ -183,6 +183,50 @@ describe('Current Workflow Executions', () => { .invoke('attr', 'title') .should('eq', newWorkflowName); }); + + it('should load items and auto scroll after filter change', () => { + createMockExecutions(); + createMockExecutions(); + cy.intercept('GET', '/rest/executions?filter=*').as('getExecutions'); + + executionsTab.actions.switchToExecutionsTab(); + + cy.wait(['@getExecutions']); + + executionsTab.getters.executionsList().scrollTo(0, 500).wait(0); + + executionsTab.getters.executionListItems().eq(10).click(); + + cy.getByTestId('executions-filter-button').click(); + cy.getByTestId('executions-filter-status-select').should('be.visible').click(); + getVisibleSelect().find('li:contains("Error")').click(); + + executionsTab.getters.executionListItems().should('have.length', 5); + executionsTab.getters.successfulExecutionListItems().should('have.length', 1); + executionsTab.getters.failedExecutionListItems().should('have.length', 4); + + cy.getByTestId('executions-filter-button').click(); + cy.getByTestId('executions-filter-status-select').should('be.visible').click(); + getVisibleSelect().find('li:contains("Success")').click(); + + // check if the list is scrolled + executionsTab.getters.executionListItems().eq(10).should('be.visible'); + executionsTab.getters.executionsList().then(($el) => { + const { scrollTop, scrollHeight, clientHeight } = $el[0]; + expect(scrollTop).to.be.greaterThan(0); + expect(scrollTop + clientHeight).to.be.lessThan(scrollHeight); + + // scroll to the bottom + $el[0].scrollTo(0, scrollHeight); + executionsTab.getters.executionListItems().should('have.length', 18); + executionsTab.getters.successfulExecutionListItems().should('have.length', 18); + executionsTab.getters.failedExecutionListItems().should('have.length', 0); + }); + + cy.getByTestId('executions-filter-button').click(); + cy.getByTestId('executions-filter-reset-button').should('be.visible').click(); + executionsTab.getters.executionListItems().eq(11).should('be.visible'); + }); }); const createMockExecutions = () => { diff --git a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsSidebar.vue b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsSidebar.vue index df30fd0eb0438..0186ef4830a1a 100644 --- a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsSidebar.vue +++ b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsSidebar.vue @@ -186,6 +186,9 @@ export default defineComponent({ this.$emit('refresh'); }, onFilterChanged(filter: ExecutionFilterType) { + this.autoScrollDeps.activeExecutionSet = false; + this.autoScrollDeps.scroll = true; + this.mountedItems = []; this.$emit('filterUpdated', filter); }, reloadExecutions(): void { From 519fc4d75325a80b84cc4dcacf52d6f4c02e3a44 Mon Sep 17 00:00:00 2001 From: Shireen Missi <94372015+ShireenMissi@users.noreply.github.com> Date: Wed, 31 Jul 2024 08:39:37 +0100 Subject: [PATCH 13/50] fix(Postgres Node): Expressions in query parameters for Postgres executeQuery operation (#10217) --- .../nodes/Postgres/Postgres.node.ts | 3 +- .../database/executeQuery.operation.ts | 38 +++++++++++++++---- .../Postgres/v2/actions/versionDescription.ts | 2 +- 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/packages/nodes-base/nodes/Postgres/Postgres.node.ts b/packages/nodes-base/nodes/Postgres/Postgres.node.ts index 05e09dff4dfcb..0e1760faeac94 100644 --- a/packages/nodes-base/nodes/Postgres/Postgres.node.ts +++ b/packages/nodes-base/nodes/Postgres/Postgres.node.ts @@ -11,7 +11,7 @@ export class Postgres extends VersionedNodeType { name: 'postgres', icon: 'file:postgres.svg', group: ['input'], - defaultVersion: 2.4, + defaultVersion: 2.5, description: 'Get, add and update data in Postgres', parameterPane: 'wide', }; @@ -23,6 +23,7 @@ export class Postgres extends VersionedNodeType { 2.2: new PostgresV2(baseDescription), 2.3: new PostgresV2(baseDescription), 2.4: new PostgresV2(baseDescription), + 2.5: new PostgresV2(baseDescription), }; super(nodeVersions, baseDescription); diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts index 2b8449e983ccb..4e6100e5755a2 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/database/executeQuery.operation.ts @@ -3,6 +3,7 @@ import type { IExecuteFunctions, INodeExecutionData, INodeProperties, + NodeParameterValueType, } from 'n8n-workflow'; import { NodeOperationError } from 'n8n-workflow'; @@ -78,22 +79,45 @@ export async function execute( const rawReplacements = (node.parameters.options as IDataObject)?.queryReplacement as string; - if (rawReplacements) { - const rawValues = rawReplacements - .replace(/^=+/, '') + const stringToArray = (str: NodeParameterValueType | undefined) => { + if (!str) return []; + return String(str) .split(',') .filter((entry) => entry) .map((entry) => entry.trim()); + }; - for (const rawValue of rawValues) { - const resolvables = getResolvables(rawValue); + if (rawReplacements) { + const nodeVersion = nodeOptions.nodeVersion as number; + if (nodeVersion >= 2.5) { + const rawValues = rawReplacements.replace(/^=+/, ''); + const resolvables = getResolvables(rawValues); if (resolvables.length) { for (const resolvable of resolvables) { - values.push(this.evaluateExpression(`${resolvable}`, i) as IDataObject); + const evaluatedValues = stringToArray(this.evaluateExpression(`${resolvable}`, i)); + if (evaluatedValues.length) values.push(...evaluatedValues); } } else { - values.push(rawValue); + values.push(...stringToArray(rawValues)); + } + } else { + const rawValues = rawReplacements + .replace(/^=+/, '') + .split(',') + .filter((entry) => entry) + .map((entry) => entry.trim()); + + for (const rawValue of rawValues) { + const resolvables = getResolvables(rawValue); + + if (resolvables.length) { + for (const resolvable of resolvables) { + values.push(this.evaluateExpression(`${resolvable}`, i) as IDataObject); + } + } else { + values.push(rawValue); + } } } } diff --git a/packages/nodes-base/nodes/Postgres/v2/actions/versionDescription.ts b/packages/nodes-base/nodes/Postgres/v2/actions/versionDescription.ts index 1687accf1cde4..de6047003de9e 100644 --- a/packages/nodes-base/nodes/Postgres/v2/actions/versionDescription.ts +++ b/packages/nodes-base/nodes/Postgres/v2/actions/versionDescription.ts @@ -8,7 +8,7 @@ export const versionDescription: INodeTypeDescription = { name: 'postgres', icon: 'file:postgres.svg', group: ['input'], - version: [2, 2.1, 2.2, 2.3, 2.4], + version: [2, 2.1, 2.2, 2.3, 2.4, 2.5], subtitle: '={{ $parameter["operation"] }}', description: 'Get, add and update data in Postgres', defaults: { From efee25ddaaf072713d56d956d57fc388cb427811 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 31 Jul 2024 11:53:26 +0200 Subject: [PATCH 14/50] test(core): Stop showing JWT warning during test runs (no-changelog) (#10255) --- packages/cli/src/config/index.ts | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/packages/cli/src/config/index.ts b/packages/cli/src/config/index.ts index f743e7961dba5..dedc803839292 100644 --- a/packages/cli/src/config/index.ts +++ b/packages/cli/src/config/index.ts @@ -96,9 +96,10 @@ config.validate({ }); const userManagement = config.get('userManagement'); if (userManagement.jwtRefreshTimeoutHours >= userManagement.jwtSessionDurationHours) { - console.warn( - 'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS needs to smaller than N8N_USER_MANAGEMENT_JWT_DURATION_HOURS. Setting N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS to 0 for now.', - ); + if (!inTest) + console.warn( + 'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS needs to smaller than N8N_USER_MANAGEMENT_JWT_DURATION_HOURS. Setting N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS to 0 for now.', + ); config.set('userManagement.jwtRefreshTimeoutHours', 0); } From 3e96b293329525c9d4b2fcef87b3803e458c8e7f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 31 Jul 2024 12:22:52 +0200 Subject: [PATCH 15/50] fix(core): Restore log event `n8n.workflow.failed` (#10253) --- .../cli/src/WorkflowExecuteAdditionalData.ts | 2 + packages/cli/src/WorkflowRunner.ts | 1 + .../audit-event-relay.service.test.ts | 69 ++++++++++++++++++- .../src/eventbus/audit-event-relay.service.ts | 23 ++++++- packages/cli/src/eventbus/event.types.ts | 3 +- .../executions/execution-recovery.service.ts | 1 + 6 files changed, 94 insertions(+), 5 deletions(-) diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 754cfea693c3d..98ad9acde2936 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -651,6 +651,7 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { executionId, success: runData.status === 'success', isManual: runData.mode === 'manual', + runData, }); }, async function (this: WorkflowHooks, fullRunData: IRun) { @@ -940,6 +941,7 @@ async function executeWorkflow( success: data.status === 'success', isManual: data.mode === 'manual', userId: additionalData.userId, + runData: data, }); // subworkflow either finished, or is in status waiting due to a wait node, both cases are considered successes here diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index f8faf55fbe49d..3318dd283cd60 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -173,6 +173,7 @@ export class WorkflowRunner { success: executionData?.status === 'success', isManual: data.executionMode === 'manual', userId: data.userId, + runData: executionData, }); if (this.externalHooks.exists('workflow.postExecute')) { try { diff --git a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts index 80392206079b7..145bf2929f07a 100644 --- a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts +++ b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts @@ -2,12 +2,14 @@ import { mock } from 'jest-mock-extended'; import { AuditEventRelay } from '../audit-event-relay.service'; import type { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import type { Event } from '../event.types'; -import type { EventService } from '../event.service'; +import { EventService } from '../event.service'; +import type { INode, IRun } from 'n8n-workflow'; describe('AuditorService', () => { const eventBus = mock(); - const eventService = mock(); + const eventService = new EventService(); const auditor = new AuditEventRelay(eventService, eventBus); + auditor.init(); afterEach(() => { jest.clearAllMocks(); @@ -80,4 +82,67 @@ describe('AuditorService', () => { }, }); }); + + it('should log on `workflow-post-execute` for successful execution', () => { + const payload = mock({ + executionId: 'some-id', + success: true, + userId: 'some-id', + workflowId: 'some-id', + isManual: true, + workflowName: 'some-name', + metadata: {}, + runData: mock({ data: { resultData: {} } }), + }); + + eventService.emit('workflow-post-execute', payload); + + const { runData: _, ...rest } = payload; + + expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ + eventName: 'n8n.workflow.success', + payload: rest, + }); + }); + + it('should handle `workflow-post-execute` event for unsuccessful execution', () => { + const runData = mock({ + data: { + resultData: { + lastNodeExecuted: 'some-node', + // @ts-expect-error Partial mock + error: { + node: mock({ type: 'some-type' }), + message: 'some-message', + }, + errorMessage: 'some-message', + }, + }, + }) as unknown as IRun; + + const event = { + executionId: 'some-id', + success: false, + userId: 'some-id', + workflowId: 'some-id', + isManual: true, + workflowName: 'some-name', + metadata: {}, + runData, + }; + + eventService.emit('workflow-post-execute', event); + + const { runData: _, ...rest } = event; + + expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ + eventName: 'n8n.workflow.failed', + payload: { + ...rest, + lastNodeExecuted: 'some-node', + errorNodeType: 'some-type', + errorMessage: 'some-message', + }, + }); + }); }); diff --git a/packages/cli/src/eventbus/audit-event-relay.service.ts b/packages/cli/src/eventbus/audit-event-relay.service.ts index 9c73494520dbe..56f4dd95cd1e6 100644 --- a/packages/cli/src/eventbus/audit-event-relay.service.ts +++ b/packages/cli/src/eventbus/audit-event-relay.service.ts @@ -122,9 +122,28 @@ export class AuditEventRelay { } private workflowPostExecute(event: Event['workflow-post-execute']) { + const { runData, ...rest } = event; + + if (event.success) { + void this.eventBus.sendWorkflowEvent({ + eventName: 'n8n.workflow.success', + payload: rest, + }); + + return; + } + void this.eventBus.sendWorkflowEvent({ - eventName: 'n8n.workflow.success', - payload: event, + eventName: 'n8n.workflow.failed', + payload: { + ...rest, + lastNodeExecuted: runData?.data.resultData.lastNodeExecuted, + errorNodeType: + runData?.data.resultData.error && 'node' in runData?.data.resultData.error + ? runData?.data.resultData.error.node?.type + : undefined, + errorMessage: runData?.data.resultData.error?.message.toString(), + }, }); } diff --git a/packages/cli/src/eventbus/event.types.ts b/packages/cli/src/eventbus/event.types.ts index 225f9aca8c873..513c659d3e2a3 100644 --- a/packages/cli/src/eventbus/event.types.ts +++ b/packages/cli/src/eventbus/event.types.ts @@ -1,4 +1,4 @@ -import type { AuthenticationMethod, IWorkflowBase } from 'n8n-workflow'; +import type { AuthenticationMethod, IRun, IWorkflowBase } from 'n8n-workflow'; import type { IWorkflowExecutionDataProcess } from '@/Interfaces'; import type { ProjectRole } from '@/databases/entities/ProjectRelation'; import type { GlobalRole } from '@/databases/entities/User'; @@ -46,6 +46,7 @@ export type Event = { isManual: boolean; workflowName: string; metadata?: Record; + runData?: IRun; }; 'node-pre-execute': { diff --git a/packages/cli/src/executions/execution-recovery.service.ts b/packages/cli/src/executions/execution-recovery.service.ts index 05431a6d9c9d5..b72fc490dddbb 100644 --- a/packages/cli/src/executions/execution-recovery.service.ts +++ b/packages/cli/src/executions/execution-recovery.service.ts @@ -296,6 +296,7 @@ export class ExecutionRecoveryService { executionId: execution.id, success: execution.status === 'success', isManual: execution.mode === 'manual', + runData: execution, }); const externalHooks = getWorkflowHooksMain( From 8e960db16c28ad2b137120c3bf50b1c20eb1036b Mon Sep 17 00:00:00 2001 From: Ricardo Espinoza Date: Wed, 31 Jul 2024 07:11:24 -0400 Subject: [PATCH 16/50] refactor(editor): Migrate `WorkflowExecutionsListView.vue` to composition API (no-changelog) (#10198) --- .../workflow/WorkflowExecutionsList.vue | 253 ++++++------------ .../src/views/WorkflowExecutionsView.vue | 4 - 2 files changed, 87 insertions(+), 170 deletions(-) diff --git a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsList.vue b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsList.vue index 22dd7c00e0fad..9e66df8117baa 100644 --- a/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsList.vue +++ b/packages/editor-ui/src/components/executions/workflow/WorkflowExecutionsList.vue @@ -5,10 +5,10 @@ :loading="loading && !executions.length" :loading-more="loadingMore" :temporary-execution="temporaryExecution" - @update:auto-refresh="$emit('update:auto-refresh', $event)" - @reload-executions="$emit('reload')" - @filter-updated="$emit('update:filters', $event)" - @load-more="$emit('load-more')" + @update:auto-refresh="emit('update:auto-refresh', $event)" + @reload-executions="emit('reload')" + @filter-updated="emit('update:filters', $event)" + @load-more="emit('load-more')" @retry-execution="onRetryExecution" />
@@ -23,177 +23,98 @@
- diff --git a/packages/editor-ui/src/views/WorkflowExecutionsView.vue b/packages/editor-ui/src/views/WorkflowExecutionsView.vue index 3cca54466e02c..86a19f603ba20 100644 --- a/packages/editor-ui/src/views/WorkflowExecutionsView.vue +++ b/packages/editor-ui/src/views/WorkflowExecutionsView.vue @@ -12,7 +12,6 @@ import { PLACEHOLDER_EMPTY_WORKFLOW_ID, VIEWS } from '@/constants'; import { useRoute, useRouter } from 'vue-router'; import type { ExecutionSummary } from 'n8n-workflow'; import { useDebounce } from '@/composables/useDebounce'; -import { storeToRefs } from 'pinia'; import { useTelemetry } from '@/composables/useTelemetry'; import { useWorkflowHelpers } from '@/composables/useWorkflowHelpers'; import { useNodeHelpers } from '@/composables/useNodeHelpers'; @@ -29,8 +28,6 @@ const { callDebounced } = useDebounce(); const workflowHelpers = useWorkflowHelpers({ router }); const nodeHelpers = useNodeHelpers(); -const { filters } = storeToRefs(executionsStore); - const loading = ref(false); const loadingMore = ref(false); @@ -311,7 +308,6 @@ async function loadMore(): Promise { v-if="workflow" :executions="executions" :execution="execution" - :filters="filters" :workflow="workflow" :loading="loading" :loading-more="loadingMore" From cf73e29b6151fdc390e9e06036654d6b69d91016 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 31 Jul 2024 13:40:01 +0200 Subject: [PATCH 17/50] test(core): Improve and expand log streaming tests (no-changelog) (#10261) --- .../audit-event-relay.service.test.ts | 724 +++++++++++++++--- 1 file changed, 617 insertions(+), 107 deletions(-) diff --git a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts index 145bf2929f07a..7196b9203c407 100644 --- a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts +++ b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts @@ -3,9 +3,9 @@ import { AuditEventRelay } from '../audit-event-relay.service'; import type { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import type { Event } from '../event.types'; import { EventService } from '../event.service'; -import type { INode, IRun } from 'n8n-workflow'; +import type { INode, IRun, IWorkflowBase } from 'n8n-workflow'; -describe('AuditorService', () => { +describe('AuditEventRelay', () => { const eventBus = mock(); const eventService = new EventService(); const auditor = new AuditEventRelay(eventService, eventBus); @@ -15,134 +15,644 @@ describe('AuditorService', () => { jest.clearAllMocks(); }); - it('should handle `user-deleted` event', () => { - const arg: Event['user-deleted'] = { - user: { - id: '123', - email: 'john@n8n.io', - firstName: 'John', - lastName: 'Doe', - role: 'some-role', - }, - }; - - // @ts-expect-error Private method - auditor.userDeleted(arg); - - expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ - eventName: 'n8n.audit.user.deleted', - payload: { - userId: '123', - _email: 'john@n8n.io', - _firstName: 'John', - _lastName: 'Doe', - globalRole: 'some-role', - }, - }); - }); + describe('workflow events', () => { + it('should log on `workflow-created` event', () => { + const event: Event['workflow-created'] = { + user: { + id: '123', + email: 'john@n8n.io', + firstName: 'John', + lastName: 'Doe', + role: 'owner', + }, + workflow: mock({ + id: 'wf123', + name: 'Test Workflow', + }), + }; - it('should handle `user-invite-email-click` event', () => { - const arg: Event['user-invite-email-click'] = { - inviter: { - id: '123', - email: 'john@n8n.io', - firstName: 'John', - lastName: 'Doe', - role: 'some-role', - }, - invitee: { - id: '456', - email: 'jane@n8n.io', - firstName: 'Jane', - lastName: 'Doe', - role: 'some-other-role', - }, - }; - - // @ts-expect-error Private method - auditor.userInviteEmailClick(arg); - - expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ - eventName: 'n8n.audit.user.invitation.accepted', - payload: { - inviter: { + eventService.emit('workflow-created', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.workflow.created', + payload: { userId: '123', _email: 'john@n8n.io', _firstName: 'John', _lastName: 'Doe', - globalRole: 'some-role', + globalRole: 'owner', + workflowId: 'wf123', + workflowName: 'Test Workflow', }, - invitee: { + }); + }); + + it('should log on `workflow-deleted` event', () => { + const event: Event['workflow-deleted'] = { + user: { + id: '456', + email: 'jane@n8n.io', + firstName: 'Jane', + lastName: 'Smith', + role: 'user', + }, + workflowId: 'wf789', + }; + + eventService.emit('workflow-deleted', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.workflow.deleted', + payload: { userId: '456', _email: 'jane@n8n.io', _firstName: 'Jane', + _lastName: 'Smith', + globalRole: 'user', + workflowId: 'wf789', + }, + }); + }); + + it('should log on `workflow-saved` event', () => { + const event: Event['workflow-saved'] = { + user: { + id: '789', + email: 'alex@n8n.io', + firstName: 'Alex', + lastName: 'Johnson', + role: 'editor', + }, + workflowId: 'wf101', + workflowName: 'Updated Workflow', + }; + + eventService.emit('workflow-saved', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.workflow.updated', + payload: { + userId: '789', + _email: 'alex@n8n.io', + _firstName: 'Alex', + _lastName: 'Johnson', + globalRole: 'editor', + workflowId: 'wf101', + workflowName: 'Updated Workflow', + }, + }); + }); + + it('should log on `workflow-pre-execute` event', () => { + const workflow = mock({ + id: 'wf202', + name: 'Test Workflow', + active: true, + nodes: [], + connections: {}, + staticData: undefined, + settings: {}, + }); + + const event: Event['workflow-pre-execute'] = { + executionId: 'exec123', + data: workflow, + }; + + eventService.emit('workflow-pre-execute', event); + + expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ + eventName: 'n8n.workflow.started', + payload: { + executionId: 'exec123', + userId: undefined, + workflowId: 'wf202', + isManual: false, + workflowName: 'Test Workflow', + }, + }); + }); + + it('should log on `workflow-post-execute` for successful execution', () => { + const payload = mock({ + executionId: 'some-id', + success: true, + userId: 'some-id', + workflowId: 'some-id', + isManual: true, + workflowName: 'some-name', + metadata: {}, + runData: mock({ data: { resultData: {} } }), + }); + + eventService.emit('workflow-post-execute', payload); + + const { runData: _, ...rest } = payload; + + expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ + eventName: 'n8n.workflow.success', + payload: rest, + }); + }); + + it('should handle `workflow-post-execute` event for unsuccessful execution', () => { + const runData = mock({ + data: { + resultData: { + lastNodeExecuted: 'some-node', + // @ts-expect-error Partial mock + error: { + node: mock({ type: 'some-type' }), + message: 'some-message', + }, + errorMessage: 'some-message', + }, + }, + }) as unknown as IRun; + + const event = { + executionId: 'some-id', + success: false, + userId: 'some-id', + workflowId: 'some-id', + isManual: true, + workflowName: 'some-name', + metadata: {}, + runData, + }; + + eventService.emit('workflow-post-execute', event); + + const { runData: _, ...rest } = event; + + expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ + eventName: 'n8n.workflow.failed', + payload: { + ...rest, + lastNodeExecuted: 'some-node', + errorNodeType: 'some-type', + errorMessage: 'some-message', + }, + }); + }); + }); + + describe('user events', () => { + it('should log on `user-updated` event', () => { + const event: Event['user-updated'] = { + user: { + id: 'user456', + email: 'updated@example.com', + firstName: 'Updated', + lastName: 'User', + role: 'global:member', + }, + fieldsChanged: ['firstName', 'lastName', 'password'], + }; + + eventService.emit('user-updated', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.updated', + payload: { + userId: 'user456', + _email: 'updated@example.com', + _firstName: 'Updated', + _lastName: 'User', + globalRole: 'global:member', + fieldsChanged: ['firstName', 'lastName', 'password'], + }, + }); + }); + + it('should log on `user-deleted` event', () => { + const event: Event['user-deleted'] = { + user: { + id: '123', + email: 'john@n8n.io', + firstName: 'John', + lastName: 'Doe', + role: 'some-role', + }, + }; + + eventService.emit('user-deleted', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.deleted', + payload: { + userId: '123', + _email: 'john@n8n.io', + _firstName: 'John', _lastName: 'Doe', - globalRole: 'some-other-role', + globalRole: 'some-role', }, - }, + }); }); }); - it('should log on `workflow-post-execute` for successful execution', () => { - const payload = mock({ - executionId: 'some-id', - success: true, - userId: 'some-id', - workflowId: 'some-id', - isManual: true, - workflowName: 'some-name', - metadata: {}, - runData: mock({ data: { resultData: {} } }), + describe('click events', () => { + it('should log on `user-password-reset-request-click` event', () => { + const event: Event['user-password-reset-request-click'] = { + user: { + id: 'user101', + email: 'user101@example.com', + firstName: 'John', + lastName: 'Doe', + role: 'global:member', + }, + }; + + eventService.emit('user-password-reset-request-click', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.reset.requested', + payload: { + userId: 'user101', + _email: 'user101@example.com', + _firstName: 'John', + _lastName: 'Doe', + globalRole: 'global:member', + }, + }); }); - eventService.emit('workflow-post-execute', payload); + it('should log on `user-invite-email-click` event', () => { + const event: Event['user-invite-email-click'] = { + inviter: { + id: '123', + email: 'john@n8n.io', + firstName: 'John', + lastName: 'Doe', + role: 'some-role', + }, + invitee: { + id: '456', + email: 'jane@n8n.io', + firstName: 'Jane', + lastName: 'Doe', + role: 'some-other-role', + }, + }; - const { runData: _, ...rest } = payload; + eventService.emit('user-invite-email-click', event); - expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ - eventName: 'n8n.workflow.success', - payload: rest, + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.invitation.accepted', + payload: { + inviter: { + userId: '123', + _email: 'john@n8n.io', + _firstName: 'John', + _lastName: 'Doe', + globalRole: 'some-role', + }, + invitee: { + userId: '456', + _email: 'jane@n8n.io', + _firstName: 'Jane', + _lastName: 'Doe', + globalRole: 'some-other-role', + }, + }, + }); }); }); - it('should handle `workflow-post-execute` event for unsuccessful execution', () => { - const runData = mock({ - data: { - resultData: { - lastNodeExecuted: 'some-node', - // @ts-expect-error Partial mock - error: { - node: mock({ type: 'some-type' }), - message: 'some-message', + describe('node events', () => { + it('should log on `node-pre-execute` event', () => { + const workflow = mock({ + id: 'wf303', + name: 'Test Workflow with Nodes', + active: true, + nodes: [ + { + id: 'node1', + name: 'Start Node', + type: 'n8n-nodes-base.start', + typeVersion: 1, + position: [100, 200], }, - errorMessage: 'some-message', + { + id: 'node2', + name: 'HTTP Request', + type: 'n8n-nodes-base.httpRequest', + typeVersion: 1, + position: [300, 200], + }, + ], + connections: {}, + settings: {}, + }); + + const event: Event['node-pre-execute'] = { + executionId: 'exec456', + nodeName: 'HTTP Request', + workflow, + }; + + eventService.emit('node-pre-execute', event); + + expect(eventBus.sendNodeEvent).toHaveBeenCalledWith({ + eventName: 'n8n.node.started', + payload: { + executionId: 'exec456', + nodeName: 'HTTP Request', + workflowId: 'wf303', + workflowName: 'Test Workflow with Nodes', + nodeType: 'n8n-nodes-base.httpRequest', + }, + }); + }); + + it('should log on `node-post-execute` event', () => { + const workflow = mock({ + id: 'wf404', + name: 'Test Workflow with Completed Node', + active: true, + nodes: [ + { + id: 'node1', + name: 'Start Node', + type: 'n8n-nodes-base.start', + typeVersion: 1, + position: [100, 200], + }, + { + id: 'node2', + name: 'HTTP Response', + type: 'n8n-nodes-base.httpResponse', + typeVersion: 1, + position: [300, 200], + }, + ], + connections: {}, + settings: {}, + }); + + const event: Event['node-post-execute'] = { + executionId: 'exec789', + nodeName: 'HTTP Response', + workflow, + }; + + eventService.emit('node-post-execute', event); + + expect(eventBus.sendNodeEvent).toHaveBeenCalledWith({ + eventName: 'n8n.node.finished', + payload: { + executionId: 'exec789', + nodeName: 'HTTP Response', + workflowId: 'wf404', + workflowName: 'Test Workflow with Completed Node', + nodeType: 'n8n-nodes-base.httpResponse', + }, + }); + }); + }); + + describe('credentials events', () => { + it('should log on `credentials-shared` event', () => { + const event: Event['credentials-shared'] = { + user: { + id: 'user123', + email: 'sharer@example.com', + firstName: 'Alice', + lastName: 'Sharer', + role: 'global:owner', + }, + credentialId: 'cred789', + credentialType: 'githubApi', + userIdSharer: 'user123', + userIdsShareesAdded: ['user456', 'user789'], + shareesRemoved: null, + }; + + eventService.emit('credentials-shared', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.credentials.shared', + payload: { + userId: 'user123', + _email: 'sharer@example.com', + _firstName: 'Alice', + _lastName: 'Sharer', + globalRole: 'global:owner', + credentialId: 'cred789', + credentialType: 'githubApi', + userIdSharer: 'user123', + userIdsShareesAdded: ['user456', 'user789'], + shareesRemoved: null, + }, + }); + }); + + it('should log on `credentials-created` event', () => { + const event: Event['credentials-created'] = { + user: { + id: 'user123', + email: 'user@example.com', + firstName: 'Test', + lastName: 'User', + role: 'global:owner', + }, + credentialType: 'githubApi', + credentialId: 'cred456', + publicApi: false, + projectId: 'proj789', + projectType: 'Personal', + }; + + eventService.emit('credentials-created', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.credentials.created', + payload: { + userId: 'user123', + _email: 'user@example.com', + _firstName: 'Test', + _lastName: 'User', + globalRole: 'global:owner', + credentialType: 'githubApi', + credentialId: 'cred456', + publicApi: false, + projectId: 'proj789', + projectType: 'Personal', + }, + }); + }); + }); + + describe('auth events', () => { + it('should log on `user-login-failed` event', () => { + const event: Event['user-login-failed'] = { + userEmail: 'user@example.com', + authenticationMethod: 'email', + reason: 'Invalid password', + }; + + eventService.emit('user-login-failed', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.login.failed', + payload: { + userEmail: 'user@example.com', + authenticationMethod: 'email', + reason: 'Invalid password', + }, + }); + }); + }); + + describe('community package events', () => { + it('should log on `community-package-updated` event', () => { + const event: Event['community-package-updated'] = { + user: { + id: 'user202', + email: 'packageupdater@example.com', + firstName: 'Package', + lastName: 'Updater', + role: 'global:admin', + }, + packageName: 'n8n-nodes-awesome-package', + packageVersionCurrent: '1.0.0', + packageVersionNew: '1.1.0', + packageNodeNames: ['AwesomeNode1', 'AwesomeNode2'], + packageAuthor: 'Jane Doe', + packageAuthorEmail: 'jane@example.com', + }; + + eventService.emit('community-package-updated', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.package.updated', + payload: { + userId: 'user202', + _email: 'packageupdater@example.com', + _firstName: 'Package', + _lastName: 'Updater', + globalRole: 'global:admin', + packageName: 'n8n-nodes-awesome-package', + packageVersionCurrent: '1.0.0', + packageVersionNew: '1.1.0', + packageNodeNames: ['AwesomeNode1', 'AwesomeNode2'], + packageAuthor: 'Jane Doe', + packageAuthorEmail: 'jane@example.com', + }, + }); + }); + + it('should log on `community-package-installed` event', () => { + const event: Event['community-package-installed'] = { + user: { + id: 'user789', + email: 'admin@example.com', + firstName: 'Admin', + lastName: 'User', + role: 'global:admin', + }, + inputString: 'n8n-nodes-custom-package', + packageName: 'n8n-nodes-custom-package', + success: true, + packageVersion: '1.0.0', + packageNodeNames: ['CustomNode1', 'CustomNode2'], + packageAuthor: 'John Doe', + packageAuthorEmail: 'john@example.com', + }; + + eventService.emit('community-package-installed', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.package.installed', + payload: { + userId: 'user789', + _email: 'admin@example.com', + _firstName: 'Admin', + _lastName: 'User', + globalRole: 'global:admin', + inputString: 'n8n-nodes-custom-package', + packageName: 'n8n-nodes-custom-package', + success: true, + packageVersion: '1.0.0', + packageNodeNames: ['CustomNode1', 'CustomNode2'], + packageAuthor: 'John Doe', + packageAuthorEmail: 'john@example.com', + }, + }); + }); + }); + + describe('email events', () => { + it('should log on `email-failed` event', () => { + const event: Event['email-failed'] = { + user: { + id: 'user789', + email: 'recipient@example.com', + firstName: 'Failed', + lastName: 'Recipient', + role: 'global:member', + }, + messageType: 'New user invite', + }; + + eventService.emit('email-failed', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.email.failed', + payload: { + userId: 'user789', + _email: 'recipient@example.com', + _firstName: 'Failed', + _lastName: 'Recipient', + globalRole: 'global:member', + messageType: 'New user invite', + }, + }); + }); + }); + + describe('public API events', () => { + it('should log on `public-api-key-created` event', () => { + const event: Event['public-api-key-created'] = { + user: { + id: 'user101', + email: 'apiuser@example.com', + firstName: 'API', + lastName: 'User', + role: 'global:owner', + }, + publicApi: true, + }; + + eventService.emit('public-api-key-created', event); + + expect(eventBus.sendAuditEvent).toHaveBeenCalledWith({ + eventName: 'n8n.audit.user.api.created', + payload: { + userId: 'user101', + _email: 'apiuser@example.com', + _firstName: 'API', + _lastName: 'User', + globalRole: 'global:owner', + }, + }); + }); + }); + + describe('execution events', () => { + it('should log on `execution-throttled` event', () => { + const event: Event['execution-throttled'] = { + executionId: 'exec123456', + }; + + eventService.emit('execution-throttled', event); + + expect(eventBus.sendExecutionEvent).toHaveBeenCalledWith({ + eventName: 'n8n.execution.throttled', + payload: { + executionId: 'exec123456', }, - }, - }) as unknown as IRun; - - const event = { - executionId: 'some-id', - success: false, - userId: 'some-id', - workflowId: 'some-id', - isManual: true, - workflowName: 'some-name', - metadata: {}, - runData, - }; - - eventService.emit('workflow-post-execute', event); - - const { runData: _, ...rest } = event; - - expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ - eventName: 'n8n.workflow.failed', - payload: { - ...rest, - lastNodeExecuted: 'some-node', - errorNodeType: 'some-type', - errorMessage: 'some-message', - }, + }); }); }); }); From 326c983915a2c382e32398358e7dcadd022c0b77 Mon Sep 17 00:00:00 2001 From: Oz Weiss Date: Wed, 31 Jul 2024 15:26:24 +0300 Subject: [PATCH 18/50] feat(Calendly Trigger Node): Add OAuth Credentials Support (#10251) --- .../CalendlyOAuth2Api.credentials.ts | 52 +++++++++++++++++ .../nodes-base/credentials/icons/Calendly.svg | 1 + .../nodes/Calendly/CalendlyTrigger.node.ts | 40 +++++++++++-- .../nodes/Calendly/GenericFunctions.ts | 57 +++++++------------ packages/nodes-base/package.json | 1 + 5 files changed, 107 insertions(+), 44 deletions(-) create mode 100644 packages/nodes-base/credentials/CalendlyOAuth2Api.credentials.ts create mode 100644 packages/nodes-base/credentials/icons/Calendly.svg diff --git a/packages/nodes-base/credentials/CalendlyOAuth2Api.credentials.ts b/packages/nodes-base/credentials/CalendlyOAuth2Api.credentials.ts new file mode 100644 index 0000000000000..200d238c6d73f --- /dev/null +++ b/packages/nodes-base/credentials/CalendlyOAuth2Api.credentials.ts @@ -0,0 +1,52 @@ +import type { ICredentialType, INodeProperties, Icon } from 'n8n-workflow'; + +export class CalendlyOAuth2Api implements ICredentialType { + name = 'calendlyOAuth2Api'; + + extends = ['oAuth2Api']; + + displayName = 'Calendly OAuth2 API'; + + documentationUrl = 'calendly'; + + icon: Icon = 'file:icons/Calendly.svg'; + + properties: INodeProperties[] = [ + { + displayName: 'Grant Type', + name: 'grantType', + type: 'hidden', + default: 'authorizationCode', + }, + { + displayName: 'Authorization URL', + name: 'authUrl', + type: 'hidden', + default: 'https://auth.calendly.com/oauth/authorize', + }, + { + displayName: 'Access Token URL', + name: 'accessTokenUrl', + type: 'hidden', + default: 'https://auth.calendly.com/oauth/token', + }, + { + displayName: 'Authentication', + name: 'authentication', + type: 'hidden', + default: 'header', + }, + { + displayName: 'Auth URI Query Parameters', + name: 'authQueryParameters', + type: 'hidden', + default: '', + }, + { + displayName: 'Scope', + name: 'scope', + type: 'hidden', + default: '', + }, + ]; +} diff --git a/packages/nodes-base/credentials/icons/Calendly.svg b/packages/nodes-base/credentials/icons/Calendly.svg new file mode 100644 index 0000000000000..195a7461e33b6 --- /dev/null +++ b/packages/nodes-base/credentials/icons/Calendly.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts b/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts index d2e7b127237d2..40a10cab92cfa 100644 --- a/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts +++ b/packages/nodes-base/nodes/Calendly/CalendlyTrigger.node.ts @@ -26,6 +26,20 @@ export class CalendlyTrigger implements INodeType { { name: 'calendlyApi', required: true, + displayOptions: { + show: { + authentication: ['apiKey'], + }, + }, + }, + { + name: 'calendlyOAuth2Api', + required: true, + displayOptions: { + show: { + authentication: ['oAuth2'], + }, + }, }, ], webhooks: [ @@ -37,6 +51,23 @@ export class CalendlyTrigger implements INodeType { }, ], properties: [ + { + displayName: 'Authentication', + name: 'authentication', + type: 'options', + options: [ + { + // eslint-disable-next-line n8n-nodes-base/node-param-display-name-miscased + name: 'OAuth2 (recommended)', + value: 'oAuth2', + }, + { + name: 'API Key or Personal Access Token', + value: 'apiKey', + }, + ], + default: 'apiKey', + }, { displayName: 'Scope', name: 'scope', @@ -86,9 +117,8 @@ export class CalendlyTrigger implements INodeType { const webhookUrl = this.getNodeWebhookUrl('default'); const webhookData = this.getWorkflowStaticData('node'); const events = this.getNodeParameter('events') as string; - const { apiKey } = (await this.getCredentials('calendlyApi')) as { apiKey: string }; - const authenticationType = getAuthenticationType(apiKey); + const authenticationType = await getAuthenticationType.call(this); // remove condition once API Keys are deprecated if (authenticationType === 'apiKey') { @@ -149,9 +179,8 @@ export class CalendlyTrigger implements INodeType { const webhookData = this.getWorkflowStaticData('node'); const webhookUrl = this.getNodeWebhookUrl('default'); const events = this.getNodeParameter('events') as string; - const { apiKey } = (await this.getCredentials('calendlyApi')) as { apiKey: string }; - const authenticationType = getAuthenticationType(apiKey); + const authenticationType = await getAuthenticationType.call(this); // remove condition once API Keys are deprecated if (authenticationType === 'apiKey') { @@ -201,8 +230,7 @@ export class CalendlyTrigger implements INodeType { }, async delete(this: IHookFunctions): Promise { const webhookData = this.getWorkflowStaticData('node'); - const { apiKey } = (await this.getCredentials('calendlyApi')) as { apiKey: string }; - const authenticationType = getAuthenticationType(apiKey); + const authenticationType = await getAuthenticationType.call(this); // remove condition once API Keys are deprecated if (authenticationType === 'apiKey') { diff --git a/packages/nodes-base/nodes/Calendly/GenericFunctions.ts b/packages/nodes-base/nodes/Calendly/GenericFunctions.ts index 4befbb5ceed21..f39aa877618b2 100644 --- a/packages/nodes-base/nodes/Calendly/GenericFunctions.ts +++ b/packages/nodes-base/nodes/Calendly/GenericFunctions.ts @@ -1,6 +1,4 @@ import type { - ICredentialDataDecryptedObject, - ICredentialTestFunctions, IDataObject, IExecuteFunctions, ILoadOptionsFunctions, @@ -10,12 +8,24 @@ import type { IRequestOptions, } from 'n8n-workflow'; -export function getAuthenticationType(data: string): 'accessToken' | 'apiKey' { +function getAuthenticationTypeFromApiKey(data: string): 'accessToken' | 'apiKey' { // The access token is a JWT, so it will always include dots to separate // header, payoload and signature. return data.includes('.') ? 'accessToken' : 'apiKey'; } +export async function getAuthenticationType( + this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, +): Promise<'accessToken' | 'apiKey'> { + const authentication = this.getNodeParameter('authentication', 0) as string; + if (authentication === 'apiKey') { + const { apiKey } = (await this.getCredentials('calendlyApi')) as { apiKey: string }; + return getAuthenticationTypeFromApiKey(apiKey); + } else { + return 'accessToken'; + } +} + export async function calendlyApiRequest( this: IExecuteFunctions | IWebhookFunctions | IHookFunctions | ILoadOptionsFunctions, method: IHttpRequestMethods, @@ -26,9 +36,7 @@ export async function calendlyApiRequest( uri?: string, option: IDataObject = {}, ): Promise { - const { apiKey } = (await this.getCredentials('calendlyApi')) as { apiKey: string }; - - const authenticationType = getAuthenticationType(apiKey); + const authenticationType = await getAuthenticationType.call(this); const headers: IDataObject = { 'Content-Type': 'application/json', @@ -57,37 +65,10 @@ export async function calendlyApiRequest( delete options.qs; } options = Object.assign({}, options, option); - return await this.helpers.requestWithAuthentication.call(this, 'calendlyApi', options); -} - -export async function validateCredentials( - this: ICredentialTestFunctions, - decryptedCredentials: ICredentialDataDecryptedObject, -): Promise { - const credentials = decryptedCredentials; - const { apiKey } = credentials as { - apiKey: string; - }; - - const authenticationType = getAuthenticationType(apiKey); - - const options: IRequestOptions = { - method: 'GET', - uri: '', - json: true, - }; - - if (authenticationType === 'accessToken') { - Object.assign(options, { - headers: { Authorization: `Bearer ${apiKey}` }, - uri: 'https://api.calendly.com/users/me', - }); - } else { - Object.assign(options, { - headers: { 'X-TOKEN': apiKey }, - uri: 'https://calendly.com/api/v1/users/me', - }); - } - return await this.helpers.request(options); + const credentialsType = + (this.getNodeParameter('authentication', 0) as string) === 'apiKey' + ? 'calendlyApi' + : 'calendlyOAuth2Api'; + return await this.helpers.requestWithAuthentication.call(this, credentialsType, options); } diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index e512fb4ef89bc..85eca2fdcd1ff 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -52,6 +52,7 @@ "dist/credentials/BubbleApi.credentials.js", "dist/credentials/CalApi.credentials.js", "dist/credentials/CalendlyApi.credentials.js", + "dist/credentials/CalendlyOAuth2Api.credentials.js", "dist/credentials/CarbonBlackApi.credentials.js", "dist/credentials/ChargebeeApi.credentials.js", "dist/credentials/CircleCiApi.credentials.js", From 2a09a036d2e916acff7ee50904f1d011a93758e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Wed, 31 Jul 2024 14:38:49 +0200 Subject: [PATCH 19/50] =?UTF-8?q?fix(core):=20Upgrade=20@n8n/vm2=20to=20ad?= =?UTF-8?q?dress=20CVE=E2=80=912023=E2=80=9137466=20(#10265)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/@n8n/nodes-langchain/package.json | 2 +- packages/nodes-base/package.json | 2 +- pnpm-lock.yaml | 52 ++++++++++------------ 3 files changed, 25 insertions(+), 31 deletions(-) diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index a52670e3e3faf..fdcf6d76f4dfa 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -153,7 +153,7 @@ "@langchain/textsplitters": "0.0.3", "@mozilla/readability": "^0.5.0", "@n8n/typeorm": "0.3.20-10", - "@n8n/vm2": "3.9.20", + "@n8n/vm2": "3.9.24", "@pinecone-database/pinecone": "3.0.0", "@qdrant/js-client-rest": "1.9.0", "@supabase/supabase-js": "2.43.4", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 85eca2fdcd1ff..118117ae4339c 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -830,7 +830,7 @@ "dependencies": { "@kafkajs/confluent-schema-registry": "1.0.6", "@n8n/imap": "workspace:*", - "@n8n/vm2": "3.9.20", + "@n8n/vm2": "3.9.24", "amqplib": "0.10.3", "alasql": "^4.4.0", "aws4": "1.11.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 005cdc310122d..8c8ff04428456 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -390,8 +390,8 @@ importers: specifier: 0.3.20-10 version: 0.3.20-10(@sentry/node@7.87.0)(ioredis@5.3.2)(mssql@10.0.2)(mysql2@3.10.0)(pg@8.11.3)(redis@4.6.12)(sqlite3@5.1.7) '@n8n/vm2': - specifier: 3.9.20 - version: 3.9.20 + specifier: 3.9.24 + version: 3.9.24 '@pinecone-database/pinecone': specifier: 3.0.0 version: 3.0.0 @@ -1448,8 +1448,8 @@ importers: specifier: workspace:* version: link:../@n8n/imap '@n8n/vm2': - specifier: 3.9.20 - version: 3.9.20 + specifier: 3.9.24 + version: 3.9.24 alasql: specifier: ^4.4.0 version: 4.4.0(encoding@0.1.13) @@ -4211,9 +4211,9 @@ packages: typeorm-aurora-data-api-driver: optional: true - '@n8n/vm2@3.9.20': - resolution: {integrity: sha512-qk2oJYkuFRVSTxoro4obX/sv/wT1pViZjHh/isjOvFB93D52QIg3TCjMPsHOfHTmkxCKJffjLrUvjIwvWzSMCQ==} - engines: {node: '>=18.10', pnpm: '>=8.6.12'} + '@n8n/vm2@3.9.24': + resolution: {integrity: sha512-O4z67yVgUs2FHkcw3vbGnxdC1EglpzOj966kPkK4gtW+ZmTTFRfEB+2Ehq6PMthgg/Ou5JCLSR3wvQIZFFt4Pg==} + engines: {node: '>=18.10', pnpm: '>=9.6'} hasBin: true '@n8n_io/license-sdk@2.13.0': @@ -5999,10 +5999,6 @@ packages: peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 - acorn-walk@8.2.0: - resolution: {integrity: sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==} - engines: {node: '>=0.4.0'} - acorn-walk@8.3.2: resolution: {integrity: sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==} engines: {node: '>=0.4.0'} @@ -16720,10 +16716,10 @@ snapshots: transitivePeerDependencies: - supports-color - '@n8n/vm2@3.9.20': + '@n8n/vm2@3.9.24': dependencies: - acorn: 8.11.2 - acorn-walk: 8.2.0 + acorn: 8.12.1 + acorn-walk: 8.3.2 '@n8n_io/license-sdk@2.13.0': dependencies: @@ -19367,15 +19363,13 @@ snapshots: acorn-globals@7.0.1: dependencies: - acorn: 8.11.2 + acorn: 8.12.1 acorn-walk: 8.3.2 acorn-jsx@5.3.2(acorn@8.11.2): dependencies: acorn: 8.11.2 - acorn-walk@8.2.0: {} - acorn-walk@8.3.2: {} acorn@7.4.1: {} @@ -21307,7 +21301,7 @@ snapshots: eslint-import-resolver-node@0.3.9: dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) is-core-module: 2.13.1 resolve: 1.22.8 transitivePeerDependencies: @@ -21332,7 +21326,7 @@ snapshots: eslint-module-utils@2.8.0(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.6.1(@typescript-eslint/parser@7.2.0(eslint@8.57.0)(typescript@5.5.2))(eslint-plugin-import@2.29.1)(eslint@8.57.0))(eslint@8.57.0): dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) optionalDependencies: '@typescript-eslint/parser': 7.2.0(eslint@8.57.0)(typescript@5.5.2) eslint: 8.57.0 @@ -21352,7 +21346,7 @@ snapshots: array.prototype.findlastindex: 1.2.3 array.prototype.flat: 1.3.2 array.prototype.flatmap: 1.3.2 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) doctrine: 2.1.0 eslint: 8.57.0 eslint-import-resolver-node: 0.3.9 @@ -21882,7 +21876,7 @@ snapshots: follow-redirects@1.15.6(debug@3.2.7): optionalDependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) follow-redirects@1.15.6(debug@4.3.4): optionalDependencies: @@ -22223,7 +22217,7 @@ snapshots: array-parallel: 0.1.3 array-series: 0.1.5 cross-spawn: 4.0.2 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -23353,7 +23347,7 @@ snapshots: jsdom@20.0.2: dependencies: abab: 2.0.6 - acorn: 8.11.2 + acorn: 8.12.1 acorn-globals: 7.0.1 cssom: 0.5.0 cssstyle: 2.3.0 @@ -24209,7 +24203,7 @@ snapshots: mlly@1.4.2: dependencies: - acorn: 8.11.2 + acorn: 8.12.1 pathe: 1.1.2 pkg-types: 1.0.3 ufo: 1.3.2 @@ -24916,7 +24910,7 @@ snapshots: pdf-parse@1.1.1: dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) node-ensure: 0.0.0 transitivePeerDependencies: - supports-color @@ -25828,7 +25822,7 @@ snapshots: rhea@1.0.24: dependencies: - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) transitivePeerDependencies: - supports-color @@ -26202,7 +26196,7 @@ snapshots: binascii: 0.0.2 bn.js: 5.2.1 browser-request: 0.3.3 - debug: 3.2.7(supports-color@8.1.1) + debug: 3.2.7(supports-color@5.5.0) expand-tilde: 2.0.2 extend: 3.0.2 fast-xml-parser: 4.2.7 @@ -27190,7 +27184,7 @@ snapshots: unplugin@1.0.1: dependencies: - acorn: 8.11.2 + acorn: 8.12.1 chokidar: 3.5.2 webpack-sources: 3.2.3 webpack-virtual-modules: 0.5.0 @@ -27204,7 +27198,7 @@ snapshots: unplugin@1.5.1: dependencies: - acorn: 8.11.2 + acorn: 8.12.1 chokidar: 3.5.2 webpack-sources: 3.2.3 webpack-virtual-modules: 0.6.1 From 7ffaec03eaa77c0c812adee1a71f5e9aeb3724b3 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Wed, 31 Jul 2024 14:56:18 +0200 Subject: [PATCH 20/50] :rocket: Release 1.53.0 (#10266) Co-authored-by: despairblue --- CHANGELOG.md | 45 ++++++++++++++++++++++ package.json | 2 +- packages/@n8n/chat/package.json | 2 +- packages/@n8n/client-oauth2/package.json | 2 +- packages/@n8n/config/package.json | 2 +- packages/@n8n/nodes-langchain/package.json | 2 +- packages/cli/package.json | 2 +- packages/core/package.json | 2 +- packages/design-system/package.json | 2 +- packages/editor-ui/package.json | 2 +- packages/node-dev/package.json | 2 +- packages/nodes-base/package.json | 2 +- packages/workflow/package.json | 2 +- 13 files changed, 57 insertions(+), 12 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index cc2a07dda09b1..986b85b0682ea 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,48 @@ +# [1.53.0](https://github.com/n8n-io/n8n/compare/n8n@1.52.0...n8n@1.53.0) (2024-07-31) + + +### Bug Fixes + +* Better error message when calling data transformation functions on a null value ([#10210](https://github.com/n8n-io/n8n/issues/10210)) ([1718125](https://github.com/n8n-io/n8n/commit/1718125c6d8589cf24dc8d34f6808dd6f1802691)) +* **core:** Fix missing successful items on continueErrorOutput with multiple outputs ([#10218](https://github.com/n8n-io/n8n/issues/10218)) ([1a7713e](https://github.com/n8n-io/n8n/commit/1a7713ef263680da43f08b6c8a15aee7a0341493)) +* **core:** Flush instance stopped event immediately ([#10238](https://github.com/n8n-io/n8n/issues/10238)) ([d6770b5](https://github.com/n8n-io/n8n/commit/d6770b5fcaec6438d677b918aaeb1669ad7424c2)) +* **core:** Restore log event `n8n.workflow.failed` ([#10253](https://github.com/n8n-io/n8n/issues/10253)) ([3e96b29](https://github.com/n8n-io/n8n/commit/3e96b293329525c9d4b2fcef87b3803e458c8e7f)) +* **core:** Upgrade @n8n/vm2 to address CVE‑2023‑37466 ([#10265](https://github.com/n8n-io/n8n/issues/10265)) ([2a09a03](https://github.com/n8n-io/n8n/commit/2a09a036d2e916acff7ee50904f1d011a93758e1)) +* **editor:** Defer `User saved credentials` telemetry event for OAuth credentials ([#10215](https://github.com/n8n-io/n8n/issues/10215)) ([40a5226](https://github.com/n8n-io/n8n/commit/40a5226e24448a4428143e69d80ebc78238365a1)) +* **editor:** Fix custom API call notice ([#10227](https://github.com/n8n-io/n8n/issues/10227)) ([5b47c8b](https://github.com/n8n-io/n8n/commit/5b47c8b57b25528cd2d6f97bc6d98707d47f35bc)) +* **editor:** Fix issue with existing credential not opening in HTTP agent tool ([#10167](https://github.com/n8n-io/n8n/issues/10167)) ([906b4c3](https://github.com/n8n-io/n8n/commit/906b4c3c7b2919111cf23eaa12b3c4d507969179)) +* **editor:** Fix parameter input glitch when there was an error loading remote options ([#10209](https://github.com/n8n-io/n8n/issues/10209)) ([c0e3743](https://github.com/n8n-io/n8n/commit/c0e37439a87105a0e66c8ebced42c06dab30dc5e)) +* **editor:** Fix workflow execution list scrolling after filter change ([#10226](https://github.com/n8n-io/n8n/issues/10226)) ([7e64358](https://github.com/n8n-io/n8n/commit/7e643589c67adc0218216ec4b89a95f0edfedbee)) +* **Google BigQuery Node:** Send timeoutMs in query, pagination support ([#10205](https://github.com/n8n-io/n8n/issues/10205)) ([f5722e8](https://github.com/n8n-io/n8n/commit/f5722e8823ccd2bc2b5f43ba3c849797d5690a93)) +* **Google Sheets Node:** Add column names row if sheet is empty ([#10200](https://github.com/n8n-io/n8n/issues/10200)) ([82eba9f](https://github.com/n8n-io/n8n/commit/82eba9fc5ff49b8e2a9db93c10b253fb67a8c644)) +* **Google Sheets Node:** Do not insert row_number as a new column, do not checkForSchemaChanges in update operation ([#10201](https://github.com/n8n-io/n8n/issues/10201)) ([5136d10](https://github.com/n8n-io/n8n/commit/5136d10ca3492f92af67d4a1d4abc774419580cc)) +* **Google Sheets Node:** Fix Google Sheet URL regex ([#10195](https://github.com/n8n-io/n8n/issues/10195)) ([e6fd996](https://github.com/n8n-io/n8n/commit/e6fd996973d4f40facf0ebf1eea3cc26acd0603d)) +* **HTTP Request Node:** Resolve max pages expression ([#10192](https://github.com/n8n-io/n8n/issues/10192)) ([bfc8e1b](https://github.com/n8n-io/n8n/commit/bfc8e1b56f7714e1f52aae747d58d686b86e60f0)) +* **LinkedIn Node:** Fix issue with some characters cutting off posts early ([#10185](https://github.com/n8n-io/n8n/issues/10185)) ([361b5e7](https://github.com/n8n-io/n8n/commit/361b5e7c37ba49b68dcf5b8122621aad4d8d96e0)) +* **Postgres Node:** Expressions in query parameters for Postgres executeQuery operation ([#10217](https://github.com/n8n-io/n8n/issues/10217)) ([519fc4d](https://github.com/n8n-io/n8n/commit/519fc4d75325a80b84cc4dcacf52d6f4c02e3a44)) +* **Postgres Node:** Option to treat query parameters enclosed in single quotas as text ([#10214](https://github.com/n8n-io/n8n/issues/10214)) ([00ec253](https://github.com/n8n-io/n8n/commit/00ec2533374d3def465efee718592fc4001d5602)) +* **Read/Write Files from Disk Node:** Notice update in file selector, replace backslashes with forward slashes if windows path ([#10186](https://github.com/n8n-io/n8n/issues/10186)) ([3eac673](https://github.com/n8n-io/n8n/commit/3eac673b17986c5c74bd2adb5ad589ba0ca55319)) +* **Text Classifier Node:** Use proper documentation URL and respect continueOnFail ([#10216](https://github.com/n8n-io/n8n/issues/10216)) ([452f52c](https://github.com/n8n-io/n8n/commit/452f52c124017e002e86c547ba42b1633b14beed)) +* **Trello Node:** Use body for POST requests ([#10189](https://github.com/n8n-io/n8n/issues/10189)) ([7775d50](https://github.com/n8n-io/n8n/commit/7775d5059b7f69d9af22e7ad7d12c6cf9092a4e5)) +* **Wait Node:** Authentication fix ([#10236](https://github.com/n8n-io/n8n/issues/10236)) ([f87854f](https://github.com/n8n-io/n8n/commit/f87854f8db360b7b870583753fcfb4af95adab8c)) + + +### Features + +* **Calendly Trigger Node:** Add OAuth Credentials Support ([#10251](https://github.com/n8n-io/n8n/issues/10251)) ([326c983](https://github.com/n8n-io/n8n/commit/326c983915a2c382e32398358e7dcadd022c0b77)) +* **core:** Allow filtering workflows by project and transferring workflows in Public API ([#10231](https://github.com/n8n-io/n8n/issues/10231)) ([d719899](https://github.com/n8n-io/n8n/commit/d719899223907b20a17883a35e4ef637a3453532)) +* **editor:** Show new executions as `Queued` in the UI, until they actually start ([#10204](https://github.com/n8n-io/n8n/issues/10204)) ([44728d7](https://github.com/n8n-io/n8n/commit/44728d72423f5549dda09589f4a618ebd80899cb)) +* **HTTP Request Node:** Add option to disable lowercase headers ([#10154](https://github.com/n8n-io/n8n/issues/10154)) ([5aba69b](https://github.com/n8n-io/n8n/commit/5aba69bcf4d232d9860f3cd9fe57cb8839a2f96f)) +* **Information Extractor Node:** Add new simplified AI-node for information extraction ([#10149](https://github.com/n8n-io/n8n/issues/10149)) ([3d235b0](https://github.com/n8n-io/n8n/commit/3d235b0b2df756df35ac60e3dcd87ad183a07167)) +* Introduce Google Cloud Platform as external secrets provider ([#10146](https://github.com/n8n-io/n8n/issues/10146)) ([3ccb9df](https://github.com/n8n-io/n8n/commit/3ccb9df2f902e46f8cbb9c46c0727f29d752a773)) +* **n8n Form Trigger Node:** Improvements ([#10092](https://github.com/n8n-io/n8n/issues/10092)) ([711b667](https://github.com/n8n-io/n8n/commit/711b667ebefe55740e5eb39f1f0f24ceee10e7b0)) +* Recovery option for jsonParse helper ([#10182](https://github.com/n8n-io/n8n/issues/10182)) ([d165b33](https://github.com/n8n-io/n8n/commit/d165b33ceac4d24d0fc290bffe63b5f551204e38)) +* **Sentiment Analysis Node:** Implement Sentiment Analysis node ([#10184](https://github.com/n8n-io/n8n/issues/10184)) ([8ef0a0c](https://github.com/n8n-io/n8n/commit/8ef0a0c58ac2a84aad649ccbe72aa907d005cc44)) +* **Shopify Node:** Update Shopify API version ([#10155](https://github.com/n8n-io/n8n/issues/10155)) ([e2ee915](https://github.com/n8n-io/n8n/commit/e2ee91569a382bfbf787cf45204c72c821a860a0)) +* Support create, read, delete variables in Public API ([#10241](https://github.com/n8n-io/n8n/issues/10241)) ([af695eb](https://github.com/n8n-io/n8n/commit/af695ebf934526d926ea87fe87df61aa73d70979)) + + + # [1.52.0](https://github.com/n8n-io/n8n/compare/n8n@1.51.0...n8n@1.52.0) (2024-07-24) diff --git a/package.json b/package.json index 3dd939d9d30fa..e05eeb9845f1b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "n8n-monorepo", - "version": "1.52.0", + "version": "1.53.0", "private": true, "engines": { "node": ">=20.15", diff --git a/packages/@n8n/chat/package.json b/packages/@n8n/chat/package.json index 4f425b6fa8237..5ba8a1c39f201 100644 --- a/packages/@n8n/chat/package.json +++ b/packages/@n8n/chat/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/chat", - "version": "0.21.0", + "version": "0.22.0", "scripts": { "dev": "pnpm run storybook", "build": "pnpm build:vite && pnpm build:bundle", diff --git a/packages/@n8n/client-oauth2/package.json b/packages/@n8n/client-oauth2/package.json index 83b858a2e9fa0..7ec91b98390b1 100644 --- a/packages/@n8n/client-oauth2/package.json +++ b/packages/@n8n/client-oauth2/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/client-oauth2", - "version": "0.19.0", + "version": "0.20.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/config/package.json b/packages/@n8n/config/package.json index 66332540fa2e6..a4abfa677adc1 100644 --- a/packages/@n8n/config/package.json +++ b/packages/@n8n/config/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/config", - "version": "1.2.0", + "version": "1.3.0", "scripts": { "clean": "rimraf dist .turbo", "dev": "pnpm watch", diff --git a/packages/@n8n/nodes-langchain/package.json b/packages/@n8n/nodes-langchain/package.json index fdcf6d76f4dfa..3a328af0ae880 100644 --- a/packages/@n8n/nodes-langchain/package.json +++ b/packages/@n8n/nodes-langchain/package.json @@ -1,6 +1,6 @@ { "name": "@n8n/n8n-nodes-langchain", - "version": "1.52.0", + "version": "1.53.0", "description": "", "main": "index.js", "scripts": { diff --git a/packages/cli/package.json b/packages/cli/package.json index 60ace14d9bce8..ca8f192e40152 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -1,6 +1,6 @@ { "name": "n8n", - "version": "1.52.0", + "version": "1.53.0", "description": "n8n Workflow Automation Tool", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/core/package.json b/packages/core/package.json index 17f57cc99bad1..dfcc23db99d92 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,6 +1,6 @@ { "name": "n8n-core", - "version": "1.52.0", + "version": "1.53.0", "description": "Core functionality of n8n", "main": "dist/index", "types": "dist/index.d.ts", diff --git a/packages/design-system/package.json b/packages/design-system/package.json index 150636a64c3f0..386a7e1e862c8 100644 --- a/packages/design-system/package.json +++ b/packages/design-system/package.json @@ -1,6 +1,6 @@ { "name": "n8n-design-system", - "version": "1.42.0", + "version": "1.43.0", "main": "src/main.ts", "import": "src/main.ts", "scripts": { diff --git a/packages/editor-ui/package.json b/packages/editor-ui/package.json index d4acc7843d1b2..caf156fa83c9e 100644 --- a/packages/editor-ui/package.json +++ b/packages/editor-ui/package.json @@ -1,6 +1,6 @@ { "name": "n8n-editor-ui", - "version": "1.52.0", + "version": "1.53.0", "description": "Workflow Editor UI for n8n", "main": "index.js", "scripts": { diff --git a/packages/node-dev/package.json b/packages/node-dev/package.json index 92db12b997737..d7b16155a1aa5 100644 --- a/packages/node-dev/package.json +++ b/packages/node-dev/package.json @@ -1,6 +1,6 @@ { "name": "n8n-node-dev", - "version": "1.52.0", + "version": "1.53.0", "description": "CLI to simplify n8n credentials/node development", "main": "dist/src/index", "types": "dist/src/index.d.ts", diff --git a/packages/nodes-base/package.json b/packages/nodes-base/package.json index 118117ae4339c..e9da85e9faf47 100644 --- a/packages/nodes-base/package.json +++ b/packages/nodes-base/package.json @@ -1,6 +1,6 @@ { "name": "n8n-nodes-base", - "version": "1.52.0", + "version": "1.53.0", "description": "Base nodes of n8n", "main": "index.js", "scripts": { diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 83de78636bf8c..179ae29311c11 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -1,6 +1,6 @@ { "name": "n8n-workflow", - "version": "1.51.0", + "version": "1.52.0", "description": "Workflow base code of n8n", "main": "dist/index.js", "module": "src/index.ts", From a8e2774f5382e202556b5506c7788265786aa973 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Wed, 31 Jul 2024 15:03:37 +0200 Subject: [PATCH 21/50] fix(core): Make OAuth1/OAuth2 callback not require auth (#10263) --- packages/cli/src/auth/auth.service.ts | 4 ---- .../oauth/oAuth1Credential.controller.ts | 15 +++++---------- .../oauth/oAuth2Credential.controller.ts | 10 ++++------ packages/cli/src/requests.ts | 4 ++-- 4 files changed, 11 insertions(+), 22 deletions(-) diff --git a/packages/cli/src/auth/auth.service.ts b/packages/cli/src/auth/auth.service.ts index ccf562e27e050..8cbb0c01cfa21 100644 --- a/packages/cli/src/auth/auth.service.ts +++ b/packages/cli/src/auth/auth.service.ts @@ -42,10 +42,6 @@ const skipBrowserIdCheckEndpoints = [ // We need to exclude binary-data downloading endpoint because we can't send custom headers on `` tags `/${restEndpoint}/binary-data/`, - - // oAuth callback urls aren't called by the frontend. therefore we can't send custom header on these requests - `/${restEndpoint}/oauth1-credential/callback`, - `/${restEndpoint}/oauth2-credential/callback`, ]; @Service() diff --git a/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts b/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts index 578a209e3664f..d967d2f83435a 100644 --- a/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts +++ b/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts @@ -99,9 +99,8 @@ export class OAuth1CredentialController extends AbstractOAuthController { } /** Verify and store app code. Generate access tokens and store for respective credential */ - @Get('/callback', { usesTemplates: true }) + @Get('/callback', { usesTemplates: true, skipAuth: true }) async handleCallback(req: OAuthRequest.OAuth1Credential.Callback, res: Response) { - const userId = req.user?.id; try { const { oauth_verifier, oauth_token, state: encodedState } = req.query; @@ -124,7 +123,7 @@ export class OAuth1CredentialController extends AbstractOAuthController { const credential = await this.getCredentialWithoutUser(credentialId); if (!credential) { const errorMessage = 'OAuth1 callback failed because of insufficient permissions'; - this.logger.error(errorMessage, { userId, credentialId }); + this.logger.error(errorMessage, { credentialId }); return this.renderCallbackError(res, errorMessage); } @@ -138,7 +137,7 @@ export class OAuth1CredentialController extends AbstractOAuthController { if (this.verifyCsrfState(decryptedDataOriginal, state)) { const errorMessage = 'The OAuth1 callback state is invalid!'; - this.logger.debug(errorMessage, { userId, credentialId }); + this.logger.debug(errorMessage, { credentialId }); return this.renderCallbackError(res, errorMessage); } @@ -156,7 +155,7 @@ export class OAuth1CredentialController extends AbstractOAuthController { try { oauthToken = await axios.request(options); } catch (error) { - this.logger.error('Unable to fetch tokens for OAuth1 callback', { userId, credentialId }); + this.logger.error('Unable to fetch tokens for OAuth1 callback', { credentialId }); const errorResponse = new NotFoundError('Unable to get access tokens!'); return sendErrorResponse(res, errorResponse); } @@ -172,15 +171,11 @@ export class OAuth1CredentialController extends AbstractOAuthController { await this.encryptAndSaveData(credential, decryptedDataOriginal); this.logger.verbose('OAuth1 callback successful for new credential', { - userId, credentialId, }); return res.render('oauth-callback'); } catch (error) { - this.logger.error('OAuth1 callback failed because of insufficient user permissions', { - userId, - }); - // Error response + this.logger.error('OAuth1 callback failed because of insufficient user permissions'); return sendErrorResponse(res, error as Error); } } diff --git a/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts b/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts index 71a0fe140c4c1..719043e9834b7 100644 --- a/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts +++ b/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts @@ -80,9 +80,8 @@ export class OAuth2CredentialController extends AbstractOAuthController { } /** Verify and store app code. Generate access tokens and store for respective credential */ - @Get('/callback', { usesTemplates: true }) + @Get('/callback', { usesTemplates: true, skipAuth: true }) async handleCallback(req: OAuthRequest.OAuth2Credential.Callback, res: Response) { - const userId = req.user?.id; try { const { code, state: encodedState } = req.query; if (!code || !encodedState) { @@ -104,7 +103,7 @@ export class OAuth2CredentialController extends AbstractOAuthController { const credential = await this.getCredentialWithoutUser(credentialId); if (!credential) { const errorMessage = 'OAuth2 callback failed because of insufficient permissions'; - this.logger.error(errorMessage, { userId, credentialId }); + this.logger.error(errorMessage, { credentialId }); return this.renderCallbackError(res, errorMessage); } @@ -118,7 +117,7 @@ export class OAuth2CredentialController extends AbstractOAuthController { if (this.verifyCsrfState(decryptedDataOriginal, state)) { const errorMessage = 'The OAuth2 callback state is invalid!'; - this.logger.debug(errorMessage, { userId, credentialId }); + this.logger.debug(errorMessage, { credentialId }); return this.renderCallbackError(res, errorMessage); } @@ -157,7 +156,7 @@ export class OAuth2CredentialController extends AbstractOAuthController { if (oauthToken === undefined) { const errorMessage = 'Unable to get OAuth2 access tokens!'; - this.logger.error(errorMessage, { userId, credentialId }); + this.logger.error(errorMessage, { credentialId }); return this.renderCallbackError(res, errorMessage); } @@ -174,7 +173,6 @@ export class OAuth2CredentialController extends AbstractOAuthController { await this.encryptAndSaveData(credential, decryptedDataOriginal); this.logger.verbose('OAuth2 callback successful for credential', { - userId, credentialId, }); diff --git a/packages/cli/src/requests.ts b/packages/cli/src/requests.ts index 1ff6537257529..02ef5acb6b015 100644 --- a/packages/cli/src/requests.ts +++ b/packages/cli/src/requests.ts @@ -371,7 +371,7 @@ export declare namespace MFA { export declare namespace OAuthRequest { namespace OAuth1Credential { type Auth = AuthenticatedRequest<{}, {}, {}, { id: string }>; - type Callback = AuthenticatedRequest< + type Callback = AuthlessRequest< {}, {}, {}, @@ -383,7 +383,7 @@ export declare namespace OAuthRequest { namespace OAuth2Credential { type Auth = AuthenticatedRequest<{}, {}, {}, { id: string }>; - type Callback = AuthenticatedRequest<{}, {}, {}, { code: string; state: string }>; + type Callback = AuthlessRequest<{}, {}, {}, { code: string; state: string }>; } } From c8ee852159207be0cfe2c3e0ee8e7b29d838aa35 Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Wed, 31 Jul 2024 16:11:42 +0300 Subject: [PATCH 22/50] fix: Loop node no input data shown (#10224) --- packages/editor-ui/src/composables/useNodeHelpers.ts | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/packages/editor-ui/src/composables/useNodeHelpers.ts b/packages/editor-ui/src/composables/useNodeHelpers.ts index 19e46339bcdd2..a8c763b11f5a2 100644 --- a/packages/editor-ui/src/composables/useNodeHelpers.ts +++ b/packages/editor-ui/src/composables/useNodeHelpers.ts @@ -8,6 +8,7 @@ import { FORM_TRIGGER_NODE_TYPE, NODE_OUTPUT_DEFAULT_KEY, PLACEHOLDER_FILLED_AT_EXECUTION_TIME, + SPLIT_IN_BATCHES_NODE_TYPE, WEBHOOK_NODE_TYPE, } from '@/constants'; @@ -571,6 +572,16 @@ export function useNodeHelpers() { paneType: NodePanelType = 'output', connectionType: ConnectionTypes = NodeConnectionType.Main, ): INodeExecutionData[] { + //TODO: check if this needs to be fixed in different place + if ( + node?.type === SPLIT_IN_BATCHES_NODE_TYPE && + paneType === 'input' && + runIndex !== 0 && + outputIndex !== 0 + ) { + runIndex = runIndex - 1; + } + if (node === null) { return []; } From d91eb2cdd50136b217c3423bfad53aaea707cbb6 Mon Sep 17 00:00:00 2001 From: Jon Date: Wed, 31 Jul 2024 16:19:25 +0100 Subject: [PATCH 23/50] feat(AWS Lambda Node): Add support for paired items (no-changelog) (#10270) --- packages/nodes-base/nodes/Aws/AwsLambda.node.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/packages/nodes-base/nodes/Aws/AwsLambda.node.ts b/packages/nodes-base/nodes/Aws/AwsLambda.node.ts index e1fd51f13086c..ed69ab35bd1b8 100644 --- a/packages/nodes-base/nodes/Aws/AwsLambda.node.ts +++ b/packages/nodes-base/nodes/Aws/AwsLambda.node.ts @@ -195,13 +195,21 @@ export class AwsLambda implements INodeType { throw new NodeApiError(this.getNode(), responseData as JsonObject); } else { - returnData.push({ - result: responseData, - } as IDataObject); + const executionData = this.helpers.constructExecutionMetaData( + this.helpers.returnJsonArray({ + result: responseData, + }), + { itemData: { item: i } }, + ); + returnData.push(...executionData); } } catch (error) { if (this.continueOnFail(error)) { - returnData.push({ error: (error as JsonObject).message }); + const executionData = this.helpers.constructExecutionMetaData( + this.helpers.returnJsonArray({ error: (error as JsonObject).message }), + { itemData: { item: i } }, + ); + returnData.push(...executionData); continue; } throw error; From 1608d2527b2ec0897d4a69cda7e576167c5ae3d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Wed, 31 Jul 2024 17:45:11 +0200 Subject: [PATCH 24/50] refactor(core): Port endpoints config (no-changelog) (#10268) --- packages/@n8n/config/src/configs/endpoints.ts | 102 +++++++++++++ packages/@n8n/config/src/index.ts | 4 + packages/@n8n/config/test/config.test.ts | 34 ++++- packages/cli/src/AbstractServer.ts | 27 ++-- packages/cli/src/Server.ts | 15 +- .../cli/src/WorkflowExecuteAdditionalData.ts | 16 +- packages/cli/src/auth/auth.service.ts | 5 +- packages/cli/src/commands/BaseCommand.ts | 2 +- packages/cli/src/commands/start.ts | 10 +- packages/cli/src/config/schema.ts | 143 ------------------ .../oauth/abstractOAuth.controller.ts | 5 +- .../cli/src/decorators/controller.registry.ts | 6 +- .../prometheus-metrics.service.test.ts | 34 +++-- .../src/metrics/prometheus-metrics.service.ts | 25 +-- packages/cli/src/middlewares/bodyParser.ts | 5 +- packages/cli/src/services/frontend.service.ts | 14 +- .../telemetry-event-relay.service.ts | 5 +- .../integration/prometheus-metrics.test.ts | 37 ++++- .../cli/test/integration/shared/constants.ts | 3 +- .../decorators/controller.registry.test.ts | 4 +- packages/cli/test/unit/webhooks.test.ts | 7 +- 21 files changed, 275 insertions(+), 228 deletions(-) create mode 100644 packages/@n8n/config/src/configs/endpoints.ts diff --git a/packages/@n8n/config/src/configs/endpoints.ts b/packages/@n8n/config/src/configs/endpoints.ts new file mode 100644 index 0000000000000..7a04a8249f692 --- /dev/null +++ b/packages/@n8n/config/src/configs/endpoints.ts @@ -0,0 +1,102 @@ +import { Config, Env, Nested } from '../decorators'; + +@Config +class PrometheusMetricsConfig { + /** Whether to enable the `/metrics` endpoint to expose Prometheus metrics. */ + @Env('N8N_METRICS') + readonly enable: boolean = false; + + /** Prefix for Prometheus metric names. */ + @Env('N8N_METRICS_PREFIX') + readonly prefix: string = 'n8n_'; + + /** Whether to expose system and Node.js metrics. See: https://www.npmjs.com/package/prom-client */ + @Env('N8N_METRICS_INCLUDE_DEFAULT_METRICS') + readonly includeDefaultMetrics = true; + + /** Whether to include a label for workflow ID on workflow metrics. */ + @Env('N8N_METRICS_INCLUDE_WORKFLOW_ID_LABEL') + readonly includeWorkflowIdLabel: boolean = false; + + /** Whether to include a label for node type on node metrics. */ + @Env('N8N_METRICS_INCLUDE_NODE_TYPE_LABEL') + readonly includeNodeTypeLabel: boolean = false; + + /** Whether to include a label for credential type on credential metrics. */ + @Env('N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABEL') + readonly includeCredentialTypeLabel: boolean = false; + + /** Whether to expose metrics for API endpoints. See: https://www.npmjs.com/package/express-prom-bundle */ + @Env('N8N_METRICS_INCLUDE_API_ENDPOINTS') + readonly includeApiEndpoints: boolean = false; + + /** Whether to include a label for the path of API endpoint calls. */ + @Env('N8N_METRICS_INCLUDE_API_PATH_LABEL') + readonly includeApiPathLabel: boolean = false; + + /** Whether to include a label for the HTTP method of API endpoint calls. */ + @Env('N8N_METRICS_INCLUDE_API_METHOD_LABEL') + readonly includeApiMethodLabel: boolean = false; + + /** Whether to include a label for the status code of API endpoint calls. */ + @Env('N8N_METRICS_INCLUDE_API_STATUS_CODE_LABEL') + readonly includeApiStatusCodeLabel: boolean = false; + + /** Whether to include metrics for cache hits and misses. */ + @Env('N8N_METRICS_INCLUDE_CACHE_METRICS') + readonly includeCacheMetrics: boolean = false; + + /** Whether to include metrics derived from n8n's internal events */ + @Env('N8N_METRICS_INCLUDE_MESSAGE_EVENT_BUS_METRICS') + readonly includeMessageEventBusMetrics: boolean = false; +} + +@Config +export class EndpointsConfig { + /** Max payload size in MiB */ + @Env('N8N_PAYLOAD_SIZE_MAX') + readonly payloadSizeMax: number = 16; + + @Nested + readonly metrics: PrometheusMetricsConfig; + + /** Path segment for REST API endpoints. */ + @Env('N8N_ENDPOINT_REST') + readonly rest: string = 'rest'; + + /** Path segment for form endpoints. */ + @Env('N8N_ENDPOINT_FORM') + readonly form: string = 'form'; + + /** Path segment for test form endpoints. */ + @Env('N8N_ENDPOINT_FORM_TEST') + readonly formTest: string = 'form-test'; + + /** Path segment for waiting form endpoints. */ + @Env('N8N_ENDPOINT_FORM_WAIT') + readonly formWaiting: string = 'form-waiting'; + + /** Path segment for webhook endpoints. */ + @Env('N8N_ENDPOINT_WEBHOOK') + readonly webhook: string = 'webhook'; + + /** Path segment for test webhook endpoints. */ + @Env('N8N_ENDPOINT_WEBHOOK_TEST') + readonly webhookTest: string = 'webhook-test'; + + /** Path segment for waiting webhook endpoints. */ + @Env('N8N_ENDPOINT_WEBHOOK_WAIT') + readonly webhookWaiting: string = 'webhook-waiting'; + + /** Whether to disable n8n's UI (frontend). */ + @Env('N8N_DISABLE_UI') + readonly disableUi: boolean = false; + + /** Whether to disable production webhooks on the main process, when using webhook-specific processes. */ + @Env('N8N_DISABLE_PRODUCTION_MAIN_PROCESS') + readonly disableProductionWebhooksOnMainProcess: boolean = false; + + /** Colon-delimited list of additional endpoints to not open the UI on. */ + @Env('N8N_ADDITIONAL_NON_UI_ROUTES') + readonly additionalNonUIRoutes: string = ''; +} diff --git a/packages/@n8n/config/src/index.ts b/packages/@n8n/config/src/index.ts index fee6718c6acdb..d7bb09889d5ab 100644 --- a/packages/@n8n/config/src/index.ts +++ b/packages/@n8n/config/src/index.ts @@ -10,6 +10,7 @@ import { EventBusConfig } from './configs/event-bus'; import { NodesConfig } from './configs/nodes'; import { ExternalStorageConfig } from './configs/external-storage'; import { WorkflowsConfig } from './configs/workflows'; +import { EndpointsConfig } from './configs/endpoints'; @Config class UserManagementConfig { @@ -71,4 +72,7 @@ export class GlobalConfig { /** HTTP Protocol via which n8n can be reached */ @Env('N8N_PROTOCOL') readonly protocol: 'http' | 'https' = 'http'; + + @Nested + readonly endpoints: EndpointsConfig; } diff --git a/packages/@n8n/config/test/config.test.ts b/packages/@n8n/config/test/config.test.ts index 97afb0cda405e..a36a74d1e25c7 100644 --- a/packages/@n8n/config/test/config.test.ts +++ b/packages/@n8n/config/test/config.test.ts @@ -145,12 +145,44 @@ describe('GlobalConfig', () => { onboardingFlowDisabled: false, callerPolicyDefaultOption: 'workflowsFromSameOwner', }, + endpoints: { + metrics: { + enable: false, + prefix: 'n8n_', + includeWorkflowIdLabel: false, + includeDefaultMetrics: true, + includeMessageEventBusMetrics: false, + includeNodeTypeLabel: false, + includeCacheMetrics: false, + includeApiEndpoints: false, + includeApiPathLabel: false, + includeApiMethodLabel: false, + includeCredentialTypeLabel: false, + includeApiStatusCodeLabel: false, + }, + additionalNonUIRoutes: '', + disableProductionWebhooksOnMainProcess: false, + disableUi: false, + form: 'form', + formTest: 'form-test', + formWaiting: 'form-waiting', + payloadSizeMax: 16, + rest: 'rest', + webhook: 'webhook', + webhookTest: 'webhook-test', + webhookWaiting: 'webhook-waiting', + }, }; it('should use all default values when no env variables are defined', () => { process.env = {}; const config = Container.get(GlobalConfig); - expect(config).toEqual(defaultConfig); + + // deepCopy for diff to show plain objects + // eslint-disable-next-line n8n-local-rules/no-json-parse-json-stringify + const deepCopy = (obj: T): T => JSON.parse(JSON.stringify(obj)); + + expect(deepCopy(config)).toEqual(defaultConfig); expect(mockFs.readFileSync).not.toHaveBeenCalled(); }); diff --git a/packages/cli/src/AbstractServer.ts b/packages/cli/src/AbstractServer.ts index bef45bb1433d2..ab150e2947328 100644 --- a/packages/cli/src/AbstractServer.ts +++ b/packages/cli/src/AbstractServer.ts @@ -34,7 +34,7 @@ export abstract class AbstractServer { protected externalHooks: ExternalHooks; - protected protocol = Container.get(GlobalConfig).protocol; + protected globalConfig = Container.get(GlobalConfig); protected sslKey: string; @@ -74,15 +74,15 @@ export abstract class AbstractServer { this.sslKey = config.getEnv('ssl_key'); this.sslCert = config.getEnv('ssl_cert'); - this.restEndpoint = config.getEnv('endpoints.rest'); + this.restEndpoint = this.globalConfig.endpoints.rest; - this.endpointForm = config.getEnv('endpoints.form'); - this.endpointFormTest = config.getEnv('endpoints.formTest'); - this.endpointFormWaiting = config.getEnv('endpoints.formWaiting'); + this.endpointForm = this.globalConfig.endpoints.form; + this.endpointFormTest = this.globalConfig.endpoints.formTest; + this.endpointFormWaiting = this.globalConfig.endpoints.formWaiting; - this.endpointWebhook = config.getEnv('endpoints.webhook'); - this.endpointWebhookTest = config.getEnv('endpoints.webhookTest'); - this.endpointWebhookWaiting = config.getEnv('endpoints.webhookWaiting'); + this.endpointWebhook = this.globalConfig.endpoints.webhook; + this.endpointWebhookTest = this.globalConfig.endpoints.webhookTest; + this.endpointWebhookWaiting = this.globalConfig.endpoints.webhookWaiting; this.uniqueInstanceId = generateHostInstanceId(instanceType); @@ -134,7 +134,8 @@ export abstract class AbstractServer { } async init(): Promise { - const { app, protocol, sslKey, sslCert } = this; + const { app, sslKey, sslCert } = this; + const { protocol } = this.globalConfig; if (protocol === 'https' && sslKey && sslCert) { const https = await import('https'); @@ -261,14 +262,16 @@ export abstract class AbstractServer { return; } - this.logger.debug(`Shutting down ${this.protocol} server`); + const { protocol } = this.globalConfig; + + this.logger.debug(`Shutting down ${protocol} server`); this.server.close((error) => { if (error) { - this.logger.error(`Error while shutting down ${this.protocol} server`, { error }); + this.logger.error(`Error while shutting down ${protocol} server`, { error }); } - this.logger.debug(`${this.protocol} server shut down`); + this.logger.debug(`${protocol} server shut down`); }); } } diff --git a/packages/cli/src/Server.ts b/packages/cli/src/Server.ts index dc63d697e77b8..4c9628bc1bf1d 100644 --- a/packages/cli/src/Server.ts +++ b/packages/cli/src/Server.ts @@ -6,7 +6,6 @@ import { promisify } from 'util'; import cookieParser from 'cookie-parser'; import express from 'express'; import helmet from 'helmet'; -import { GlobalConfig } from '@n8n/config'; import { InstanceSettings } from 'n8n-core'; import type { IN8nUISettings } from 'n8n-workflow'; @@ -81,17 +80,16 @@ export class Server extends AbstractServer { private readonly loadNodesAndCredentials: LoadNodesAndCredentials, private readonly orchestrationService: OrchestrationService, private readonly postHogClient: PostHogClient, - private readonly globalConfig: GlobalConfig, private readonly eventService: EventService, ) { super('main'); this.testWebhooksEnabled = true; - this.webhooksEnabled = !config.getEnv('endpoints.disableProductionWebhooksOnMainProcess'); + this.webhooksEnabled = !this.globalConfig.endpoints.disableProductionWebhooksOnMainProcess; } async start() { - if (!config.getEnv('endpoints.disableUi')) { + if (!this.globalConfig.endpoints.disableUi) { const { FrontendService } = await import('@/services/frontend.service'); this.frontendService = Container.get(FrontendService); } @@ -133,7 +131,7 @@ export class Server extends AbstractServer { await import('@/controllers/mfa.controller'); } - if (!config.getEnv('endpoints.disableUi')) { + if (!this.globalConfig.endpoints.disableUi) { await import('@/controllers/cta.controller'); } @@ -167,7 +165,7 @@ export class Server extends AbstractServer { } async configure(): Promise { - if (config.getEnv('endpoints.metrics.enable')) { + if (this.globalConfig.endpoints.metrics.enable) { const { PrometheusMetricsService } = await import('@/metrics/prometheus-metrics.service'); await Container.get(PrometheusMetricsService).init(this.app); } @@ -307,7 +305,8 @@ export class Server extends AbstractServer { this.app.use('/icons/@:scope/:packageName/*/*.(svg|png)', serveIcons); this.app.use('/icons/:packageName/*/*.(svg|png)', serveIcons); - const isTLSEnabled = this.protocol === 'https' && !!(this.sslKey && this.sslCert); + const isTLSEnabled = + this.globalConfig.protocol === 'https' && !!(this.sslKey && this.sslCert); const isPreviewMode = process.env.N8N_PREVIEW_MODE === 'true'; const securityHeadersMiddleware = helmet({ contentSecurityPolicy: false, @@ -341,7 +340,7 @@ export class Server extends AbstractServer { this.restEndpoint, this.endpointPresetCredentials, isApiEnabled() ? '' : publicApiEndpoint, - ...config.getEnv('endpoints.additionalNonUIRoutes').split(':'), + ...this.globalConfig.endpoints.additionalNonUIRoutes.split(':'), ].filter((u) => !!u); const nonUIRoutesRegex = new RegExp(`^/(${nonUIRoutes.join('|')})/?.*$`); const historyApiHandler: express.RequestHandler = (req, res, next) => { diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index 98ad9acde2936..f4acd61b6aa55 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -1002,23 +1002,19 @@ export async function getBase( ): Promise { const urlBaseWebhook = Container.get(UrlService).getWebhookBaseUrl(); - const formWaitingBaseUrl = urlBaseWebhook + config.getEnv('endpoints.formWaiting'); - - const webhookBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhook'); - const webhookTestBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhookTest'); - const webhookWaitingBaseUrl = urlBaseWebhook + config.getEnv('endpoints.webhookWaiting'); + const globalConfig = Container.get(GlobalConfig); const variables = await WorkflowHelpers.getVariables(); return { credentialsHelper: Container.get(CredentialsHelper), executeWorkflow, - restApiUrl: urlBaseWebhook + config.getEnv('endpoints.rest'), + restApiUrl: urlBaseWebhook + globalConfig.endpoints.rest, instanceBaseUrl: urlBaseWebhook, - formWaitingBaseUrl, - webhookBaseUrl, - webhookWaitingBaseUrl, - webhookTestBaseUrl, + formWaitingBaseUrl: globalConfig.endpoints.formWaiting, + webhookBaseUrl: globalConfig.endpoints.webhook, + webhookWaitingBaseUrl: globalConfig.endpoints.webhookWaiting, + webhookTestBaseUrl: globalConfig.endpoints.webhookTest, currentNodeParameters, executionTimeoutTimestamp, userId, diff --git a/packages/cli/src/auth/auth.service.ts b/packages/cli/src/auth/auth.service.ts index 8cbb0c01cfa21..e2487e6f6f9a6 100644 --- a/packages/cli/src/auth/auth.service.ts +++ b/packages/cli/src/auth/auth.service.ts @@ -1,4 +1,4 @@ -import { Service } from 'typedi'; +import Container, { Service } from 'typedi'; import type { NextFunction, Response } from 'express'; import { createHash } from 'crypto'; import { JsonWebTokenError, TokenExpiredError } from 'jsonwebtoken'; @@ -14,6 +14,7 @@ import { Logger } from '@/Logger'; import type { AuthenticatedRequest } from '@/requests'; import { JwtService } from '@/services/jwt.service'; import { UrlService } from '@/services/url.service'; +import { GlobalConfig } from '@n8n/config'; interface AuthJwtPayload { /** User Id */ @@ -33,7 +34,7 @@ interface PasswordResetToken { hash: string; } -const restEndpoint = config.get('endpoints.rest'); +const restEndpoint = Container.get(GlobalConfig).endpoints.rest; // The browser-id check needs to be skipped on these endpoints const skipBrowserIdCheckEndpoints = [ // we need to exclude push endpoint because we can't send custom header on websocket requests diff --git a/packages/cli/src/commands/BaseCommand.ts b/packages/cli/src/commands/BaseCommand.ts index b00d314b91212..4cebc7fbb66f6 100644 --- a/packages/cli/src/commands/BaseCommand.ts +++ b/packages/cli/src/commands/BaseCommand.ts @@ -44,7 +44,7 @@ export abstract class BaseCommand extends Command { protected license: License; - private globalConfig = Container.get(GlobalConfig); + protected globalConfig = Container.get(GlobalConfig); /** * How long to wait for graceful shutdown before force killing the process. diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index c35344326e8e7..4d7cd888b4cc0 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -124,8 +124,8 @@ export class Start extends BaseCommand { private async generateStaticAssets() { // Read the index file and replace the path placeholder - const n8nPath = Container.get(GlobalConfig).path; - const restEndpoint = config.getEnv('endpoints.rest'); + const n8nPath = this.globalConfig.path; + const hooksUrls = config.getEnv('externalFrontendHooksUrls'); let scriptsString = ''; @@ -151,7 +151,9 @@ export class Start extends BaseCommand { ]; if (filePath.endsWith('index.html')) { streams.push( - replaceStream('{{REST_ENDPOINT}}', restEndpoint, { ignoreCase: false }), + replaceStream('{{REST_ENDPOINT}}', this.globalConfig.endpoints.rest, { + ignoreCase: false, + }), replaceStream(closingTitleTag, closingTitleTag + scriptsString, { ignoreCase: false, }), @@ -201,7 +203,7 @@ export class Start extends BaseCommand { this.initWorkflowHistory(); this.logger.debug('Workflow history init complete'); - if (!config.getEnv('endpoints.disableUi')) { + if (!this.globalConfig.endpoints.disableUi) { await this.generateStaticAssets(); } } diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index 0b689b8f4cfbe..068317e4918f6 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -355,149 +355,6 @@ export const schema = { }, }, - endpoints: { - payloadSizeMax: { - format: Number, - default: 16, - env: 'N8N_PAYLOAD_SIZE_MAX', - doc: 'Maximum payload size in MB.', - }, - metrics: { - enable: { - format: Boolean, - default: false, - env: 'N8N_METRICS', - doc: 'Enable /metrics endpoint. Default: false', - }, - prefix: { - format: String, - default: 'n8n_', - env: 'N8N_METRICS_PREFIX', - doc: 'An optional prefix for metric names. Default: n8n_', - }, - includeDefaultMetrics: { - format: Boolean, - default: true, - env: 'N8N_METRICS_INCLUDE_DEFAULT_METRICS', - doc: 'Whether to expose default system and node.js metrics. Default: true', - }, - includeWorkflowIdLabel: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_WORKFLOW_ID_LABEL', - doc: 'Whether to include a label for the workflow ID on workflow metrics. Default: false', - }, - includeNodeTypeLabel: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_NODE_TYPE_LABEL', - doc: 'Whether to include a label for the node type on node metrics. Default: false', - }, - includeCredentialTypeLabel: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_CREDENTIAL_TYPE_LABEL', - doc: 'Whether to include a label for the credential type on credential metrics. Default: false', - }, - includeApiEndpoints: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_API_ENDPOINTS', - doc: 'Whether to expose metrics for API endpoints. Default: false', - }, - includeApiPathLabel: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_API_PATH_LABEL', - doc: 'Whether to include a label for the path of API invocations. Default: false', - }, - includeApiMethodLabel: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_API_METHOD_LABEL', - doc: 'Whether to include a label for the HTTP method (GET, POST, ...) of API invocations. Default: false', - }, - includeApiStatusCodeLabel: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_API_STATUS_CODE_LABEL', - doc: 'Whether to include a label for the HTTP status code (200, 404, ...) of API invocations. Default: false', - }, - includeCacheMetrics: { - format: Boolean, - default: false, - env: 'N8N_METRICS_INCLUDE_CACHE_METRICS', - doc: 'Whether to include metrics for cache hits and misses. Default: false', - }, - includeMessageEventBusMetrics: { - format: Boolean, - default: true, - env: 'N8N_METRICS_INCLUDE_MESSAGE_EVENT_BUS_METRICS', - doc: 'Whether to include metrics for events. Default: false', - }, - }, - rest: { - format: String, - default: 'rest', - env: 'N8N_ENDPOINT_REST', - doc: 'Path for rest endpoint', - }, - form: { - format: String, - default: 'form', - env: 'N8N_ENDPOINT_FORM', - doc: 'Path for form endpoint', - }, - formTest: { - format: String, - default: 'form-test', - env: 'N8N_ENDPOINT_FORM_TEST', - doc: 'Path for test form endpoint', - }, - formWaiting: { - format: String, - default: 'form-waiting', - env: 'N8N_ENDPOINT_FORM_WAIT', - doc: 'Path for waiting form endpoint', - }, - webhook: { - format: String, - default: 'webhook', - env: 'N8N_ENDPOINT_WEBHOOK', - doc: 'Path for webhook endpoint', - }, - webhookWaiting: { - format: String, - default: 'webhook-waiting', - env: 'N8N_ENDPOINT_WEBHOOK_WAIT', - doc: 'Path for waiting-webhook endpoint', - }, - webhookTest: { - format: String, - default: 'webhook-test', - env: 'N8N_ENDPOINT_WEBHOOK_TEST', - doc: 'Path for test-webhook endpoint', - }, - disableUi: { - format: Boolean, - default: false, - env: 'N8N_DISABLE_UI', - doc: 'Disable N8N UI (Frontend).', - }, - disableProductionWebhooksOnMainProcess: { - format: Boolean, - default: false, - env: 'N8N_DISABLE_PRODUCTION_MAIN_PROCESS', - doc: 'Disable production webhooks from main process. This helps ensures no http traffic load to main process when using webhook-specific processes.', - }, - additionalNonUIRoutes: { - doc: 'Additional endpoints to not open the UI on. Multiple endpoints can be separated by colon (":")', - format: String, - default: '', - env: 'N8N_ADDITIONAL_NON_UI_ROUTES', - }, - }, - workflowTagsDisabled: { format: Boolean, default: false, diff --git a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts index 9454b4ee7d1ce..f9ffd591feb22 100644 --- a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts +++ b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts @@ -5,7 +5,6 @@ import { Credentials } from 'n8n-core'; import type { ICredentialDataDecryptedObject, IWorkflowExecuteAdditionalData } from 'n8n-workflow'; import { jsonParse, ApplicationError } from 'n8n-workflow'; -import config from '@/config'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; import type { User } from '@db/entities/User'; import { CredentialsRepository } from '@db/repositories/credentials.repository'; @@ -20,6 +19,7 @@ import { ExternalHooks } from '@/ExternalHooks'; import { UrlService } from '@/services/url.service'; import { BadRequestError } from '@/errors/response-errors/bad-request.error'; import { NotFoundError } from '@/errors/response-errors/not-found.error'; +import { GlobalConfig } from '@n8n/config'; export interface CsrfStateParam { cid: string; @@ -37,10 +37,11 @@ export abstract class AbstractOAuthController { private readonly credentialsRepository: CredentialsRepository, private readonly sharedCredentialsRepository: SharedCredentialsRepository, private readonly urlService: UrlService, + private readonly globalConfig: GlobalConfig, ) {} get baseUrl() { - const restUrl = `${this.urlService.getInstanceBaseUrl()}/${config.getEnv('endpoints.rest')}`; + const restUrl = `${this.urlService.getInstanceBaseUrl()}/${this.globalConfig.endpoints.rest}`; return `${restUrl}/oauth${this.oauthVersion}-credential`; } diff --git a/packages/cli/src/decorators/controller.registry.ts b/packages/cli/src/decorators/controller.registry.ts index c012922c16728..4173f9309ef3c 100644 --- a/packages/cli/src/decorators/controller.registry.ts +++ b/packages/cli/src/decorators/controller.registry.ts @@ -4,7 +4,6 @@ import type { Application, Request, Response, RequestHandler } from 'express'; import { rateLimit as expressRateLimit } from 'express-rate-limit'; import { AuthService } from '@/auth/auth.service'; -import config from '@/config'; import { UnauthenticatedError } from '@/errors/response-errors/unauthenticated.error'; import { inProduction, RESPONSE_ERROR_MESSAGES } from '@/constants'; import type { BooleanLicenseFeature } from '@/Interfaces'; @@ -12,7 +11,7 @@ import { License } from '@/License'; import type { AuthenticatedRequest } from '@/requests'; import { send } from '@/ResponseHelper'; // TODO: move `ResponseHelper.send` to this file import { userHasScope } from '@/permissions/checkAccess'; - +import { GlobalConfig } from '@n8n/config'; import type { AccessScope, Controller, @@ -52,6 +51,7 @@ export class ControllerRegistry { constructor( private readonly license: License, private readonly authService: AuthService, + private readonly globalConfig: GlobalConfig, ) {} activate(app: Application) { @@ -64,7 +64,7 @@ export class ControllerRegistry { const metadata = registry.get(controllerClass)!; const router = Router({ mergeParams: true }); - const prefix = `/${config.getEnv('endpoints.rest')}/${metadata.basePath}` + const prefix = `/${this.globalConfig.endpoints.rest}/${metadata.basePath}` .replace(/\/+/g, '/') .replace(/\/$/, ''); app.use(prefix, router); diff --git a/packages/cli/src/metrics/__tests__/prometheus-metrics.service.test.ts b/packages/cli/src/metrics/__tests__/prometheus-metrics.service.test.ts index 8b89878203ed5..219170ac085a8 100644 --- a/packages/cli/src/metrics/__tests__/prometheus-metrics.service.test.ts +++ b/packages/cli/src/metrics/__tests__/prometheus-metrics.service.test.ts @@ -5,6 +5,8 @@ import { mock } from 'jest-mock-extended'; import { PrometheusMetricsService } from '../prometheus-metrics.service'; import type express from 'express'; import type { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus'; +import { mockInstance } from '@test/mocking'; +import { GlobalConfig } from '@n8n/config'; const mockMiddleware = ( _req: express.Request, @@ -16,13 +18,27 @@ jest.mock('prom-client'); jest.mock('express-prom-bundle', () => jest.fn(() => mockMiddleware)); describe('PrometheusMetricsService', () => { - beforeEach(() => { - config.load(config.default); + const globalConfig = mockInstance(GlobalConfig, { + endpoints: { + metrics: { + prefix: 'n8n_', + includeDefaultMetrics: true, + includeApiEndpoints: true, + includeCacheMetrics: true, + includeMessageEventBusMetrics: true, + includeCredentialTypeLabel: false, + includeNodeTypeLabel: false, + includeWorkflowIdLabel: false, + includeApiPathLabel: true, + includeApiMethodLabel: true, + includeApiStatusCodeLabel: true, + }, + }, }); describe('init', () => { it('should set up `n8n_version_info`', async () => { - const service = new PrometheusMetricsService(mock(), mock()); + const service = new PrometheusMetricsService(mock(), mock(), globalConfig); await service.init(mock()); @@ -34,7 +50,7 @@ describe('PrometheusMetricsService', () => { }); it('should set up default metrics collection with `prom-client`', async () => { - const service = new PrometheusMetricsService(mock(), mock()); + const service = new PrometheusMetricsService(mock(), mock(), globalConfig); await service.init(mock()); @@ -43,7 +59,7 @@ describe('PrometheusMetricsService', () => { it('should set up `n8n_cache_hits_total`', async () => { config.set('endpoints.metrics.includeCacheMetrics', true); - const service = new PrometheusMetricsService(mock(), mock()); + const service = new PrometheusMetricsService(mock(), mock(), globalConfig); await service.init(mock()); @@ -58,7 +74,7 @@ describe('PrometheusMetricsService', () => { it('should set up `n8n_cache_misses_total`', async () => { config.set('endpoints.metrics.includeCacheMetrics', true); - const service = new PrometheusMetricsService(mock(), mock()); + const service = new PrometheusMetricsService(mock(), mock(), globalConfig); await service.init(mock()); @@ -73,7 +89,7 @@ describe('PrometheusMetricsService', () => { it('should set up `n8n_cache_updates_total`', async () => { config.set('endpoints.metrics.includeCacheMetrics', true); - const service = new PrometheusMetricsService(mock(), mock()); + const service = new PrometheusMetricsService(mock(), mock(), globalConfig); await service.init(mock()); @@ -91,7 +107,7 @@ describe('PrometheusMetricsService', () => { config.set('endpoints.metrics.includeApiPathLabel', true); config.set('endpoints.metrics.includeApiMethodLabel', true); config.set('endpoints.metrics.includeApiStatusCodeLabel', true); - const service = new PrometheusMetricsService(mock(), mock()); + const service = new PrometheusMetricsService(mock(), mock(), globalConfig); const app = mock(); @@ -122,7 +138,7 @@ describe('PrometheusMetricsService', () => { it('should set up event bus metrics', async () => { const eventBus = mock(); - const service = new PrometheusMetricsService(mock(), eventBus); + const service = new PrometheusMetricsService(mock(), eventBus, globalConfig); await service.init(mock()); diff --git a/packages/cli/src/metrics/prometheus-metrics.service.ts b/packages/cli/src/metrics/prometheus-metrics.service.ts index b2d38424bccf7..1444f6f694bbd 100644 --- a/packages/cli/src/metrics/prometheus-metrics.service.ts +++ b/packages/cli/src/metrics/prometheus-metrics.service.ts @@ -1,4 +1,3 @@ -import config from '@/config'; import { N8N_VERSION } from '@/constants'; import type express from 'express'; import promBundle from 'express-prom-bundle'; @@ -11,32 +10,34 @@ import { MessageEventBus } from '@/eventbus/MessageEventBus/MessageEventBus'; import { EventMessageTypeNames } from 'n8n-workflow'; import type { EventMessageTypes } from '@/eventbus'; import type { Includes, MetricCategory, MetricLabel } from './types'; +import { GlobalConfig } from '@n8n/config'; @Service() export class PrometheusMetricsService { constructor( private readonly cacheService: CacheService, private readonly eventBus: MessageEventBus, + private readonly globalConfig: GlobalConfig, ) {} private readonly counters: { [key: string]: Counter | null } = {}; - private readonly prefix = config.getEnv('endpoints.metrics.prefix'); + private readonly prefix = this.globalConfig.endpoints.metrics.prefix; private readonly includes: Includes = { metrics: { - default: config.getEnv('endpoints.metrics.includeDefaultMetrics'), - routes: config.getEnv('endpoints.metrics.includeApiEndpoints'), - cache: config.getEnv('endpoints.metrics.includeCacheMetrics'), - logs: config.getEnv('endpoints.metrics.includeMessageEventBusMetrics'), + default: this.globalConfig.endpoints.metrics.includeDefaultMetrics, + routes: this.globalConfig.endpoints.metrics.includeApiEndpoints, + cache: this.globalConfig.endpoints.metrics.includeCacheMetrics, + logs: this.globalConfig.endpoints.metrics.includeMessageEventBusMetrics, }, labels: { - credentialsType: config.getEnv('endpoints.metrics.includeCredentialTypeLabel'), - nodeType: config.getEnv('endpoints.metrics.includeNodeTypeLabel'), - workflowId: config.getEnv('endpoints.metrics.includeWorkflowIdLabel'), - apiPath: config.getEnv('endpoints.metrics.includeApiPathLabel'), - apiMethod: config.getEnv('endpoints.metrics.includeApiMethodLabel'), - apiStatusCode: config.getEnv('endpoints.metrics.includeApiStatusCodeLabel'), + credentialsType: this.globalConfig.endpoints.metrics.includeCredentialTypeLabel, + nodeType: this.globalConfig.endpoints.metrics.includeNodeTypeLabel, + workflowId: this.globalConfig.endpoints.metrics.includeWorkflowIdLabel, + apiPath: this.globalConfig.endpoints.metrics.includeApiPathLabel, + apiMethod: this.globalConfig.endpoints.metrics.includeApiMethodLabel, + apiStatusCode: this.globalConfig.endpoints.metrics.includeApiStatusCodeLabel, }, }; diff --git a/packages/cli/src/middlewares/bodyParser.ts b/packages/cli/src/middlewares/bodyParser.ts index d48bf593cceee..5efe388e6f1e1 100644 --- a/packages/cli/src/middlewares/bodyParser.ts +++ b/packages/cli/src/middlewares/bodyParser.ts @@ -6,8 +6,9 @@ import { parse as parseQueryString } from 'querystring'; import { Parser as XmlParser } from 'xml2js'; import { parseIncomingMessage } from 'n8n-core'; import { jsonParse } from 'n8n-workflow'; -import config from '@/config'; import { UnprocessableRequestError } from '@/errors/response-errors/unprocessable.error'; +import { GlobalConfig } from '@n8n/config'; +import Container from 'typedi'; const xmlParser = new XmlParser({ async: true, @@ -16,7 +17,7 @@ const xmlParser = new XmlParser({ explicitArray: false, // Only put properties in array if length > 1 }); -const payloadSizeMax = config.getEnv('endpoints.payloadSizeMax'); +const payloadSizeMax = Container.get(GlobalConfig).endpoints.payloadSizeMax; export const rawBodyReader: RequestHandler = async (req, _res, next) => { parseIncomingMessage(req); diff --git a/packages/cli/src/services/frontend.service.ts b/packages/cli/src/services/frontend.service.ts index 4ada98a9cf7d7..7cd9843c1d97f 100644 --- a/packages/cli/src/services/frontend.service.ts +++ b/packages/cli/src/services/frontend.service.ts @@ -66,7 +66,7 @@ export class FrontendService { private initSettings() { const instanceBaseUrl = this.urlService.getInstanceBaseUrl(); - const restEndpoint = config.getEnv('endpoints.rest'); + const restEndpoint = this.globalConfig.endpoints.rest; const telemetrySettings: ITelemetrySettings = { enabled: config.getEnv('diagnostics.enabled'), @@ -88,11 +88,11 @@ export class FrontendService { isDocker: this.isDocker(), databaseType: this.globalConfig.database.type, previewMode: process.env.N8N_PREVIEW_MODE === 'true', - endpointForm: config.getEnv('endpoints.form'), - endpointFormTest: config.getEnv('endpoints.formTest'), - endpointFormWaiting: config.getEnv('endpoints.formWaiting'), - endpointWebhook: config.getEnv('endpoints.webhook'), - endpointWebhookTest: config.getEnv('endpoints.webhookTest'), + endpointForm: this.globalConfig.endpoints.form, + endpointFormTest: this.globalConfig.endpoints.formTest, + endpointFormWaiting: this.globalConfig.endpoints.formWaiting, + endpointWebhook: this.globalConfig.endpoints.webhook, + endpointWebhookTest: this.globalConfig.endpoints.webhookTest, saveDataErrorExecution: config.getEnv('executions.saveDataOnError'), saveDataSuccessExecution: config.getEnv('executions.saveDataOnSuccess'), saveManualExecutions: config.getEnv('executions.saveDataManualExecutions'), @@ -246,7 +246,7 @@ export class FrontendService { getSettings(pushRef?: string): IN8nUISettings { this.internalHooks.onFrontendSettingsAPI(pushRef); - const restEndpoint = config.getEnv('endpoints.rest'); + const restEndpoint = this.globalConfig.endpoints.rest; // Update all urls, in case `WEBHOOK_URL` was updated by `--tunnel` const instanceBaseUrl = this.urlService.getInstanceBaseUrl(); diff --git a/packages/cli/src/telemetry/telemetry-event-relay.service.ts b/packages/cli/src/telemetry/telemetry-event-relay.service.ts index bb1d4b8d560a2..97c30cae40920 100644 --- a/packages/cli/src/telemetry/telemetry-event-relay.service.ts +++ b/packages/cli/src/telemetry/telemetry-event-relay.service.ts @@ -444,9 +444,8 @@ export class TelemetryEventRelay { version_cli: N8N_VERSION, db_type: this.globalConfig.database.type, n8n_version_notifications_enabled: this.globalConfig.versionNotifications.enabled, - n8n_disable_production_main_process: config.getEnv( - 'endpoints.disableProductionWebhooksOnMainProcess', - ), + n8n_disable_production_main_process: + this.globalConfig.endpoints.disableProductionWebhooksOnMainProcess, system_info: { os: { type: os.type(), diff --git a/packages/cli/test/integration/prometheus-metrics.test.ts b/packages/cli/test/integration/prometheus-metrics.test.ts index 68c8756a86ec0..4c00a98f74a49 100644 --- a/packages/cli/test/integration/prometheus-metrics.test.ts +++ b/packages/cli/test/integration/prometheus-metrics.test.ts @@ -2,17 +2,48 @@ import { Container } from 'typedi'; import { parse as semverParse } from 'semver'; import request, { type Response } from 'supertest'; -import config from '@/config'; import { N8N_VERSION } from '@/constants'; import { PrometheusMetricsService } from '@/metrics/prometheus-metrics.service'; import { setupTestServer } from './shared/utils'; +import { mockInstance } from '@test/mocking'; +import { GlobalConfig } from '@n8n/config'; jest.unmock('@/eventbus/MessageEventBus/MessageEventBus'); const toLines = (response: Response) => response.text.trim().split('\n'); -config.set('endpoints.metrics.enable', true); -config.set('endpoints.metrics.prefix', 'n8n_test_'); +mockInstance(GlobalConfig, { + database: { + type: 'sqlite', + sqlite: { + database: 'database.sqlite', + enableWAL: false, + executeVacuumOnStartup: false, + poolSize: 0, + }, + logging: { + enabled: false, + maxQueryExecutionTime: 0, + options: 'error', + }, + tablePrefix: '', + }, + endpoints: { + metrics: { + prefix: 'n8n_test_', + includeDefaultMetrics: true, + includeApiEndpoints: true, + includeCacheMetrics: true, + includeMessageEventBusMetrics: true, + includeCredentialTypeLabel: false, + includeNodeTypeLabel: false, + includeWorkflowIdLabel: false, + includeApiPathLabel: true, + includeApiMethodLabel: true, + includeApiStatusCodeLabel: true, + }, + }, +}); const server = setupTestServer({ endpointGroups: ['metrics'] }); const agent = request.agent(server.app); diff --git a/packages/cli/test/integration/shared/constants.ts b/packages/cli/test/integration/shared/constants.ts index caa3667c23012..5fffacbd11788 100644 --- a/packages/cli/test/integration/shared/constants.ts +++ b/packages/cli/test/integration/shared/constants.ts @@ -1,8 +1,7 @@ -import config from '@/config'; import { GlobalConfig } from '@n8n/config'; import Container from 'typedi'; -export const REST_PATH_SEGMENT = config.getEnv('endpoints.rest'); +export const REST_PATH_SEGMENT = Container.get(GlobalConfig).endpoints.rest; export const PUBLIC_API_REST_PATH_SEGMENT = Container.get(GlobalConfig).publicApi.path; diff --git a/packages/cli/test/unit/decorators/controller.registry.test.ts b/packages/cli/test/unit/decorators/controller.registry.test.ts index 04b4884dcc331..05a97aab5378e 100644 --- a/packages/cli/test/unit/decorators/controller.registry.test.ts +++ b/packages/cli/test/unit/decorators/controller.registry.test.ts @@ -10,16 +10,18 @@ import { ControllerRegistry, Get, Licensed, RestController } from '@/decorators' import type { AuthService } from '@/auth/auth.service'; import type { License } from '@/License'; import type { SuperAgentTest } from '@test-integration/types'; +import type { GlobalConfig } from '@n8n/config'; describe('ControllerRegistry', () => { const license = mock(); const authService = mock(); + const globalConfig = mock({ endpoints: { rest: 'rest' } }); let agent: SuperAgentTest; beforeEach(() => { jest.resetAllMocks(); const app = express(); - new ControllerRegistry(license, authService).activate(app); + new ControllerRegistry(license, authService, globalConfig).activate(app); agent = testAgent(app); }); diff --git a/packages/cli/test/unit/webhooks.test.ts b/packages/cli/test/unit/webhooks.test.ts index 5fe8f937de660..c891588597ac5 100644 --- a/packages/cli/test/unit/webhooks.test.ts +++ b/packages/cli/test/unit/webhooks.test.ts @@ -2,7 +2,6 @@ import type SuperAgentTest from 'supertest/lib/agent'; import { agent as testAgent } from 'supertest'; import { mock } from 'jest-mock-extended'; -import config from '@/config'; import { AbstractServer } from '@/AbstractServer'; import { ActiveWebhooks } from '@/ActiveWebhooks'; import { ExternalHooks } from '@/ExternalHooks'; @@ -13,6 +12,8 @@ import { WaitingForms } from '@/WaitingForms'; import type { IResponseCallbackData } from '@/Interfaces'; import { mockInstance } from '../shared/mocking'; +import { GlobalConfig } from '@n8n/config'; +import Container from 'typedi'; let agent: SuperAgentTest; @@ -46,7 +47,7 @@ describe('WebhookServer', () => { for (const [key, manager] of tests) { describe(`for ${key}`, () => { it('should handle preflight requests', async () => { - const pathPrefix = config.getEnv(`endpoints.${key}`); + const pathPrefix = Container.get(GlobalConfig).endpoints[key]; manager.getWebhookMethods.mockResolvedValueOnce(['GET']); const response = await agent @@ -60,7 +61,7 @@ describe('WebhookServer', () => { }); it('should handle regular requests', async () => { - const pathPrefix = config.getEnv(`endpoints.${key}`); + const pathPrefix = Container.get(GlobalConfig).endpoints[key]; manager.getWebhookMethods.mockResolvedValueOnce(['GET']); manager.executeWebhook.mockResolvedValueOnce( mockResponse({ test: true }, { key: 'value ' }), From 88a170176a3447e7f847e9cf145aeb867b1c5fcf Mon Sep 17 00:00:00 2001 From: Michael Kret <88898367+michael-radency@users.noreply.github.com> Date: Thu, 1 Aug 2024 11:21:58 +0300 Subject: [PATCH 25/50] fix(core): Fix expressions in webhook nodes(Form, Webhook) to access previous node's data (#10247) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- packages/cli/src/WebhookHelpers.ts | 5 +++++ packages/core/src/NodeExecuteFunctions.ts | 24 +++++++++++++++++------ packages/workflow/src/Interfaces.ts | 1 + packages/workflow/src/Workflow.ts | 2 ++ 4 files changed, 26 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/WebhookHelpers.ts b/packages/cli/src/WebhookHelpers.ts index 9342a97cda69c..eafec28133326 100644 --- a/packages/cli/src/WebhookHelpers.ts +++ b/packages/cli/src/WebhookHelpers.ts @@ -256,6 +256,10 @@ export async function executeWebhook( // Prepare everything that is needed to run the workflow const additionalData = await WorkflowExecuteAdditionalData.getBase(); + if (executionId) { + additionalData.executionId = executionId; + } + // Get the responseMode const responseMode = workflow.expression.getSimpleParameterValue( workflowStartNode, @@ -359,6 +363,7 @@ export async function executeWebhook( additionalData, NodeExecuteFunctions, executionMode, + runExecutionData ?? null, ); Container.get(WorkflowStatisticsService).emit('nodeFetchedData', { workflowId: workflow.id, diff --git a/packages/core/src/NodeExecuteFunctions.ts b/packages/core/src/NodeExecuteFunctions.ts index f9e27cc15c0ac..cf677ba5bd036 100644 --- a/packages/core/src/NodeExecuteFunctions.ts +++ b/packages/core/src/NodeExecuteFunctions.ts @@ -4212,8 +4212,9 @@ export function getExecuteWebhookFunctions( mode: WorkflowExecuteMode, webhookData: IWebhookData, closeFunctions: CloseFunction[], + runExecutionData: IRunExecutionData | null, ): IWebhookFunctions { - return ((workflow: Workflow, node: INode) => { + return ((workflow: Workflow, node: INode, runExecutionData: IRunExecutionData | null) => { return { ...getCommonWorkflowFunctions(workflow, node, additionalData), getBodyData(): IDataObject { @@ -4274,10 +4275,21 @@ export function getExecuteWebhookFunctions( fallbackValue?: any, options?: IGetNodeParameterOptions, ): NodeParameterValueType | object => { - const runExecutionData: IRunExecutionData | null = null; const itemIndex = 0; const runIndex = 0; - const connectionInputData: INodeExecutionData[] = []; + + let connectionInputData: INodeExecutionData[] = []; + let executionData: IExecuteData | undefined; + + if (runExecutionData?.executionData !== undefined) { + executionData = runExecutionData.executionData.nodeExecutionStack[0]; + + if (executionData !== undefined) { + connectionInputData = executionData.data.main[0]!; + } + } + + const additionalKeys = getAdditionalKeys(additionalData, mode, runExecutionData); return getNodeParameter( workflow, @@ -4288,8 +4300,8 @@ export function getExecuteWebhookFunctions( parameterName, itemIndex, mode, - getAdditionalKeys(additionalData, mode, null), - undefined, + additionalKeys, + executionData, fallbackValue, options, ); @@ -4336,5 +4348,5 @@ export function getExecuteWebhookFunctions( }, nodeHelpers: getNodeHelperFunctions(additionalData, workflow.id), }; - })(workflow, node); + })(workflow, node, runExecutionData); } diff --git a/packages/workflow/src/Interfaces.ts b/packages/workflow/src/Interfaces.ts index ea31b06fef34b..39b4d06b47d98 100644 --- a/packages/workflow/src/Interfaces.ts +++ b/packages/workflow/src/Interfaces.ts @@ -466,6 +466,7 @@ export interface IGetExecuteWebhookFunctions { mode: WorkflowExecuteMode, webhookData: IWebhookData, closeFunctions: CloseFunction[], + runExecutionData: IRunExecutionData | null, ): IWebhookFunctions; } diff --git a/packages/workflow/src/Workflow.ts b/packages/workflow/src/Workflow.ts index 3c74480b3864f..ef0539db28a97 100644 --- a/packages/workflow/src/Workflow.ts +++ b/packages/workflow/src/Workflow.ts @@ -1237,6 +1237,7 @@ export class Workflow { additionalData: IWorkflowExecuteAdditionalData, nodeExecuteFunctions: INodeExecuteFunctions, mode: WorkflowExecuteMode, + runExecutionData: IRunExecutionData | null, ): Promise { const nodeType = this.nodeTypes.getByNameAndVersion(node.type, node.typeVersion); if (nodeType === undefined) { @@ -1258,6 +1259,7 @@ export class Workflow { mode, webhookData, closeFunctions, + runExecutionData, ); return nodeType instanceof Node ? await nodeType.webhook(context) From 785c82cfece04322ba4586fc463e17c18d27720b Mon Sep 17 00:00:00 2001 From: Giulio Andreini Date: Thu, 1 Aug 2024 10:57:05 +0200 Subject: [PATCH 26/50] fix(n8n Form Trigger Node): Improve copy and a few tweaks (no-changelog) (#10243) Co-authored-by: Michael Kret --- packages/cli/templates/form-trigger.handlebars | 13 ++++++++----- .../nodes-base/nodes/Form/common.descriptions.ts | 9 +++++---- 2 files changed, 13 insertions(+), 9 deletions(-) diff --git a/packages/cli/templates/form-trigger.handlebars b/packages/cli/templates/form-trigger.handlebars index a1abca87b81a4..67818629f57db 100644 --- a/packages/cli/templates/form-trigger.handlebars +++ b/packages/cli/templates/form-trigger.handlebars @@ -282,7 +282,10 @@ display: inline-block; } - @media only screen and (max-width: 400px) { + @media only screen and (max-width: 500px) { + body { + background-color: white; + } hr { display: block; } @@ -291,16 +294,16 @@ min-height: 100vh; padding: 24px; background-color: white; - border: 1px solid #dbdfe7; - border-radius: 8px; - box-shadow: 0px 4px 16px 0px #634dff0f; + border: 0px solid #dbdfe7; + border-radius: 0px; + box-shadow: 0px 0px 0px 0px white; } .card { padding: 0px; background-color: white; border: 0px solid #dbdfe7; border-radius: 0px; - box-shadow: 0px 0px 10px 0px #634dff0f; + box-shadow: 0px 0px 0px 0px white; margin-bottom: 0px; } } diff --git a/packages/nodes-base/nodes/Form/common.descriptions.ts b/packages/nodes-base/nodes/Form/common.descriptions.ts index 8ab9cc0927a53..c3505e9509f86 100644 --- a/packages/nodes-base/nodes/Form/common.descriptions.ts +++ b/packages/nodes-base/nodes/Form/common.descriptions.ts @@ -54,7 +54,7 @@ export const formFields: INodeProperties = { type: 'string', default: '', placeholder: 'e.g. What is your name?', - description: 'Label appears above the input field', + description: 'Label that appears above the input field', required: true, }, { @@ -102,6 +102,7 @@ export const formFields: INodeProperties = { { displayName: 'Placeholder', name: 'placeholder', + description: 'Sample text to display inside the field', type: 'string', default: '', displayOptions: { @@ -169,11 +170,11 @@ export const formFields: INodeProperties = { }, }, { - displayName: 'Accept File Types', + displayName: 'Accepted File Types', name: 'acceptFileTypes', type: 'string', default: '', - description: 'List of file types that can be uploaded, separated by commas', + description: 'Comma-separated list of allowed file extensions', hint: 'Leave empty to allow all file types', placeholder: 'e.g. .jpg, .png', displayOptions: { @@ -188,7 +189,7 @@ export const formFields: INodeProperties = { type: 'string', default: '', description: - 'Returns a string representation of this field formatted according to the specified format string. For a table of tokens and their interpretations, see here.', + 'How to format the date in the output data. For a table of tokens and their interpretations, see here.', placeholder: 'e.g. dd/mm/yyyy', hint: 'Leave empty to use the default format', displayOptions: { From efb71dd9adfe33ed21e16ff94ba322443d8eadbd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 1 Aug 2024 11:02:36 +0200 Subject: [PATCH 27/50] fix(core): Fix oauth2 callback and add integration tests (no-changelog) (#10272) --- .../oauth/abstractOAuth.controller.ts | 7 +- .../oauth/oAuth1Credential.controller.ts | 4 +- .../oauth/oAuth2Credential.controller.ts | 4 +- packages/cli/src/requests.ts | 4 +- .../controllers/oauth/oauth2.api.test.ts | 107 ++++++++++++++++++ packages/cli/test/integration/shared/types.ts | 1 + .../integration/shared/utils/testServer.ts | 4 + 7 files changed, 120 insertions(+), 11 deletions(-) create mode 100644 packages/cli/test/integration/controllers/oauth/oauth2.api.test.ts diff --git a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts index f9ffd591feb22..b9f0d64446ad5 100644 --- a/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts +++ b/packages/cli/src/controllers/oauth/abstractOAuth.controller.ts @@ -6,7 +6,6 @@ import type { ICredentialDataDecryptedObject, IWorkflowExecuteAdditionalData } f import { jsonParse, ApplicationError } from 'n8n-workflow'; import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; -import type { User } from '@db/entities/User'; import { CredentialsRepository } from '@db/repositories/credentials.repository'; import { SharedCredentialsRepository } from '@db/repositories/sharedCredentials.repository'; import type { ICredentialsDb } from '@/Interfaces'; @@ -71,8 +70,8 @@ export abstract class AbstractOAuthController { return credential; } - protected async getAdditionalData(user: User) { - return await WorkflowExecuteAdditionalData.getBase(user.id); + protected async getAdditionalData() { + return await WorkflowExecuteAdditionalData.getBase(); } protected async getDecryptedData( @@ -119,7 +118,7 @@ export abstract class AbstractOAuthController { return await this.credentialsRepository.findOneBy({ id: credentialId }); } - protected createCsrfState(credentialsId: string): [string, string] { + createCsrfState(credentialsId: string): [string, string] { const token = new Csrf(); const csrfSecret = token.secretSync(); const state: CsrfStateParam = { diff --git a/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts b/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts index d967d2f83435a..2a50b00bf9d57 100644 --- a/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts +++ b/packages/cli/src/controllers/oauth/oAuth1Credential.controller.ts @@ -33,7 +33,7 @@ export class OAuth1CredentialController extends AbstractOAuthController { @Get('/auth') async getAuthUri(req: OAuthRequest.OAuth1Credential.Auth): Promise { const credential = await this.getCredential(req); - const additionalData = await this.getAdditionalData(req.user); + const additionalData = await this.getAdditionalData(); const decryptedDataOriginal = await this.getDecryptedData(credential, additionalData); const oauthCredentials = this.applyDefaultsAndOverwrites( credential, @@ -127,7 +127,7 @@ export class OAuth1CredentialController extends AbstractOAuthController { return this.renderCallbackError(res, errorMessage); } - const additionalData = await this.getAdditionalData(req.user); + const additionalData = await this.getAdditionalData(); const decryptedDataOriginal = await this.getDecryptedData(credential, additionalData); const oauthCredentials = this.applyDefaultsAndOverwrites( credential, diff --git a/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts b/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts index 719043e9834b7..5b7929495fd42 100644 --- a/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts +++ b/packages/cli/src/controllers/oauth/oAuth2Credential.controller.ts @@ -20,7 +20,7 @@ export class OAuth2CredentialController extends AbstractOAuthController { @Get('/auth') async getAuthUri(req: OAuthRequest.OAuth2Credential.Auth): Promise { const credential = await this.getCredential(req); - const additionalData = await this.getAdditionalData(req.user); + const additionalData = await this.getAdditionalData(); const decryptedDataOriginal = await this.getDecryptedData(credential, additionalData); // At some point in the past we saved hidden scopes to credentials (but shouldn't) @@ -107,7 +107,7 @@ export class OAuth2CredentialController extends AbstractOAuthController { return this.renderCallbackError(res, errorMessage); } - const additionalData = await this.getAdditionalData(req.user); + const additionalData = await this.getAdditionalData(); const decryptedDataOriginal = await this.getDecryptedData(credential, additionalData); const oauthCredentials = this.applyDefaultsAndOverwrites( credential, diff --git a/packages/cli/src/requests.ts b/packages/cli/src/requests.ts index 02ef5acb6b015..1ea265f2394e7 100644 --- a/packages/cli/src/requests.ts +++ b/packages/cli/src/requests.ts @@ -76,9 +76,7 @@ export type AuthlessRequest< ResponseBody = {}, RequestBody = {}, RequestQuery = {}, -> = APIRequest & { - user: never; -}; +> = APIRequest; export type AuthenticatedRequest< RouteParams = {}, diff --git a/packages/cli/test/integration/controllers/oauth/oauth2.api.test.ts b/packages/cli/test/integration/controllers/oauth/oauth2.api.test.ts new file mode 100644 index 0000000000000..970727c2c8c64 --- /dev/null +++ b/packages/cli/test/integration/controllers/oauth/oauth2.api.test.ts @@ -0,0 +1,107 @@ +import { Container } from 'typedi'; +import { response as Response } from 'express'; +import nock from 'nock'; +import { parse as parseQs } from 'querystring'; + +import type { CredentialsEntity } from '@db/entities/CredentialsEntity'; +import type { User } from '@db/entities/User'; +import { CredentialsHelper } from '@/CredentialsHelper'; +import { OAuth2CredentialController } from '@/controllers/oauth/oAuth2Credential.controller'; + +import { createOwner } from '@test-integration/db/users'; +import { saveCredential } from '@test-integration/db/credentials'; +import * as testDb from '@test-integration/testDb'; +import { setupTestServer } from '@test-integration/utils'; +import type { SuperAgentTest } from '@test-integration/types'; + +describe('OAuth2 API', () => { + const testServer = setupTestServer({ endpointGroups: ['oauth2'] }); + + let owner: User; + let ownerAgent: SuperAgentTest; + let credential: CredentialsEntity; + const credentialData = { + clientId: 'client_id', + clientSecret: 'client_secret', + authUrl: 'https://test.domain/oauth2/auth', + accessTokenUrl: 'https://test.domain/oauth2/token', + authQueryParameters: 'access_type=offline', + }; + + CredentialsHelper.prototype.applyDefaultsAndOverwrites = (_, decryptedDataOriginal) => + decryptedDataOriginal; + + beforeAll(async () => { + owner = await createOwner(); + ownerAgent = testServer.authAgentFor(owner); + }); + + beforeEach(async () => { + await testDb.truncate(['SharedCredentials', 'Credentials']); + credential = await saveCredential( + { + name: 'Test', + type: 'testOAuth2Api', + data: credentialData, + }, + { + user: owner, + role: 'credential:owner', + }, + ); + }); + + it('should return a valid auth URL when the auth flow is initiated', async () => { + const controller = Container.get(OAuth2CredentialController); + const csrfSpy = jest.spyOn(controller, 'createCsrfState').mockClear(); + + const response = await ownerAgent + .get('/oauth2-credential/auth') + .query({ id: credential.id }) + .expect(200); + const authUrl = new URL(response.body.data); + expect(authUrl.hostname).toBe('test.domain'); + expect(authUrl.pathname).toBe('/oauth2/auth'); + + expect(csrfSpy).toHaveBeenCalled(); + const [_, state] = csrfSpy.mock.results[0].value; + expect(parseQs(authUrl.search.slice(1))).toEqual({ + access_type: 'offline', + client_id: 'client_id', + redirect_uri: 'http://localhost:5678/rest/oauth2-credential/callback', + response_type: 'code', + state, + scope: 'openid', + }); + }); + + it('should handle a valid callback without auth', async () => { + const controller = Container.get(OAuth2CredentialController); + const csrfSpy = jest.spyOn(controller, 'createCsrfState').mockClear(); + const renderSpy = (Response.render = jest.fn(function () { + this.end(); + })); + + await ownerAgent.get('/oauth2-credential/auth').query({ id: credential.id }).expect(200); + + const [_, state] = csrfSpy.mock.results[0].value; + + nock('https://test.domain').post('/oauth2/token').reply(200, { access_token: 'updated_token' }); + + await testServer.authlessAgent + .get('/oauth2-credential/callback') + .query({ code: 'auth_code', state }) + .expect(200); + + expect(renderSpy).toHaveBeenCalledWith('oauth-callback'); + + const updatedCredential = await Container.get(CredentialsHelper).getCredentials( + credential, + credential.type, + ); + expect(updatedCredential.getData()).toEqual({ + ...credentialData, + oauthTokenData: { access_token: 'updated_token' }, + }); + }); +}); diff --git a/packages/cli/test/integration/shared/types.ts b/packages/cli/test/integration/shared/types.ts index 0352386590a3d..cb794d0f95b92 100644 --- a/packages/cli/test/integration/shared/types.ts +++ b/packages/cli/test/integration/shared/types.ts @@ -13,6 +13,7 @@ type EndpointGroup = | 'me' | 'users' | 'auth' + | 'oauth2' | 'owner' | 'passwordReset' | 'credentials' diff --git a/packages/cli/test/integration/shared/utils/testServer.ts b/packages/cli/test/integration/shared/utils/testServer.ts index 3fc4cb5642b49..7776b7e669415 100644 --- a/packages/cli/test/integration/shared/utils/testServer.ts +++ b/packages/cli/test/integration/shared/utils/testServer.ts @@ -159,6 +159,10 @@ export const setupTestServer = ({ await import('@/controllers/auth.controller'); break; + case 'oauth2': + await import('@/controllers/oauth/oAuth2Credential.controller'); + break; + case 'mfa': await import('@/controllers/mfa.controller'); break; From d8688bd463a22ba417ed99553f21742eedc4db87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Thu, 1 Aug 2024 13:44:23 +0200 Subject: [PATCH 28/50] refactor(core): Decouple workflow created, saved, deleted events from internal hooks (no-changelog) (#10264) --- packages/cli/src/InternalHooks.ts | 82 +---------------- .../handlers/workflows/workflows.handler.ts | 9 +- .../audit-event-relay.service.test.ts | 9 +- .../src/eventbus/audit-event-relay.service.ts | 8 +- packages/cli/src/eventbus/event.types.ts | 10 ++- .../telemetry-event-relay.service.ts | 90 +++++++++++++++++++ .../cli/src/workflows/workflow.service.ts | 11 +-- .../cli/src/workflows/workflows.controller.ts | 9 +- 8 files changed, 125 insertions(+), 103 deletions(-) diff --git a/packages/cli/src/InternalHooks.ts b/packages/cli/src/InternalHooks.ts index 7912054085824..fda2c3f21dfc7 100644 --- a/packages/cli/src/InternalHooks.ts +++ b/packages/cli/src/InternalHooks.ts @@ -10,22 +10,15 @@ import type { } from 'n8n-workflow'; import { TelemetryHelpers } from 'n8n-workflow'; -import config from '@/config'; import { N8N_VERSION } from '@/constants'; import type { AuthProviderType } from '@db/entities/AuthIdentity'; import type { User } from '@db/entities/User'; import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions'; -import type { - ITelemetryUserDeletionData, - IWorkflowDb, - IExecutionTrackProperties, -} from '@/Interfaces'; +import type { ITelemetryUserDeletionData, IExecutionTrackProperties } from '@/Interfaces'; import { WorkflowStatisticsService } from '@/services/workflow-statistics.service'; import { NodeTypes } from '@/NodeTypes'; import { Telemetry } from '@/telemetry'; -import type { Project } from '@db/entities/Project'; -import { ProjectRelationRepository } from './databases/repositories/projectRelation.repository'; import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus'; /** @@ -40,7 +33,6 @@ export class InternalHooks { private readonly nodeTypes: NodeTypes, private readonly sharedWorkflowRepository: SharedWorkflowRepository, workflowStatisticsService: WorkflowStatisticsService, - private readonly projectRelationRepository: ProjectRelationRepository, // Can't use @ts-expect-error because only dev time tsconfig considers this as an error, but not build time // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore - needed until we decouple telemetry @@ -72,78 +64,6 @@ export class InternalHooks { this.telemetry.track('User responded to personalization questions', personalizationSurveyData); } - onWorkflowCreated( - user: User, - workflow: IWorkflowBase, - project: Project, - publicApi: boolean, - ): void { - const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); - - this.telemetry.track('User created workflow', { - user_id: user.id, - workflow_id: workflow.id, - node_graph_string: JSON.stringify(nodeGraph), - public_api: publicApi, - project_id: project.id, - project_type: project.type, - }); - } - - onWorkflowDeleted(user: User, workflowId: string, publicApi: boolean): void { - this.telemetry.track('User deleted workflow', { - user_id: user.id, - workflow_id: workflowId, - public_api: publicApi, - }); - } - - async onWorkflowSaved(user: User, workflow: IWorkflowDb, publicApi: boolean): Promise { - const isCloudDeployment = config.getEnv('deployment.type') === 'cloud'; - - const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, { - isCloudDeployment, - }); - - let userRole: 'owner' | 'sharee' | 'member' | undefined = undefined; - const role = await this.sharedWorkflowRepository.findSharingRole(user.id, workflow.id); - if (role) { - userRole = role === 'workflow:owner' ? 'owner' : 'sharee'; - } else { - const workflowOwner = await this.sharedWorkflowRepository.getWorkflowOwningProject( - workflow.id, - ); - - if (workflowOwner) { - const projectRole = await this.projectRelationRepository.findProjectRole({ - userId: user.id, - projectId: workflowOwner.id, - }); - - if (projectRole && projectRole !== 'project:personalOwner') { - userRole = 'member'; - } - } - } - - const notesCount = Object.keys(nodeGraph.notes).length; - const overlappingCount = Object.values(nodeGraph.notes).filter( - (note) => note.overlapping, - ).length; - - this.telemetry.track('User saved workflow', { - user_id: user.id, - workflow_id: workflow.id, - node_graph_string: JSON.stringify(nodeGraph), - notes_count_overlapping: overlappingCount, - notes_count_non_overlapping: notesCount - overlappingCount, - version_cli: N8N_VERSION, - num_tags: workflow.tags?.length ?? 0, - public_api: publicApi, - sharing_role: userRole, - }); - } - // eslint-disable-next-line complexity async onWorkflowPostExecute( _executionId: string, diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts index ea5eebdfdbe76..4063d0b611d35 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.handler.ts @@ -60,10 +60,12 @@ export = { ); await Container.get(ExternalHooks).run('workflow.afterCreate', [createdWorkflow]); - Container.get(InternalHooks).onWorkflowCreated(req.user, createdWorkflow, project, true); Container.get(EventService).emit('workflow-created', { workflow: createdWorkflow, user: req.user, + publicApi: true, + projectId: project.id, + projectType: project.type, }); return res.json(createdWorkflow); @@ -259,11 +261,10 @@ export = { } await Container.get(ExternalHooks).run('workflow.afterUpdate', [updateData]); - void Container.get(InternalHooks).onWorkflowSaved(req.user, updateData, true); Container.get(EventService).emit('workflow-saved', { user: req.user, - workflowId: updateData.id, - workflowName: updateData.name, + workflow: updateData, + publicApi: true, }); return res.json(updatedWorkflow); diff --git a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts index 7196b9203c407..52b86b58e1b6b 100644 --- a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts +++ b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts @@ -4,6 +4,7 @@ import type { MessageEventBus } from '../MessageEventBus/MessageEventBus'; import type { Event } from '../event.types'; import { EventService } from '../event.service'; import type { INode, IRun, IWorkflowBase } from 'n8n-workflow'; +import type { IWorkflowDb } from '@/Interfaces'; describe('AuditEventRelay', () => { const eventBus = mock(); @@ -29,6 +30,9 @@ describe('AuditEventRelay', () => { id: 'wf123', name: 'Test Workflow', }), + publicApi: false, + projectId: 'proj123', + projectType: 'personal', }; eventService.emit('workflow-created', event); @@ -57,6 +61,7 @@ describe('AuditEventRelay', () => { role: 'user', }, workflowId: 'wf789', + publicApi: false, }; eventService.emit('workflow-deleted', event); @@ -83,8 +88,8 @@ describe('AuditEventRelay', () => { lastName: 'Johnson', role: 'editor', }, - workflowId: 'wf101', - workflowName: 'Updated Workflow', + workflow: mock({ id: 'wf101', name: 'Updated Workflow' }), + publicApi: false, }; eventService.emit('workflow-saved', event); diff --git a/packages/cli/src/eventbus/audit-event-relay.service.ts b/packages/cli/src/eventbus/audit-event-relay.service.ts index 56f4dd95cd1e6..f8a95a3ebf359 100644 --- a/packages/cli/src/eventbus/audit-event-relay.service.ts +++ b/packages/cli/src/eventbus/audit-event-relay.service.ts @@ -86,13 +86,13 @@ export class AuditEventRelay { } @Redactable() - private workflowSaved({ user, workflowId, workflowName }: Event['workflow-saved']) { + private workflowSaved({ user, workflow }: Event['workflow-saved']) { void this.eventBus.sendAuditEvent({ eventName: 'n8n.audit.workflow.updated', payload: { ...user, - workflowId, - workflowName, + workflowId: workflow.id, + workflowName: workflow.name, }, }); } @@ -272,7 +272,7 @@ export class AuditEventRelay { } /** - * API key + * Public API */ @Redactable() diff --git a/packages/cli/src/eventbus/event.types.ts b/packages/cli/src/eventbus/event.types.ts index 513c659d3e2a3..b62d3bc141031 100644 --- a/packages/cli/src/eventbus/event.types.ts +++ b/packages/cli/src/eventbus/event.types.ts @@ -1,5 +1,5 @@ import type { AuthenticationMethod, IRun, IWorkflowBase } from 'n8n-workflow'; -import type { IWorkflowExecutionDataProcess } from '@/Interfaces'; +import type { IWorkflowDb, IWorkflowExecutionDataProcess } from '@/Interfaces'; import type { ProjectRole } from '@/databases/entities/ProjectRelation'; import type { GlobalRole } from '@/databases/entities/User'; @@ -20,17 +20,21 @@ export type Event = { 'workflow-created': { user: UserLike; workflow: IWorkflowBase; + publicApi: boolean; + projectId: string; + projectType: string; }; 'workflow-deleted': { user: UserLike; workflowId: string; + publicApi: boolean; }; 'workflow-saved': { user: UserLike; - workflowId: string; - workflowName: string; + workflow: IWorkflowDb; + publicApi: boolean; }; 'workflow-pre-execute': { diff --git a/packages/cli/src/telemetry/telemetry-event-relay.service.ts b/packages/cli/src/telemetry/telemetry-event-relay.service.ts index 97c30cae40920..9077abdf9ce23 100644 --- a/packages/cli/src/telemetry/telemetry-event-relay.service.ts +++ b/packages/cli/src/telemetry/telemetry-event-relay.service.ts @@ -8,6 +8,10 @@ import { License } from '@/License'; import { GlobalConfig } from '@n8n/config'; import { N8N_VERSION } from '@/constants'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import { TelemetryHelpers } from 'n8n-workflow'; +import { NodeTypes } from '@/NodeTypes'; +import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository'; +import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository'; @Service() export class TelemetryEventRelay { @@ -17,6 +21,9 @@ export class TelemetryEventRelay { private readonly license: License, private readonly globalConfig: GlobalConfig, private readonly workflowRepository: WorkflowRepository, + private readonly nodeTypes: NodeTypes, + private readonly sharedWorkflowRepository: SharedWorkflowRepository, + private readonly projectRelationRepository: ProjectRelationRepository, ) {} async init() { @@ -101,6 +108,16 @@ export class TelemetryEventRelay { this.eventService.on('login-failed-due-to-ldap-disabled', (event) => { this.loginFailedDueToLdapDisabled(event); }); + + this.eventService.on('workflow-created', (event) => { + this.workflowCreated(event); + }); + this.eventService.on('workflow-deleted', (event) => { + this.workflowDeleted(event); + }); + this.eventService.on('workflow-saved', async (event) => { + await this.workflowSaved(event); + }); } private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) { @@ -431,6 +448,79 @@ export class TelemetryEventRelay { this.telemetry.track('User login failed since ldap disabled', { user_ud: userId }); } + private workflowCreated({ + user, + workflow, + publicApi, + projectId, + projectType, + }: Event['workflow-created']) { + const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); + + this.telemetry.track('User created workflow', { + user_id: user.id, + workflow_id: workflow.id, + node_graph_string: JSON.stringify(nodeGraph), + public_api: publicApi, + project_id: projectId, + project_type: projectType, + }); + } + + private workflowDeleted({ user, workflowId, publicApi }: Event['workflow-deleted']) { + this.telemetry.track('User deleted workflow', { + user_id: user.id, + workflow_id: workflowId, + public_api: publicApi, + }); + } + + private async workflowSaved({ user, workflow, publicApi }: Event['workflow-saved']) { + const isCloudDeployment = config.getEnv('deployment.type') === 'cloud'; + + const { nodeGraph } = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes, { + isCloudDeployment, + }); + + let userRole: 'owner' | 'sharee' | 'member' | undefined = undefined; + const role = await this.sharedWorkflowRepository.findSharingRole(user.id, workflow.id); + if (role) { + userRole = role === 'workflow:owner' ? 'owner' : 'sharee'; + } else { + const workflowOwner = await this.sharedWorkflowRepository.getWorkflowOwningProject( + workflow.id, + ); + + if (workflowOwner) { + const projectRole = await this.projectRelationRepository.findProjectRole({ + userId: user.id, + projectId: workflowOwner.id, + }); + + if (projectRole && projectRole !== 'project:personalOwner') { + userRole = 'member'; + } + } + } + + const notesCount = Object.keys(nodeGraph.notes).length; + const overlappingCount = Object.values(nodeGraph.notes).filter( + (note) => note.overlapping, + ).length; + + this.telemetry.track('User saved workflow', { + user_id: user.id, + workflow_id: workflow.id, + node_graph_string: JSON.stringify(nodeGraph), + notes_count_overlapping: overlappingCount, + notes_count_non_overlapping: notesCount - overlappingCount, + version_cli: N8N_VERSION, + num_tags: workflow.tags?.length ?? 0, + public_api: publicApi, + sharing_role: userRole, + }); + } + private async serverStarted() { const cpus = os.cpus(); const binaryDataConfig = config.getEnv('binaryDataManager'); diff --git a/packages/cli/src/workflows/workflow.service.ts b/packages/cli/src/workflows/workflow.service.ts index 7c0cbfc242e1b..4f59f8238fe68 100644 --- a/packages/cli/src/workflows/workflow.service.ts +++ b/packages/cli/src/workflows/workflow.service.ts @@ -1,4 +1,4 @@ -import Container, { Service } from 'typedi'; +import { Service } from 'typedi'; import { NodeApiError } from 'n8n-workflow'; import pick from 'lodash/pick'; import omit from 'lodash/omit'; @@ -17,7 +17,6 @@ import { validateEntity } from '@/GenericHelpers'; import { ExternalHooks } from '@/ExternalHooks'; import { hasSharing, type ListQuery } from '@/requests'; import { TagService } from '@/services/tag.service'; -import { InternalHooks } from '@/InternalHooks'; import { OwnershipService } from '@/services/ownership.service'; import { WorkflowHistoryService } from './workflowHistory/workflowHistory.service.ee'; import { Logger } from '@/Logger'; @@ -219,11 +218,10 @@ export class WorkflowService { } await this.externalHooks.run('workflow.afterUpdate', [updatedWorkflow]); - void Container.get(InternalHooks).onWorkflowSaved(user, updatedWorkflow, false); this.eventService.emit('workflow-saved', { user, - workflowId: updatedWorkflow.id, - workflowName: updatedWorkflow.name, + workflow: updatedWorkflow, + publicApi: false, }); if (updatedWorkflow.active) { @@ -282,8 +280,7 @@ export class WorkflowService { await this.workflowRepository.delete(workflowId); await this.binaryDataService.deleteMany(idsForDeletion); - Container.get(InternalHooks).onWorkflowDeleted(user, workflowId, false); - this.eventService.emit('workflow-deleted', { user, workflowId }); + this.eventService.emit('workflow-deleted', { user, workflowId, publicApi: false }); await this.externalHooks.run('workflow.afterDelete', [workflowId]); return workflow; diff --git a/packages/cli/src/workflows/workflows.controller.ts b/packages/cli/src/workflows/workflows.controller.ts index 05863edb84c04..c774b21917c98 100644 --- a/packages/cli/src/workflows/workflows.controller.ts +++ b/packages/cli/src/workflows/workflows.controller.ts @@ -179,8 +179,13 @@ export class WorkflowsController { delete savedWorkflowWithMetaData.shared; await this.externalHooks.run('workflow.afterCreate', [savedWorkflow]); - this.internalHooks.onWorkflowCreated(req.user, newWorkflow, project!, false); - this.eventService.emit('workflow-created', { user: req.user, workflow: newWorkflow }); + this.eventService.emit('workflow-created', { + user: req.user, + workflow: newWorkflow, + publicApi: false, + projectId: project!.id, + projectType: project!.type, + }); const scopes = await this.workflowService.getWorkflowScopes(req.user, savedWorkflow.id); From 43ae159ea40c574f8e41bdfd221ab2bf3268eee7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Thu, 1 Aug 2024 17:19:31 +0200 Subject: [PATCH 29/50] fix(core): Upgrade tournament to address some XSS vulnerabilities (#10277) --- packages/workflow/package.json | 2 +- pnpm-lock.yaml | 27 ++++++--------------------- 2 files changed, 7 insertions(+), 22 deletions(-) diff --git a/packages/workflow/package.json b/packages/workflow/package.json index 179ae29311c11..77348f2a945e1 100644 --- a/packages/workflow/package.json +++ b/packages/workflow/package.json @@ -39,7 +39,7 @@ "@types/xml2js": "catalog:" }, "dependencies": { - "@n8n/tournament": "1.0.2", + "@n8n/tournament": "1.0.3", "@n8n_io/riot-tmpl": "4.0.0", "ast-types": "0.15.2", "axios": "catalog:", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8c8ff04428456..8287287e22db9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1710,8 +1710,8 @@ importers: packages/workflow: dependencies: '@n8n/tournament': - specifier: 1.0.2 - version: 1.0.2 + specifier: 1.0.3 + version: 1.0.3 '@n8n_io/riot-tmpl': specifier: 4.0.0 version: 4.0.0 @@ -4143,8 +4143,8 @@ packages: resolution: {integrity: sha512-rbnMnSdEwq2yuYMgzOQ4jTXm+oH7yjN/0ISfB/7O6pUcEPsZt9UW60BYfQ1WWHkKa/evI8vgER2zV5/RC1BupQ==} engines: {node: '>=18.10'} - '@n8n/tournament@1.0.2': - resolution: {integrity: sha512-fTpi7F8ra5flGSVfRzohPyG7czAAKCZPlLjdKdwbLJivLoI/Ekhgodov1jfVSCVFVbwQ06gRQRxLEDzl2jl8ig==} + '@n8n/tournament@1.0.3': + resolution: {integrity: sha512-GnmDD5wKAxKfxnSzhENHPn5n91/1c3/psnuT7D+jHHVQdMe8qaCcSq15rcGRfDfTf2v+BZBT0yeyK8Cfexr9yw==} engines: {node: '>=18.10', pnpm: '>=8.6'} '@n8n/typeorm@0.3.20-10': @@ -6231,9 +6231,6 @@ packages: resolution: {integrity: sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==} engines: {node: '>=0.8'} - assert@2.0.0: - resolution: {integrity: sha512-se5Cd+js9dXJnu6Ag2JFc00t+HmHOen+8Q+L7O9zI0PqQXr20uk2J0XQqMxZEeo5U50o8Nvmmx7dZrl+Ufr35A==} - assert@2.1.0: resolution: {integrity: sha512-eLHpSK/Y4nhMJ07gDaAzoX/XAKS8PSaojml3M0DM4JpV1LAi5JOJ/p6H/XWrl8L+DzVEvVCW1z3vWAaB9oTsQw==} @@ -7557,9 +7554,6 @@ packages: resolution: {integrity: sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==} engines: {node: '>= 0.4'} - es6-object-assign@1.1.0: - resolution: {integrity: sha512-MEl9uirslVwqQU369iHNWZXsI8yaZYGg/D65aOgZkeyFJwHYSxilf7rQzXKI7DdDuBPrBXbfk3sl9hJhmd5AUw==} - es6-promise@3.3.1: resolution: {integrity: sha512-SOp9Phqvqn7jtEUxPWdWfWoLmyt2VaJ6MpvP9Comy1MceMXqE6bxvaTu4iaxpYYPzhny28Lc+M87/c2cPK6lDg==} @@ -16649,7 +16643,7 @@ snapshots: '@types/retry': 0.12.5 retry: 0.13.1 - '@n8n/tournament@1.0.2': + '@n8n/tournament@1.0.3': dependencies: '@n8n_io/riot-tmpl': 4.0.1 ast-types: 0.16.1 @@ -19612,13 +19606,6 @@ snapshots: assert-plus@1.0.0: {} - assert@2.0.0: - dependencies: - es6-object-assign: 1.1.0 - is-nan: 1.3.2 - object-is: 1.1.5 - util: 0.12.5 - assert@2.1.0: dependencies: call-bind: 1.0.7 @@ -21208,8 +21195,6 @@ snapshots: is-date-object: 1.0.5 is-symbol: 1.0.4 - es6-object-assign@1.1.0: {} - es6-promise@3.3.1: {} esbuild-plugin-alias@0.2.1: {} @@ -25579,7 +25564,7 @@ snapshots: recast@0.22.0: dependencies: - assert: 2.0.0 + assert: 2.1.0 ast-types: 0.15.2 esprima: 4.0.1 source-map: 0.6.1 From 42ba8841c401126c77158a53dc8fcbb45dfce8fd Mon Sep 17 00:00:00 2001 From: Csaba Tuncsik Date: Thu, 1 Aug 2024 17:33:10 +0200 Subject: [PATCH 30/50] fix(editor): Enable moving resources only if team projects are available by the license (#10271) --- .../editor-ui/src/components/CredentialCard.test.ts | 8 ++++---- packages/editor-ui/src/components/CredentialCard.vue | 4 +--- .../src/components/Projects/ProjectNavigation.vue | 8 +++++--- packages/editor-ui/src/components/WorkflowCard.test.ts | 10 +++++----- packages/editor-ui/src/components/WorkflowCard.vue | 2 +- packages/editor-ui/src/stores/projects.store.ts | 8 ++++---- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/packages/editor-ui/src/components/CredentialCard.test.ts b/packages/editor-ui/src/components/CredentialCard.test.ts index 06760c75da7c5..9ca589923d59e 100644 --- a/packages/editor-ui/src/components/CredentialCard.test.ts +++ b/packages/editor-ui/src/components/CredentialCard.test.ts @@ -6,7 +6,7 @@ import { createComponentRenderer } from '@/__tests__/render'; import CredentialCard from '@/components/CredentialCard.vue'; import type { ICredentialsResponse } from '@/Interface'; import type { ProjectSharingData } from '@/types/projects.types'; -import { useSettingsStore } from '@/stores/settings.store'; +import { useProjectsStore } from '@/stores/projects.store'; const renderComponent = createComponentRenderer(CredentialCard); @@ -22,12 +22,12 @@ const createCredential = (overrides = {}): ICredentialsResponse => ({ }); describe('CredentialCard', () => { - let settingsStore: ReturnType; + let projectsStore: ReturnType; beforeEach(() => { const pinia = createTestingPinia(); setActivePinia(pinia); - settingsStore = useSettingsStore(); + projectsStore = useProjectsStore(); }); it('should render name and home project name', () => { @@ -63,7 +63,7 @@ describe('CredentialCard', () => { }); it('should show Move action only if there is resource permission and not on community plan', async () => { - vi.spyOn(settingsStore, 'isCommunityPlan', 'get').mockReturnValue(false); + vi.spyOn(projectsStore, 'isTeamProjectFeatureEnabled', 'get').mockReturnValue(true); const data = createCredential({ scopes: ['credential:move'], diff --git a/packages/editor-ui/src/components/CredentialCard.vue b/packages/editor-ui/src/components/CredentialCard.vue index f36926a19b09d..d9ac12ffd9a54 100644 --- a/packages/editor-ui/src/components/CredentialCard.vue +++ b/packages/editor-ui/src/components/CredentialCard.vue @@ -14,7 +14,6 @@ import { useProjectsStore } from '@/stores/projects.store'; import ProjectCardBadge from '@/components/Projects/ProjectCardBadge.vue'; import { useI18n } from '@/composables/useI18n'; import { ResourceType } from '@/utils/projects.utils'; -import { useSettingsStore } from '@/stores/settings.store'; const CREDENTIAL_LIST_ITEM_ACTIONS = { OPEN: 'open', @@ -46,7 +45,6 @@ const message = useMessage(); const uiStore = useUIStore(); const credentialsStore = useCredentialsStore(); const projectsStore = useProjectsStore(); -const settingsStore = useSettingsStore(); const resourceTypeLabel = computed(() => locale.baseText('generic.credential').toLowerCase()); const credentialType = computed(() => credentialsStore.getCredentialTypeByName(props.data.type)); @@ -66,7 +64,7 @@ const actions = computed(() => { }); } - if (credentialPermissions.value.move && !settingsStore.isCommunityPlan) { + if (credentialPermissions.value.move && projectsStore.isTeamProjectFeatureEnabled) { items.push({ label: locale.baseText('credentials.item.move'), value: CREDENTIAL_LIST_ITEM_ACTIONS.MOVE, diff --git a/packages/editor-ui/src/components/Projects/ProjectNavigation.vue b/packages/editor-ui/src/components/Projects/ProjectNavigation.vue index d1f5bac9ce60a..2b4909e0f2f95 100644 --- a/packages/editor-ui/src/components/Projects/ProjectNavigation.vue +++ b/packages/editor-ui/src/components/Projects/ProjectNavigation.vue @@ -116,7 +116,7 @@ onMounted(async () => {
@@ -137,7 +137,9 @@ onMounted(async () => { @@ -171,7 +173,7 @@ onMounted(async () => {
diff --git a/packages/editor-ui/src/components/WorkflowCard.test.ts b/packages/editor-ui/src/components/WorkflowCard.test.ts index 97be0e7fd4536..735b20ada5f0b 100644 --- a/packages/editor-ui/src/components/WorkflowCard.test.ts +++ b/packages/editor-ui/src/components/WorkflowCard.test.ts @@ -7,7 +7,7 @@ import { VIEWS } from '@/constants'; import WorkflowCard from '@/components/WorkflowCard.vue'; import type { IWorkflowDb } from '@/Interface'; import { useRouter } from 'vue-router'; -import { useSettingsStore } from '@/stores/settings.store'; +import { useProjectsStore } from '@/stores/projects.store'; vi.mock('vue-router', () => { const push = vi.fn(); @@ -40,13 +40,13 @@ describe('WorkflowCard', () => { let pinia: ReturnType; let windowOpenSpy: MockInstance; let router: ReturnType; - let settingsStore: ReturnType; + let projectsStore: ReturnType; beforeEach(async () => { pinia = createPinia(); setActivePinia(pinia); router = useRouter(); - settingsStore = useSettingsStore(); + projectsStore = useProjectsStore(); windowOpenSpy = vi.spyOn(window, 'open').mockImplementation(() => null); }); @@ -143,8 +143,8 @@ describe('WorkflowCard', () => { expect(badge).toHaveTextContent('John Doe'); }); - it('should show Move action only if there is resource permission and not on community plan', async () => { - vi.spyOn(settingsStore, 'isCommunityPlan', 'get').mockReturnValue(false); + it('should show Move action only if there is resource permission and team projects available', async () => { + vi.spyOn(projectsStore, 'isTeamProjectFeatureEnabled', 'get').mockReturnValue(true); const data = createWorkflow({ scopes: ['workflow:move'], diff --git a/packages/editor-ui/src/components/WorkflowCard.vue b/packages/editor-ui/src/components/WorkflowCard.vue index 21220e94924dc..6cc73db627817 100644 --- a/packages/editor-ui/src/components/WorkflowCard.vue +++ b/packages/editor-ui/src/components/WorkflowCard.vue @@ -95,7 +95,7 @@ const actions = computed(() => { }); } - if (workflowPermissions.value.move && !settingsStore.isCommunityPlan) { + if (workflowPermissions.value.move && projectsStore.isTeamProjectFeatureEnabled) { items.push({ label: locale.baseText('workflows.item.move'), value: WORKFLOW_LIST_ITEM_ACTIONS.MOVE, diff --git a/packages/editor-ui/src/stores/projects.store.ts b/packages/editor-ui/src/stores/projects.store.ts index f5fbaf6200762..6bc61d246c7c9 100644 --- a/packages/editor-ui/src/stores/projects.store.ts +++ b/packages/editor-ui/src/stores/projects.store.ts @@ -49,19 +49,19 @@ export const useProjectsStore = defineStore('projects', () => { ); const teamProjects = computed(() => projects.value.filter((p) => p.type === ProjectTypes.Team)); const teamProjectsLimit = computed(() => settingsStore.settings.enterprise.projects.team.limit); - const teamProjectsAvailable = computed( + const isTeamProjectFeatureEnabled = computed( () => settingsStore.settings.enterprise.projects.team.limit !== 0, ); const hasUnlimitedProjects = computed( () => settingsStore.settings.enterprise.projects.team.limit === -1, ); - const teamProjectLimitExceeded = computed( + const isTeamProjectLimitExceeded = computed( () => projectsCount.value.team >= teamProjectsLimit.value, ); const canCreateProjects = computed( () => hasUnlimitedProjects.value || - (teamProjectsAvailable.value && !teamProjectLimitExceeded.value), + (isTeamProjectFeatureEnabled.value && !isTeamProjectLimitExceeded.value), ); const hasPermissionToCreateProjects = computed(() => hasPermission(['rbac'], { rbac: { scope: 'project:create' } }), @@ -199,7 +199,7 @@ export const useProjectsStore = defineStore('projects', () => { hasUnlimitedProjects, canCreateProjects, hasPermissionToCreateProjects, - teamProjectsAvailable, + isTeamProjectFeatureEnabled, projectNavActiveId, setCurrentProject, getAllProjects, From 432ac1da59e173ce4c0f2abbc416743d9953ba70 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 12:01:10 +0200 Subject: [PATCH 31/50] fix(core): Surface enterprise trial error message (#10267) --- packages/cli/src/license/license.controller.ts | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/cli/src/license/license.controller.ts b/packages/cli/src/license/license.controller.ts index 1c0ca8c0d68ed..945f3650ea340 100644 --- a/packages/cli/src/license/license.controller.ts +++ b/packages/cli/src/license/license.controller.ts @@ -1,6 +1,8 @@ import { Get, Post, RestController, GlobalScope } from '@/decorators'; import { AuthenticatedRequest, LicenseRequest } from '@/requests'; import { LicenseService } from './license.service'; +import { BadRequestError } from '@/errors/response-errors/bad-request.error'; +import type { AxiosError } from 'axios'; @RestController('/license') export class LicenseController { @@ -14,7 +16,18 @@ export class LicenseController { @Post('/enterprise/request_trial') @GlobalScope('license:manage') async requestEnterpriseTrial(req: AuthenticatedRequest) { - await this.licenseService.requestEnterpriseTrial(req.user); + try { + await this.licenseService.requestEnterpriseTrial(req.user); + } catch (error: unknown) { + if (error instanceof Error) { + const errorMsg = + (error as AxiosError<{ message: string }>).response?.data?.message ?? error.message; + + throw new BadRequestError(errorMsg); + } else { + throw new BadRequestError('Failed to request trial'); + } + } } @Post('/activate') From dc8c94d036ca6ee5f3fbed839770ce5a902cca11 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 12:01:42 +0200 Subject: [PATCH 32/50] ci: Introduce lint rule `no-type-unsafe-event-emitter` (no-changelog) (#10254) --- packages/@n8n_io/eslint-config/local-rules.js | 30 +++++++++++++++++++ packages/cli/.eslintrc.js | 7 +++++ .../MessageEventBus/MessageEventBus.ts | 2 ++ 3 files changed, 39 insertions(+) diff --git a/packages/@n8n_io/eslint-config/local-rules.js b/packages/@n8n_io/eslint-config/local-rules.js index c9b533a032e1e..152415e14c8d0 100644 --- a/packages/@n8n_io/eslint-config/local-rules.js +++ b/packages/@n8n_io/eslint-config/local-rules.js @@ -448,6 +448,36 @@ module.exports = { }; }, }, + + 'no-type-unsafe-event-emitter': { + meta: { + type: 'problem', + docs: { + description: 'Disallow extending from `EventEmitter`, which is not type-safe.', + recommended: 'error', + }, + messages: { + noExtendsEventEmitter: 'Extend from the type-safe `TypedEmitter` class instead.', + }, + }, + create(context) { + return { + ClassDeclaration(node) { + if ( + node.superClass && + node.superClass.type === 'Identifier' && + node.superClass.name === 'EventEmitter' && + node.id.name !== 'TypedEmitter' + ) { + context.report({ + node: node.superClass, + messageId: 'noExtendsEventEmitter', + }); + } + }, + }; + }, + }, }; const isJsonParseCall = (node) => diff --git a/packages/cli/.eslintrc.js b/packages/cli/.eslintrc.js index ac66574887551..bb1041607ddcd 100644 --- a/packages/cli/.eslintrc.js +++ b/packages/cli/.eslintrc.js @@ -21,6 +21,7 @@ module.exports = { rules: { 'n8n-local-rules/no-dynamic-import-template': 'error', 'n8n-local-rules/misplaced-n8n-typeorm-import': 'error', + 'n8n-local-rules/no-type-unsafe-event-emitter': 'error', complexity: 'error', // TODO: Remove this @@ -44,6 +45,12 @@ module.exports = { 'n8n-local-rules/misplaced-n8n-typeorm-import': 'off', }, }, + { + files: ['./test/**/*.ts'], + rules: { + 'n8n-local-rules/no-type-unsafe-event-emitter': 'off', + }, + }, { files: ['./src/decorators/**/*.ts'], rules: { diff --git a/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts b/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts index 758eeb5ae590f..eeb868798bf6b 100644 --- a/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts +++ b/packages/cli/src/eventbus/MessageEventBus/MessageEventBus.ts @@ -52,6 +52,8 @@ export interface MessageEventBusInitializeOptions { } @Service() +// TODO: Convert to TypedEventEmitter +// eslint-disable-next-line n8n-local-rules/no-type-unsafe-event-emitter export class MessageEventBus extends EventEmitter { private isInitialized = false; From 489ce100634c3af678fb300e9a39d273042542e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 12:02:05 +0200 Subject: [PATCH 33/50] feat(core): Support create, read, update, delete projects in Public API (#10269) --- .../v1/handlers/projects/projects.handler.ts | 65 +++ .../spec/paths/projects.projectId.yml | 43 ++ .../handlers/projects/spec/paths/projects.yml | 40 ++ .../spec/schemas/parameters/projectId.yml | 6 + .../projects/spec/schemas/project.yml | 13 + .../projects/spec/schemas/projectList.yml | 11 + packages/cli/src/PublicApi/v1/openapi.yml | 6 + .../integration/publicApi/projects.test.ts | 401 ++++++++++++++++++ .../test/integration/shared/db/projects.ts | 4 + .../cli/test/integration/shared/db/users.ts | 6 +- 10 files changed, 594 insertions(+), 1 deletion(-) create mode 100644 packages/cli/src/PublicApi/v1/handlers/projects/projects.handler.ts create mode 100644 packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.projectId.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/parameters/projectId.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/project.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/projectList.yml create mode 100644 packages/cli/test/integration/publicApi/projects.test.ts diff --git a/packages/cli/src/PublicApi/v1/handlers/projects/projects.handler.ts b/packages/cli/src/PublicApi/v1/handlers/projects/projects.handler.ts new file mode 100644 index 0000000000000..e61e808daf301 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/projects/projects.handler.ts @@ -0,0 +1,65 @@ +import { globalScope, isLicensed, validCursor } from '../../shared/middlewares/global.middleware'; +import type { Response } from 'express'; +import type { ProjectRequest } from '@/requests'; +import type { PaginatedRequest } from '@/PublicApi/types'; +import Container from 'typedi'; +import { ProjectController } from '@/controllers/project.controller'; +import { ProjectRepository } from '@/databases/repositories/project.repository'; +import { encodeNextCursor } from '../../shared/services/pagination.service'; + +type Create = ProjectRequest.Create; +type Update = ProjectRequest.Update; +type Delete = ProjectRequest.Delete; +type GetAll = PaginatedRequest; + +export = { + createProject: [ + isLicensed('feat:projectRole:admin'), + globalScope('project:create'), + async (req: Create, res: Response) => { + const project = await Container.get(ProjectController).createProject(req); + + return res.status(201).json(project); + }, + ], + updateProject: [ + isLicensed('feat:projectRole:admin'), + globalScope('project:update'), + async (req: Update, res: Response) => { + await Container.get(ProjectController).updateProject(req); + + return res.status(204).send(); + }, + ], + deleteProject: [ + isLicensed('feat:projectRole:admin'), + globalScope('project:delete'), + async (req: Delete, res: Response) => { + await Container.get(ProjectController).deleteProject(req); + + return res.status(204).send(); + }, + ], + getProjects: [ + isLicensed('feat:projectRole:admin'), + globalScope('project:list'), + validCursor, + async (req: GetAll, res: Response) => { + const { offset = 0, limit = 100 } = req.query; + + const [projects, count] = await Container.get(ProjectRepository).findAndCount({ + skip: offset, + take: limit, + }); + + return res.json({ + data: projects, + nextCursor: encodeNextCursor({ + offset, + limit, + numberOfTotalRecords: count, + }), + }); + }, + ], +}; diff --git a/packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.projectId.yml b/packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.projectId.yml new file mode 100644 index 0000000000000..a5aab19b3d6ed --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.projectId.yml @@ -0,0 +1,43 @@ +delete: + x-eov-operation-id: deleteProject + x-eov-operation-handler: v1/handlers/projects/projects.handler + tags: + - Projects + summary: Delete a project + description: Delete a project from your instance. + parameters: + - $ref: '../schemas/parameters/projectId.yml' + responses: + '204': + description: Operation successful. + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '403': + $ref: '../../../../shared/spec/responses/forbidden.yml' + '404': + $ref: '../../../../shared/spec/responses/notFound.yml' +put: + x-eov-operation-id: updateProject + x-eov-operation-handler: v1/handlers/projects/projects.handler + tags: + - Project + summary: Update a project + description: Update a project. + requestBody: + description: Updated project object. + content: + application/json: + schema: + $ref: '../schemas/project.yml' + required: true + responses: + '204': + description: Operation successful. + '400': + $ref: '../../../../shared/spec/responses/badRequest.yml' + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '403': + $ref: '../../../../shared/spec/responses/forbidden.yml' + '404': + $ref: '../../../../shared/spec/responses/notFound.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.yml b/packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.yml new file mode 100644 index 0000000000000..1babd3dd6ea03 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/projects/spec/paths/projects.yml @@ -0,0 +1,40 @@ +post: + x-eov-operation-id: createProject + x-eov-operation-handler: v1/handlers/projects/projects.handler + tags: + - Projects + summary: Create a project + description: Create a project in your instance. + requestBody: + description: Payload for project to create. + content: + application/json: + schema: + $ref: '../schemas/project.yml' + required: true + responses: + '201': + description: Operation successful. + '400': + $ref: '../../../../shared/spec/responses/badRequest.yml' + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' +get: + x-eov-operation-id: getProjects + x-eov-operation-handler: v1/handlers/projects/projects.handler + tags: + - Projects + summary: Retrieve projects + description: Retrieve projects from your instance. + parameters: + - $ref: '../../../../shared/spec/parameters/limit.yml' + - $ref: '../../../../shared/spec/parameters/cursor.yml' + responses: + '200': + description: Operation successful. + content: + application/json: + schema: + $ref: '../schemas/projectList.yml' + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/parameters/projectId.yml b/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/parameters/projectId.yml new file mode 100644 index 0000000000000..32961f46016f9 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/parameters/projectId.yml @@ -0,0 +1,6 @@ +name: projectId +in: path +description: The ID of the project. +required: true +schema: + type: string diff --git a/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/project.yml b/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/project.yml new file mode 100644 index 0000000000000..7a4d2ec432bdd --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/project.yml @@ -0,0 +1,13 @@ +type: object +additionalProperties: false +required: + - name +properties: + id: + type: string + readOnly: true + name: + type: string + type: + type: string + readOnly: true diff --git a/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/projectList.yml b/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/projectList.yml new file mode 100644 index 0000000000000..7d88be72fb008 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/projects/spec/schemas/projectList.yml @@ -0,0 +1,11 @@ +type: object +properties: + data: + type: array + items: + $ref: './project.yml' + nextCursor: + type: string + description: Paginate through projects by setting the cursor parameter to a nextCursor attribute returned by a previous request. Default value fetches the first "page" of the collection. + nullable: true + example: MTIzZTQ1NjctZTg5Yi0xMmQzLWE0NTYtNDI2NjE0MTc0MDA diff --git a/packages/cli/src/PublicApi/v1/openapi.yml b/packages/cli/src/PublicApi/v1/openapi.yml index fd3b286a17fb5..e49bfb71c5668 100644 --- a/packages/cli/src/PublicApi/v1/openapi.yml +++ b/packages/cli/src/PublicApi/v1/openapi.yml @@ -32,6 +32,8 @@ tags: description: Operations about source control - name: Variables description: Operations about variables + - name: Projects + description: Operations about projects paths: /audit: @@ -72,6 +74,10 @@ paths: $ref: './handlers/variables/spec/paths/variables.yml' /variables/{id}: $ref: './handlers/variables/spec/paths/variables.id.yml' + /projects: + $ref: './handlers/projects/spec/paths/projects.yml' + /projects/{projectId}: + $ref: './handlers/projects/spec/paths/projects.projectId.yml' components: schemas: $ref: './shared/spec/schemas/_index.yml' diff --git a/packages/cli/test/integration/publicApi/projects.test.ts b/packages/cli/test/integration/publicApi/projects.test.ts new file mode 100644 index 0000000000000..0554fd6f4116f --- /dev/null +++ b/packages/cli/test/integration/publicApi/projects.test.ts @@ -0,0 +1,401 @@ +import { setupTestServer } from '@test-integration/utils'; +import { createMember, createOwner } from '@test-integration/db/users'; +import * as testDb from '../shared/testDb'; +import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; +import { createTeamProject, getProjectByNameOrFail } from '@test-integration/db/projects'; +import { mockInstance } from '@test/mocking'; +import { Telemetry } from '@/telemetry'; + +describe('Projects in Public API', () => { + const testServer = setupTestServer({ endpointGroups: ['publicApi'] }); + mockInstance(Telemetry); + + beforeAll(async () => { + await testDb.init(); + }); + + beforeEach(async () => { + await testDb.truncate(['Project', 'User']); + }); + + describe('GET /projects', () => { + it('if licensed, should return all projects with pagination', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const owner = await createOwner({ withApiKey: true }); + const projects = await Promise.all([ + createTeamProject(), + createTeamProject(), + createTeamProject(), + ]); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).get('/projects'); + + /** + * Assert + */ + expect(response.status).toBe(200); + expect(response.body).toHaveProperty('data'); + expect(response.body).toHaveProperty('nextCursor'); + expect(Array.isArray(response.body.data)).toBe(true); + expect(response.body.data.length).toBe(projects.length + 1); // +1 for the owner's personal project + + projects.forEach(({ id, name }) => { + expect(response.body.data).toContainEqual(expect.objectContaining({ id, name })); + }); + }); + + it('if not authenticated, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: false }); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).get('/projects'); + + /** + * Assert + */ + expect(response.status).toBe(401); + expect(response.body).toHaveProperty('message', "'X-N8N-API-KEY' header required"); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).get('/projects'); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:projectRole:admin').message, + ); + }); + + it('if missing scope, should reject', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const owner = await createMember({ withApiKey: true }); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).get('/projects'); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + }); + + describe('POST /projects', () => { + it('if licensed, should create a new project', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const owner = await createOwner({ withApiKey: true }); + const projectPayload = { name: 'some-project' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .post('/projects') + .send(projectPayload); + + /** + * Assert + */ + expect(response.status).toBe(201); + expect(response.body).toEqual({ + name: 'some-project', + type: 'team', + id: expect.any(String), + createdAt: expect.any(String), + updatedAt: expect.any(String), + role: 'project:admin', + scopes: expect.any(Array), + }); + await expect(getProjectByNameOrFail(projectPayload.name)).resolves.not.toThrow(); + }); + + it('if not authenticated, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: false }); + const projectPayload = { name: 'some-project' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .post('/projects') + .send(projectPayload); + + /** + * Assert + */ + expect(response.status).toBe(401); + expect(response.body).toHaveProperty('message', "'X-N8N-API-KEY' header required"); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + const projectPayload = { name: 'some-project' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .post('/projects') + .send(projectPayload); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:projectRole:admin').message, + ); + }); + + it('if missing scope, should reject', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const member = await createMember({ withApiKey: true }); + const projectPayload = { name: 'some-project' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(member) + .post('/projects') + .send(projectPayload); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + }); + + describe('DELETE /projects/:id', () => { + it('if licensed, should delete a project', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const owner = await createOwner({ withApiKey: true }); + const project = await createTeamProject(); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).delete(`/projects/${project.id}`); + + /** + * Assert + */ + expect(response.status).toBe(204); + await expect(getProjectByNameOrFail(project.id)).rejects.toThrow(); + }); + + it('if not authenticated, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: false }); + const project = await createTeamProject(); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).delete(`/projects/${project.id}`); + + /** + * Assert + */ + expect(response.status).toBe(401); + expect(response.body).toHaveProperty('message', "'X-N8N-API-KEY' header required"); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + const project = await createTeamProject(); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).delete(`/projects/${project.id}`); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:projectRole:admin').message, + ); + }); + + it('if missing scope, should reject', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const member = await createMember({ withApiKey: true }); + const project = await createTeamProject(); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(member).delete(`/projects/${project.id}`); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + }); + + describe('PUT /projects/:id', () => { + it('if licensed, should update a project', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const owner = await createOwner({ withApiKey: true }); + const project = await createTeamProject('old-name'); + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .put(`/projects/${project.id}`) + .send({ name: 'new-name' }); + + /** + * Assert + */ + expect(response.status).toBe(204); + await expect(getProjectByNameOrFail('new-name')).resolves.not.toThrow(); + }); + + it('if not authenticated, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: false }); + const project = await createTeamProject(); + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .put(`/projects/${project.id}`) + .send({ name: 'new-name' }); + + /** + * Assert + */ + expect(response.status).toBe(401); + expect(response.body).toHaveProperty('message', "'X-N8N-API-KEY' header required"); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + const project = await createTeamProject(); + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .put(`/projects/${project.id}`) + .send({ name: 'new-name' }); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:projectRole:admin').message, + ); + }); + + it('if missing scope, should reject', async () => { + /** + * Arrange + */ + testServer.license.setQuota('quota:maxTeamProjects', -1); + testServer.license.enable('feat:projectRole:admin'); + const member = await createMember({ withApiKey: true }); + const project = await createTeamProject(); + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(member) + .put(`/projects/${project.id}`) + .send({ name: 'new-name' }); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + }); +}); diff --git a/packages/cli/test/integration/shared/db/projects.ts b/packages/cli/test/integration/shared/db/projects.ts index 60548575b362b..3de7de5bb95f9 100644 --- a/packages/cli/test/integration/shared/db/projects.ts +++ b/packages/cli/test/integration/shared/db/projects.ts @@ -34,6 +34,10 @@ export const linkUserToProject = async (user: User, project: Project, role: Proj ); }; +export async function getProjectByNameOrFail(name: string) { + return await Container.get(ProjectRepository).findOneOrFail({ where: { name } }); +} + export const getPersonalProject = async (user: User): Promise => { return await Container.get(ProjectRepository).findOneOrFail({ where: { diff --git a/packages/cli/test/integration/shared/db/users.ts b/packages/cli/test/integration/shared/db/users.ts index f125f5ccded4f..98626bc549d9e 100644 --- a/packages/cli/test/integration/shared/db/users.ts +++ b/packages/cli/test/integration/shared/db/users.ts @@ -86,7 +86,11 @@ export async function createOwner({ withApiKey } = { withApiKey: false }) { return await createUser({ role: 'global:owner' }); } -export async function createMember() { +export async function createMember({ withApiKey } = { withApiKey: false }) { + if (withApiKey) { + return await addApiKey(await createUser({ role: 'global:member' })); + } + return await createUser({ role: 'global:member' }); } From 07d7b247f02a9d7185beca7817deb779a3d665dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 12:02:38 +0200 Subject: [PATCH 34/50] feat(core): Allow transferring credentials in Public API (#10259) --- packages/cli/src/PublicApi/types.ts | 6 +++ .../credentials/credentials.handler.ts | 16 ++++++ .../spec/paths/credentials.id.transfer.yml | 31 ++++++++++++ .../spec/schemas/parameters/credentialId.yml | 6 +++ packages/cli/src/PublicApi/v1/openapi.yml | 2 + .../integration/publicApi/credentials.test.ts | 50 ++++++++++++++++++- .../integration/publicApi/workflows.test.ts | 2 +- .../test/integration/shared/db/credentials.ts | 19 +++++-- 8 files changed, 127 insertions(+), 5 deletions(-) create mode 100644 packages/cli/src/PublicApi/v1/handlers/credentials/spec/paths/credentials.id.transfer.yml create mode 100644 packages/cli/src/PublicApi/v1/handlers/credentials/spec/schemas/parameters/credentialId.yml diff --git a/packages/cli/src/PublicApi/types.ts b/packages/cli/src/PublicApi/types.ts index f58b521e04a88..831dd03ebfb9c 100644 --- a/packages/cli/src/PublicApi/types.ts +++ b/packages/cli/src/PublicApi/types.ts @@ -142,6 +142,12 @@ export declare namespace CredentialRequest { >; type Delete = AuthenticatedRequest<{ id: string }, {}, {}, Record>; + + type Transfer = AuthenticatedRequest< + { workflowId: string }, + {}, + { destinationProjectId: string } + >; } export type OperationID = 'getUsers' | 'getUser'; diff --git a/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts b/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts index 4da7635831340..57b6bd66f6841 100644 --- a/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/credentials/credentials.handler.ts @@ -19,6 +19,8 @@ import { toJsonSchema, } from './credentials.service'; import { Container } from 'typedi'; +import { z } from 'zod'; +import { EnterpriseCredentialsService } from '@/credentials/credentials.service.ee'; export = { createCredential: [ @@ -44,6 +46,20 @@ export = { } }, ], + transferCredential: [ + projectScope('credential:move', 'credential'), + async (req: CredentialRequest.Transfer, res: express.Response) => { + const body = z.object({ destinationProjectId: z.string() }).parse(req.body); + + await Container.get(EnterpriseCredentialsService).transferOne( + req.user, + req.params.workflowId, + body.destinationProjectId, + ); + + res.status(204).send(); + }, + ], deleteCredential: [ projectScope('credential:delete', 'credential'), async ( diff --git a/packages/cli/src/PublicApi/v1/handlers/credentials/spec/paths/credentials.id.transfer.yml b/packages/cli/src/PublicApi/v1/handlers/credentials/spec/paths/credentials.id.transfer.yml new file mode 100644 index 0000000000000..a9e9c5cf7c229 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/credentials/spec/paths/credentials.id.transfer.yml @@ -0,0 +1,31 @@ +put: + x-eov-operation-id: transferCredential + x-eov-operation-handler: v1/handlers/credentials/credentials.handler + tags: + - Workflow + summary: Transfer a credential to another project. + description: Transfer a credential to another project. + parameters: + - $ref: '../schemas/parameters/credentialId.yml' + requestBody: + description: Destination project for the credential transfer. + content: + application/json: + schema: + type: object + properties: + destinationProjectId: + type: string + description: The ID of the project to transfer the credential to. + required: + - destinationProjectId + required: true + responses: + '200': + description: Operation successful. + '400': + $ref: '../../../../shared/spec/responses/badRequest.yml' + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '404': + $ref: '../../../../shared/spec/responses/notFound.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/credentials/spec/schemas/parameters/credentialId.yml b/packages/cli/src/PublicApi/v1/handlers/credentials/spec/schemas/parameters/credentialId.yml new file mode 100644 index 0000000000000..f16676ce0b96c --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/credentials/spec/schemas/parameters/credentialId.yml @@ -0,0 +1,6 @@ +name: id +in: path +description: The ID of the credential. +required: true +schema: + type: string diff --git a/packages/cli/src/PublicApi/v1/openapi.yml b/packages/cli/src/PublicApi/v1/openapi.yml index e49bfb71c5668..1d4e14079f541 100644 --- a/packages/cli/src/PublicApi/v1/openapi.yml +++ b/packages/cli/src/PublicApi/v1/openapi.yml @@ -62,6 +62,8 @@ paths: $ref: './handlers/workflows/spec/paths/workflows.id.deactivate.yml' /workflows/{id}/transfer: $ref: './handlers/workflows/spec/paths/workflows.id.transfer.yml' + /credentials/{id}/transfer: + $ref: './handlers/credentials/spec/paths/credentials.id.transfer.yml' /workflows/{id}/tags: $ref: './handlers/workflows/spec/paths/workflows.id.tags.yml' /users: diff --git a/packages/cli/test/integration/publicApi/credentials.test.ts b/packages/cli/test/integration/publicApi/credentials.test.ts index dfa6c44dd4037..b5ed2bfb31d82 100644 --- a/packages/cli/test/integration/publicApi/credentials.test.ts +++ b/packages/cli/test/integration/publicApi/credentials.test.ts @@ -9,9 +9,10 @@ import { randomApiKey, randomName } from '../shared/random'; import * as utils from '../shared/utils/'; import type { CredentialPayload, SaveCredentialFunction } from '../shared/types'; import * as testDb from '../shared/testDb'; -import { affixRoleToSaveCredential } from '../shared/db/credentials'; +import { affixRoleToSaveCredential, createCredentials } from '../shared/db/credentials'; import { addApiKey, createUser, createUserShell } from '../shared/db/users'; import type { SuperAgentTest } from '../shared/types'; +import { createTeamProject } from '@test-integration/db/projects'; let owner: User; let member: User; @@ -256,6 +257,53 @@ describe('GET /credentials/schema/:credentialType', () => { }); }); +describe('PUT /credentials/:id/transfer', () => { + test('should transfer credential to project', async () => { + /** + * Arrange + */ + const [firstProject, secondProject] = await Promise.all([ + createTeamProject('first-project', owner), + createTeamProject('second-project', owner), + ]); + + const credentials = await createCredentials( + { name: 'Test', type: 'test', data: '' }, + firstProject, + ); + + /** + * Act + */ + const response = await authOwnerAgent.put(`/credentials/${credentials.id}/transfer`).send({ + destinationProjectId: secondProject.id, + }); + + /** + * Assert + */ + expect(response.statusCode).toBe(204); + }); + + test('if no destination project, should reject', async () => { + /** + * Arrange + */ + const project = await createTeamProject('first-project', member); + const credentials = await createCredentials({ name: 'Test', type: 'test', data: '' }, project); + + /** + * Act + */ + const response = await authOwnerAgent.put(`/credentials/${credentials.id}/transfer`).send({}); + + /** + * Assert + */ + expect(response.statusCode).toBe(400); + }); +}); + const credentialPayload = (): CredentialPayload => ({ name: randomName(), type: 'githubApi', diff --git a/packages/cli/test/integration/publicApi/workflows.test.ts b/packages/cli/test/integration/publicApi/workflows.test.ts index 855e69e3df12a..1e292af64d92f 100644 --- a/packages/cli/test/integration/publicApi/workflows.test.ts +++ b/packages/cli/test/integration/publicApi/workflows.test.ts @@ -1493,7 +1493,7 @@ describe('PUT /workflows/:id/transfer', () => { * Arrange */ const firstProject = await createTeamProject('first-project', member); - const secondProject = await createTeamProject('secon-project', member); + const secondProject = await createTeamProject('second-project', member); const workflow = await createWorkflow({}, firstProject); /** diff --git a/packages/cli/test/integration/shared/db/credentials.ts b/packages/cli/test/integration/shared/db/credentials.ts index 046d27db261a0..588fee6b51196 100644 --- a/packages/cli/test/integration/shared/db/credentials.ts +++ b/packages/cli/test/integration/shared/db/credentials.ts @@ -38,11 +38,24 @@ export async function createManyCredentials( ); } -export async function createCredentials(attributes: Partial = emptyAttributes) { +export async function createCredentials( + attributes: Partial = emptyAttributes, + project?: Project, +) { const credentialsRepository = Container.get(CredentialsRepository); - const entity = credentialsRepository.create(attributes); + const credentials = await credentialsRepository.save(credentialsRepository.create(attributes)); + + if (project) { + await Container.get(SharedCredentialsRepository).save( + Container.get(SharedCredentialsRepository).create({ + project, + credentials, + role: 'credential:owner', + }), + ); + } - return await credentialsRepository.save(entity); + return credentials; } /** From a5339166285b896a8ac2723723ca9544e0d2e6cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 12:05:06 +0200 Subject: [PATCH 35/50] refactor(core): Decouple post workflow execute event from internal hooks (no-changelog) (#10280) --- packages/cli/src/InternalHooks.ts | 158 +----------------- .../cli/src/WorkflowExecuteAdditionalData.ts | 15 +- packages/cli/src/WorkflowRunner.ts | 12 +- .../audit-event-relay.service.test.ts | 34 ++-- .../src/eventbus/audit-event-relay.service.ts | 16 +- packages/cli/src/eventbus/event.types.ts | 6 +- .../executions/execution-recovery.service.ts | 16 +- .../telemetry-event-relay.service.ts | 141 ++++++++++++++++ 8 files changed, 179 insertions(+), 219 deletions(-) diff --git a/packages/cli/src/InternalHooks.ts b/packages/cli/src/InternalHooks.ts index fda2c3f21dfc7..46637106b7a47 100644 --- a/packages/cli/src/InternalHooks.ts +++ b/packages/cli/src/InternalHooks.ts @@ -1,23 +1,10 @@ import { Service } from 'typedi'; import { snakeCase } from 'change-case'; -import { get as pslGet } from 'psl'; -import type { - ExecutionStatus, - INodesGraphResult, - IRun, - ITelemetryTrackProperties, - IWorkflowBase, -} from 'n8n-workflow'; -import { TelemetryHelpers } from 'n8n-workflow'; - -import { N8N_VERSION } from '@/constants'; +import type { ITelemetryTrackProperties } from 'n8n-workflow'; import type { AuthProviderType } from '@db/entities/AuthIdentity'; import type { User } from '@db/entities/User'; -import { SharedWorkflowRepository } from '@db/repositories/sharedWorkflow.repository'; -import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions'; -import type { ITelemetryUserDeletionData, IExecutionTrackProperties } from '@/Interfaces'; +import type { ITelemetryUserDeletionData } from '@/Interfaces'; import { WorkflowStatisticsService } from '@/services/workflow-statistics.service'; -import { NodeTypes } from '@/NodeTypes'; import { Telemetry } from '@/telemetry'; import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus'; @@ -30,8 +17,6 @@ import { MessageEventBus } from './eventbus/MessageEventBus/MessageEventBus'; export class InternalHooks { constructor( private readonly telemetry: Telemetry, - private readonly nodeTypes: NodeTypes, - private readonly sharedWorkflowRepository: SharedWorkflowRepository, workflowStatisticsService: WorkflowStatisticsService, // Can't use @ts-expect-error because only dev time tsconfig considers this as an error, but not build time // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -64,145 +49,6 @@ export class InternalHooks { this.telemetry.track('User responded to personalization questions', personalizationSurveyData); } - // eslint-disable-next-line complexity - async onWorkflowPostExecute( - _executionId: string, - workflow: IWorkflowBase, - runData?: IRun, - userId?: string, - ) { - if (!workflow.id) { - return; - } - - if (runData?.status === 'waiting') { - // No need to send telemetry or logs when the workflow hasn't finished yet. - return; - } - - const telemetryProperties: IExecutionTrackProperties = { - workflow_id: workflow.id, - is_manual: false, - version_cli: N8N_VERSION, - success: false, - }; - - if (userId) { - telemetryProperties.user_id = userId; - } - - if (runData?.data.resultData.error?.message?.includes('canceled')) { - runData.status = 'canceled'; - } - - telemetryProperties.success = !!runData?.finished; - - // const executionStatus: ExecutionStatus = runData?.status ?? 'unknown'; - const executionStatus: ExecutionStatus = runData - ? determineFinalExecutionStatus(runData) - : 'unknown'; - - if (runData !== undefined) { - telemetryProperties.execution_mode = runData.mode; - telemetryProperties.is_manual = runData.mode === 'manual'; - - let nodeGraphResult: INodesGraphResult | null = null; - - if (!telemetryProperties.success && runData?.data.resultData.error) { - telemetryProperties.error_message = runData?.data.resultData.error.message; - let errorNodeName = - 'node' in runData?.data.resultData.error - ? runData?.data.resultData.error.node?.name - : undefined; - telemetryProperties.error_node_type = - 'node' in runData?.data.resultData.error - ? runData?.data.resultData.error.node?.type - : undefined; - - if (runData.data.resultData.lastNodeExecuted) { - const lastNode = TelemetryHelpers.getNodeTypeForName( - workflow, - runData.data.resultData.lastNodeExecuted, - ); - - if (lastNode !== undefined) { - telemetryProperties.error_node_type = lastNode.type; - errorNodeName = lastNode.name; - } - } - - if (telemetryProperties.is_manual) { - nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); - telemetryProperties.node_graph = nodeGraphResult.nodeGraph; - telemetryProperties.node_graph_string = JSON.stringify(nodeGraphResult.nodeGraph); - - if (errorNodeName) { - telemetryProperties.error_node_id = nodeGraphResult.nameIndices[errorNodeName]; - } - } - } - - if (telemetryProperties.is_manual) { - if (!nodeGraphResult) { - nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); - } - - let userRole: 'owner' | 'sharee' | undefined = undefined; - if (userId) { - const role = await this.sharedWorkflowRepository.findSharingRole(userId, workflow.id); - if (role) { - userRole = role === 'workflow:owner' ? 'owner' : 'sharee'; - } - } - - const manualExecEventProperties: ITelemetryTrackProperties = { - user_id: userId, - workflow_id: workflow.id, - status: executionStatus, - executionStatus: runData?.status ?? 'unknown', - error_message: telemetryProperties.error_message as string, - error_node_type: telemetryProperties.error_node_type, - node_graph_string: telemetryProperties.node_graph_string as string, - error_node_id: telemetryProperties.error_node_id as string, - webhook_domain: null, - sharing_role: userRole, - }; - - if (!manualExecEventProperties.node_graph_string) { - nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); - manualExecEventProperties.node_graph_string = JSON.stringify(nodeGraphResult.nodeGraph); - } - - if (runData.data.startData?.destinationNode) { - const telemetryPayload = { - ...manualExecEventProperties, - node_type: TelemetryHelpers.getNodeTypeForName( - workflow, - runData.data.startData?.destinationNode, - )?.type, - node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode], - }; - - this.telemetry.track('Manual node exec finished', telemetryPayload); - } else { - nodeGraphResult.webhookNodeNames.forEach((name: string) => { - const execJson = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0] - ?.json as { headers?: { origin?: string } }; - if (execJson?.headers?.origin && execJson.headers.origin !== '') { - manualExecEventProperties.webhook_domain = pslGet( - execJson.headers.origin.replace(/^https?:\/\//, ''), - ); - } - }); - - this.telemetry.track('Manual workflow exec finished', manualExecEventProperties); - } - } - } - - this.telemetry.trackWorkflowExecution(telemetryProperties); - } - onWorkflowSharingUpdate(workflowId: string, userId: string, userList: string[]) { const properties: ITelemetryTrackProperties = { workflow_id: workflowId, diff --git a/packages/cli/src/WorkflowExecuteAdditionalData.ts b/packages/cli/src/WorkflowExecuteAdditionalData.ts index f4acd61b6aa55..9538ec91988fd 100644 --- a/packages/cli/src/WorkflowExecuteAdditionalData.ts +++ b/packages/cli/src/WorkflowExecuteAdditionalData.ts @@ -52,7 +52,6 @@ import { Push } from '@/push'; import * as WorkflowHelpers from '@/WorkflowHelpers'; import { findSubworkflowStart, isWorkflowIdValid } from '@/utils'; import { PermissionChecker } from './UserManagement/PermissionChecker'; -import { InternalHooks } from '@/InternalHooks'; import { ExecutionRepository } from '@db/repositories/execution.repository'; import { WorkflowStatisticsService } from '@/services/workflow-statistics.service'; import { SecretsHelper } from './SecretsHelpers'; @@ -548,7 +547,6 @@ function hookFunctionsSave(): IWorkflowExecuteHooks { */ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { const logger = Container.get(Logger); - const internalHooks = Container.get(InternalHooks); const workflowStatisticsService = Container.get(WorkflowStatisticsService); const eventService = Container.get(EventService); return { @@ -644,13 +642,9 @@ function hookFunctionsSaveWorker(): IWorkflowExecuteHooks { async function (this: WorkflowHooks, runData: IRun): Promise { const { executionId, workflowData: workflow } = this; - void internalHooks.onWorkflowPostExecute(executionId, workflow, runData); eventService.emit('workflow-post-execute', { - workflowId: workflow.id, - workflowName: workflow.name, + workflow, executionId, - success: runData.status === 'success', - isManual: runData.mode === 'manual', runData, }); }, @@ -787,7 +781,6 @@ async function executeWorkflow( parentCallbackManager?: CallbackManager; }, ): Promise | IWorkflowExecuteProcess> { - const internalHooks = Container.get(InternalHooks); const externalHooks = Container.get(ExternalHooks); await externalHooks.init(); @@ -933,13 +926,9 @@ async function executeWorkflow( await externalHooks.run('workflow.postExecute', [data, workflowData, executionId]); - void internalHooks.onWorkflowPostExecute(executionId, workflowData, data, additionalData.userId); eventService.emit('workflow-post-execute', { - workflowId: workflowData.id, - workflowName: workflowData.name, + workflow: workflowData, executionId, - success: data.status === 'success', - isManual: data.mode === 'manual', userId: additionalData.userId, runData: data, }); diff --git a/packages/cli/src/WorkflowRunner.ts b/packages/cli/src/WorkflowRunner.ts index 3318dd283cd60..f51a44cc4d2a3 100644 --- a/packages/cli/src/WorkflowRunner.ts +++ b/packages/cli/src/WorkflowRunner.ts @@ -34,7 +34,6 @@ import * as WorkflowHelpers from '@/WorkflowHelpers'; import * as WorkflowExecuteAdditionalData from '@/WorkflowExecuteAdditionalData'; import { generateFailedExecutionFromError } from '@/WorkflowHelpers'; import { PermissionChecker } from '@/UserManagement/PermissionChecker'; -import { InternalHooks } from '@/InternalHooks'; import { Logger } from '@/Logger'; import { WorkflowStaticDataService } from '@/workflows/workflowStaticData.service'; import { EventService } from './eventbus/event.service'; @@ -160,18 +159,9 @@ export class WorkflowRunner { const postExecutePromise = this.activeExecutions.getPostExecutePromise(executionId); postExecutePromise .then(async (executionData) => { - void Container.get(InternalHooks).onWorkflowPostExecute( - executionId, - data.workflowData, - executionData, - data.userId, - ); this.eventService.emit('workflow-post-execute', { - workflowId: data.workflowData.id, - workflowName: data.workflowData.name, + workflow: data.workflowData, executionId, - success: executionData?.status === 'success', - isManual: data.executionMode === 'manual', userId: data.userId, runData: executionData, }); diff --git a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts index 52b86b58e1b6b..44277f1de4d0d 100644 --- a/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts +++ b/packages/cli/src/eventbus/__tests__/audit-event-relay.service.test.ts @@ -141,27 +141,31 @@ describe('AuditEventRelay', () => { it('should log on `workflow-post-execute` for successful execution', () => { const payload = mock({ executionId: 'some-id', - success: true, userId: 'some-id', - workflowId: 'some-id', - isManual: true, - workflowName: 'some-name', - metadata: {}, - runData: mock({ data: { resultData: {} } }), + workflow: mock({ id: 'some-id', name: 'some-name' }), + runData: mock({ status: 'success', mode: 'manual', data: { resultData: {} } }), }); eventService.emit('workflow-post-execute', payload); - const { runData: _, ...rest } = payload; + const { runData: _, workflow: __, ...rest } = payload; expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ eventName: 'n8n.workflow.success', - payload: rest, + payload: { + ...rest, + success: true, + isManual: true, + workflowName: 'some-name', + workflowId: 'some-id', + }, }); }); - it('should handle `workflow-post-execute` event for unsuccessful execution', () => { + it('should log on `workflow-post-execute` event for unsuccessful execution', () => { const runData = mock({ + status: 'error', + mode: 'manual', data: { resultData: { lastNodeExecuted: 'some-node', @@ -177,23 +181,23 @@ describe('AuditEventRelay', () => { const event = { executionId: 'some-id', - success: false, userId: 'some-id', - workflowId: 'some-id', - isManual: true, - workflowName: 'some-name', - metadata: {}, + workflow: mock({ id: 'some-id', name: 'some-name' }), runData, }; eventService.emit('workflow-post-execute', event); - const { runData: _, ...rest } = event; + const { runData: _, workflow: __, ...rest } = event; expect(eventBus.sendWorkflowEvent).toHaveBeenCalledWith({ eventName: 'n8n.workflow.failed', payload: { ...rest, + success: false, + isManual: true, + workflowName: 'some-name', + workflowId: 'some-id', lastNodeExecuted: 'some-node', errorNodeType: 'some-type', errorMessage: 'some-message', diff --git a/packages/cli/src/eventbus/audit-event-relay.service.ts b/packages/cli/src/eventbus/audit-event-relay.service.ts index f8a95a3ebf359..dcefeac0bd317 100644 --- a/packages/cli/src/eventbus/audit-event-relay.service.ts +++ b/packages/cli/src/eventbus/audit-event-relay.service.ts @@ -122,12 +122,20 @@ export class AuditEventRelay { } private workflowPostExecute(event: Event['workflow-post-execute']) { - const { runData, ...rest } = event; + const { runData, workflow, ...rest } = event; - if (event.success) { + const payload = { + ...rest, + success: runData?.status === 'success', + isManual: runData?.mode === 'manual', + workflowId: workflow.id, + workflowName: workflow.name, + }; + + if (payload.success) { void this.eventBus.sendWorkflowEvent({ eventName: 'n8n.workflow.success', - payload: rest, + payload, }); return; @@ -136,7 +144,7 @@ export class AuditEventRelay { void this.eventBus.sendWorkflowEvent({ eventName: 'n8n.workflow.failed', payload: { - ...rest, + ...payload, lastNodeExecuted: runData?.data.resultData.lastNodeExecuted, errorNodeType: runData?.data.resultData.error && 'node' in runData?.data.resultData.error diff --git a/packages/cli/src/eventbus/event.types.ts b/packages/cli/src/eventbus/event.types.ts index b62d3bc141031..bcca98b919637 100644 --- a/packages/cli/src/eventbus/event.types.ts +++ b/packages/cli/src/eventbus/event.types.ts @@ -44,12 +44,8 @@ export type Event = { 'workflow-post-execute': { executionId: string; - success: boolean; userId?: string; - workflowId: string; - isManual: boolean; - workflowName: string; - metadata?: Record; + workflow: IWorkflowBase; runData?: IRun; }; diff --git a/packages/cli/src/executions/execution-recovery.service.ts b/packages/cli/src/executions/execution-recovery.service.ts index b72fc490dddbb..615c65ea6ef5f 100644 --- a/packages/cli/src/executions/execution-recovery.service.ts +++ b/packages/cli/src/executions/execution-recovery.service.ts @@ -3,7 +3,6 @@ import { Push } from '@/push'; import { jsonStringify, sleep } from 'n8n-workflow'; import { ExecutionRepository } from '@db/repositories/execution.repository'; import { getWorkflowHooksMain } from '@/WorkflowExecuteAdditionalData'; // @TODO: Dependency cycle -import { InternalHooks } from '@/InternalHooks'; // @TODO: Dependency cycle if injected import type { DateTime } from 'luxon'; import type { IRun, ITaskData } from 'n8n-workflow'; import type { EventMessageTypes } from '../eventbus/EventMessageClasses'; @@ -280,22 +279,9 @@ export class ExecutionRecoveryService { private async runHooks(execution: IExecutionResponse) { execution.data ??= { resultData: { runData: {} } }; - await Container.get(InternalHooks).onWorkflowPostExecute(execution.id, execution.workflowData, { - data: execution.data, - finished: false, - mode: execution.mode, - waitTill: execution.waitTill, - startedAt: execution.startedAt, - stoppedAt: execution.stoppedAt, - status: execution.status, - }); - this.eventService.emit('workflow-post-execute', { - workflowId: execution.workflowData.id, - workflowName: execution.workflowData.name, + workflow: execution.workflowData, executionId: execution.id, - success: execution.status === 'success', - isManual: execution.mode === 'manual', runData: execution, }); diff --git a/packages/cli/src/telemetry/telemetry-event-relay.service.ts b/packages/cli/src/telemetry/telemetry-event-relay.service.ts index 9077abdf9ce23..f92b38987b1e3 100644 --- a/packages/cli/src/telemetry/telemetry-event-relay.service.ts +++ b/packages/cli/src/telemetry/telemetry-event-relay.service.ts @@ -8,10 +8,14 @@ import { License } from '@/License'; import { GlobalConfig } from '@n8n/config'; import { N8N_VERSION } from '@/constants'; import { WorkflowRepository } from '@/databases/repositories/workflow.repository'; +import type { ExecutionStatus, INodesGraphResult, ITelemetryTrackProperties } from 'n8n-workflow'; +import { get as pslGet } from 'psl'; import { TelemetryHelpers } from 'n8n-workflow'; import { NodeTypes } from '@/NodeTypes'; import { SharedWorkflowRepository } from '@/databases/repositories/sharedWorkflow.repository'; import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository'; +import type { IExecutionTrackProperties } from '@/Interfaces'; +import { determineFinalExecutionStatus } from '@/executionLifecycleHooks/shared/sharedHookFunctions'; @Service() export class TelemetryEventRelay { @@ -118,6 +122,9 @@ export class TelemetryEventRelay { this.eventService.on('workflow-saved', async (event) => { await this.workflowSaved(event); }); + this.eventService.on('workflow-post-execute', async (event) => { + await this.workflowPostExecute(event); + }); } private teamProjectUpdated({ userId, role, members, projectId }: Event['team-project-updated']) { @@ -584,4 +591,138 @@ export class TelemetryEventRelay { earliest_workflow_created: firstWorkflow?.createdAt, }); } + + // eslint-disable-next-line complexity + private async workflowPostExecute({ workflow, runData, userId }: Event['workflow-post-execute']) { + if (!workflow.id) { + return; + } + + if (runData?.status === 'waiting') { + // No need to send telemetry or logs when the workflow hasn't finished yet. + return; + } + + const telemetryProperties: IExecutionTrackProperties = { + workflow_id: workflow.id, + is_manual: false, + version_cli: N8N_VERSION, + success: false, + }; + + if (userId) { + telemetryProperties.user_id = userId; + } + + if (runData?.data.resultData.error?.message?.includes('canceled')) { + runData.status = 'canceled'; + } + + telemetryProperties.success = !!runData?.finished; + + // const executionStatus: ExecutionStatus = runData?.status ?? 'unknown'; + const executionStatus: ExecutionStatus = runData + ? determineFinalExecutionStatus(runData) + : 'unknown'; + + if (runData !== undefined) { + telemetryProperties.execution_mode = runData.mode; + telemetryProperties.is_manual = runData.mode === 'manual'; + + let nodeGraphResult: INodesGraphResult | null = null; + + if (!telemetryProperties.success && runData?.data.resultData.error) { + telemetryProperties.error_message = runData?.data.resultData.error.message; + let errorNodeName = + 'node' in runData?.data.resultData.error + ? runData?.data.resultData.error.node?.name + : undefined; + telemetryProperties.error_node_type = + 'node' in runData?.data.resultData.error + ? runData?.data.resultData.error.node?.type + : undefined; + + if (runData.data.resultData.lastNodeExecuted) { + const lastNode = TelemetryHelpers.getNodeTypeForName( + workflow, + runData.data.resultData.lastNodeExecuted, + ); + + if (lastNode !== undefined) { + telemetryProperties.error_node_type = lastNode.type; + errorNodeName = lastNode.name; + } + } + + if (telemetryProperties.is_manual) { + nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); + telemetryProperties.node_graph = nodeGraphResult.nodeGraph; + telemetryProperties.node_graph_string = JSON.stringify(nodeGraphResult.nodeGraph); + + if (errorNodeName) { + telemetryProperties.error_node_id = nodeGraphResult.nameIndices[errorNodeName]; + } + } + } + + if (telemetryProperties.is_manual) { + if (!nodeGraphResult) { + nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); + } + + let userRole: 'owner' | 'sharee' | undefined = undefined; + if (userId) { + const role = await this.sharedWorkflowRepository.findSharingRole(userId, workflow.id); + if (role) { + userRole = role === 'workflow:owner' ? 'owner' : 'sharee'; + } + } + + const manualExecEventProperties: ITelemetryTrackProperties = { + user_id: userId, + workflow_id: workflow.id, + status: executionStatus, + executionStatus: runData?.status ?? 'unknown', + error_message: telemetryProperties.error_message as string, + error_node_type: telemetryProperties.error_node_type, + node_graph_string: telemetryProperties.node_graph_string as string, + error_node_id: telemetryProperties.error_node_id as string, + webhook_domain: null, + sharing_role: userRole, + }; + + if (!manualExecEventProperties.node_graph_string) { + nodeGraphResult = TelemetryHelpers.generateNodesGraph(workflow, this.nodeTypes); + manualExecEventProperties.node_graph_string = JSON.stringify(nodeGraphResult.nodeGraph); + } + + if (runData.data.startData?.destinationNode) { + const telemetryPayload = { + ...manualExecEventProperties, + node_type: TelemetryHelpers.getNodeTypeForName( + workflow, + runData.data.startData?.destinationNode, + )?.type, + node_id: nodeGraphResult.nameIndices[runData.data.startData?.destinationNode], + }; + + this.telemetry.track('Manual node exec finished', telemetryPayload); + } else { + nodeGraphResult.webhookNodeNames.forEach((name: string) => { + const execJson = runData.data.resultData.runData[name]?.[0]?.data?.main?.[0]?.[0] + ?.json as { headers?: { origin?: string } }; + if (execJson?.headers?.origin && execJson.headers.origin !== '') { + manualExecEventProperties.webhook_domain = pslGet( + execJson.headers.origin.replace(/^https?:\/\//, ''), + ); + } + }); + + this.telemetry.track('Manual workflow exec finished', manualExecEventProperties); + } + } + } + + this.telemetry.trackWorkflowExecution(telemetryProperties); + } } From 84efbd9b9c51f536b21a4f969ab607d277bef692 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 12:06:17 +0200 Subject: [PATCH 36/50] feat(core): Support create, delete, edit role for users in Public API (#10279) --- .../users/spec/paths/users.id.role.yml | 31 +++ .../v1/handlers/users/spec/paths/users.id.yml | 18 ++ .../v1/handlers/users/spec/paths/users.yml | 50 ++++ .../v1/handlers/users/users.handler.ee.ts | 33 +++ packages/cli/src/PublicApi/v1/openapi.yml | 2 + .../test/integration/publicApi/users.test.ts | 252 ++++++++++++++++++ 6 files changed, 386 insertions(+) create mode 100644 packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.role.yml create mode 100644 packages/cli/test/integration/publicApi/users.test.ts diff --git a/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.role.yml b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.role.yml new file mode 100644 index 0000000000000..92993adf7f9d4 --- /dev/null +++ b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.role.yml @@ -0,0 +1,31 @@ +patch: + x-eov-operation-id: changeRole + x-eov-operation-handler: v1/handlers/users/users.handler.ee + tags: + - User + summary: Change a user's global role + description: Change a user's global role + parameters: + - $ref: '../schemas/parameters/userIdentifier.yml' + requestBody: + description: New role for the user + required: true + content: + application/json: + schema: + type: object + properties: + newRoleName: + type: string + enum: [global:admin, global:member] + required: + - newRoleName + responses: + '200': + description: Operation successful. + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '403': + $ref: '../../../../shared/spec/responses/forbidden.yml' + '404': + $ref: '../../../../shared/spec/responses/notFound.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.yml b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.yml index 0d3c86c4cef97..f3dcae00534c4 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.yml +++ b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.id.yml @@ -17,3 +17,21 @@ get: $ref: '../schemas/user.yml' '401': $ref: '../../../../shared/spec/responses/unauthorized.yml' +delete: + x-eov-operation-id: deleteUser + x-eov-operation-handler: v1/handlers/users/users.handler.ee + tags: + - User + summary: Delete a user + description: Delete a user from your instance. + parameters: + - $ref: '../schemas/parameters/userIdentifier.yml' + responses: + '204': + description: Operation successful. + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '403': + $ref: '../../../../shared/spec/responses/forbidden.yml' + '404': + $ref: '../../../../shared/spec/responses/notFound.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml index 1d618435531b5..7778a08e35c60 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml +++ b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml @@ -18,3 +18,53 @@ get: $ref: '../schemas/userList.yml' '401': $ref: '../../../../shared/spec/responses/unauthorized.yml' +post: + x-eov-operation-id: createUser + x-eov-operation-handler: v1/handlers/users/users.handler.ee + tags: + - User + summary: Create multiple users + description: Create one or more users. + requestBody: + description: Array of users to be created. + required: true + content: + application/json: + schema: + type: array + items: + type: object + properties: + email: + type: string + format: email + role: + type: string + enum: [global:admin, global:member] + required: + - email + responses: + '200': + description: Operation successful. + content: + application/json: + schema: + type: object + properties: + user: + type: object + properties: + id: + type: string + email: + type: string + inviteAcceptUrl: + type: string + emailSent: + type: boolean + error: + type: string + '401': + $ref: '../../../../shared/spec/responses/unauthorized.yml' + '403': + $ref: '../../../../shared/spec/responses/forbidden.yml' diff --git a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts index 0df22ec4aa9ef..cbfc43c0d100f 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts +++ b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts @@ -6,11 +6,19 @@ import { clean, getAllUsersAndCount, getUser } from './users.service.ee'; import { encodeNextCursor } from '../../shared/services/pagination.service'; import { globalScope, + isLicensed, validCursor, validLicenseWithUserQuota, } from '../../shared/middlewares/global.middleware'; import type { UserRequest } from '@/requests'; import { InternalHooks } from '@/InternalHooks'; +import type { Response } from 'express'; +import { InvitationController } from '@/controllers/invitation.controller'; +import { UsersController } from '@/controllers/users.controller'; + +type Create = UserRequest.Invite; +type Delete = UserRequest.Delete; +type ChangeRole = UserRequest.ChangeRole; export = { getUser: [ @@ -68,4 +76,29 @@ export = { }); }, ], + createUser: [ + globalScope('user:create'), + async (req: Create, res: Response) => { + const usersInvited = await Container.get(InvitationController).inviteUser(req); + + return res.status(201).json(usersInvited); + }, + ], + deleteUser: [ + globalScope('user:delete'), + async (req: Delete, res: Response) => { + await Container.get(UsersController).deleteUser(req); + + return res.status(204).send(); + }, + ], + changeRole: [ + isLicensed('feat:advancedPermissions'), + globalScope('user:changeRole'), + async (req: ChangeRole, res: Response) => { + await Container.get(UsersController).changeGlobalRole(req); + + return res.status(204).send(); + }, + ], }; diff --git a/packages/cli/src/PublicApi/v1/openapi.yml b/packages/cli/src/PublicApi/v1/openapi.yml index 1d4e14079f541..30c3a73bde0c7 100644 --- a/packages/cli/src/PublicApi/v1/openapi.yml +++ b/packages/cli/src/PublicApi/v1/openapi.yml @@ -70,6 +70,8 @@ paths: $ref: './handlers/users/spec/paths/users.yml' /users/{id}: $ref: './handlers/users/spec/paths/users.id.yml' + /users/{id}/role: + $ref: './handlers/users/spec/paths/users.id.role.yml' /source-control/pull: $ref: './handlers/sourceControl/spec/paths/sourceControl.yml' /variables: diff --git a/packages/cli/test/integration/publicApi/users.test.ts b/packages/cli/test/integration/publicApi/users.test.ts new file mode 100644 index 0000000000000..6021ae01a35f5 --- /dev/null +++ b/packages/cli/test/integration/publicApi/users.test.ts @@ -0,0 +1,252 @@ +import { setupTestServer } from '@test-integration/utils'; +import * as testDb from '../shared/testDb'; +import { createMember, createOwner, getUserById } from '@test-integration/db/users'; +import { mockInstance } from '@test/mocking'; +import { Telemetry } from '@/telemetry'; +import { FeatureNotLicensedError } from '@/errors/feature-not-licensed.error'; + +describe('Users in Public API', () => { + const testServer = setupTestServer({ endpointGroups: ['publicApi'] }); + mockInstance(Telemetry); + + beforeAll(async () => { + await testDb.init(); + }); + + beforeEach(async () => { + await testDb.truncate(['User']); + }); + + describe('POST /users', () => { + it('if not authenticated, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: false }); + const payload = { email: 'test@test.com', role: 'global:admin' }; + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).post('/users').send(payload); + + /** + * Assert + */ + expect(response.status).toBe(401); + }); + + it('if missing scope, should reject', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:advancedPermissions'); + const member = await createMember({ withApiKey: true }); + const payload = [{ email: 'test@test.com', role: 'global:admin' }]; + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(member).post('/users').send(payload); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + + it('should create a user', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:advancedPermissions'); + const owner = await createOwner({ withApiKey: true }); + const payload = [{ email: 'test@test.com', role: 'global:admin' }]; + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).post('/users').send(payload); + + /** + * Assert + */ + expect(response.status).toBe(201); + + expect(response.body).toHaveLength(1); + + const [result] = response.body; + const { user: returnedUser, error } = result; + const payloadUser = payload[0]; + + expect(returnedUser).toHaveProperty('email', payload[0].email); + expect(typeof returnedUser.inviteAcceptUrl).toBe('string'); + expect(typeof returnedUser.emailSent).toBe('boolean'); + expect(error).toBe(''); + + const storedUser = await getUserById(returnedUser.id); + expect(returnedUser.id).toBe(storedUser.id); + expect(returnedUser.email).toBe(storedUser.email); + expect(returnedUser.email).toBe(payloadUser.email); + expect(storedUser.role).toBe(payloadUser.role); + }); + }); + + describe('DELETE /users/:id', () => { + it('if not authenticated, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: false }); + const member = await createMember(); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).delete(`/users/${member.id}`); + + /** + * Assert + */ + expect(response.status).toBe(401); + }); + + it('if missing scope, should reject', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:advancedPermissions'); + const firstMember = await createMember({ withApiKey: true }); + const secondMember = await createMember(); + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(firstMember) + .delete(`/users/${secondMember.id}`); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + + it('should delete a user', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:advancedPermissions'); + const owner = await createOwner({ withApiKey: true }); + const member = await createMember(); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).delete(`/users/${member.id}`); + + /** + * Assert + */ + expect(response.status).toBe(204); + await expect(getUserById(member.id)).rejects.toThrow(); + }); + }); + + describe('PATCH /users/:id/role', () => { + it('if not authenticated, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: false }); + const member = await createMember(); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).patch(`/users/${member.id}/role`); + + /** + * Assert + */ + expect(response.status).toBe(401); + }); + + it('if not licensed, should reject', async () => { + /** + * Arrange + */ + const owner = await createOwner({ withApiKey: true }); + const member = await createMember(); + const payload = { newRoleName: 'global:admin' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .patch(`/users/${member.id}/role`) + .send(payload); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty( + 'message', + new FeatureNotLicensedError('feat:advancedPermissions').message, + ); + }); + + it('if missing scope, should reject', async () => { + /** + * Arrange + */ + testServer.license.enable('feat:advancedPermissions'); + const firstMember = await createMember({ withApiKey: true }); + const secondMember = await createMember(); + const payload = { newRoleName: 'global:admin' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(firstMember) + .patch(`/users/${secondMember.id}/role`) + .send(payload); + + /** + * Assert + */ + expect(response.status).toBe(403); + expect(response.body).toHaveProperty('message', 'Forbidden'); + }); + + it("should change a user's role", async () => { + /** + * Arrange + */ + testServer.license.enable('feat:advancedPermissions'); + const owner = await createOwner({ withApiKey: true }); + const member = await createMember(); + const payload = { newRoleName: 'global:admin' }; + + /** + * Act + */ + const response = await testServer + .publicApiAgentFor(owner) + .patch(`/users/${member.id}/role`) + .send(payload); + + /** + * Assert + */ + expect(response.status).toBe(204); + const storedUser = await getUserById(member.id); + expect(storedUser.role).toBe(payload.newRoleName); + }); + }); +}); From 47a68b0220ce3e908e1e353b9e275dbc84a62402 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Fri, 2 Aug 2024 12:20:34 +0200 Subject: [PATCH 37/50] ci: Fix DB tests (no-changelog) (#10282) --- .../integration/prometheus-metrics.test.ts | 48 ++++++------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/packages/cli/test/integration/prometheus-metrics.test.ts b/packages/cli/test/integration/prometheus-metrics.test.ts index 4c00a98f74a49..9f6a0fad6e89d 100644 --- a/packages/cli/test/integration/prometheus-metrics.test.ts +++ b/packages/cli/test/integration/prometheus-metrics.test.ts @@ -5,45 +5,27 @@ import request, { type Response } from 'supertest'; import { N8N_VERSION } from '@/constants'; import { PrometheusMetricsService } from '@/metrics/prometheus-metrics.service'; import { setupTestServer } from './shared/utils'; -import { mockInstance } from '@test/mocking'; import { GlobalConfig } from '@n8n/config'; jest.unmock('@/eventbus/MessageEventBus/MessageEventBus'); const toLines = (response: Response) => response.text.trim().split('\n'); -mockInstance(GlobalConfig, { - database: { - type: 'sqlite', - sqlite: { - database: 'database.sqlite', - enableWAL: false, - executeVacuumOnStartup: false, - poolSize: 0, - }, - logging: { - enabled: false, - maxQueryExecutionTime: 0, - options: 'error', - }, - tablePrefix: '', - }, - endpoints: { - metrics: { - prefix: 'n8n_test_', - includeDefaultMetrics: true, - includeApiEndpoints: true, - includeCacheMetrics: true, - includeMessageEventBusMetrics: true, - includeCredentialTypeLabel: false, - includeNodeTypeLabel: false, - includeWorkflowIdLabel: false, - includeApiPathLabel: true, - includeApiMethodLabel: true, - includeApiStatusCodeLabel: true, - }, - }, -}); +const globalConfig = Container.get(GlobalConfig); +// @ts-expect-error `metrics` is a readonly property +globalConfig.endpoints.metrics = { + prefix: 'n8n_test_', + includeDefaultMetrics: true, + includeApiEndpoints: true, + includeCacheMetrics: true, + includeMessageEventBusMetrics: true, + includeCredentialTypeLabel: false, + includeNodeTypeLabel: false, + includeWorkflowIdLabel: false, + includeApiPathLabel: true, + includeApiMethodLabel: true, + includeApiStatusCodeLabel: true, +}; const server = setupTestServer({ endpointGroups: ['metrics'] }); const agent = request.agent(server.app); From c3e2e84065d6a5e55439450070243881b83c6aa6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 12:25:57 +0200 Subject: [PATCH 38/50] refactor(core): Mark schema env vars used by cloud hooks (no-changelog) (#10283) --- packages/cli/src/config/schema.ts | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index 068317e4918f6..dc63f53099659 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -4,6 +4,7 @@ import { Container } from 'typedi'; import { InstanceSettings } from 'n8n-core'; import { LOG_LEVELS } from 'n8n-workflow'; import { ensureStringArray } from './utils'; +import { GlobalConfig } from '@n8n/config'; convict.addFormat({ name: 'comma-separated-list', @@ -381,12 +382,17 @@ export const schema = { default: 0, env: 'N8N_USER_MANAGEMENT_JWT_REFRESH_TIMEOUT_HOURS', }, + + /** + * @important Do not remove until after cloud hooks are updated to stop using convict config. + */ isInstanceOwnerSetUp: { // n8n loads this setting from DB on startup doc: "Whether the instance owner's account has been set up", format: Boolean, default: false, }, + authenticationMethod: { doc: 'How to authenticate users (e.g. "email", "ldap", "saml")', format: ['email', 'ldap', 'saml'] as const, @@ -691,6 +697,19 @@ export const schema = { }, }, + /** + * @important Do not remove until after cloud hooks are updated to stop using convict config. + */ + endpoints: { + rest: { + format: String, + default: Container.get(GlobalConfig).endpoints.rest, + }, + }, + + /** + * @important Do not remove until after cloud hooks are updated to stop using convict config. + */ ai: { enabled: { doc: 'Whether AI features are enabled', From ae50bb95a8e5bf1cdbf9483da54b84094b82e260 Mon Sep 17 00:00:00 2001 From: Tomi Turtiainen <10324676+tomi@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:46:35 +0300 Subject: [PATCH 39/50] fix(core): Make execution and its data creation atomic (#10276) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: कारतोफ्फेलस्क्रिप्ट™ --- packages/cli/src/ActiveExecutions.ts | 5 ++-- .../repositories/execution.repository.ts | 28 ++++++++++++------ .../repositories/executionData.repository.ts | 19 ++++++++++++ .../repositories/execution.repository.test.ts | 29 +++++++++++++++++++ 4 files changed, 69 insertions(+), 12 deletions(-) diff --git a/packages/cli/src/ActiveExecutions.ts b/packages/cli/src/ActiveExecutions.ts index 97313d5cb2f07..c1a6e8ffd65a9 100644 --- a/packages/cli/src/ActiveExecutions.ts +++ b/packages/cli/src/ActiveExecutions.ts @@ -12,6 +12,7 @@ import { ExecutionCancelledError, sleep, } from 'n8n-workflow'; +import { strict as assert } from 'node:assert'; import type { ExecutionPayload, @@ -74,9 +75,7 @@ export class ActiveExecutions { } executionId = await this.executionRepository.createNewExecution(fullExecutionData); - if (executionId === undefined) { - throw new ApplicationError('There was an issue assigning an execution id to the execution'); - } + assert(executionId); await this.concurrencyControl.throttle({ mode, executionId }); executionStatus = 'running'; diff --git a/packages/cli/src/databases/repositories/execution.repository.ts b/packages/cli/src/databases/repositories/execution.repository.ts index 1ebb22d8eb4bc..a8605147aa751 100644 --- a/packages/cli/src/databases/repositories/execution.repository.ts +++ b/packages/cli/src/databases/repositories/execution.repository.ts @@ -270,17 +270,27 @@ export class ExecutionRepository extends Repository { return rest; } + /** + * Insert a new execution and its execution data using a transaction. + */ async createNewExecution(execution: ExecutionPayload): Promise { - const { data, workflowData, ...rest } = execution; - const { identifiers: inserted } = await this.insert(rest); - const { id: executionId } = inserted[0] as { id: string }; - const { connections, nodes, name, settings } = workflowData ?? {}; - await this.executionDataRepository.insert({ - executionId, - workflowData: { connections, nodes, name, settings, id: workflowData.id }, - data: stringify(data), + return await this.manager.transaction(async (transactionManager) => { + const { data, workflowData, ...rest } = execution; + const insertResult = await transactionManager.insert(ExecutionEntity, rest); + const { id: executionId } = insertResult.identifiers[0] as { id: string }; + + const { connections, nodes, name, settings } = workflowData ?? {}; + await this.executionDataRepository.createExecutionDataForExecution( + { + executionId, + workflowData: { connections, nodes, name, settings, id: workflowData.id }, + data: stringify(data), + }, + transactionManager, + ); + + return String(executionId); }); - return String(executionId); } async markAsCrashed(executionIds: string | string[]) { diff --git a/packages/cli/src/databases/repositories/executionData.repository.ts b/packages/cli/src/databases/repositories/executionData.repository.ts index 5872f9888cd66..013453d998e42 100644 --- a/packages/cli/src/databases/repositories/executionData.repository.ts +++ b/packages/cli/src/databases/repositories/executionData.repository.ts @@ -1,13 +1,32 @@ import { Service } from 'typedi'; +import type { EntityManager } from '@n8n/typeorm'; +import type { IWorkflowBase } from 'n8n-workflow'; import { DataSource, In, Repository } from '@n8n/typeorm'; import { ExecutionData } from '../entities/ExecutionData'; +export interface CreateExecutionDataOpts extends Pick { + workflowData: Pick; +} + @Service() export class ExecutionDataRepository extends Repository { constructor(dataSource: DataSource) { super(ExecutionData, dataSource.manager); } + async createExecutionDataForExecution( + executionData: CreateExecutionDataOpts, + transactionManager: EntityManager, + ) { + const { data, executionId, workflowData } = executionData; + + return await transactionManager.insert(ExecutionData, { + executionId, + data, + workflowData, + }); + } + async findByExecutionIds(executionIds: string[]) { return await this.find({ select: ['workflowData'], diff --git a/packages/cli/test/integration/database/repositories/execution.repository.test.ts b/packages/cli/test/integration/database/repositories/execution.repository.test.ts index cfb897d627a56..e777f624299aa 100644 --- a/packages/cli/test/integration/database/repositories/execution.repository.test.ts +++ b/packages/cli/test/integration/database/repositories/execution.repository.test.ts @@ -52,5 +52,34 @@ describe('ExecutionRepository', () => { }); expect(executionData?.data).toEqual('[{"resultData":"1"},{}]'); }); + + it('should not create execution if execution data insert fails', async () => { + const executionRepo = Container.get(ExecutionRepository); + const executionDataRepo = Container.get(ExecutionDataRepository); + + const workflow = await createWorkflow({ settings: { executionOrder: 'v1' } }); + jest + .spyOn(executionDataRepo, 'createExecutionDataForExecution') + .mockRejectedValueOnce(new Error()); + + await expect( + async () => + await executionRepo.createNewExecution({ + workflowId: workflow.id, + data: { + //@ts-expect-error This is not needed for tests + resultData: {}, + }, + workflowData: workflow, + mode: 'manual', + startedAt: new Date(), + status: 'new', + finished: false, + }), + ).rejects.toThrow(); + + const executionEntities = await executionRepo.find(); + expect(executionEntities).toBeEmptyArray(); + }); }); }); From 7056e50b006bda665f64ce6234c5c1967891c415 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Ovejero?= Date: Fri, 2 Aug 2024 14:16:17 +0200 Subject: [PATCH 40/50] feat(core): Allow filtering executions and users by project in Public API (#10250) --- packages/cli/src/PublicApi/types.ts | 1 + .../handlers/executions/executions.handler.ts | 3 +- .../executions/spec/paths/executions.yml | 8 ++++ .../v1/handlers/users/spec/paths/users.yml | 8 ++++ .../v1/handlers/users/users.handler.ee.ts | 8 +++- .../v1/handlers/users/users.service.ee.ts | 7 ++- .../handlers/workflows/workflows.service.ts | 8 +++- .../projectRelation.repository.ts | 9 ++++ packages/cli/src/requests.ts | 2 +- .../src/workflows/workflowSharing.service.ts | 11 +++-- .../integration/publicApi/executions.test.ts | 38 ++++++++++++++++ .../integration/publicApi/users.ee.test.ts | 44 ++++++++++++++++++- 12 files changed, 138 insertions(+), 9 deletions(-) diff --git a/packages/cli/src/PublicApi/types.ts b/packages/cli/src/PublicApi/types.ts index 831dd03ebfb9c..631a8c09d7236 100644 --- a/packages/cli/src/PublicApi/types.ts +++ b/packages/cli/src/PublicApi/types.ts @@ -29,6 +29,7 @@ export declare namespace ExecutionRequest { includeData?: boolean; workflowId?: string; lastId?: string; + projectId?: string; } >; diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts index 16ca8093cb325..ed078b52d5249 100644 --- a/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts +++ b/packages/cli/src/PublicApi/v1/handlers/executions/executions.handler.ts @@ -95,9 +95,10 @@ export = { status = undefined, includeData = false, workflowId = undefined, + projectId, } = req.query; - const sharedWorkflowsIds = await getSharedWorkflowIds(req.user, ['workflow:read']); + const sharedWorkflowsIds = await getSharedWorkflowIds(req.user, ['workflow:read'], projectId); // user does not have workflows hence no executions // or the execution they are trying to access belongs to a workflow they do not own diff --git a/packages/cli/src/PublicApi/v1/handlers/executions/spec/paths/executions.yml b/packages/cli/src/PublicApi/v1/handlers/executions/spec/paths/executions.yml index 8d26492ec30bf..6fcdaf356e4bb 100644 --- a/packages/cli/src/PublicApi/v1/handlers/executions/spec/paths/executions.yml +++ b/packages/cli/src/PublicApi/v1/handlers/executions/spec/paths/executions.yml @@ -21,6 +21,14 @@ get: schema: type: string example: '1000' + - name: projectId + in: query + required: false + explode: false + allowReserved: true + schema: + type: string + example: VmwOO9HeTEj20kxM - $ref: '../../../../shared/spec/parameters/limit.yml' - $ref: '../../../../shared/spec/parameters/cursor.yml' responses: diff --git a/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml index 7778a08e35c60..e767ab33cc16b 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml +++ b/packages/cli/src/PublicApi/v1/handlers/users/spec/paths/users.yml @@ -9,6 +9,14 @@ get: - $ref: '../../../../shared/spec/parameters/limit.yml' - $ref: '../../../../shared/spec/parameters/cursor.yml' - $ref: '../schemas/parameters/includeRole.yml' + - name: projectId + in: query + required: false + explode: false + allowReserved: true + schema: + type: string + example: VmwOO9HeTEj20kxM responses: '200': description: Operation successful. diff --git a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts index cbfc43c0d100f..53c568ee69acd 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts +++ b/packages/cli/src/PublicApi/v1/handlers/users/users.handler.ee.ts @@ -12,6 +12,7 @@ import { } from '../../shared/middlewares/global.middleware'; import type { UserRequest } from '@/requests'; import { InternalHooks } from '@/InternalHooks'; +import { ProjectRelationRepository } from '@/databases/repositories/projectRelation.repository'; import type { Response } from 'express'; import { InvitationController } from '@/controllers/invitation.controller'; import { UsersController } from '@/controllers/users.controller'; @@ -51,12 +52,17 @@ export = { validCursor, globalScope(['user:list', 'user:read']), async (req: UserRequest.Get, res: express.Response) => { - const { offset = 0, limit = 100, includeRole = false } = req.query; + const { offset = 0, limit = 100, includeRole = false, projectId } = req.query; + + const _in = projectId + ? await Container.get(ProjectRelationRepository).findUserIdsByProjectId(projectId) + : undefined; const [users, count] = await getAllUsersAndCount({ includeRole, limit, offset, + in: _in, }); const telemetryData = { diff --git a/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts b/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts index f7bf6618168f0..e62c9465747ed 100644 --- a/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts +++ b/packages/cli/src/PublicApi/v1/handlers/users/users.service.ee.ts @@ -3,6 +3,8 @@ import { UserRepository } from '@db/repositories/user.repository'; import type { User } from '@db/entities/User'; import pick from 'lodash/pick'; import { validate as uuidValidate } from 'uuid'; +// eslint-disable-next-line n8n-local-rules/misplaced-n8n-typeorm-import +import { In } from '@n8n/typeorm'; export async function getUser(data: { withIdentifier: string; @@ -25,9 +27,12 @@ export async function getAllUsersAndCount(data: { includeRole?: boolean; limit?: number; offset?: number; + in?: string[]; }): Promise<[User[], number]> { + const { in: _in } = data; + const users = await Container.get(UserRepository).find({ - where: {}, + where: { ...(_in && { id: In(_in) }) }, skip: data.offset, take: data.limit, }); diff --git a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts index d301e61c93966..3e3afc078ed92 100644 --- a/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts +++ b/packages/cli/src/PublicApi/v1/handlers/workflows/workflows.service.ts @@ -17,15 +17,21 @@ function insertIf(condition: boolean, elements: string[]): string[] { return condition ? elements : []; } -export async function getSharedWorkflowIds(user: User, scopes: Scope[]): Promise { +export async function getSharedWorkflowIds( + user: User, + scopes: Scope[], + projectId?: string, +): Promise { if (Container.get(License).isSharingEnabled()) { return await Container.get(WorkflowSharingService).getSharedWorkflowIds(user, { scopes, + projectId, }); } else { return await Container.get(WorkflowSharingService).getSharedWorkflowIds(user, { workflowRoles: ['workflow:owner'], projectRoles: ['project:personalOwner'], + projectId, }); } } diff --git a/packages/cli/src/databases/repositories/projectRelation.repository.ts b/packages/cli/src/databases/repositories/projectRelation.repository.ts index bddfd6e38d66f..00fc4de34a195 100644 --- a/packages/cli/src/databases/repositories/projectRelation.repository.ts +++ b/packages/cli/src/databases/repositories/projectRelation.repository.ts @@ -52,4 +52,13 @@ export class ProjectRelationRepository extends Repository { {} as Record, ); } + + async findUserIdsByProjectId(projectId: string): Promise { + const rows = await this.find({ + select: ['userId'], + where: { projectId }, + }); + + return [...new Set(rows.map((r) => r.userId))]; + } } diff --git a/packages/cli/src/requests.ts b/packages/cli/src/requests.ts index 1ea265f2394e7..4fe369857eb7a 100644 --- a/packages/cli/src/requests.ts +++ b/packages/cli/src/requests.ts @@ -306,7 +306,7 @@ export declare namespace UserRequest { { id: string; email: string; identifier: string }, {}, {}, - { limit?: number; offset?: number; cursor?: string; includeRole?: boolean } + { limit?: number; offset?: number; cursor?: string; includeRole?: boolean; projectId?: string } >; export type PasswordResetLink = AuthenticatedRequest<{ id: string }, {}, {}, {}>; diff --git a/packages/cli/src/workflows/workflowSharing.service.ts b/packages/cli/src/workflows/workflowSharing.service.ts index 93ef76438f7b0..add5bb31b8fc9 100644 --- a/packages/cli/src/workflows/workflowSharing.service.ts +++ b/packages/cli/src/workflows/workflowSharing.service.ts @@ -27,11 +27,16 @@ export class WorkflowSharingService { async getSharedWorkflowIds( user: User, options: - | { scopes: Scope[] } - | { projectRoles: ProjectRole[]; workflowRoles: WorkflowSharingRole[] }, + | { scopes: Scope[]; projectId?: string } + | { projectRoles: ProjectRole[]; workflowRoles: WorkflowSharingRole[]; projectId?: string }, ): Promise { + const { projectId } = options; + if (user.hasGlobalScope('workflow:read')) { - const sharedWorkflows = await this.sharedWorkflowRepository.find({ select: ['workflowId'] }); + const sharedWorkflows = await this.sharedWorkflowRepository.find({ + select: ['workflowId'], + ...(projectId && { where: { projectId } }), + }); return sharedWorkflows.map(({ workflowId }) => workflowId); } diff --git a/packages/cli/test/integration/publicApi/executions.test.ts b/packages/cli/test/integration/publicApi/executions.test.ts index 9961dac545036..8dac97fc22381 100644 --- a/packages/cli/test/integration/publicApi/executions.test.ts +++ b/packages/cli/test/integration/publicApi/executions.test.ts @@ -20,6 +20,8 @@ import { import type { SuperAgentTest } from '../shared/types'; import { mockInstance } from '@test/mocking'; import { Telemetry } from '@/telemetry'; +import { createTeamProject } from '@test-integration/db/projects'; +import type { ExecutionEntity } from '@/databases/entities/ExecutionEntity'; let owner: User; let user1: User; @@ -447,6 +449,42 @@ describe('GET /executions', () => { } }); + test('should return executions filtered by project ID', async () => { + /** + * Arrange + */ + const [firstProject, secondProject] = await Promise.all([ + createTeamProject(), + createTeamProject(), + ]); + const [firstWorkflow, secondWorkflow] = await Promise.all([ + createWorkflow({}, firstProject), + createWorkflow({}, secondProject), + ]); + const [firstExecution, secondExecution, _] = await Promise.all([ + createExecution({}, firstWorkflow), + createExecution({}, firstWorkflow), + createExecution({}, secondWorkflow), + ]); + + /** + * Act + */ + const response = await authOwnerAgent.get('/executions').query({ + projectId: firstProject.id, + }); + + /** + * Assert + */ + expect(response.statusCode).toBe(200); + expect(response.body.data.length).toBe(2); + expect(response.body.nextCursor).toBeNull(); + expect(response.body.data.map((execution: ExecutionEntity) => execution.id)).toEqual( + expect.arrayContaining([firstExecution.id, secondExecution.id]), + ); + }); + test('owner should retrieve all executions regardless of ownership', async () => { const [firstWorkflowForUser1, secondWorkflowForUser1] = await createManyWorkflows(2, {}, user1); await createManyExecutions(2, firstWorkflowForUser1, createSuccessfulExecution); diff --git a/packages/cli/test/integration/publicApi/users.ee.test.ts b/packages/cli/test/integration/publicApi/users.ee.test.ts index 6e57a629d7dc7..be23d8f45a519 100644 --- a/packages/cli/test/integration/publicApi/users.ee.test.ts +++ b/packages/cli/test/integration/publicApi/users.ee.test.ts @@ -7,8 +7,10 @@ import { mockInstance } from '../../shared/mocking'; import { randomApiKey } from '../shared/random'; import * as utils from '../shared/utils/'; import * as testDb from '../shared/testDb'; -import { createUser, createUserShell } from '../shared/db/users'; +import { createOwner, createUser, createUserShell } from '../shared/db/users'; import type { SuperAgentTest } from '../shared/types'; +import { createTeamProject, linkUserToProject } from '@test-integration/db/projects'; +import type { User } from '@/databases/entities/User'; mockInstance(License, { getUsersLimit: jest.fn().mockReturnValue(-1), @@ -84,6 +86,46 @@ describe('With license unlimited quota:users', () => { expect(updatedAt).toBeDefined(); } }); + + it('should return users filtered by project ID', async () => { + /** + * Arrange + */ + const [owner, firstMember, secondMember, thirdMember] = await Promise.all([ + createOwner({ withApiKey: true }), + createUser({ role: 'global:member' }), + createUser({ role: 'global:member' }), + createUser({ role: 'global:member' }), + ]); + + const [firstProject, secondProject] = await Promise.all([ + createTeamProject(), + createTeamProject(), + ]); + + await Promise.all([ + linkUserToProject(firstMember, firstProject, 'project:admin'), + linkUserToProject(secondMember, firstProject, 'project:viewer'), + linkUserToProject(thirdMember, secondProject, 'project:admin'), + ]); + + /** + * Act + */ + const response = await testServer.publicApiAgentFor(owner).get('/users').query({ + projectId: firstProject.id, + }); + + /** + * Assert + */ + expect(response.status).toBe(200); + expect(response.body.data.length).toBe(2); + expect(response.body.nextCursor).toBeNull(); + expect(response.body.data.map((user: User) => user.id)).toEqual( + expect.arrayContaining([firstMember.id, secondMember.id]), + ); + }); }); describe('GET /users/:id', () => { From 0faf46f4f87bc35406392f735e794e7aeceb6ead Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Fri, 2 Aug 2024 15:18:33 +0200 Subject: [PATCH 41/50] refactor(core): Move instanceRole to InstanceSettings (no-changelog) (#10242) --- packages/cli/src/License.ts | 2 +- packages/cli/src/commands/start.ts | 4 ++-- packages/cli/src/config/schema.ts | 6 ----- .../execution-recovery.service.test.ts | 23 +++++++++---------- .../executions/execution-recovery.service.ts | 10 ++++---- .../cli/src/services/orchestration.service.ts | 10 +++++--- .../orchestration/main/MultiMainSetup.ee.ts | 14 ++++++----- packages/cli/src/services/pruning.service.ts | 5 ++-- .../test/integration/pruning.service.test.ts | 8 ++++--- packages/cli/test/unit/WaitTracker.test.ts | 2 +- .../services/orchestration.service.test.ts | 20 +++++++++------- packages/core/src/InstanceSettings.ts | 21 +++++++++++++++++ 12 files changed, 77 insertions(+), 48 deletions(-) diff --git a/packages/cli/src/License.ts b/packages/cli/src/License.ts index 9a2740ce7cbb7..643da18fd26e9 100644 --- a/packages/cli/src/License.ts +++ b/packages/cli/src/License.ts @@ -55,7 +55,7 @@ export class License { * This ensures the mains do not cause a 429 (too many requests) on license init. */ if (config.getEnv('multiMainSetup.enabled')) { - return autoRenewEnabled && config.getEnv('instanceRole') === 'leader'; + return autoRenewEnabled && this.instanceSettings.isLeader; } return autoRenewEnabled; diff --git a/packages/cli/src/commands/start.ts b/packages/cli/src/commands/start.ts index 4d7cd888b4cc0..63349ab6d1f0e 100644 --- a/packages/cli/src/commands/start.ts +++ b/packages/cli/src/commands/start.ts @@ -186,7 +186,7 @@ export class Start extends BaseCommand { await this.initOrchestration(); this.logger.debug('Orchestration init complete'); - if (!config.getEnv('license.autoRenewEnabled') && config.getEnv('instanceRole') === 'leader') { + if (!config.getEnv('license.autoRenewEnabled') && this.instanceSettings.isLeader) { this.logger.warn( 'Automatic license renewal is disabled. The license will not renew automatically, and access to licensed features may be lost!', ); @@ -210,7 +210,7 @@ export class Start extends BaseCommand { async initOrchestration() { if (config.getEnv('executions.mode') === 'regular') { - config.set('instanceRole', 'leader'); + this.instanceSettings.markAsLeader(); return; } diff --git a/packages/cli/src/config/schema.ts b/packages/cli/src/config/schema.ts index dc63f53099659..f99c14885ddf9 100644 --- a/packages/cli/src/config/schema.ts +++ b/packages/cli/src/config/schema.ts @@ -759,12 +759,6 @@ export const schema = { }, }, - instanceRole: { - doc: 'Always `leader` in single-main setup. `leader` or `follower` in multi-main setup.', - format: ['unset', 'leader', 'follower'] as const, - default: 'unset', // only until Start.initOrchestration - }, - multiMainSetup: { enabled: { doc: 'Whether to enable multi-main setup for queue mode (license required)', diff --git a/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts b/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts index 7e33c6ac74a8a..39a84d27a78c1 100644 --- a/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts +++ b/packages/cli/src/executions/__tests__/execution-recovery.service.test.ts @@ -1,6 +1,7 @@ import Container from 'typedi'; import { stringify } from 'flatted'; import { randomInt } from 'n8n-workflow'; +import { InstanceSettings } from 'n8n-core'; import { mockInstance } from '@test/mocking'; import { createWorkflow } from '@test-integration/db/workflows'; @@ -21,38 +22,37 @@ import { EventMessageNode } from '@/eventbus/EventMessageClasses/EventMessageNod import { IN_PROGRESS_EXECUTION_DATA, OOM_WORKFLOW } from './constants'; import { setupMessages } from './utils'; -import type { EventService } from '@/eventbus/event.service'; import type { EventMessageTypes as EventMessage } from '@/eventbus/EventMessageClasses'; -import type { Logger } from '@/Logger'; describe('ExecutionRecoveryService', () => { - let push: Push; + const push = mockInstance(Push); + mockInstance(InternalHooks); + const instanceSettings = new InstanceSettings(); + let executionRecoveryService: ExecutionRecoveryService; let orchestrationService: OrchestrationService; let executionRepository: ExecutionRepository; beforeAll(async () => { await testDb.init(); - push = mockInstance(Push); executionRepository = Container.get(ExecutionRepository); orchestrationService = Container.get(OrchestrationService); - mockInstance(InternalHooks); executionRecoveryService = new ExecutionRecoveryService( - mock(), + mock(), + instanceSettings, push, executionRepository, orchestrationService, - mock(), + mock(), ); }); beforeEach(() => { - config.set('instanceRole', 'leader'); + instanceSettings.markAsLeader(); }); afterEach(async () => { - config.load(config.default); jest.restoreAllMocks(); await testDb.truncate(['Execution', 'ExecutionData', 'Workflow']); executionRecoveryService.shutdown(); @@ -69,7 +69,6 @@ describe('ExecutionRecoveryService', () => { * Arrange */ config.set('executions.mode', 'queue'); - jest.spyOn(orchestrationService, 'isLeader', 'get').mockReturnValue(true); const scheduleSpy = jest.spyOn(executionRecoveryService, 'scheduleQueueRecovery'); /** @@ -88,7 +87,7 @@ describe('ExecutionRecoveryService', () => { * Arrange */ config.set('executions.mode', 'queue'); - jest.spyOn(orchestrationService, 'isLeader', 'get').mockReturnValue(false); + instanceSettings.markAsFollower(); const scheduleSpy = jest.spyOn(executionRecoveryService, 'scheduleQueueRecovery'); /** @@ -130,7 +129,7 @@ describe('ExecutionRecoveryService', () => { /** * Arrange */ - config.set('instanceRole', 'follower'); + instanceSettings.markAsFollower(); // @ts-expect-error Private method const amendSpy = jest.spyOn(executionRecoveryService, 'amend'); const messages = setupMessages('123', 'Some workflow'); diff --git a/packages/cli/src/executions/execution-recovery.service.ts b/packages/cli/src/executions/execution-recovery.service.ts index 615c65ea6ef5f..152d4ad4f2964 100644 --- a/packages/cli/src/executions/execution-recovery.service.ts +++ b/packages/cli/src/executions/execution-recovery.service.ts @@ -5,6 +5,7 @@ import { ExecutionRepository } from '@db/repositories/execution.repository'; import { getWorkflowHooksMain } from '@/WorkflowExecuteAdditionalData'; // @TODO: Dependency cycle import type { DateTime } from 'luxon'; import type { IRun, ITaskData } from 'n8n-workflow'; +import { InstanceSettings } from 'n8n-core'; import type { EventMessageTypes } from '../eventbus/EventMessageClasses'; import type { IExecutionResponse } from '@/Interfaces'; import { NodeCrashedError } from '@/errors/node-crashed.error'; @@ -24,6 +25,7 @@ import { EventService } from '@/eventbus/event.service'; export class ExecutionRecoveryService { constructor( private readonly logger: Logger, + private readonly instanceSettings: InstanceSettings, private readonly push: Push, private readonly executionRepository: ExecutionRepository, private readonly orchestrationService: OrchestrationService, @@ -36,10 +38,10 @@ export class ExecutionRecoveryService { init() { if (config.getEnv('executions.mode') === 'regular') return; - const { isLeader, isMultiMainSetupEnabled } = this.orchestrationService; - + const { isLeader } = this.instanceSettings; if (isLeader) this.scheduleQueueRecovery(); + const { isMultiMainSetupEnabled } = this.orchestrationService; if (isMultiMainSetupEnabled) { this.orchestrationService.multiMainSetup .on('leader-takeover', () => this.scheduleQueueRecovery()) @@ -58,7 +60,7 @@ export class ExecutionRecoveryService { * Recover key properties of a truncated execution using event logs. */ async recoverFromLogs(executionId: string, messages: EventMessageTypes[]) { - if (this.orchestrationService.isFollower) return; + if (this.instanceSettings.isFollower) return; const amendedExecution = await this.amend(executionId, messages); @@ -319,7 +321,7 @@ export class ExecutionRecoveryService { private shouldScheduleQueueRecovery() { return ( config.getEnv('executions.mode') === 'queue' && - config.getEnv('instanceRole') === 'leader' && + this.instanceSettings.isLeader && !this.isShuttingDown ); } diff --git a/packages/cli/src/services/orchestration.service.ts b/packages/cli/src/services/orchestration.service.ts index bfc1d140fc6fa..283470f4d2114 100644 --- a/packages/cli/src/services/orchestration.service.ts +++ b/packages/cli/src/services/orchestration.service.ts @@ -7,11 +7,13 @@ import type { RedisServiceBaseCommand, RedisServiceCommand } from './redis/Redis import { RedisService } from './redis.service'; import { MultiMainSetup } from './orchestration/main/MultiMainSetup.ee'; import type { WorkflowActivateMode } from 'n8n-workflow'; +import { InstanceSettings } from 'n8n-core'; @Service() export class OrchestrationService { constructor( private readonly logger: Logger, + private readonly instanceSettings: InstanceSettings, private readonly redisService: RedisService, readonly multiMainSetup: MultiMainSetup, ) {} @@ -43,12 +45,14 @@ export class OrchestrationService { return config.getEnv('redis.queueModeId'); } + /** @deprecated use InstanceSettings.isLeader */ get isLeader() { - return config.getEnv('instanceRole') === 'leader'; + return this.instanceSettings.isLeader; } + /** @deprecated use InstanceSettings.isFollower */ get isFollower() { - return config.getEnv('instanceRole') !== 'leader'; + return this.instanceSettings.isFollower; } sanityCheck() { @@ -63,7 +67,7 @@ export class OrchestrationService { if (this.isMultiMainSetupEnabled) { await this.multiMainSetup.init(); } else { - config.set('instanceRole', 'leader'); + this.instanceSettings.markAsLeader(); } this.isInitialized = true; diff --git a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts index cabcf5996bc08..89c6ef725ab45 100644 --- a/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts +++ b/packages/cli/src/services/orchestration/main/MultiMainSetup.ee.ts @@ -1,6 +1,7 @@ import config from '@/config'; import { Service } from 'typedi'; import { TIME } from '@/constants'; +import { InstanceSettings } from 'n8n-core'; import { ErrorReporterProxy as EventReporter } from 'n8n-workflow'; import { Logger } from '@/Logger'; import { RedisServicePubSubPublisher } from '@/services/redis/RedisServicePubSubPublisher'; @@ -16,6 +17,7 @@ type MultiMainEvents = { export class MultiMainSetup extends TypedEmitter { constructor( private readonly logger: Logger, + private readonly instanceSettings: InstanceSettings, private readonly redisPublisher: RedisServicePubSubPublisher, private readonly redisClientService: RedisClientService, ) { @@ -50,7 +52,7 @@ export class MultiMainSetup extends TypedEmitter { async shutdown() { clearInterval(this.leaderCheckInterval); - const isLeader = config.getEnv('instanceRole') === 'leader'; + const { isLeader } = this.instanceSettings; if (isLeader) await this.redisPublisher.clear(this.leaderKey); } @@ -69,8 +71,8 @@ export class MultiMainSetup extends TypedEmitter { if (leaderId && leaderId !== this.instanceId) { this.logger.debug(`[Instance ID ${this.instanceId}] Leader is other instance "${leaderId}"`); - if (config.getEnv('instanceRole') === 'leader') { - config.set('instanceRole', 'follower'); + if (this.instanceSettings.isLeader) { + this.instanceSettings.markAsFollower(); this.emit('leader-stepdown'); // lost leadership - stop triggers, pollers, pruning, wait-tracking, queue recovery @@ -85,7 +87,7 @@ export class MultiMainSetup extends TypedEmitter { `[Instance ID ${this.instanceId}] Leadership vacant, attempting to become leader...`, ); - config.set('instanceRole', 'follower'); + this.instanceSettings.markAsFollower(); /** * Lost leadership - stop triggers, pollers, pruning, wait tracking, license renewal, queue recovery @@ -106,7 +108,7 @@ export class MultiMainSetup extends TypedEmitter { if (keySetSuccessfully) { this.logger.debug(`[Instance ID ${this.instanceId}] Leader is now this instance`); - config.set('instanceRole', 'leader'); + this.instanceSettings.markAsLeader(); await this.redisPublisher.setExpiration(this.leaderKey, this.leaderKeyTtl); @@ -115,7 +117,7 @@ export class MultiMainSetup extends TypedEmitter { */ this.emit('leader-takeover'); } else { - config.set('instanceRole', 'follower'); + this.instanceSettings.markAsFollower(); } } diff --git a/packages/cli/src/services/pruning.service.ts b/packages/cli/src/services/pruning.service.ts index 78c0aad03750c..7f824836ae4ee 100644 --- a/packages/cli/src/services/pruning.service.ts +++ b/packages/cli/src/services/pruning.service.ts @@ -1,5 +1,5 @@ import { Service } from 'typedi'; -import { BinaryDataService } from 'n8n-core'; +import { BinaryDataService, InstanceSettings } from 'n8n-core'; import { inTest, TIME } from '@/constants'; import config from '@/config'; import { ExecutionRepository } from '@db/repositories/execution.repository'; @@ -25,6 +25,7 @@ export class PruningService { constructor( private readonly logger: Logger, + private readonly instanceSettings: InstanceSettings, private readonly executionRepository: ExecutionRepository, private readonly binaryDataService: BinaryDataService, private readonly orchestrationService: OrchestrationService, @@ -56,7 +57,7 @@ export class PruningService { if ( config.getEnv('multiMainSetup.enabled') && config.getEnv('generic.instanceType') === 'main' && - config.getEnv('instanceRole') === 'follower' + this.instanceSettings.isFollower ) { return false; } diff --git a/packages/cli/test/integration/pruning.service.test.ts b/packages/cli/test/integration/pruning.service.test.ts index 09a43a5b5b123..37d218c09d438 100644 --- a/packages/cli/test/integration/pruning.service.test.ts +++ b/packages/cli/test/integration/pruning.service.test.ts @@ -1,5 +1,5 @@ import config from '@/config'; -import { BinaryDataService } from 'n8n-core'; +import { BinaryDataService, InstanceSettings } from 'n8n-core'; import type { ExecutionStatus } from 'n8n-workflow'; import Container from 'typedi'; @@ -15,10 +15,11 @@ import { mockInstance } from '../shared/mocking'; import { createWorkflow } from './shared/db/workflows'; import { createExecution, createSuccessfulExecution } from './shared/db/executions'; import { mock } from 'jest-mock-extended'; -import type { OrchestrationService } from '@/services/orchestration.service'; describe('softDeleteOnPruningCycle()', () => { let pruningService: PruningService; + const instanceSettings = new InstanceSettings(); + instanceSettings.markAsLeader(); const now = new Date(); const yesterday = new Date(Date.now() - TIME.DAY); @@ -29,9 +30,10 @@ describe('softDeleteOnPruningCycle()', () => { pruningService = new PruningService( mockInstance(Logger), + instanceSettings, Container.get(ExecutionRepository), mockInstance(BinaryDataService), - mock(), + mock(), ); workflow = await createWorkflow(); diff --git a/packages/cli/test/unit/WaitTracker.test.ts b/packages/cli/test/unit/WaitTracker.test.ts index 0f8464e18d14d..fb51d2e25bd30 100644 --- a/packages/cli/test/unit/WaitTracker.test.ts +++ b/packages/cli/test/unit/WaitTracker.test.ts @@ -10,7 +10,7 @@ jest.useFakeTimers(); describe('WaitTracker', () => { const executionRepository = mock(); const multiMainSetup = mock(); - const orchestrationService = new OrchestrationService(mock(), mock(), multiMainSetup); + const orchestrationService = new OrchestrationService(mock(), mock(), mock(), multiMainSetup); const execution = mock({ id: '123', diff --git a/packages/cli/test/unit/services/orchestration.service.test.ts b/packages/cli/test/unit/services/orchestration.service.test.ts index 75c7c06e40bb6..0b63f74344b57 100644 --- a/packages/cli/test/unit/services/orchestration.service.test.ts +++ b/packages/cli/test/unit/services/orchestration.service.test.ts @@ -1,4 +1,9 @@ import Container from 'typedi'; +import type Redis from 'ioredis'; +import { mock } from 'jest-mock-extended'; +import { InstanceSettings } from 'n8n-core'; +import type { WorkflowActivateMode } from 'n8n-workflow'; + import config from '@/config'; import { OrchestrationService } from '@/services/orchestration.service'; import type { RedisServiceWorkerResponseObject } from '@/services/redis/RedisServiceCommands'; @@ -13,11 +18,9 @@ import { Logger } from '@/Logger'; import { Push } from '@/push'; import { ActiveWorkflowManager } from '@/ActiveWorkflowManager'; import { mockInstance } from '../../shared/mocking'; -import type { WorkflowActivateMode } from 'n8n-workflow'; import { RedisClientService } from '@/services/redis/redis-client.service'; -import type Redis from 'ioredis'; -import { mock } from 'jest-mock-extended'; +const instanceSettings = Container.get(InstanceSettings); const redisClientService = mockInstance(RedisClientService); const mockRedisClient = mock(); redisClientService.createClient.mockReturnValue(mockRedisClient); @@ -72,6 +75,10 @@ describe('Orchestration Service', () => { queueModeId = config.get('redis.queueModeId'); }); + beforeEach(() => { + instanceSettings.markAsLeader(); + }); + afterAll(async () => { jest.mock('@/services/redis/RedisServicePubSubPublisher').restoreAllMocks(); jest.mock('@/services/redis/RedisServicePubSubSubscriber').restoreAllMocks(); @@ -141,13 +148,10 @@ describe('Orchestration Service', () => { ); expect(helpers.debounceMessageReceiver).toHaveBeenCalledTimes(2); expect(res1!.payload).toBeUndefined(); - expect((res2!.payload as { result: string }).result).toEqual('debounced'); + expect(res2!.payload).toEqual({ result: 'debounced' }); }); describe('shouldAddWebhooks', () => { - beforeEach(() => { - config.set('instanceRole', 'leader'); - }); test('should return true for init', () => { // We want to ensure that webhooks are populated on init // more https://github.com/n8n-io/n8n/pull/8830 @@ -169,7 +173,7 @@ describe('Orchestration Service', () => { }); test('should return false for update or activate when not leader', () => { - config.set('instanceRole', 'follower'); + instanceSettings.markAsFollower(); const modes = ['update', 'activate'] as WorkflowActivateMode[]; for (const mode of modes) { const result = os.shouldAddWebhooks(mode); diff --git a/packages/core/src/InstanceSettings.ts b/packages/core/src/InstanceSettings.ts index e8ab9aa553223..f75e1df712108 100644 --- a/packages/core/src/InstanceSettings.ts +++ b/packages/core/src/InstanceSettings.ts @@ -14,6 +14,8 @@ interface WritableSettings { type Settings = ReadOnlySettings & WritableSettings; +type InstanceRole = 'unset' | 'leader' | 'follower'; + const inTest = process.env.NODE_ENV === 'test'; @Service() @@ -38,6 +40,25 @@ export class InstanceSettings { readonly instanceId = this.generateInstanceId(); + /** Always `leader` in single-main setup. `leader` or `follower` in multi-main setup. */ + private instanceRole: InstanceRole = 'unset'; + + get isLeader() { + return this.instanceRole === 'leader'; + } + + markAsLeader() { + this.instanceRole = 'leader'; + } + + get isFollower() { + return this.instanceRole === 'follower'; + } + + markAsFollower() { + this.instanceRole = 'follower'; + } + get encryptionKey() { return this.settings.encryptionKey; } From 57d1c9a99e97308f2f1b8ae05ac3861a835e8e5a Mon Sep 17 00:00:00 2001 From: Eugene Date: Fri, 2 Aug 2024 16:00:09 +0200 Subject: [PATCH 42/50] feat(core): Show sub-node error on the logs pane. Open logs pane on sub-node error (#10248) --- cypress/constants.ts | 2 + .../e2e/233-AI-switch-to-logs-on-error.cy.ts | 279 ++++++++++++++++++ cypress/pages/ndv.ts | 1 + .../nodes/tools/ToolCode/ToolCode.node.ts | 4 +- .../src/components/Error/NodeErrorView.vue | 3 +- .../editor-ui/src/components/OutputPanel.vue | 32 +- packages/editor-ui/src/components/RunData.vue | 19 +- 7 files changed, 333 insertions(+), 7 deletions(-) create mode 100644 cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts diff --git a/cypress/constants.ts b/cypress/constants.ts index 8439952ac7b81..8fdb03ef9cfba 100644 --- a/cypress/constants.ts +++ b/cypress/constants.ts @@ -37,6 +37,7 @@ export const INSTANCE_MEMBERS = [ export const MANUAL_TRIGGER_NODE_NAME = 'Manual Trigger'; export const MANUAL_TRIGGER_NODE_DISPLAY_NAME = 'When clicking ‘Test workflow’'; export const MANUAL_CHAT_TRIGGER_NODE_NAME = 'Chat Trigger'; +export const MANUAL_CHAT_TRIGGER_NODE_DISPLAY_NAME = 'When chat message received'; export const SCHEDULE_TRIGGER_NODE_NAME = 'Schedule Trigger'; export const CODE_NODE_NAME = 'Code'; export const SET_NODE_NAME = 'Set'; @@ -57,6 +58,7 @@ export const AI_TOOL_CODE_NODE_NAME = 'Code Tool'; export const AI_TOOL_WIKIPEDIA_NODE_NAME = 'Wikipedia'; export const AI_TOOL_HTTP_NODE_NAME = 'HTTP Request Tool'; export const AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME = 'OpenAI Chat Model'; +export const AI_MEMORY_POSTGRES_NODE_NAME = 'Postgres Chat Memory'; export const AI_OUTPUT_PARSER_AUTO_FIXING_NODE_NAME = 'Auto-fixing Output Parser'; export const WEBHOOK_NODE_NAME = 'Webhook'; diff --git a/cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts b/cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts new file mode 100644 index 0000000000000..78d4b61449c23 --- /dev/null +++ b/cypress/e2e/233-AI-switch-to-logs-on-error.cy.ts @@ -0,0 +1,279 @@ +import type { ExecutionError } from 'n8n-workflow/src'; +import { NDV, WorkflowPage as WorkflowPageClass } from '../pages'; +import { + addLanguageModelNodeToParent, + addMemoryNodeToParent, + addNodeToCanvas, + addToolNodeToParent, + navigateToNewWorkflowPage, + openNode, +} from '../composables/workflow'; +import { + AGENT_NODE_NAME, + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + AI_MEMORY_POSTGRES_NODE_NAME, + AI_TOOL_CALCULATOR_NODE_NAME, + MANUAL_CHAT_TRIGGER_NODE_DISPLAY_NAME, + MANUAL_CHAT_TRIGGER_NODE_NAME, + MANUAL_TRIGGER_NODE_DISPLAY_NAME, + MANUAL_TRIGGER_NODE_NAME, +} from '../constants'; +import { + clickCreateNewCredential, + clickExecuteNode, + clickGetBackToCanvas, +} from '../composables/ndv'; +import { setCredentialValues } from '../composables/modals/credential-modal'; +import { + closeManualChatModal, + getManualChatMessages, + getManualChatModalLogs, + getManualChatModalLogsEntries, + sendManualChatMessage, +} from '../composables/modals/chat-modal'; +import { createMockNodeExecutionData, getVisibleSelect, runMockWorkflowExecution } from '../utils'; + +const ndv = new NDV(); +const WorkflowPage = new WorkflowPageClass(); + +function createRunDataWithError(inputMessage: string) { + return [ + createMockNodeExecutionData(MANUAL_CHAT_TRIGGER_NODE_NAME, { + jsonData: { + main: { input: inputMessage }, + }, + }), + createMockNodeExecutionData(AI_MEMORY_POSTGRES_NODE_NAME, { + jsonData: { + ai_memory: { + json: { + action: 'loadMemoryVariables', + values: { + input: inputMessage, + system_message: 'You are a helpful assistant', + formatting_instructions: + 'IMPORTANT: Always call `format_final_response` to format your final response!', + }, + }, + }, + }, + inputOverride: { + ai_memory: [ + [ + { + json: { + action: 'loadMemoryVariables', + values: { + input: inputMessage, + system_message: 'You are a helpful assistant', + formatting_instructions: + 'IMPORTANT: Always call `format_final_response` to format your final response!', + }, + }, + }, + ], + ], + }, + error: { + message: 'Internal error', + timestamp: 1722591723244, + name: 'NodeOperationError', + description: 'Internal error', + context: {}, + cause: { + name: 'error', + severity: 'FATAL', + code: '3D000', + file: 'postinit.c', + line: '885', + routine: 'InitPostgres', + } as unknown as Error, + } as ExecutionError, + }), + createMockNodeExecutionData(AGENT_NODE_NAME, { + executionStatus: 'error', + error: { + level: 'error', + tags: { + packageName: 'workflow', + }, + context: {}, + functionality: 'configuration-node', + name: 'NodeOperationError', + timestamp: 1722591723244, + node: { + parameters: { + notice: '', + sessionIdType: 'fromInput', + tableName: 'n8n_chat_histories', + }, + id: '6b9141da-0135-4e9d-94d1-2d658cbf48b5', + name: 'Postgres Chat Memory', + type: '@n8n/n8n-nodes-langchain.memoryPostgresChat', + typeVersion: 1, + position: [1140, 500], + credentials: { + postgres: { + id: 'RkyZetVpGsSfEAhQ', + name: 'Postgres account', + }, + }, + }, + messages: ['database "chat11" does not exist'], + description: 'Internal error', + message: 'Internal error', + } as unknown as ExecutionError, + metadata: { + subRun: [ + { + node: 'Postgres Chat Memory', + runIndex: 0, + }, + ], + }, + }), + ]; +} + +function setupTestWorkflow(chatTrigger: boolean = false) { + // Setup test workflow with AI Agent, Postgres Memory Node (source of error), Calculator Tool, and OpenAI Chat Model + if (chatTrigger) { + addNodeToCanvas(MANUAL_CHAT_TRIGGER_NODE_NAME, true); + } else { + addNodeToCanvas(MANUAL_TRIGGER_NODE_NAME, true); + } + + addNodeToCanvas(AGENT_NODE_NAME, true); + + if (!chatTrigger) { + // Remove chat trigger + WorkflowPage.getters + .canvasNodeByName(MANUAL_CHAT_TRIGGER_NODE_DISPLAY_NAME) + .find('[data-test-id="delete-node-button"]') + .click({ force: true }); + + // Set manual trigger to output standard pinned data + openNode(MANUAL_TRIGGER_NODE_DISPLAY_NAME); + ndv.actions.editPinnedData(); + ndv.actions.savePinnedData(); + ndv.actions.close(); + } + + // Calculator is added just to make OpenAI Chat Model work (tools can not be empty with OpenAI model) + addToolNodeToParent(AI_TOOL_CALCULATOR_NODE_NAME, AGENT_NODE_NAME); + clickGetBackToCanvas(); + + addMemoryNodeToParent(AI_MEMORY_POSTGRES_NODE_NAME, AGENT_NODE_NAME); + + clickCreateNewCredential(); + setCredentialValues({ + password: 'testtesttest', + }); + + ndv.getters.parameterInput('sessionIdType').click(); + getVisibleSelect().contains('Define below').click(); + ndv.getters.parameterInput('sessionKey').type('asdasd'); + + clickGetBackToCanvas(); + + addLanguageModelNodeToParent( + AI_LANGUAGE_MODEL_OPENAI_CHAT_MODEL_NODE_NAME, + AGENT_NODE_NAME, + true, + ); + + clickCreateNewCredential(); + setCredentialValues({ + apiKey: 'sk_test_123', + }); + clickGetBackToCanvas(); + + WorkflowPage.actions.zoomToFit(); +} + +function checkMessages(inputMessage: string, outputMessage: string) { + const messages = getManualChatMessages(); + messages.should('have.length', 2); + messages.should('contain', inputMessage); + messages.should('contain', outputMessage); + + getManualChatModalLogs().should('exist'); + getManualChatModalLogsEntries() + .should('have.length', 1) + .should('contain', AI_MEMORY_POSTGRES_NODE_NAME); +} + +describe("AI-233 Make root node's logs pane active in case of an error in sub-nodes", () => { + beforeEach(() => { + navigateToNewWorkflowPage(); + }); + + it('should open logs tab by default when there was an error', () => { + setupTestWorkflow(true); + + openNode(AGENT_NODE_NAME); + + const inputMessage = 'Test the code tool'; + + clickExecuteNode(); + runMockWorkflowExecution({ + trigger: () => sendManualChatMessage(inputMessage), + runData: createRunDataWithError(inputMessage), + lastNodeExecuted: AGENT_NODE_NAME, + }); + + checkMessages(inputMessage, '[ERROR: Internal error]'); + closeManualChatModal(); + + // Open the AI Agent node to see the logs + openNode(AGENT_NODE_NAME); + + // Finally check that logs pane is opened by default + ndv.getters.outputDataContainer().should('be.visible'); + + ndv.getters.aiOutputModeToggle().should('be.visible'); + ndv.getters + .aiOutputModeToggle() + .find('[role="radio"]') + .should('have.length', 2) + .eq(1) + .should('have.attr', 'aria-checked', 'true'); + + ndv.getters + .outputPanel() + .findChildByTestId('node-error-message') + .should('be.visible') + .should('contain', 'Error in sub-node'); + }); + + it('should switch to logs tab on error, when NDV is already opened', () => { + setupTestWorkflow(false); + + openNode(AGENT_NODE_NAME); + + const inputMessage = 'Test the code tool'; + + runMockWorkflowExecution({ + trigger: () => clickExecuteNode(), + runData: createRunDataWithError(inputMessage), + lastNodeExecuted: AGENT_NODE_NAME, + }); + + // Check that logs pane is opened by default + ndv.getters.outputDataContainer().should('be.visible'); + + ndv.getters.aiOutputModeToggle().should('be.visible'); + ndv.getters + .aiOutputModeToggle() + .find('[role="radio"]') + .should('have.length', 2) + .eq(1) + .should('have.attr', 'aria-checked', 'true'); + + ndv.getters + .outputPanel() + .findChildByTestId('node-error-message') + .should('be.visible') + .should('contain', 'Error in sub-node'); + }); +}); diff --git a/cypress/pages/ndv.ts b/cypress/pages/ndv.ts index 018ec43a5de9c..24e1922663371 100644 --- a/cypress/pages/ndv.ts +++ b/cypress/pages/ndv.ts @@ -24,6 +24,7 @@ export class NDV extends BasePage { editPinnedDataButton: () => cy.getByTestId('ndv-edit-pinned-data'), pinnedDataEditor: () => this.getters.outputPanel().find('.cm-editor .cm-scroller .cm-content'), runDataPaneHeader: () => cy.getByTestId('run-data-pane-header'), + aiOutputModeToggle: () => cy.getByTestId('ai-output-mode-select'), nodeOutputHint: () => cy.getByTestId('ndv-output-run-node-hint'), savePinnedDataButton: () => this.getters.runDataPaneHeader().find('button').filter(':visible').contains('Save'), diff --git a/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts b/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts index 492284b190c78..6ae5a8807539b 100644 --- a/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts +++ b/packages/@n8n/nodes-langchain/nodes/tools/ToolCode/ToolCode.node.ts @@ -6,6 +6,7 @@ import type { SupplyData, ExecutionError, } from 'n8n-workflow'; + import { NodeConnectionType, NodeOperationError } from 'n8n-workflow'; import type { Sandbox } from 'n8n-nodes-base/dist/nodes/Code/Sandbox'; import { getSandboxContext } from 'n8n-nodes-base/dist/nodes/Code/Sandbox'; @@ -208,7 +209,7 @@ export class ToolCode implements INodeType { try { response = await runFunction(query); } catch (error: unknown) { - executionError = error as ExecutionError; + executionError = new NodeOperationError(this.getNode(), error as ExecutionError); response = `There was an error: "${executionError.message}"`; } @@ -229,6 +230,7 @@ export class ToolCode implements INodeType { } else { void this.addOutputData(NodeConnectionType.AiTool, index, [[{ json: { response } }]]); } + return response; }, }), diff --git a/packages/editor-ui/src/components/Error/NodeErrorView.vue b/packages/editor-ui/src/components/Error/NodeErrorView.vue index 5244371d0fc47..a0d6396df1416 100644 --- a/packages/editor-ui/src/components/Error/NodeErrorView.vue +++ b/packages/editor-ui/src/components/Error/NodeErrorView.vue @@ -21,6 +21,7 @@ import type { BaseTextKey } from '@/plugins/i18n'; type Props = { error: NodeError | NodeApiError | NodeOperationError; + compact?: boolean; }; const props = defineProps(); @@ -377,7 +378,7 @@ function copySuccess() { > -
+

{{ i18n.baseText('nodeErrorView.details.title') }} diff --git a/packages/editor-ui/src/components/OutputPanel.vue b/packages/editor-ui/src/components/OutputPanel.vue index 6d76c34295978..af1a7801f6652 100644 --- a/packages/editor-ui/src/components/OutputPanel.vue +++ b/packages/editor-ui/src/components/OutputPanel.vue @@ -28,6 +28,7 @@

+