Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the "Elastic License
* 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
* Public License v 1"; you may not use this file except in compliance with, at
* your election, the "Elastic License 2.0", the "GNU Affero General Public
* License v3.0 only", or the "Server Side Public License, v 1".
*/

/**
* Regression tests for the lazy-loading boundary in schema.ts (see #264175).
*
* schema.ts is the sole consumer of connector_action_schema.ts. It defers the
* require() so the heavy stack_connectors_schema/* and @kbn/connector-specs
* modules are not loaded at Kibana startup. These tests guard that invariant.
*/

const SEP = __dirname.includes('\\') ? '\\' : '/';
const CONNECTOR_ACTION_SCHEMA_PATH = require.resolve('./connector_action_schema');
const STACK_CONNECTOR_SCHEMA_DIR = `${__dirname}${SEP}stack_connectors_schema`;
const SCHEMA_PATH = require.resolve('./schema');

const CONNECTOR_SPECS_RESOLVED_PATH: string = require.resolve('@kbn/connector-specs');
const CONNECTOR_SPECS_DIR = CONNECTOR_SPECS_RESOLVED_PATH.slice(
0,
CONNECTOR_SPECS_RESOLVED_PATH.lastIndexOf(SEP)
);

const isHeavyModule = (p: string) =>
p === CONNECTOR_ACTION_SCHEMA_PATH ||
p.startsWith(STACK_CONNECTOR_SCHEMA_DIR + SEP) ||
p.startsWith(CONNECTOR_SPECS_DIR);

const getLoadedHeavyModules = () => Object.keys(require.cache).filter(isHeavyModule);

describe('schema.ts lazy-loading boundary', () => {
beforeEach(() => {
jest.resetModules();
for (const modulePath of Object.keys(require.cache)) {
if (modulePath === SCHEMA_PATH || isHeavyModule(modulePath)) {
delete require.cache[modulePath];
}
}
});

it('does not load connector_action_schema or its transitive deps when schema.ts is imported', () => {
require('./schema');
expect(getLoadedHeavyModules()).toEqual([]);
});

it('loads connector_action_schema after a consumer function triggers the boundary', () => {
// eslint-disable-next-line @typescript-eslint/no-var-requires
const { getAllConnectors } = require('./schema') as typeof import('./schema');
expect(getLoadedHeavyModules()).toEqual([]);

getAllConnectors();

expect(getLoadedHeavyModules().length).toBeGreaterThan(0);
});
});
43 changes: 24 additions & 19 deletions src/platform/plugins/shared/workflows_management/common/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,22 +25,28 @@ import {
} from '@kbn/workflows';
import { z } from '@kbn/zod/v4';

// Import connector schemas from the organized structure
import {
ConnectorActionInputSchemas,
ConnectorActionOutputSchemas,
ConnectorInputSchemas,
ConnectorOutputSchemas,
ConnectorSpecsInputSchemas,
staticConnectors,
} from './connector_action_schema';
// Import the singleton instance of StepSchemas
import { stepSchemas } from './step_schemas';

// Defers ~16 MB of zod-schema heap until the first workflow edit/execute call.
// connector_action_schema.ts eagerly builds Maps of Zod schemas from
// stack_connectors_schema/* and @kbn/connector-specs; keeping it behind a
// lazy require() avoids that cost at Kibana startup. See #264175.
let _connectorSchemas: typeof import('./connector_action_schema') | null = null;
function getConnectorSchemas(): typeof import('./connector_action_schema') {
if (_connectorSchemas === null) {
_connectorSchemas = require('./connector_action_schema');
}
return _connectorSchemas as typeof import('./connector_action_schema');
}

/**
* Get parameter schema for a specific sub-action
*/
function getSubActionParamsSchema(actionTypeId: string, subActionName: string): z.ZodSchema {
const { ConnectorInputSchemas, ConnectorActionInputSchemas, ConnectorSpecsInputSchemas } =
getConnectorSchemas();

const schema = ConnectorInputSchemas.get(actionTypeId);
if (schema) {
return schema;
Expand Down Expand Up @@ -70,6 +76,8 @@ function getSubActionParamsSchema(actionTypeId: string, subActionName: string):
* Get output schema for a specific sub-action
*/
function getSubActionOutputSchema(actionTypeId: string, subActionName: string): z.ZodSchema {
const { ConnectorOutputSchemas, ConnectorActionOutputSchemas } = getConnectorSchemas();

const schema = ConnectorOutputSchemas.get(actionTypeId);
if (schema) {
return schema;
Expand Down Expand Up @@ -200,14 +208,11 @@ function convertDynamicConnectorsToContractsInternal(
export type WorkflowZodSchemaType = z.infer<ReturnType<typeof getWorkflowZodSchema>>;
export type WorkflowZodSchemaLooseType = z.infer<ReturnType<typeof getWorkflowZodSchemaLoose>>;

// Legacy exports for backward compatibility - these will be deprecated
// TODO: Remove these once all consumers are updated to use the lazy-loaded versions
export const WORKFLOW_ZOD_SCHEMA = generateYamlSchemaFromConnectors(staticConnectors);
export const WORKFLOW_ZOD_SCHEMA_LOOSE = generateYamlSchemaFromConnectors(
staticConnectors,
[],
true
);
// NOTE: The former `WORKFLOW_ZOD_SCHEMA` / `WORKFLOW_ZOD_SCHEMA_LOOSE`
// module-level constants were removed in favour of `getWorkflowZodSchema()` /
// `getWorkflowZodSchemaLoose()`. They were unreferenced and their eager
// `generateYamlSchemaFromConnectors(...)` calls were a significant contributor
// to the startup heap described in https://github.com/elastic/kibana/issues/264175.

/**
* Combine static connectors with dynamic Elasticsearch and Kibana connectors
Expand All @@ -227,7 +232,7 @@ export function getAllConnectorsInternal(): ConnectorContractUnion[] {
const elasticsearchConnectors = getElasticsearchConnectors();
const kibanaConnectors = getKibanaConnectors();
const allConnectors = [
...staticConnectors,
...getConnectorSchemas().staticConnectors,
...elasticsearchConnectors,
...kibanaConnectors,
...registeredStepDefinitions,
Expand Down Expand Up @@ -318,7 +323,7 @@ export function addDynamicConnectorsToCache(
const elasticsearchConnectors = getElasticsearchConnectors();
const kibanaConnectors = getKibanaConnectors();
const baseConnectors = [
...staticConnectors,
...getConnectorSchemas().staticConnectors,
...elasticsearchConnectors,
...kibanaConnectors,
...registeredStepDefinitions,
Expand Down
Loading