From be2ee6f35661f4e052cc600047594a153a71bdc3 Mon Sep 17 00:00:00 2001 From: "n8n-cat-bot[bot]" <283985454+n8n-cat-bot[bot]@users.noreply.github.com> Date: Mon, 5 Oct 2026 04:50:04 +0000 Subject: [PATCH] fix: Replace deprecated APIs in services and tooling (#40199) Co-authored-by: n8n-cat-bot[bot] --- .../__tests__/support/otel-test-provider.ts | 8 +- packages/@n8n/errors/src/index.ts | 4 +- .../src/sensitivity/html-probe.test.ts | 5 +- .../@n8n/node-cli/src/configs/eslint.test.ts | 5 +- packages/@n8n/node-cli/src/configs/eslint.ts | 16 ++- packages/cli/package.json | 1 + packages/cli/src/config/types.ts | 128 ++---------------- .../__tests__/otel.service.provider.test.ts | 41 +++--- .../otel/__tests__/otel.service.test.ts | 11 +- packages/cli/src/modules/otel/otel.service.ts | 40 +++--- .../__tests__/saml.service.ee.test.ts | 25 +++- .../src/modules/sso-saml/saml.service.ee.ts | 6 +- .../utils/scheduling-helper-functions.ts | 21 +-- .../scheduled-task-manager.ts | 5 +- packages/workflow/src/interfaces.ts | 3 +- pnpm-lock.yaml | 3 + 16 files changed, 118 insertions(+), 204 deletions(-) diff --git a/packages/@n8n/agents/src/runtime/__tests__/support/otel-test-provider.ts b/packages/@n8n/agents/src/runtime/__tests__/support/otel-test-provider.ts index cd7c7740566..3db18d8e15d 100644 --- a/packages/@n8n/agents/src/runtime/__tests__/support/otel-test-provider.ts +++ b/packages/@n8n/agents/src/runtime/__tests__/support/otel-test-provider.ts @@ -1,5 +1,5 @@ import { context, trace } from '@opentelemetry/api'; -import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; import { BasicTracerProvider, InMemorySpanExporter, @@ -24,8 +24,7 @@ export class OtelTestProvider { private constructor( private readonly provider: BasicTracerProvider, private readonly exporter: InMemorySpanExporter, - // oxlint-disable-next-line typescript/no-deprecated - private readonly contextManager: AsyncHooksContextManager, + private readonly contextManager: AsyncLocalStorageContextManager, ) {} static create(): OtelTestProvider { @@ -34,8 +33,7 @@ export class OtelTestProvider { spanProcessors: [new SimpleSpanProcessor(exporter)], }); trace.setGlobalTracerProvider(provider); - // oxlint-disable-next-line typescript/no-deprecated - const contextManager = new AsyncHooksContextManager().enable(); + const contextManager = new AsyncLocalStorageContextManager().enable(); context.setGlobalContextManager(contextManager); return new OtelTestProvider(provider, exporter, contextManager); } diff --git a/packages/@n8n/errors/src/index.ts b/packages/@n8n/errors/src/index.ts index aca6d5ed9f7..7d03d037b50 100644 --- a/packages/@n8n/errors/src/index.ts +++ b/packages/@n8n/errors/src/index.ts @@ -1,5 +1,5 @@ -// oxlint-disable-next-line typescript/no-deprecated -export { ApplicationError } from './application.error'; +// Keep this export for community nodes that still import ApplicationError. +export * from './application.error'; export { BaseError, type BaseErrorOptions } from './base.error'; export { IsolateError } from './isolate.error'; export { OperationalError, type OperationalErrorOptions } from './operational.error'; diff --git a/packages/@n8n/mcp-browser/src/sensitivity/html-probe.test.ts b/packages/@n8n/mcp-browser/src/sensitivity/html-probe.test.ts index bec060f8422..51c72da2271 100644 --- a/packages/@n8n/mcp-browser/src/sensitivity/html-probe.test.ts +++ b/packages/@n8n/mcp-browser/src/sensitivity/html-probe.test.ts @@ -16,10 +16,7 @@ function runProbeWithFrame(html: string, frameHtml: string) { url: 'http://test.local/page', }); const frame = dom.window.document.querySelector('iframe'); - frame?.contentDocument?.open(); - // oxlint-disable-next-line typescript/no-deprecated - frame?.contentDocument?.write(frameHtml); - frame?.contentDocument?.close(); + if (frame?.contentDocument?.body) frame.contentDocument.body.innerHTML = frameHtml; return dom.window.eval(HTML_PROBE_SCRIPT); } diff --git a/packages/@n8n/node-cli/src/configs/eslint.test.ts b/packages/@n8n/node-cli/src/configs/eslint.test.ts index 09557599977..724cd1fe61e 100644 --- a/packages/@n8n/node-cli/src/configs/eslint.test.ts +++ b/packages/@n8n/node-cli/src/configs/eslint.test.ts @@ -17,10 +17,7 @@ async function lintFile(filePath: string, eslintConfig: typeof config) { const eslint = new ESLint({ cwd: path.dirname(filePath), overrideConfigFile: true, - // `tseslint.config()` returns the typescript-eslint `ConfigArray`, which - // is structurally a `Linter.Config[]` but nominally a different type due - // to duplicated `Parser` definitions across packages. - overrideConfig: eslintConfig as unknown as ESLint.Options['overrideConfig'], + overrideConfig: eslintConfig, }); const [result] = await eslint.lintFiles([filePath]); return result; diff --git a/packages/@n8n/node-cli/src/configs/eslint.ts b/packages/@n8n/node-cli/src/configs/eslint.ts index 00f3f4603fa..46a6678efea 100644 --- a/packages/@n8n/node-cli/src/configs/eslint.ts +++ b/packages/@n8n/node-cli/src/configs/eslint.ts @@ -1,18 +1,20 @@ import eslint from '@eslint/js'; import { n8nCommunityNodesPlugin } from '@n8n/eslint-plugin-community-nodes'; -import { globalIgnores } from 'eslint/config'; +import type { Linter } from 'eslint'; +import { defineConfig, globalIgnores } from 'eslint/config'; import { createTypeScriptImportResolver } from 'eslint-import-resolver-typescript'; import importPlugin from 'eslint-plugin-import-x'; import n8nNodesPlugin from 'eslint-plugin-n8n-nodes-base'; -import tseslint, { type ConfigArray } from 'typescript-eslint'; +import tseslint from 'typescript-eslint'; + +type ConfigArray = ReturnType; function createConfig(supportCloud = true): ConfigArray { const communityNodesRecommended = supportCloud ? n8nCommunityNodesPlugin.configs.recommended : n8nCommunityNodesPlugin.configs.recommendedWithoutN8nCloudSupport; - // oxlint-disable-next-line typescript/no-deprecated - We're moving away from eslint - return tseslint.config( + return defineConfig( globalIgnores(['dist']), { files: ['**/*.ts'], @@ -20,7 +22,8 @@ function createConfig(supportCloud = true): ConfigArray { eslint.configs.recommended, tseslint.configs.recommended, communityNodesRecommended, - importPlugin.configs['flat/recommended'], + // import-x uses typescript-eslint's ESLint types for its preset. + importPlugin.configs['flat/recommended'] as Linter.Config, ], rules: { 'prefer-spread': 'off', @@ -44,7 +47,8 @@ function createConfig(supportCloud = true): ConfigArray { ...n8nNodesPlugin.configs.community.rules, }, languageOptions: { - parser: tseslint.parser, + // typescript-eslint bundles a parser type that differs from ESLint's parser type. + parser: tseslint.parser as Linter.Parser, parserOptions: { extraFileExtensions: ['.json'], }, diff --git a/packages/cli/package.json b/packages/cli/package.json index e703e2d11a8..957e513ac82 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -188,6 +188,7 @@ "@n8n_io/ai-assistant-sdk": "catalog:", "@n8n_io/license-sdk": "3.1.0", "@opentelemetry/api": "catalog:", + "@opentelemetry/context-async-hooks": "^2.7.1", "@opentelemetry/core": "2.10.0", "@opentelemetry/exporter-trace-otlp-grpc": "^0.221.0", "@opentelemetry/exporter-trace-otlp-proto": "^0.221.0", diff --git a/packages/cli/src/config/types.ts b/packages/cli/src/config/types.ts index 5702e5ad64c..e960fd6e919 100644 --- a/packages/cli/src/config/types.ts +++ b/packages/cli/src/config/types.ts @@ -1,133 +1,21 @@ import type { RedisOptions } from 'ioredis'; import type { IProcessedDataConfig } from 'n8n-workflow'; -import type { schema } from './schema'; - -// ----------------------------------- -// transformers -// ----------------------------------- - -/** - * Transform an object (convict schema) into a union of string arrays (path segments), - * one for every valid path in the schema object, filtered by type. - * - * ```ts - * ["port", "default"] | ["queue", "bull", "redis", "port", "default"] | ["queue", "bull", "redis", "db", "default"] | ["queue", "bull", "redis", "timeoutThreshold", "default"] | etc - * ``` - */ -type GetPathSegments = Traversable extends Filter - ? [] - : { - [K in ValidKeys]: [K, ...GetPathSegments]; - }[ValidKeys]; - -/** - * Transform a union of string arrays (path segments) into a union of strings (dotted paths). - * - * ```ts - * "port" | "queue.bull.redis.port" | "queue.bull.redis.db" | "queue.bull.redis.timeoutThreshold" | etc - * ``` - */ -type JoinByDotting = T extends [infer F] - ? F - : T extends [infer F, ...infer R] - ? F extends string - ? R extends string[] - ? `${F}.${JoinByDotting}` - : never - : never - : string; - -type ToDottedPath = JoinByDotting>; - -// oxlint-disable-next-line typescript/no-deprecated -type CollectPathsByType = ToDottedPath>; - -// ----------------------------------- -// path-to-return-type mapper -// ----------------------------------- - -type NumericPath = CollectPathsByType; - -type BooleanPath = CollectPathsByType; - -type StringLiteralArrayPath = CollectPathsByType; - -type StringPath = CollectPathsByType; - -type ConfigOptionPath = - | NumericPath - | BooleanPath - | StringPath - | StringLiteralArrayPath - | keyof ExceptionPaths; - -type ToReturnType = T extends NumericPath - ? number - : T extends BooleanPath - ? boolean - : T extends StringLiteralArrayPath - ? StringLiteralMap[T] - : T extends keyof ExceptionPaths - ? ExceptionPaths[T] - : T extends StringPath - ? string - : unknown; - -type ExceptionPaths = { +// Keep the typed paths that the legacy convict config and its external consumers use. +type LegacyConfigPaths = { + 'userManagement.isInstanceOwnerSetUp': boolean; + 'userManagement.authenticationMethod': 'email' | 'ldap' | 'saml'; + 'endpoints.rest': string; + 'ai.enabled': boolean; + 'ai.allowSendingParameterValues': boolean; 'queue.bull.redis': RedisOptions; processedDataManager: IProcessedDataConfig; 'ui.banners.dismissed': string[] | undefined; easyAIWorkflowOnboarded: boolean | undefined; }; -// ----------------------------------- -// string literals map -// ----------------------------------- - -type GetPathSegmentsWithUnions = T extends ReadonlyArray - ? [C] - : { - [K in ValidKeys]: [K, ...GetPathSegmentsWithUnions]; - }[ValidKeys]; - -type ToPathUnionPair = T extends [...infer Path, infer Union] - ? Path extends string[] - ? { path: ToDottedPath; union: Union } - : never - : never; - -type ToStringLiteralMap = { - [Path in T['path']]: Extract['union']; -}; - -type StringLiteralMap = ToStringLiteralMap< - // oxlint-disable-next-line typescript/no-deprecated - ToPathUnionPair> ->; - -// ----------------------------------- -// utils -// ----------------------------------- - -type ValidKeys = keyof T extends string - ? keyof T extends keyof NumberConstructor - ? never - : keyof T - : never; - -type RemoveExcess = T extends [...infer Path, 'format' | 'default'] - ? Path extends string[] - ? Path - : never - : never; - -// ----------------------------------- -// module augmentation -// ----------------------------------- - declare module 'convict' { interface Config { - getEnv(path: Path): ToReturnType; + getEnv(path: Path): LegacyConfigPaths[Path]; } } diff --git a/packages/cli/src/modules/otel/__tests__/otel.service.provider.test.ts b/packages/cli/src/modules/otel/__tests__/otel.service.provider.test.ts index 1dc69c76764..09273e1d10c 100644 --- a/packages/cli/src/modules/otel/__tests__/otel.service.provider.test.ts +++ b/packages/cli/src/modules/otel/__tests__/otel.service.provider.test.ts @@ -1,15 +1,8 @@ import type { Logger } from '@n8n/backend-common'; import type { OutboundHttp } from '@n8n/backend-network'; import type { Context } from '@opentelemetry/api'; -import { - context, - createContextKey, - propagation, - ProxyTracerProvider, - ROOT_CONTEXT, - trace, -} from '@opentelemetry/api'; -import type { ReadableSpan } from '@opentelemetry/sdk-trace-base'; +import { context, createContextKey, propagation, ROOT_CONTEXT, trace } from '@opentelemetry/api'; +import { AlwaysOffSampler, type ReadableSpan } from '@opentelemetry/sdk-trace-base'; import { InMemorySpanExporter, NodeTracerProvider, @@ -73,13 +66,6 @@ function activeContextInside(ctx: Context): Context { return context.with(ctx, () => context.active()); } -function globalTracerProviderDelegate() { - const globalProvider = trace.getTracerProvider(); - return globalProvider instanceof ProxyTracerProvider - ? globalProvider.getDelegate() - : globalProvider; -} - function registerForeignProvider() { const exporter = new InMemorySpanExporter(); const provider = new NodeTracerProvider({ spanProcessors: [new SimpleSpanProcessor(exporter)] }); @@ -155,8 +141,29 @@ describe('OtelService tracer provider', () => { expect(foreign?.exporter.getFinishedSpans().map((span) => span.name)).toEqual([ 'GET /webhook', ]); - expect(globalTracerProviderDelegate()).toBe(foreign?.provider); expect(activeContextInside(markedContext)).toBe(markedContext); + + await service.shutdown(); + expect(service.getTracer('n8n-workflow').startSpan('workflow.execute').isRecording()).toBe( + false, + ); + expect(trace.getTracer('foreign').startSpan('another request').isRecording()).toBe(true); + }); + + it('does not replace a foreign provider that samples no spans', async () => { + await foreign?.provider.shutdown(); + trace.disable(); + const provider = new NodeTracerProvider({ sampler: new AlwaysOffSampler() }); + provider.register(); + foreign = { provider, exporter: new InMemorySpanExporter() }; + + await service.init(); + await service.restart(); + + expect(trace.getTracer('foreign').startSpan('test').isRecording()).toBe(false); + service.getTracer('n8n-workflow').startSpan('workflow.execute').end(); + expect(exportedSpanNames()).toEqual(['workflow.execute']); + expect(logger.info).toHaveBeenCalledTimes(1); }); it('logs the foreign owner once for the lifetime of the service', async () => { diff --git a/packages/cli/src/modules/otel/__tests__/otel.service.test.ts b/packages/cli/src/modules/otel/__tests__/otel.service.test.ts index b0aba4bf94c..2ee5616eeba 100644 --- a/packages/cli/src/modules/otel/__tests__/otel.service.test.ts +++ b/packages/cli/src/modules/otel/__tests__/otel.service.test.ts @@ -1,6 +1,6 @@ import type { Logger } from '@n8n/backend-common'; import type { OutboundHttp } from '@n8n/backend-network'; -import { diag } from '@opentelemetry/api'; +import { diag, trace } from '@opentelemetry/api'; import { OTLPTraceExporter as OTLPGrpcTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; import { BasicTracerProvider } from '@opentelemetry/sdk-trace-base'; @@ -13,7 +13,6 @@ import type { OtelConfig } from '../otel.config'; import { ATTR, OTEL_TEST_SPAN_NAME } from '../otel.constants'; import { OtelService } from '../otel.service'; -const register = vi.fn(); const shutdown = vi.fn(); const providerGetTracer = vi.fn(); @@ -115,6 +114,7 @@ vi.mock('@grpc/grpc-js', () => ({ })); vi.mock('@opentelemetry/sdk-trace-base', () => ({ + AlwaysOffSampler: vi.fn(), BatchSpanProcessor: vi.fn(), BasicTracerProvider: vi.fn().mockImplementation(function (config: { spanProcessors?: unknown[]; @@ -123,7 +123,7 @@ vi.mock('@opentelemetry/sdk-trace-base', () => ({ mockSpanEnd.mockImplementation(() => { for (const processor of processors) processor.onEnd({ name: 'n8n.test_trace' }); }); - mockStartSpan.mockReturnValue({ end: mockSpanEnd }); + mockStartSpan.mockReturnValue({ end: mockSpanEnd, isRecording: () => false }); mockGetTracer.mockReturnValue({ startSpan: mockStartSpan }); return { getTracer: mockGetTracer, shutdown: mockProviderShutdown }; }), @@ -142,7 +142,7 @@ vi.mock('@opentelemetry/sdk-trace-node', () => ({ return {}; }), NodeTracerProvider: vi.fn().mockImplementation(function () { - return { register, shutdown, getTracer: providerGetTracer }; + return { shutdown, getTracer: providerGetTracer }; }), })); @@ -196,6 +196,7 @@ describe('OtelService', () => { ); service = new OtelService(otelSettingsService, instanceSettings, logger, outboundHttp); }); + afterEach(() => vi.restoreAllMocks()); describe('init', () => { it('does not start a tracer provider when enabled is false', async () => { @@ -484,7 +485,7 @@ describe('OtelService', () => { it('shuts the provider down when registering it with the global API throws', async () => { otelSettingsService.loadSettings.mockResolvedValue(enabledSettings); - register.mockImplementationOnce(() => { + vi.spyOn(trace, 'setGlobalTracerProvider').mockImplementationOnce(() => { throw new Error('provider registration failed'); }); diff --git a/packages/cli/src/modules/otel/otel.service.ts b/packages/cli/src/modules/otel/otel.service.ts index e38197e77e6..22660eaa0d1 100644 --- a/packages/cli/src/modules/otel/otel.service.ts +++ b/packages/cli/src/modules/otel/otel.service.ts @@ -2,8 +2,14 @@ import type { Metadata } from '@grpc/grpc-js'; import { Logger } from '@n8n/backend-common'; import { OutboundHttp } from '@n8n/backend-network'; import { Service } from '@n8n/di'; -import type { DiagLogger, Tracer, TracerProvider } from '@opentelemetry/api'; -import { DiagLogLevel, ProxyTracerProvider, diag, trace } from '@opentelemetry/api'; +import type { DiagLogger, Tracer } from '@opentelemetry/api'; +import { DiagLogLevel, context, diag, propagation, trace } from '@opentelemetry/api'; +import { AsyncLocalStorageContextManager } from '@opentelemetry/context-async-hooks'; +import { + CompositePropagator, + W3CBaggagePropagator, + W3CTraceContextPropagator, +} from '@opentelemetry/core'; import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto'; import { detectResources, @@ -13,6 +19,7 @@ import { resourceFromAttributes, } from '@opentelemetry/resources'; import { + AlwaysOffSampler, BasicTracerProvider, BatchSpanProcessor, type ReadableSpan, @@ -35,17 +42,7 @@ export type OtelTestTraceResult = { success: true } | { success: false; error: s const stripEmptyResolutionNote = (message: string) => message.replace(/\s*Resolution note:\s*$/, ''); -// Deprecated in `@opentelemetry/api` 1.x, but the only public source of a no-op tracer provider. -// oxlint-disable-next-line typescript/no-deprecated -const noopTracerProvider = new ProxyTracerProvider(); - -function registeredGlobalTracerProvider(): TracerProvider | undefined { - const globalProvider = trace.getTracerProvider(); - const delegate = - // oxlint-disable-next-line typescript/no-deprecated - globalProvider instanceof ProxyTracerProvider ? globalProvider.getDelegate() : globalProvider; - return delegate === noopTracerProvider.getDelegate() ? undefined : delegate; -} +let noopTracerProvider: BasicTracerProvider | undefined; @Service() export class OtelService { @@ -73,7 +70,9 @@ export class OtelService { } getTracer(name: string): Tracer { - return (this.provider ?? noopTracerProvider).getTracer(name); + if (this.provider) return this.provider.getTracer(name); + noopTracerProvider ??= new BasicTracerProvider({ sampler: new AlwaysOffSampler() }); + return noopTracerProvider.getTracer(name); } /** @@ -191,13 +190,20 @@ export class OtelService { return; } - if (registeredGlobalTracerProvider()) { + // Claim the global API before installing context and propagation defaults. + // A failed claim leaves another library's global registrations in place. + this.ownsGlobalApi = trace.setGlobalTracerProvider(provider); + if (!this.ownsGlobalApi) { this.logForeignGlobalApiOwner(); return; } - provider.register(); - this.ownsGlobalApi = registeredGlobalTracerProvider() === provider; + context.setGlobalContextManager(new AsyncLocalStorageContextManager().enable()); + propagation.setGlobalPropagator( + new CompositePropagator({ + propagators: [new W3CTraceContextPropagator(), new W3CBaggagePropagator()], + }), + ); } private logForeignGlobalApiOwner(): void { diff --git a/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts b/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts index cc2a0a1a4a5..d2bb9837b38 100644 --- a/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts +++ b/packages/cli/src/modules/sso-saml/__tests__/saml.service.ee.test.ts @@ -11,6 +11,7 @@ import { mock, type MockProxy } from 'vitest-mock-extended'; import type { Cipher, InstanceSettings } from 'n8n-core'; import { CREDENTIAL_BLANKING_VALUE } from 'n8n-workflow'; import type { IdentityProviderInstance, ServiceProviderInstance } from 'samlify'; +import type { BindingContext } from 'samlify/types/src/entity'; import { BadRequestError, ForbiddenError } from '@n8n/errors'; import type { ProvisioningService } from '@/modules/provisioning.ee/provisioning.service.ee'; @@ -167,6 +168,7 @@ describe('SamlService', () => { let cipher: Cipher; let cacheService: Mocked; let outboundHttp: Mocked; + let urlService: MockProxy; let httpRequest: Mock; const validator = new SamlValidator(mock()); const logger = mockLogger(); @@ -226,6 +228,7 @@ describe('SamlService', () => { httpRequest = vi.fn(); outboundHttp = mock(); outboundHttp.requests.mockReturnValue(mock({ request: httpRequest })); + urlService = mock({ getInstanceBaseUrl: () => 'http://localhost:5678' }); vi.spyOn(ssoHelpers, 'reloadAuthenticationMethod').mockImplementation( async () => await Promise.resolve(), @@ -234,7 +237,7 @@ describe('SamlService', () => { samlService = new SamlService( logger, - mock(), + urlService, validator, userRepository, settingsRepository, @@ -282,6 +285,26 @@ describe('SamlService', () => { }); }); + describe('getLoginRequestUrl', () => { + it('passes each relay state with its request and uses the instance URL by default', async () => { + const idp = mock(); + const sp = mock(); + sp.createLoginRequest.mockReturnValue(mock()); + vi.spyOn(samlService, 'getIdentityProviderInstance').mockReturnValue(idp); + vi.spyOn(samlService, 'getServiceProviderInstance').mockReturnValue(sp); + + await samlService.getLoginRequestUrl('http://localhost:5678/first', 'redirect'); + await samlService.getLoginRequestUrl(undefined, 'post'); + + expect(sp.createLoginRequest).toHaveBeenNthCalledWith(1, idp, 'redirect', { + relayState: 'http://localhost:5678/first', + }); + expect(sp.createLoginRequest).toHaveBeenNthCalledWith(2, idp, 'post', { + relayState: 'http://localhost:5678', + }); + }); + }); + describe('getAttributesFromLoginResponse', () => { test('throws when any attribute is missing', async () => { // ARRANGE diff --git a/packages/cli/src/modules/sso-saml/saml.service.ee.ts b/packages/cli/src/modules/sso-saml/saml.service.ee.ts index 4700585b6ac..4b3ae3636b0 100644 --- a/packages/cli/src/modules/sso-saml/saml.service.ee.ts +++ b/packages/cli/src/modules/sso-saml/saml.service.ee.ts @@ -308,9 +308,9 @@ export class SamlService { binding ??= this._samlPreferences.loginBinding ?? 'redirect'; const sp = this.getServiceProviderInstance(); - // oxlint-disable-next-line typescript/no-deprecated - sp.entitySetting.relayState = relayState ?? this.urlService.getInstanceBaseUrl(); - const loginRequest = sp.createLoginRequest(idp, binding); + const loginRequest = sp.createLoginRequest(idp, binding, { + relayState: relayState ?? this.urlService.getInstanceBaseUrl(), + }); return { binding, context: binding === 'post' ? loginRequest : loginRequest, diff --git a/packages/core/src/execution-engine/node-execution-context/utils/scheduling-helper-functions.ts b/packages/core/src/execution-engine/node-execution-context/utils/scheduling-helper-functions.ts index 49e042f875d..90661293062 100644 --- a/packages/core/src/execution-engine/node-execution-context/utils/scheduling-helper-functions.ts +++ b/packages/core/src/execution-engine/node-execution-context/utils/scheduling-helper-functions.ts @@ -1,5 +1,5 @@ import { Container } from '@n8n/di'; -import type { SchedulingFunctions, Workflow, CronContext, Cron } from 'n8n-workflow'; +import type { SchedulingFunctions, Workflow, Cron } from 'n8n-workflow'; import { ScheduledTaskManager } from '../../scheduled-task-manager'; @@ -13,22 +13,13 @@ export const getSchedulingFunctions = ( const scheduledTaskManager = Container.get(ScheduledTaskManager); return { registerCron: ({ expression, recurrence }: Cron, onTick: (scheduledT: Date) => void) => { - // oxlint-disable-next-line typescript/no-deprecated - const ctx: CronContext = { - expression, - recurrence, - nodeId, - workflowId, - timezone, - }; - return scheduledTaskManager.register( { - group: { type: WORKFLOW_SCHEDULE_GROUP_TYPE, id: ctx.workflowId }, - targetId: ctx.nodeId, - timezone: ctx.timezone, - expression: ctx.expression, - recurrence: ctx.recurrence, + group: { type: WORKFLOW_SCHEDULE_GROUP_TYPE, id: workflowId }, + targetId: nodeId, + timezone, + expression, + recurrence, }, onTick, ); diff --git a/packages/core/src/execution-engine/scheduled-task-manager.ts b/packages/core/src/execution-engine/scheduled-task-manager.ts index 5ea38f24951..e85ba5f91ba 100644 --- a/packages/core/src/execution-engine/scheduled-task-manager.ts +++ b/packages/core/src/execution-engine/scheduled-task-manager.ts @@ -3,7 +3,7 @@ import { CronLoggingConfig } from '@n8n/config'; import { Time } from '@n8n/constants'; import { Service } from '@n8n/di'; import { CronJob, CronTime } from 'cron'; -import type { CronContext } from 'n8n-workflow'; +import type { Cron } from 'n8n-workflow'; import { InstanceSettings } from '@/instance-settings'; @@ -21,8 +21,7 @@ export type ScheduledTaskContext = { targetId: string; timezone: string; expression: string; - // oxlint-disable-next-line typescript/no-deprecated - recurrence?: CronContext['recurrence']; + recurrence?: Cron['recurrence']; }; @Service() diff --git a/packages/workflow/src/interfaces.ts b/packages/workflow/src/interfaces.ts index fc1354699ec..786e167fd2e 100644 --- a/packages/workflow/src/interfaces.ts +++ b/packages/workflow/src/interfaces.ts @@ -1129,8 +1129,7 @@ type CronRecurrenceRule = /** * @deprecated Remnant of the legacy in-memory scheduling path. `registerCron` * takes {@link Cron}, not this type; the durable scheduler path never uses it. - * Only `ScheduledTaskManager` and its helper still reference it (and only for - * `CronContext['recurrence']`). Slated to go away with `ScheduledTaskManager`. + * Legacy test helpers still use it. Use {@link Cron} for new scheduling code. */ export type CronContext = { nodeId: string; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c9b7deaf1ac..78d4b6abca8 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -4758,6 +4758,9 @@ importers: '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 + '@opentelemetry/context-async-hooks': + specifier: ^2.7.1 + version: 2.10.0(@opentelemetry/api@1.9.1) '@opentelemetry/core': specifier: 2.10.0 version: 2.10.0(@opentelemetry/api@1.9.1)