diff --git a/oas_docs/bundle.serverless.json b/oas_docs/bundle.serverless.json new file mode 100644 index 0000000000000..1dfcdd50639b3 --- /dev/null +++ b/oas_docs/bundle.serverless.json @@ -0,0 +1,538 @@ +{ + "components": { + "schemas": { + "core_status_redactedResponse": { + "additionalProperties": false, + "description": "A minimal representation of Kibana's operational status.", + "properties": { + "status": { + "additionalProperties": false, + "properties": { + "overall": { + "additionalProperties": false, + "properties": { + "level": { + "anyOf": [ + { + "enum": [ + "available" + ], + "type": "string" + }, + { + "enum": [ + "degraded" + ], + "type": "string" + }, + { + "enum": [ + "unavailable" + ], + "type": "string" + }, + { + "enum": [ + "critical" + ], + "type": "string" + } + ], + "description": "Service status levels as human and machine readable values." + } + }, + "required": [ + "level" + ], + "type": "object" + } + }, + "required": [ + "overall" + ], + "type": "object" + } + }, + "required": [ + "status" + ], + "type": "object" + }, + "core_status_response": { + "additionalProperties": false, + "description": "Kibana's operational status as well as a detailed breakdown of plugin statuses indication of various loads (like event loop utilization and network traffic) at time of request.", + "properties": { + "metrics": { + "additionalProperties": false, + "description": "Metric groups collected by Kibana.", + "properties": { + "collection_interval_in_millis": { + "description": "The interval at which metrics should be collected.", + "type": "number" + }, + "elasticsearch_client": { + "additionalProperties": false, + "description": "Current network metrics of Kibana's Elasticsearch client.", + "properties": { + "totalActiveSockets": { + "description": "Count of network sockets currently in use.", + "type": "number" + }, + "totalIdleSockets": { + "description": "Count of network sockets currently idle.", + "type": "number" + }, + "totalQueuedRequests": { + "description": "Count of requests not yet assigned to sockets.", + "type": "number" + } + }, + "required": [ + "totalActiveSockets", + "totalIdleSockets", + "totalQueuedRequests" + ], + "type": "object" + }, + "last_updated": { + "description": "The time metrics were collected.", + "type": "string" + } + }, + "required": [ + "elasticsearch_client", + "last_updated", + "collection_interval_in_millis" + ], + "type": "object" + }, + "name": { + "description": "Kibana instance name.", + "type": "string" + }, + "status": { + "additionalProperties": false, + "properties": { + "core": { + "additionalProperties": false, + "description": "Statuses of core Kibana services.", + "properties": { + "elasticsearch": { + "additionalProperties": false, + "properties": { + "detail": { + "description": "Human readable detail of the service status.", + "type": "string" + }, + "documentationUrl": { + "description": "A URL to further documentation regarding this service.", + "type": "string" + }, + "level": { + "anyOf": [ + { + "enum": [ + "available" + ], + "type": "string" + }, + { + "enum": [ + "degraded" + ], + "type": "string" + }, + { + "enum": [ + "unavailable" + ], + "type": "string" + }, + { + "enum": [ + "critical" + ], + "type": "string" + } + ], + "description": "Service status levels as human and machine readable values." + }, + "meta": { + "additionalProperties": {}, + "description": "An unstructured set of extra metadata about this service.", + "type": "object" + }, + "summary": { + "description": "A human readable summary of the service status.", + "type": "string" + } + }, + "required": [ + "level", + "summary", + "meta" + ], + "type": "object" + }, + "savedObjects": { + "additionalProperties": false, + "properties": { + "detail": { + "description": "Human readable detail of the service status.", + "type": "string" + }, + "documentationUrl": { + "description": "A URL to further documentation regarding this service.", + "type": "string" + }, + "level": { + "anyOf": [ + { + "enum": [ + "available" + ], + "type": "string" + }, + { + "enum": [ + "degraded" + ], + "type": "string" + }, + { + "enum": [ + "unavailable" + ], + "type": "string" + }, + { + "enum": [ + "critical" + ], + "type": "string" + } + ], + "description": "Service status levels as human and machine readable values." + }, + "meta": { + "additionalProperties": {}, + "description": "An unstructured set of extra metadata about this service.", + "type": "object" + }, + "summary": { + "description": "A human readable summary of the service status.", + "type": "string" + } + }, + "required": [ + "level", + "summary", + "meta" + ], + "type": "object" + } + }, + "required": [ + "elasticsearch", + "savedObjects" + ], + "type": "object" + }, + "overall": { + "additionalProperties": false, + "properties": { + "detail": { + "description": "Human readable detail of the service status.", + "type": "string" + }, + "documentationUrl": { + "description": "A URL to further documentation regarding this service.", + "type": "string" + }, + "level": { + "anyOf": [ + { + "enum": [ + "available" + ], + "type": "string" + }, + { + "enum": [ + "degraded" + ], + "type": "string" + }, + { + "enum": [ + "unavailable" + ], + "type": "string" + }, + { + "enum": [ + "critical" + ], + "type": "string" + } + ], + "description": "Service status levels as human and machine readable values." + }, + "meta": { + "additionalProperties": {}, + "description": "An unstructured set of extra metadata about this service.", + "type": "object" + }, + "summary": { + "description": "A human readable summary of the service status.", + "type": "string" + } + }, + "required": [ + "level", + "summary", + "meta" + ], + "type": "object" + }, + "plugins": { + "additionalProperties": { + "additionalProperties": false, + "properties": { + "detail": { + "description": "Human readable detail of the service status.", + "type": "string" + }, + "documentationUrl": { + "description": "A URL to further documentation regarding this service.", + "type": "string" + }, + "level": { + "anyOf": [ + { + "enum": [ + "available" + ], + "type": "string" + }, + { + "enum": [ + "degraded" + ], + "type": "string" + }, + { + "enum": [ + "unavailable" + ], + "type": "string" + }, + { + "enum": [ + "critical" + ], + "type": "string" + } + ], + "description": "Service status levels as human and machine readable values." + }, + "meta": { + "additionalProperties": {}, + "description": "An unstructured set of extra metadata about this service.", + "type": "object" + }, + "summary": { + "description": "A human readable summary of the service status.", + "type": "string" + } + }, + "required": [ + "level", + "summary", + "meta" + ], + "type": "object" + }, + "description": "A dynamic mapping of plugin ID to plugin status.", + "type": "object" + } + }, + "required": [ + "overall", + "core", + "plugins" + ], + "type": "object" + }, + "uuid": { + "description": "Unique, generated Kibana instance UUID. This UUID should persist even if the Kibana process restarts.", + "type": "string" + }, + "version": { + "additionalProperties": false, + "properties": { + "build_date": { + "description": "The date and time of this build.", + "type": "string" + }, + "build_flavor": { + "anyOf": [ + { + "enum": [ + "serverless" + ], + "type": "string" + }, + { + "enum": [ + "traditional" + ], + "type": "string" + } + ], + "description": "The build flavour determines configuration and behavior of Kibana. On premise users will almost always run the \"traditional\" flavour, while other flavours are reserved for Elastic-specific use cases." + }, + "build_hash": { + "description": "A unique hash value representing the git commit of this Kibana build.", + "type": "string" + }, + "build_number": { + "description": "A monotonically increasing number, each subsequent build will have a higher number.", + "type": "number" + }, + "build_snapshot": { + "description": "Whether this build is a snapshot build.", + "type": "boolean" + }, + "number": { + "description": "A semantic version number.", + "type": "string" + } + }, + "required": [ + "number", + "build_hash", + "build_number", + "build_snapshot", + "build_flavor", + "build_date" + ], + "type": "object" + } + }, + "required": [ + "name", + "uuid", + "version", + "status", + "metrics" + ], + "type": "object" + } + }, + "securitySchemes": { + "apiKeyAuth": { + "in": "header", + "name": "Authorization", + "type": "apiKey" + }, + "basicAuth": { + "scheme": "basic", + "type": "http" + } + } + }, + "info": { + "title": "Kibana HTTP APIs", + "version": "0.0.0" + }, + "openapi": "3.0.0", + "paths": { + "/api/status": { + "get": { + "operationId": "/api/status#0", + "parameters": [ + { + "description": "The version of the API to use", + "in": "header", + "name": "elastic-api-version", + "schema": { + "default": "2023-10-31", + "enum": [ + "2023-10-31" + ], + "type": "string" + } + }, + { + "description": "Set to \"true\" to get the response in v7 format.", + "in": "query", + "name": "v7format", + "required": false, + "schema": { + "type": "boolean" + } + }, + { + "description": "Set to \"true\" to get the response in v8 format.", + "in": "query", + "name": "v8format", + "required": false, + "schema": { + "type": "boolean" + } + } + ], + "responses": { + "200": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/core_status_response" + }, + { + "$ref": "#/components/schemas/core_status_redactedResponse" + } + ], + "description": "Kibana's operational status. A minimal response is sent for unauthorized users." + } + } + } + }, + "503": { + "content": { + "application/json; Elastic-Api-Version=2023-10-31": { + "schema": { + "anyOf": [ + { + "$ref": "#/components/schemas/core_status_response" + }, + { + "$ref": "#/components/schemas/core_status_redactedResponse" + } + ], + "description": "Kibana's operational status. A minimal response is sent for unauthorized users." + } + } + } + } + }, + "summary": "Get Kibana's current status", + "tags": [] + } + } + }, + "security": [ + { + "basicAuth": [] + } + ], + "servers": [ + { + "url": "http://localhost:5622" + } + ], + "tags": [] +} \ No newline at end of file diff --git a/packages/core/test-helpers/core-test-helpers-kbn-server/src/create_serverless_root.ts b/packages/core/test-helpers/core-test-helpers-kbn-server/src/create_serverless_root.ts index 03d4c62ea95b9..4dd1486589dbe 100644 --- a/packages/core/test-helpers/core-test-helpers-kbn-server/src/create_serverless_root.ts +++ b/packages/core/test-helpers/core-test-helpers-kbn-server/src/create_serverless_root.ts @@ -38,13 +38,18 @@ const projectType: ServerlessProjectType = 'es'; */ export function createTestServerlessInstances({ adjustTimeout, + kibana = {}, }: { - adjustTimeout: (timeout: number) => void; -}): TestServerlessUtils { + kibana?: { + settings?: {}; + cliArgs?: Partial; + }; + adjustTimeout?: (timeout: number) => void; +} = {}): TestServerlessUtils { adjustTimeout?.(150_000); const esUtils = createServerlessES(); - const kbUtils = createServerlessKibana(); + const kbUtils = createServerlessKibana(kibana.settings, kibana.cliArgs); return { startES: async () => { diff --git a/packages/kbn-capture-oas-snapshot-cli/src/capture_oas_snapshot.ts b/packages/kbn-capture-oas-snapshot-cli/src/capture_oas_snapshot.ts new file mode 100644 index 0000000000000..b157d9a6016ba --- /dev/null +++ b/packages/kbn-capture-oas-snapshot-cli/src/capture_oas_snapshot.ts @@ -0,0 +1,125 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +import fs from 'node:fs/promises'; +import { encode } from 'node:querystring'; +import type { ChildProcess } from 'node:child_process'; +import fetch from 'node-fetch'; +import * as Rx from 'rxjs'; +import { startTSWorker } from '@kbn/dev-utils'; +import { createTestEsCluster } from '@kbn/test'; +import type { ToolingLog } from '@kbn/tooling-log'; +import { createTestServerlessInstances } from '@kbn/core-test-helpers-kbn-server'; +import type { Result } from './kibana_worker'; +import { sortAndPrettyPrint } from './run_capture_oas_snapshot_cli'; +import { buildFlavourEnvArgName } from './common'; + +interface CaptureOasSnapshotArgs { + log: ToolingLog; + buildFlavour: 'serverless' | 'traditional'; + outputFile: string; + update: boolean; + filters?: { + pathStartsWith?: string[]; + excludePathsMatching?: string[]; + }; +} + +const MB = 1024 * 1024; +const twoDeci = (num: number) => Math.round(num * 100) / 100; + +export async function captureOasSnapshot({ + log, + filters = {}, + buildFlavour, + update, + outputFile, +}: CaptureOasSnapshotArgs): Promise { + const { excludePathsMatching = [], pathStartsWith } = filters; + // internal consts + const port = 5622; + // We are only including /api/status for now + excludePathsMatching.push( + '/{path*}', + // Our internal asset paths + '/XXXXXXXXXXXX/' + ); + + let esCluster: undefined | { stop(): Promise }; + let kbWorker: undefined | ChildProcess; + + try { + log.info('Starting es...'); + esCluster = await log.indent(4, async () => { + if (buildFlavour === 'serverless') { + const { startES } = createTestServerlessInstances(); + return await startES(); + } + const cluster = createTestEsCluster({ log }); + await cluster.start(); + return { stop: () => cluster.cleanup() }; + }); + + log.info('Starting Kibana...'); + kbWorker = await log.indent(4, async () => { + log.info('Loading core with all plugins enabled so that we can capture OAS for all...'); + const { msg$, proc } = startTSWorker({ + log, + src: require.resolve('./kibana_worker'), + env: { ...process.env, [buildFlavourEnvArgName]: buildFlavour }, + }); + await Rx.firstValueFrom( + msg$.pipe( + Rx.map((msg) => { + if (msg !== 'ready') + throw new Error(`received unexpected message from worker (expected "ready"): ${msg}`); + }) + ) + ); + return proc; + }); + + const qs = encode({ + access: 'public', + version: '2023-10-31', // hard coded for now, we can make this configurable later + pathStartsWith, + excludePathsMatching, + }); + const url = `http://localhost:${port}/api/oas?${qs}`; + log.info(`Fetching OAS at ${url}...`); + const result = await fetch(url, { + headers: { + 'kbn-xsrf': 'kbn-oas-snapshot', + authorization: `Basic ${Buffer.from('elastic:changeme').toString('base64')}`, + }, + }); + if (result.status !== 200) { + log.error(`Failed to fetch OAS: ${JSON.stringify(result, null, 2)}`); + throw new Error(`Failed to fetch OAS: ${result.status}`); + } + const currentOas = await result.json(); + log.info(`Recieved OAS, writing to ${outputFile}...`); + if (update) { + await fs.writeFile(outputFile, sortAndPrettyPrint(currentOas)); + const { size: sizeBytes } = await fs.stat(outputFile); + log.success(`OAS written to ${outputFile}. File size ~${twoDeci(sizeBytes / MB)} MB.`); + } else { + log.success( + `OAS recieved, not writing to file. Got OAS for ${ + Object.keys(currentOas.paths).length + } paths.` + ); + } + } catch (err) { + log.error(`Failed to capture OAS: ${JSON.stringify(err, null, 2)}`); + throw err; + } finally { + kbWorker?.kill('SIGILL'); + await esCluster?.stop(); + } +} diff --git a/packages/kbn-capture-oas-snapshot-cli/src/common.ts b/packages/kbn-capture-oas-snapshot-cli/src/common.ts new file mode 100644 index 0000000000000..588398fd39ade --- /dev/null +++ b/packages/kbn-capture-oas-snapshot-cli/src/common.ts @@ -0,0 +1,9 @@ +/* + * 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 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 or the Server + * Side Public License, v 1. + */ + +export const buildFlavourEnvArgName = 'CAPTURE_OAS_SNAPSHOT_WORKER_BUILD_FLAVOR'; diff --git a/packages/kbn-capture-oas-snapshot-cli/src/kibana_worker.ts b/packages/kbn-capture-oas-snapshot-cli/src/kibana_worker.ts index d9f6f2e665f5f..a0fcef35c491c 100644 --- a/packages/kbn-capture-oas-snapshot-cli/src/kibana_worker.ts +++ b/packages/kbn-capture-oas-snapshot-cli/src/kibana_worker.ts @@ -6,9 +6,13 @@ * Side Public License, v 1. */ -import { createRootWithCorePlugins } from '@kbn/core-test-helpers-kbn-server'; +import { + createRootWithCorePlugins, + createTestServerlessInstances, +} from '@kbn/core-test-helpers-kbn-server'; import { set } from '@kbn/safer-lodash-set'; import { PLUGIN_SYSTEM_ENABLE_ALL_PLUGINS_CONFIG_PATH } from '@kbn/core-plugins-server-internal/src/constants'; +import { buildFlavourEnvArgName } from './common'; export type Result = 'ready'; @@ -16,6 +20,10 @@ export type Result = 'ready'; if (!process.send) { throw new Error('worker must be run in a node.js fork'); } + const buildFlavour = process.env[buildFlavourEnvArgName]; + if (!buildFlavour) throw new Error(`env arg ${buildFlavourEnvArgName} must be provided`); + + const serverless = buildFlavour === 'serverless'; const settings = { logging: { @@ -30,7 +38,8 @@ export type Result = 'ready'; }; set(settings, PLUGIN_SYSTEM_ENABLE_ALL_PLUGINS_CONFIG_PATH, true); - const root = createRootWithCorePlugins(settings, { + const cliArgs = { + serverless, basePath: false, cache: false, dev: true, @@ -40,11 +49,21 @@ export type Result = 'ready'; oss: false, runExamples: false, watch: false, - }); + }; - await root.preboot(); - await root.setup(); - await root.start(); + if (serverless) { + // Satisfy spaces config for serverless: + set(settings, 'xpack.spaces.allowFeatureVisibility', false); + const { startKibana } = createTestServerlessInstances({ + kibana: { settings, cliArgs }, + }); + await startKibana(); + } else { + const root = createRootWithCorePlugins(settings, cliArgs); + await root.preboot(); + await root.setup(); + await root.start(); + } const result: Result = 'ready'; diff --git a/packages/kbn-capture-oas-snapshot-cli/src/run_capture_oas_snapshot_cli.ts b/packages/kbn-capture-oas-snapshot-cli/src/run_capture_oas_snapshot_cli.ts index 237874f181e50..5de5ea986f5af 100644 --- a/packages/kbn-capture-oas-snapshot-cli/src/run_capture_oas_snapshot_cli.ts +++ b/packages/kbn-capture-oas-snapshot-cli/src/run_capture_oas_snapshot_cli.ts @@ -7,18 +7,10 @@ */ import path from 'node:path'; -import fs from 'node:fs/promises'; -import { encode } from 'node:querystring'; -import fetch from 'node-fetch'; import { run } from '@kbn/dev-cli-runner'; -import { startTSWorker } from '@kbn/dev-utils'; -import { createTestEsCluster } from '@kbn/test'; -import * as Rx from 'rxjs'; import { REPO_ROOT } from '@kbn/repo-info'; import chalk from 'chalk'; -import type { Result } from './kibana_worker'; - -const OAS_FILE_PATH = path.resolve(REPO_ROOT, './oas_docs/bundle.json'); +import { captureOasSnapshot } from './capture_oas_snapshot'; export const sortAndPrettyPrint = (object: object) => { const keys = new Set(); @@ -29,84 +21,45 @@ export const sortAndPrettyPrint = (object: object) => { return JSON.stringify(object, Array.from(keys).sort(), 2); }; -const MB = 1024 * 1024; -const twoDeci = (num: number) => Math.round(num * 100) / 100; +const OAS_OUTPUT_DIR = path.resolve(REPO_ROOT, './oas_docs'); run( - async ({ log, flagsReader, addCleanupTask }) => { + async ({ log, flagsReader }) => { + const serverless = flagsReader.boolean('serverless'); + const traditional = flagsReader.boolean('traditional'); + if (!serverless && !traditional) { + log.error( + 'Not capturing any OAS, remove one or both of `--no-serverless` or `--no-traditional` flags to run this CLI' + ); + process.exit(1); + } + const update = flagsReader.boolean('update'); const pathStartsWith = flagsReader.arrayOfStrings('include-path'); const excludePathsMatching = flagsReader.arrayOfStrings('exclude-path') ?? []; - // internal consts - const port = 5622; - // We are only including /api/status for now - excludePathsMatching.push( - '/{path*}', - // Our internal asset paths - '/XXXXXXXXXXXX/' - ); - - log.info('Starting es...'); - await log.indent(4, async () => { - const cluster = createTestEsCluster({ log }); - await cluster.start(); - addCleanupTask(() => cluster.cleanup()); - }); - - log.info('Starting Kibana...'); - await log.indent(4, async () => { - log.info('Loading core with all plugins enabled so that we can capture OAS for all...'); - const { msg$, proc } = startTSWorker({ + if (traditional) { + log.info('Capturing OAS for traditional Kibana...'); + await captureOasSnapshot({ log, - src: require.resolve('./kibana_worker'), + buildFlavour: 'traditional', + outputFile: path.resolve(OAS_OUTPUT_DIR, 'bundle.json'), + filters: { pathStartsWith, excludePathsMatching }, + update, }); - await Rx.firstValueFrom( - msg$.pipe( - Rx.map((msg) => { - if (msg !== 'ready') - throw new Error(`received unexpected message from worker (expected "ready"): ${msg}`); - }) - ) - ); - addCleanupTask(() => proc.kill('SIGILL')); - }); + log.success('Captured OAS for traditional Kibana.'); + } - try { - const qs = encode({ - access: 'public', - version: '2023-10-31', // hard coded for now, we can make this configurable later - pathStartsWith, - excludePathsMatching, - }); - const url = `http://localhost:${port}/api/oas?${qs}`; - log.info(`Fetching OAS at ${url}...`); - const result = await fetch(url, { - headers: { - 'kbn-xsrf': 'kbn-oas-snapshot', - authorization: `Basic ${Buffer.from('elastic:changeme').toString('base64')}`, - }, + if (serverless) { + log.info('Capturing OAS for serverless Kibana...'); + await captureOasSnapshot({ + log, + buildFlavour: 'serverless', + outputFile: path.resolve(OAS_OUTPUT_DIR, 'bundle.serverless.json'), + filters: { pathStartsWith, excludePathsMatching }, + update, }); - if (result.status !== 200) { - log.error(`Failed to fetch OAS: ${JSON.stringify(result, null, 2)}`); - throw new Error(`Failed to fetch OAS: ${result.status}`); - } - const currentOas = await result.json(); - log.info(`Recieved OAS, writing to ${OAS_FILE_PATH}...`); - if (update) { - await fs.writeFile(OAS_FILE_PATH, sortAndPrettyPrint(currentOas)); - const { size: sizeBytes } = await fs.stat(OAS_FILE_PATH); - log.success(`OAS written to ${OAS_FILE_PATH}. File size ~${twoDeci(sizeBytes / MB)} MB.`); - } else { - log.success( - `OAS recieved, not writing to file. Got OAS for ${ - Object.keys(currentOas.paths).length - } paths.` - ); - } - } catch (err) { - log.error(`Failed to capture OAS: ${JSON.stringify(err, null, 2)}`); - throw err; + log.success('Captured OAS for serverless Kibana.'); } }, { @@ -114,15 +67,19 @@ run( Get the current OAS from Kibana's /api/oas API `, flags: { - boolean: ['update'], + boolean: ['update', 'serverless', 'traditional'], string: ['include-path', 'exclude-path'], default: { fix: false, + serverless: true, + traditional: true, }, help: ` --include-path Path to include. Path must start with provided value. Can be passed multiple times. --exclude-path Path to exclude. Path must NOT start with provided value. Can be passed multiple times. - --update Write the current OAS to ${chalk.cyan(OAS_FILE_PATH)}. + --update Write the current OAS bundles to ${chalk.cyan(OAS_OUTPUT_DIR)}. + --no-serverless Whether to skip OAS for serverless Kibana. Defaults to false. + --no-traditional Whether to skip OAS for traditional Kibana. Defaults to false. `, }, } diff --git a/packages/kbn-capture-oas-snapshot-cli/tsconfig.json b/packages/kbn-capture-oas-snapshot-cli/tsconfig.json index 053f7a547b409..4839a5f9b2864 100644 --- a/packages/kbn-capture-oas-snapshot-cli/tsconfig.json +++ b/packages/kbn-capture-oas-snapshot-cli/tsconfig.json @@ -21,5 +21,6 @@ "@kbn/dev-cli-runner", "@kbn/test", "@kbn/dev-utils", + "@kbn/tooling-log", ] } diff --git a/packages/kbn-dev-utils/src/worker/index.ts b/packages/kbn-dev-utils/src/worker/index.ts index 16cbfa29116ea..dbcd589fb1788 100644 --- a/packages/kbn-dev-utils/src/worker/index.ts +++ b/packages/kbn-dev-utils/src/worker/index.ts @@ -6,7 +6,7 @@ * Side Public License, v 1. */ -import ChildProcess from 'child_process'; +import ChildProcess, { type ForkOptions } from 'child_process'; import { Readable } from 'stream'; import * as Rx from 'rxjs'; @@ -16,23 +16,29 @@ import { observeLines } from '@kbn/stdio-dev-helpers'; // import type { Result } from './kibana_worker'; -interface StartTSWorkerArgs { +interface StartTSWorkerArgs extends ForkOptions { log: SomeDevLog; - /** Path to worker source */ + /** Path to worker source. Best practice to `require.resolve('../relative/paths')` */ src: string; - /** Defaults to repo root */ - cwd?: string; } /** * Provide a TS file as the src of a NodeJS Worker with some built-in handling * of std streams and debugging. */ -export function startTSWorker({ log, src, cwd = REPO_ROOT }: StartTSWorkerArgs) { - const fork = ChildProcess.fork(require.resolve(src), { - execArgv: ['--require=@kbn/babel-register/install'], +export function startTSWorker({ + log, + src, + cwd = REPO_ROOT, + execArgv = [], + stdio = ['ignore', 'pipe', 'pipe', 'ipc'], + ...forkOptions +}: StartTSWorkerArgs) { + const fork = ChildProcess.fork(src, { + execArgv: ['--require=@kbn/babel-register/install', ...execArgv], cwd, - stdio: ['ignore', 'pipe', 'pipe', 'ipc'], + stdio, + ...forkOptions, }); const msg$ = Rx.merge(