From 4e88645b59e1d1a33d77478d742080dfd10db250 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 1 May 2023 03:27:32 -0400 Subject: [PATCH] [serverless] Create Observability Serverless plugin (#156118) > Derived from https://github.com/elastic/kibana/pull/153274 > Builds upon https://github.com/elastic/kibana/pull/155582 ## Summary This PR creates the Serverless Observability plugin, based on the work from https://github.com/elastic/kibana/pull/153274: - creates the plugin, - adds API to hide the solution navigation from Enterprise Search, - calls that API if the chrome style is `project`. Screenshot 2023-04-27 at 5 03 44 PM --------- Co-authored-by: kibanamachine <42973632+kibanamachine@users.noreply.github.com> --- .github/CODEOWNERS | 1 + config/serverless.oblt.yml | 17 ++++++++- docs/developer/plugin-list.asciidoc | 4 +++ package.json | 1 + packages/kbn-optimizer/limits.yml | 1 + tsconfig.base.json | 2 ++ .../page_template/lazy_page_template.tsx | 25 ++++++++----- .../page_template/page_template.test.tsx | 3 +- .../page_template/page_template.tsx | 8 +++-- .../observability_shared/public/plugin.ts | 10 ++++++ .../serverless_observability/.gitignore | 2 ++ .../serverless_observability/README.mdx | 3 ++ .../serverless_observability/common/index.ts | 9 +++++ .../serverless_observability/kibana.jsonc | 22 ++++++++++++ .../serverless_observability/package.json | 11 ++++++ .../serverless_observability/public/index.ts | 19 ++++++++++ .../serverless_observability/public/plugin.ts | 35 +++++++++++++++++++ .../serverless_observability/public/types.ts | 28 +++++++++++++++ .../serverless_observability/server/config.ts | 23 ++++++++++++ .../serverless_observability/server/index.ts | 23 ++++++++++++ .../serverless_observability/server/plugin.ts | 26 ++++++++++++++ .../serverless_observability/server/types.ts | 11 ++++++ .../serverless_observability/tsconfig.json | 23 ++++++++++++ yarn.lock | 4 +++ 24 files changed, 299 insertions(+), 12 deletions(-) create mode 100644 x-pack/plugins/serverless_observability/.gitignore create mode 100755 x-pack/plugins/serverless_observability/README.mdx create mode 100644 x-pack/plugins/serverless_observability/common/index.ts create mode 100644 x-pack/plugins/serverless_observability/kibana.jsonc create mode 100644 x-pack/plugins/serverless_observability/package.json create mode 100644 x-pack/plugins/serverless_observability/public/index.ts create mode 100644 x-pack/plugins/serverless_observability/public/plugin.ts create mode 100644 x-pack/plugins/serverless_observability/public/types.ts create mode 100644 x-pack/plugins/serverless_observability/server/config.ts create mode 100644 x-pack/plugins/serverless_observability/server/index.ts create mode 100644 x-pack/plugins/serverless_observability/server/plugin.ts create mode 100644 x-pack/plugins/serverless_observability/server/types.ts create mode 100644 x-pack/plugins/serverless_observability/tsconfig.json diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3899aa4d0a4e6..7437653a92e5b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -574,6 +574,7 @@ packages/kbn-securitysolution-utils @elastic/security-solution-platform packages/kbn-server-http-tools @elastic/kibana-core packages/kbn-server-route-repository @elastic/apm-ui x-pack/plugins/serverless @elastic/appex-sharedux +x-pack/plugins/serverless_observability @elastic/appex-sharedux packages/serverless/project_switcher @elastic/appex-sharedux x-pack/plugins/serverless_search @elastic/appex-sharedux packages/serverless/storybook/config @elastic/appex-sharedux diff --git a/config/serverless.oblt.yml b/config/serverless.oblt.yml index dd62007fa0dfb..5b9bb95c22cce 100644 --- a/config/serverless.oblt.yml +++ b/config/serverless.oblt.yml @@ -1,3 +1,18 @@ -uiSettings.overrides.defaultRoute: /app/observability/overview +# Observability Project config + +## Disable plugins +enterpriseSearch.enabled: false +xpack.cloudSecurityPosture.enabled: false +xpack.securitySolution.enabled: false + +## Enable the Serverless Obsersability plugin +xpack.serverless.observability.enabled: true + +## Configure plugins xpack.infra.logs.app_target: discover + +## Set the home route +uiSettings.overrides.defaultRoute: /app/observability/overview + +## Set the dev project switch current type xpack.serverless.plugin.developer.projectSwitcher.currentType: 'observability' diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc index 25d0b4ea50456..64b718e955f67 100644 --- a/docs/developer/plugin-list.asciidoc +++ b/docs/developer/plugin-list.asciidoc @@ -714,6 +714,10 @@ Kibana. | +|{kib-repo}blob/{branch}/x-pack/plugins/serverless_observability/README.mdx[serverlessObservability] +|This plugin contains configuration and code used to create a Serverless Observability project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana. + + |{kib-repo}blob/{branch}/x-pack/plugins/serverless_search/README.mdx[serverlessSearch] |This plugin contains configuration and code used to create a Serverless Search project. It leverages universal configuration and other APIs in the serverless plugin to configure Kibana. diff --git a/package.json b/package.json index 9783ad2b3e0e5..4c760c76764e3 100644 --- a/package.json +++ b/package.json @@ -575,6 +575,7 @@ "@kbn/server-http-tools": "link:packages/kbn-server-http-tools", "@kbn/server-route-repository": "link:packages/kbn-server-route-repository", "@kbn/serverless": "link:x-pack/plugins/serverless", + "@kbn/serverless-observability": "link:x-pack/plugins/serverless_observability", "@kbn/serverless-project-switcher": "link:packages/serverless/project_switcher", "@kbn/serverless-search": "link:x-pack/plugins/serverless_search", "@kbn/serverless-types": "link:packages/serverless/types", diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml index 8443465cf1a19..85c8b6c888236 100644 --- a/packages/kbn-optimizer/limits.yml +++ b/packages/kbn-optimizer/limits.yml @@ -116,6 +116,7 @@ pageLoadAssetSize: security: 65433 securitySolution: 66738 serverless: 16573 + serverlessObservability: 16582 serverlessSearch: 17548 sessionView: 77750 share: 71239 diff --git a/tsconfig.base.json b/tsconfig.base.json index 84a11c9194ba5..47391d9185f14 100644 --- a/tsconfig.base.json +++ b/tsconfig.base.json @@ -1142,6 +1142,8 @@ "@kbn/server-route-repository/*": ["packages/kbn-server-route-repository/*"], "@kbn/serverless": ["x-pack/plugins/serverless"], "@kbn/serverless/*": ["x-pack/plugins/serverless/*"], + "@kbn/serverless-observability": ["x-pack/plugins/serverless_observability"], + "@kbn/serverless-observability/*": ["x-pack/plugins/serverless_observability/*"], "@kbn/serverless-project-switcher": ["packages/serverless/project_switcher"], "@kbn/serverless-project-switcher/*": ["packages/serverless/project_switcher/*"], "@kbn/serverless-search": ["x-pack/plugins/serverless_search"], diff --git a/x-pack/plugins/observability_shared/public/components/page_template/lazy_page_template.tsx b/x-pack/plugins/observability_shared/public/components/page_template/lazy_page_template.tsx index 7c61cae4f2c73..e61254fa10ca6 100644 --- a/x-pack/plugins/observability_shared/public/components/page_template/lazy_page_template.tsx +++ b/x-pack/plugins/observability_shared/public/components/page_template/lazy_page_template.tsx @@ -6,6 +6,8 @@ */ import React from 'react'; +import useObservable from 'react-use/lib/useObservable'; + import type { ObservabilityPageTemplateDependencies, WrappedPageTemplateProps, @@ -15,12 +17,19 @@ export const LazyObservabilityPageTemplate = React.lazy(() => import('./page_tem export type LazyObservabilityPageTemplateProps = WrappedPageTemplateProps; -export function createLazyObservabilityPageTemplate( - injectedDeps: ObservabilityPageTemplateDependencies -) { - return (pageTemplateProps: LazyObservabilityPageTemplateProps) => ( - - - - ); +export function createLazyObservabilityPageTemplate({ + isSidebarEnabled$, + ...injectedDeps +}: ObservabilityPageTemplateDependencies) { + return (pageTemplateProps: LazyObservabilityPageTemplateProps) => { + const isSidebarEnabled = useObservable(isSidebarEnabled$); + const { showSolutionNav: showSolutionNavProp, ...props } = pageTemplateProps; + const showSolutionNav = !!showSolutionNavProp || isSidebarEnabled; + + return ( + + + + ); + }; } diff --git a/x-pack/plugins/observability_shared/public/components/page_template/page_template.test.tsx b/x-pack/plugins/observability_shared/public/components/page_template/page_template.test.tsx index b33925ed09e4b..3ea28007c207d 100644 --- a/x-pack/plugins/observability_shared/public/components/page_template/page_template.test.tsx +++ b/x-pack/plugins/observability_shared/public/components/page_template/page_template.test.tsx @@ -9,7 +9,7 @@ import { I18nProvider } from '@kbn/i18n-react'; import { render } from '@testing-library/react'; import { shallow } from 'enzyme'; import React from 'react'; -import { of } from 'rxjs'; +import { BehaviorSubject, of } from 'rxjs'; import { getKibanaPageTemplateKibanaDependenciesMock as getPageTemplateServices } from '@kbn/shared-ux-page-kibana-template-mocks'; import { guidedOnboardingMock } from '@kbn/guided-onboarding-plugin/public/mocks'; @@ -56,6 +56,7 @@ describe('Page template', () => { navigationSections$: navigationRegistry.sections$, getPageTemplateServices, guidedOnboardingApi: guidedOnboardingMock.createStart().guidedOnboardingApi, + isSidebarEnabled$: new BehaviorSubject(true), }); const component = shallow( diff --git a/x-pack/plugins/observability_shared/public/components/page_template/page_template.tsx b/x-pack/plugins/observability_shared/public/components/page_template/page_template.tsx index 696cda97e3e53..f2f94ed74e565 100644 --- a/x-pack/plugins/observability_shared/public/components/page_template/page_template.tsx +++ b/x-pack/plugins/observability_shared/public/components/page_template/page_template.tsx @@ -11,7 +11,7 @@ import { i18n } from '@kbn/i18n'; import React, { useMemo } from 'react'; import { matchPath, useLocation } from 'react-router-dom'; import useObservable from 'react-use/lib/useObservable'; -import type { Observable } from 'rxjs'; +import type { BehaviorSubject, Observable } from 'rxjs'; import type { ApplicationStart } from '@kbn/core/public'; import { useKibana } from '@kbn/kibana-react-plugin/public'; import { @@ -85,9 +85,13 @@ export interface ObservabilityPageTemplateDependencies { navigationSections$: Observable; getPageTemplateServices: () => KibanaPageTemplateKibanaDependencies; guidedOnboardingApi: GuidedOnboardingPluginStart['guidedOnboardingApi']; + isSidebarEnabled$: BehaviorSubject; } -export type ObservabilityPageTemplateProps = ObservabilityPageTemplateDependencies & +export type ObservabilityPageTemplateProps = Omit< + ObservabilityPageTemplateDependencies, + 'isSidebarEnabled$' +> & WrappedPageTemplateProps; export function ObservabilityPageTemplate({ diff --git a/x-pack/plugins/observability_shared/public/plugin.ts b/x-pack/plugins/observability_shared/public/plugin.ts index 527d33e8a502e..7a0ee7a452dde 100644 --- a/x-pack/plugins/observability_shared/public/plugin.ts +++ b/x-pack/plugins/observability_shared/public/plugin.ts @@ -5,6 +5,8 @@ * 2.0. */ +import { BehaviorSubject } from 'rxjs'; + import type { CoreStart, Plugin } from '@kbn/core/public'; import type { GuidedOnboardingPluginStart } from '@kbn/guided-onboarding-plugin/public'; import { createNavigationRegistry } from './components/page_template/helpers/navigation_registry'; @@ -13,6 +15,7 @@ import { updateGlobalNavigation } from './services/update_global_navigation'; export interface ObservabilitySharedStart { guidedOnboarding: GuidedOnboardingPluginStart; + setIsSidebarEnabled: (isEnabled: boolean) => void; } export type ObservabilitySharedPluginSetup = ReturnType; @@ -20,6 +23,11 @@ export type ObservabilitySharedPluginStart = ReturnType; + + constructor() { + this.isSidebarEnabled$ = new BehaviorSubject(true); + } public setup() { return { @@ -39,6 +47,7 @@ export class ObservabilitySharedPlugin implements Plugin { navigationSections$: this.navigationRegistry.sections$, guidedOnboardingApi: plugins.guidedOnboarding.guidedOnboardingApi, getPageTemplateServices: () => ({ coreStart: core }), + isSidebarEnabled$: this.isSidebarEnabled$, }); return { @@ -46,6 +55,7 @@ export class ObservabilitySharedPlugin implements Plugin { PageTemplate, }, updateGlobalNavigation, + setIsSidebarEnabled: (isEnabled: boolean) => this.isSidebarEnabled$.next(isEnabled), }; } diff --git a/x-pack/plugins/serverless_observability/.gitignore b/x-pack/plugins/serverless_observability/.gitignore new file mode 100644 index 0000000000000..c3dca1b96fcc2 --- /dev/null +++ b/x-pack/plugins/serverless_observability/.gitignore @@ -0,0 +1,2 @@ +/build +/target diff --git a/x-pack/plugins/serverless_observability/README.mdx b/x-pack/plugins/serverless_observability/README.mdx new file mode 100755 index 0000000000000..fa2dde090f4b7 --- /dev/null +++ b/x-pack/plugins/serverless_observability/README.mdx @@ -0,0 +1,3 @@ +# Serverless Observability project plugin + +This plugin contains configuration and code used to create a Serverless Observability project. It leverages universal configuration and other APIs in the [`serverless`](../serverless/README.mdx) plugin to configure Kibana. \ No newline at end of file diff --git a/x-pack/plugins/serverless_observability/common/index.ts b/x-pack/plugins/serverless_observability/common/index.ts new file mode 100644 index 0000000000000..d6a5ea767034c --- /dev/null +++ b/x-pack/plugins/serverless_observability/common/index.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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +export const PLUGIN_ID = 'serverlessObservability'; +export const PLUGIN_NAME = 'serverlessObservability'; diff --git a/x-pack/plugins/serverless_observability/kibana.jsonc b/x-pack/plugins/serverless_observability/kibana.jsonc new file mode 100644 index 0000000000000..12d528dd33ac2 --- /dev/null +++ b/x-pack/plugins/serverless_observability/kibana.jsonc @@ -0,0 +1,22 @@ +{ + "type": "plugin", + "id": "@kbn/serverless-observability", + "owner": "@elastic/appex-sharedux", + "description": "Serverless customizations for observability.", + "plugin": { + "id": "serverlessObservability", + "server": true, + "browser": true, + "configPath": [ + "xpack", + "serverless", + "observability" + ], + "requiredPlugins": [ + "serverless", + "observabilityShared" + ], + "optionalPlugins": [], + "requiredBundles": [] + } +} diff --git a/x-pack/plugins/serverless_observability/package.json b/x-pack/plugins/serverless_observability/package.json new file mode 100644 index 0000000000000..64b310d7eabae --- /dev/null +++ b/x-pack/plugins/serverless_observability/package.json @@ -0,0 +1,11 @@ +{ + "name": "@kbn/serverless-observability", + "version": "1.0.0", + "license": "Elastic License 2.0", + "private": true, + "scripts": { + "build": "yarn plugin-helpers build", + "plugin-helpers": "node ../../../scripts/plugin_helpers", + "kbn": "node ../../../scripts/kbn" + } +} \ No newline at end of file diff --git a/x-pack/plugins/serverless_observability/public/index.ts b/x-pack/plugins/serverless_observability/public/index.ts new file mode 100644 index 0000000000000..a785b68735375 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/index.ts @@ -0,0 +1,19 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ServerlessObservabilityPlugin } from './plugin'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. +export function plugin() { + return new ServerlessObservabilityPlugin(); +} + +export type { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, +} from './types'; diff --git a/x-pack/plugins/serverless_observability/public/plugin.ts b/x-pack/plugins/serverless_observability/public/plugin.ts new file mode 100644 index 0000000000000..48da91bb4ea26 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/plugin.ts @@ -0,0 +1,35 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { CoreSetup, CoreStart, Plugin } from '@kbn/core/public'; +import { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, + ServerlessObservabilityPluginSetupDependencies, + ServerlessObservabilityPluginStartDependencies, +} from './types'; + +export class ServerlessObservabilityPlugin + implements Plugin +{ + public setup( + _core: CoreSetup, + _setupDeps: ServerlessObservabilityPluginSetupDependencies + ): ServerlessObservabilityPluginSetup { + return {}; + } + + public start( + _core: CoreStart, + { observabilityShared }: ServerlessObservabilityPluginStartDependencies + ): ServerlessObservabilityPluginStart { + observabilityShared.setIsSidebarEnabled(false); + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_observability/public/types.ts b/x-pack/plugins/serverless_observability/public/types.ts new file mode 100644 index 0000000000000..417a9c1701c84 --- /dev/null +++ b/x-pack/plugins/serverless_observability/public/types.ts @@ -0,0 +1,28 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { ServerlessPluginSetup, ServerlessPluginStart } from '@kbn/serverless/public'; +import { + ObservabilitySharedPluginSetup, + ObservabilitySharedPluginStart, +} from '@kbn/observability-shared-plugin/public'; + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginSetup {} + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginStart {} + +export interface ServerlessObservabilityPluginSetupDependencies { + observabilityShared: ObservabilitySharedPluginSetup; + serverless: ServerlessPluginSetup; +} + +export interface ServerlessObservabilityPluginStartDependencies { + observabilityShared: ObservabilitySharedPluginStart; + serverless: ServerlessPluginStart; +} diff --git a/x-pack/plugins/serverless_observability/server/config.ts b/x-pack/plugins/serverless_observability/server/config.ts new file mode 100644 index 0000000000000..599a9f2bd7769 --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/config.ts @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { schema, TypeOf } from '@kbn/config-schema'; +import { PluginConfigDescriptor } from '@kbn/core/server'; + +export * from './types'; + +const configSchema = schema.object({ + enabled: schema.boolean({ defaultValue: false }), +}); + +type ConfigType = TypeOf; + +export const config: PluginConfigDescriptor = { + schema: configSchema, +}; + +export type ServerlessObservabilityConfig = TypeOf; diff --git a/x-pack/plugins/serverless_observability/server/index.ts b/x-pack/plugins/serverless_observability/server/index.ts new file mode 100644 index 0000000000000..c45e363a429bf --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/index.ts @@ -0,0 +1,23 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializerContext } from '@kbn/core/server'; + +import { ServerlessObservabilityPlugin } from './plugin'; +export { config } from './config'; + +// This exports static code and TypeScript types, +// as well as, Kibana Platform `plugin()` initializer. + +export function plugin(initializerContext: PluginInitializerContext) { + return new ServerlessObservabilityPlugin(initializerContext); +} + +export type { + ServerlessObservabilityPluginSetup, + ServerlessObservabilityPluginStart, +} from './types'; diff --git a/x-pack/plugins/serverless_observability/server/plugin.ts b/x-pack/plugins/serverless_observability/server/plugin.ts new file mode 100644 index 0000000000000..8b28ba2b0a4ac --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/plugin.ts @@ -0,0 +1,26 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +import { PluginInitializerContext, Plugin } from '@kbn/core/server'; + +import { ServerlessObservabilityPluginSetup, ServerlessObservabilityPluginStart } from './types'; + +export class ServerlessObservabilityPlugin + implements Plugin +{ + constructor(_initializerContext: PluginInitializerContext) {} + + public setup() { + return {}; + } + + public start() { + return {}; + } + + public stop() {} +} diff --git a/x-pack/plugins/serverless_observability/server/types.ts b/x-pack/plugins/serverless_observability/server/types.ts new file mode 100644 index 0000000000000..f8a587103e886 --- /dev/null +++ b/x-pack/plugins/serverless_observability/server/types.ts @@ -0,0 +1,11 @@ +/* + * 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; you may not use this file except in compliance with the Elastic License + * 2.0. + */ + +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginSetup {} +// eslint-disable-next-line @typescript-eslint/no-empty-interface +export interface ServerlessObservabilityPluginStart {} diff --git a/x-pack/plugins/serverless_observability/tsconfig.json b/x-pack/plugins/serverless_observability/tsconfig.json new file mode 100644 index 0000000000000..d3cc73ef6a7a6 --- /dev/null +++ b/x-pack/plugins/serverless_observability/tsconfig.json @@ -0,0 +1,23 @@ +{ + "extends": "../../../tsconfig.base.json", + "compilerOptions": { + "outDir": "target/types" + }, + "include": [ + "index.ts", + "common/**/*.ts", + "public/**/*.ts", + "public/**/*.tsx", + "server/**/*.ts", + "../../../typings/**/*" + ], + "exclude": [ + "target/**/*" + ], + "kbn_references": [ + "@kbn/core", + "@kbn/config-schema", + "@kbn/serverless", + "@kbn/observability-shared-plugin", + ] +} diff --git a/yarn.lock b/yarn.lock index cf370434048a1..644ac3eef8160 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5016,6 +5016,10 @@ "@kbn/server-route-repository@link:packages/kbn-server-route-repository": version "0.0.0" uid "" + +"@kbn/serverless-observability@link:x-pack/plugins/serverless_observability": + version "0.0.0" + uid "" "@kbn/serverless-project-switcher@link:packages/serverless/project_switcher": version "0.0.0"