diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 5fcb619af6570..c91d1a702b7ec 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -315,7 +315,6 @@
/src/plugins/es_ui_shared/ @elastic/kibana-stack-management
/x-pack/plugins/cross_cluster_replication/ @elastic/kibana-stack-management
/x-pack/plugins/index_lifecycle_management/ @elastic/kibana-stack-management
-/x-pack/plugins/console_extensions/ @elastic/kibana-stack-management
/x-pack/plugins/grokdebugger/ @elastic/kibana-stack-management
/x-pack/plugins/index_management/ @elastic/kibana-stack-management
/x-pack/plugins/license_api_guard/ @elastic/kibana-stack-management
@@ -330,7 +329,6 @@
/x-pack/plugins/ingest_pipelines/ @elastic/kibana-stack-management
/packages/kbn-ace/ @elastic/kibana-stack-management
/packages/kbn-monaco/ @elastic/kibana-stack-management
-#CC# /x-pack/plugins/console_extensions/ @elastic/kibana-stack-management
#CC# /x-pack/plugins/cross_cluster_replication/ @elastic/kibana-stack-management
# Security Solution
diff --git a/.github/workflows/project-assigner.yml b/.github/workflows/project-assigner.yml
index f4e62648a9741..f2359846504bf 100644
--- a/.github/workflows/project-assigner.yml
+++ b/.github/workflows/project-assigner.yml
@@ -14,6 +14,7 @@ jobs:
issue-mappings: |
[
{"label": "Feature:Lens", "projectNumber": 32, "columnName": "Long-term goals"},
+ {"label": "Feature:Discover", "projectNumber": 44, "columnName": "Inbox"},
{"label": "Feature:Canvas", "projectNumber": 38, "columnName": "Inbox"},
{"label": "Feature:Dashboard", "projectNumber": 68, "columnName": "Inbox"},
{"label": "Feature:Drilldowns", "projectNumber": 68, "columnName": "Inbox"},
diff --git a/.i18nrc.json b/.i18nrc.json
index 390e5e917d08e..732644b43e1f7 100644
--- a/.i18nrc.json
+++ b/.i18nrc.json
@@ -16,6 +16,7 @@
"esUi": "src/plugins/es_ui_shared",
"devTools": "src/plugins/dev_tools",
"expressions": "src/plugins/expressions",
+ "expressionError": "src/plugins/expression_error",
"expressionRevealImage": "src/plugins/expression_reveal_image",
"inputControl": "src/plugins/input_control_vis",
"inspector": "src/plugins/inspector",
diff --git a/docs/developer/plugin-list.asciidoc b/docs/developer/plugin-list.asciidoc
index 2144fd171ff7a..c1535e8a2146f 100644
--- a/docs/developer/plugin-list.asciidoc
+++ b/docs/developer/plugin-list.asciidoc
@@ -72,6 +72,10 @@ This API doesn't support angular, for registering angular dev tools, bootstrap a
|This plugin contains reusable code in the form of self-contained modules (or libraries). Each of these modules exports a set of functionality relevant to the domain of the module.
+|{kib-repo}blob/{branch}/src/plugins/expression_error/README.md[expressionError]
+|Expression Error plugin adds an error renderer to the expression plugin. The renderer will display the error image.
+
+
|{kib-repo}blob/{branch}/src/plugins/expression_reveal_image/README.md[expressionRevealImage]
|Expression Reveal Image plugin adds a revealImage function to the expression plugin and an associated renderer. The renderer will display the given percentage of a given image.
@@ -354,10 +358,6 @@ The plugin exposes the static DefaultEditorController class to consume.
The client-side plugin configures following values:
-|{kib-repo}blob/{branch}/x-pack/plugins/console_extensions/README.md[consoleExtensions]
-|This plugin provides autocomplete definitions of licensed APIs to the OSS Console plugin.
-
-
|{kib-repo}blob/{branch}/x-pack/plugins/cross_cluster_replication/README.md[crossClusterReplication]
|You can run a local cluster and simulate a remote cluster within a single Kibana directory.
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntype.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntype.md
new file mode 100644
index 0000000000000..46fd3a0725e40
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntype.md
@@ -0,0 +1,19 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternType](./kibana-plugin-plugins-data-public.indexpatterntype.md)
+
+## IndexPatternType enum
+
+Signature:
+
+```typescript
+export declare enum IndexPatternType
+```
+
+## Enumeration Members
+
+| Member | Value | Description |
+| --- | --- | --- |
+| DEFAULT | "default" | |
+| ROLLUP | "rollup" | |
+
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md
index e6690b244c9ea..19a884862d460 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.md
@@ -15,4 +15,5 @@ export interface TypeMeta
| Property | Type | Description |
| --- | --- | --- |
| [aggs](./kibana-plugin-plugins-data-public.indexpatterntypemeta.aggs.md) | Record<string, AggregationRestrictions> | |
+| [params](./kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md) | { rollup_index: string; } | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md
new file mode 100644
index 0000000000000..12646a39188a0
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md
@@ -0,0 +1,13 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [IndexPatternTypeMeta](./kibana-plugin-plugins-data-public.indexpatterntypemeta.md) > [params](./kibana-plugin-plugins-data-public.indexpatterntypemeta.params.md)
+
+## IndexPatternTypeMeta.params property
+
+Signature:
+
+```typescript
+params?: {
+ rollup_index: string;
+ };
+```
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
index 65c4601d5faec..7c2911875ee05 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.md
@@ -31,6 +31,7 @@
| --- | --- |
| [BUCKET\_TYPES](./kibana-plugin-plugins-data-public.bucket_types.md) | |
| [ES\_FIELD\_TYPES](./kibana-plugin-plugins-data-public.es_field_types.md) | \* |
+| [IndexPatternType](./kibana-plugin-plugins-data-public.indexpatterntype.md) | |
| [KBN\_FIELD\_TYPES](./kibana-plugin-plugins-data-public.kbn_field_types.md) | \* |
| [METRIC\_TYPES](./kibana-plugin-plugins-data-public.metric_types.md) | |
| [QuerySuggestionTypes](./kibana-plugin-plugins-data-public.querysuggestiontypes.md) | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md
index de6f4563b678a..7c850a89dff13 100644
--- a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md
@@ -19,6 +19,7 @@ export interface QuerySuggestionGetFnArgs
| [boolFilter](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.boolfilter.md) | any | |
| [indexPatterns](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.indexpatterns.md) | IIndexPattern[] | |
| [language](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.language.md) | string | |
+| [method](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md) | ValueSuggestionsMethod | |
| [query](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.query.md) | string | |
| [selectionEnd](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.selectionend.md) | number | |
| [selectionStart](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.selectionstart.md) | number | |
diff --git a/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md
new file mode 100644
index 0000000000000..2bc9a4fba61c3
--- /dev/null
+++ b/docs/development/plugins/data/public/kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md
@@ -0,0 +1,11 @@
+
+
+[Home](./index.md) > [kibana-plugin-plugins-data-public](./kibana-plugin-plugins-data-public.md) > [QuerySuggestionGetFnArgs](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.md) > [method](./kibana-plugin-plugins-data-public.querysuggestiongetfnargs.method.md)
+
+## QuerySuggestionGetFnArgs.method property
+
+Signature:
+
+```typescript
+method?: ValueSuggestionsMethod;
+```
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md
index edaf1c9a9ce9e..040bed5a8ce53 100644
--- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.md
@@ -39,5 +39,5 @@ export declare class ExecutionN.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. |
+| [start(input, isSubExpression)](./kibana-plugin-plugins-expressions-public.execution.start.md) | | Call this method to start execution.N.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. |
diff --git a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md
index 352226da6d72a..b1fa6d7d518b9 100644
--- a/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md
+++ b/docs/development/plugins/expressions/public/kibana-plugin-plugins-expressions-public.execution.start.md
@@ -11,7 +11,7 @@ N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons
Signature:
```typescript
-start(input?: Input): Observable>;
+start(input?: Input, isSubExpression?: boolean): Observable>;
```
## Parameters
@@ -19,6 +19,7 @@ start(input?: Input): Observable>
| Parameter | Type | Description |
| --- | --- | --- |
| input | Input | |
+| isSubExpression | boolean | |
Returns:
diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md
index 47963e5e5ef46..44d16ea02e270 100644
--- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md
+++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.md
@@ -39,5 +39,5 @@ export declare class ExecutionN.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. |
+| [start(input, isSubExpression)](./kibana-plugin-plugins-expressions-server.execution.start.md) | | Call this method to start execution.N.B. input is initialized to null rather than undefined for legacy reasons, because in legacy interpreter it was set to null by default. |
diff --git a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md
index 0eef7013cb3c6..23b4d414d09d1 100644
--- a/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md
+++ b/docs/development/plugins/expressions/server/kibana-plugin-plugins-expressions-server.execution.start.md
@@ -11,7 +11,7 @@ N.B. `input` is initialized to `null` rather than `undefined` for legacy reasons
Signature:
```typescript
-start(input?: Input): Observable>;
+start(input?: Input, isSubExpression?: boolean): Observable>;
```
## Parameters
@@ -19,6 +19,7 @@ start(input?: Input): Observable>
| Parameter | Type | Description |
| --- | --- | --- |
| input | Input | |
+| isSubExpression | boolean | |
Returns:
diff --git a/docs/getting-started/images/add-sample-data.png b/docs/getting-started/images/add-sample-data.png
index 9dee27dcde71b..07a536b19d7d0 100644
Binary files a/docs/getting-started/images/add-sample-data.png and b/docs/getting-started/images/add-sample-data.png differ
diff --git a/docs/getting-started/images/tutorial-discover-3.png b/docs/getting-started/images/tutorial-discover-3.png
index b024ad6dc39fe..79cf94058bb76 100644
Binary files a/docs/getting-started/images/tutorial-discover-3.png and b/docs/getting-started/images/tutorial-discover-3.png differ
diff --git a/docs/getting-started/images/tutorial-discover-4.png b/docs/getting-started/images/tutorial-discover-4.png
index 945a6155c02cd..584221e8cfd04 100644
Binary files a/docs/getting-started/images/tutorial-discover-4.png and b/docs/getting-started/images/tutorial-discover-4.png differ
diff --git a/docs/getting-started/images/tutorial-final-dashboard.gif b/docs/getting-started/images/tutorial-final-dashboard.gif
index 53b7bc04c5f65..6c82c4e53ca10 100644
Binary files a/docs/getting-started/images/tutorial-final-dashboard.gif and b/docs/getting-started/images/tutorial-final-dashboard.gif differ
diff --git a/docs/getting-started/images/tutorial-sample-dashboard.png b/docs/getting-started/images/tutorial-sample-dashboard.png
index 4c95c04c5e43e..5e06009d0824e 100644
Binary files a/docs/getting-started/images/tutorial-sample-dashboard.png and b/docs/getting-started/images/tutorial-sample-dashboard.png differ
diff --git a/docs/getting-started/images/tutorial-sample-filter.png b/docs/getting-started/images/tutorial-sample-filter.png
index 56ebacadbef45..8823da311ebb5 100644
Binary files a/docs/getting-started/images/tutorial-sample-filter.png and b/docs/getting-started/images/tutorial-sample-filter.png differ
diff --git a/docs/getting-started/images/tutorial-sample-filter2.png b/docs/getting-started/images/tutorial-sample-filter2.png
index 21402feacdecd..4215b63d89fa1 100644
Binary files a/docs/getting-started/images/tutorial-sample-filter2.png and b/docs/getting-started/images/tutorial-sample-filter2.png differ
diff --git a/docs/getting-started/images/tutorial-sample-query.png b/docs/getting-started/images/tutorial-sample-query.png
deleted file mode 100644
index 4f1ca24924b28..0000000000000
Binary files a/docs/getting-started/images/tutorial-sample-query.png and /dev/null differ
diff --git a/docs/getting-started/images/tutorial-sample-query2.png b/docs/getting-started/images/tutorial-sample-query2.png
deleted file mode 100644
index 0e91e1069a201..0000000000000
Binary files a/docs/getting-started/images/tutorial-sample-query2.png and /dev/null differ
diff --git a/docs/getting-started/images/tutorial-treemap.png b/docs/getting-started/images/tutorial-treemap.png
deleted file mode 100644
index 32e14fd2308e3..0000000000000
Binary files a/docs/getting-started/images/tutorial-treemap.png and /dev/null differ
diff --git a/docs/getting-started/images/tutorial-visualization-dropdown.png b/docs/getting-started/images/tutorial-visualization-dropdown.png
index 29d1b99700964..a069af95ed14a 100644
Binary files a/docs/getting-started/images/tutorial-visualization-dropdown.png and b/docs/getting-started/images/tutorial-visualization-dropdown.png differ
diff --git a/docs/getting-started/images/tutorial-visualization-treemap.png b/docs/getting-started/images/tutorial-visualization-treemap.png
new file mode 100644
index 0000000000000..c6e8db133cb44
Binary files /dev/null and b/docs/getting-started/images/tutorial-visualization-treemap.png differ
diff --git a/docs/getting-started/quick-start-guide.asciidoc b/docs/getting-started/quick-start-guide.asciidoc
index d9835b312f3ee..ed249008ac8de 100644
--- a/docs/getting-started/quick-start-guide.asciidoc
+++ b/docs/getting-started/quick-start-guide.asciidoc
@@ -7,7 +7,7 @@ When you've finished, you'll know how to:
* <>
-* <>
+* <>
[float]
=== Required privileges
@@ -24,125 +24,125 @@ include::{docs-root}/shared/cloud/ess-getting-started.asciidoc[]
[[gs-get-data-into-kibana]]
== Add the sample data
-Sample data sets come with sample visualizations, dashboards, and more to help you explore {kib} without adding your own data.
+Sample data sets come with sample visualizations, dashboards, and more to help you explore {kib} before you ingest or add your own data.
-. From the home page, click *Try our sample data*.
+. On the home page, click *Try our sample data*.
. On the *Sample eCommerce orders* card, click *Add data*.
+
[role="screenshot"]
-image::getting-started/images/add-sample-data.png[Add data UI]
+image::images/add-sample-data.png[Add data UI for the sample data sets]
[float]
[[explore-the-data]]
== Explore the data
-*Discover* displays an interactive histogram that shows the distribution of of data, or documents, over time, and a table that lists the fields for each document that matches the index. By default, all fields are shown for each matching document.
+*Discover* displays the data in an interactive histogram that shows the distribution of data, or documents, over time, and a table that lists the fields for each document that matches the index pattern. To view a subset of the documents, you can apply filters to the data, and customize the table to display only the fields you want to explore.
. Open the main menu, then click *Discover*.
. Change the <> to *Last 7 days*.
+
[role="screenshot"]
-image::images/tutorial-discover-2.png[]
+image::images/tutorial-discover-2.png[Time filter menu with Last 7 days filter configured]
-. To focus in on the documents you want to view, use the <>. In the *KQL* search field, enter:
+. To view the sales orders for women's clothing that are $60 or more, use the <> search field:
+
[source,text]
-products.taxless_price >= 60 AND category : Women's Clothing
-+
-The query returns the women's clothing orders for $60 and more.
+products.taxless_price >= 60 and category : Women's Clothing
+
[role="screenshot"]
-image::images/tutorial-discover-4.png[]
+image::images/tutorial-discover-4.png[Discover tables that displays only the orders for women's clothing that are $60 or more]
-. Hover over the list of *Available fields*, then click *+* next to the fields you want to view in the table.
-+
-For example, when you add the *category* field, the table displays the product categories for the orders.
+. To view only the product categories that contain sales orders, hover over the *category* field, then click *+*.
+
[role="screenshot"]
-image::images/tutorial-discover-3.png[]
-+
-For more information, refer to <>.
+image::images/tutorial-discover-3.png[Discover table that displays only the product categories that contain orders]
[float]
[[view-and-analyze-the-data]]
== View and analyze the data
-A dashboard is a collection of panels that you can use to view and analyze the data. Panels contain visualizations, interactive controls, Markdown, and more.
+A dashboard is a collection of panels that you can use to view and analyze the data. Panels contain visualizations, interactive controls, text, and more.
. Open the main menu, then click *Dashboard*.
. Click *[eCommerce] Revenue Dashboard*.
+
[role="screenshot"]
-image::getting-started/images/tutorial-sample-dashboard.png[]
+image::getting-started/images/tutorial-sample-dashboard.png[The [eCommerce] Revenue Dashboard that comes with the Sample eCommerce order data set]
[float]
-[[filter-and-query-the-data]]
-=== Filter the data
+[[create-a-visualization]]
+=== Create a visualization panel
+
+Create a treemap panel that shows the top sales regions and manufacturers, then add the panel to the dashboard.
-To focus in on the data you want to view on the dashboard, use filters.
+. From the toolbar, click *Edit*, then click *Create visualzation*.
-. From the *[eCommerce] Controls* panel, make a selection from the *Manufacturer* and *Category* dropdowns, then click *Apply changes*.
+. Open the *Chart type* menu, then select *Treemap*.
+
-For example, the following dashboard shows the data for women's clothing from Gnomehouse.
+[role="screenshot"]
+image::getting-started/images/tutorial-visualization-dropdown.png[Chart type menu with Treemap selected]
+
+. From the *Available fields* list, drag and drop the following fields onto the workspace:
+
+* *geoip.city_name*
+
+* *manufacturer.keyword*
+
[role="screenshot"]
-image::getting-started/images/tutorial-sample-filter.png[]
+image::getting-started/images/tutorial-visualization-treemap.png[Treemap that displays Top values of geoip.city_name and Top values or manufacturer.keyword fields]
-. To manually add a filter, click *Add filter*, then specify the options.
+. Click *Save and return*.
+
-For example, to view the orders for Wednesday, select *day_of_week* from the *Field* dropdown, select *is* from the *Operator* dropdown, then select *Wednesday* from the *Value* dropdown.
+The treemap appears as the last visualization panel on the dashboard.
+
[role="screenshot"]
-image::getting-started/images/tutorial-sample-filter2.png[]
+image::getting-started/images/tutorial-final-dashboard.gif[Final dashboard with new treemap visualization]
+
+[float]
+[[interact-with-the-data]]
+=== Interact with the data
+
+You can interact with the dashboard data using controls that allow you to apply dashboard-level filters. Interact with the *[eCommerce] Controls* panel to view the women's clothing data from the Gnomehouse manufacturer.
-. When you are done, remove the filters.
+. From the *Manufacturer* dropdown, select *Gnomehouse*.
+
+. From the *Category* dropdown, select *Women's Clothing*.
+
+. Click *Apply changes*.
+
-For more information, refer to <>.
+[role="screenshot"]
+image::getting-started/images/tutorial-sample-filter.png[The [eCommerce] Revenue Dashboard that shows only the women's clothing data from the Gnomehouse manufacturer]
[float]
-[[create-a-visualization]]
-=== Create a visualization panel
-
-Create a treemap panel that shows the top regions and manufacturers, then add the panel to the dashboard.
+[[filter-and-query-the-data]]
+=== Filter the data
-. From the toolbar, click *Edit*, then click *Create new*.
+To view a subset of the data, you can apply filters to the dashboard panels. Apply a filter to view the women's clothing data generated on Wednesday from the Gnomehouse manufacturer.
-. On the *New Visualization* window, click *Lens*.
+. Click *Add filter*.
-. From the *Available fields* list, drag and drop the following fields to the visualization builder:
+. From the *Field* dropdown, select *day_of_week*.
-* *geoip.city_name*
+. From the *Operator* dropdown, select *is*.
-* *manufacturer.keyword*
-+
-. From the visualization dropdown, select *Treemap*.
-+
-[role="screenshot"]
-image::getting-started/images/tutorial-visualization-dropdown.png[Visualization dropdown with Treemap selected]
+. From the *Value* dropdown, select *Wednesday*.
. Click *Save*.
-
-. On the *Save Lens visualization*, enter a title and make sure *Add to Dashboard after saving* is selected, then click *Save and return*.
-+
-The treemap appears as the last visualization panel on the dashboard.
+
[role="screenshot"]
-image::getting-started/images/tutorial-final-dashboard.gif[Final dashboard with new treemap visualization]
-+
-For more information, refer to <>.
+image::getting-started/images/tutorial-sample-filter2.png[The [eCommerce] Revenue Dashboard that shows only the women's clothing data generated on Wednesday from the Gnomehouse manufacturer]
[float]
[[quick-start-whats-next]]
== What's next?
-If you are you ready to add your own data, refer to <>.
+*Add your own data.* Ready to add your own data? Go to {fleet-guide}/fleet-quick-start.html[Quick start: Get logs and metrics into the Elastic Stack] to learn how to ingest your data, or go to <> and learn about all the other ways you can add data.
-If you want to ingest your data, refer to {fleet-guide}/fleet-quick-start.html[Quick start: Get logs and metrics into the Elastic Stack].
+*Explore your own data in Discover.* Ready to learn more about exploring your data in *Discover*? Go to <>.
-If you want to secure access to your data, refer to our guide on <>
+*Create a dashboard with your own data.* Ready to learn more about analyzing your data in *Dashboard*? Go to <>.
-If you want to try out {ml-features} with the sample data sets, refer to
-{ml-docs}/ml-getting-started.html[Getting started with {ml}].
\ No newline at end of file
+*Try out the {ml-features}.* Ready to analyze the sample data sets and generate models for its patterns of behavior? Go to {ml-docs}/ml-getting-started.html[Getting started with {ml}].
\ No newline at end of file
diff --git a/docs/management/images/management-create-rollup-bar-chart.png b/docs/management/images/management-create-rollup-bar-chart.png
index 324cfcb9ee5fb..68ba4344c0ecf 100644
Binary files a/docs/management/images/management-create-rollup-bar-chart.png and b/docs/management/images/management-create-rollup-bar-chart.png differ
diff --git a/docs/management/images/management-rollup-index-pattern.png b/docs/management/images/management-rollup-index-pattern.png
index 57ac00be7977c..de7976e63f050 100644
Binary files a/docs/management/images/management-rollup-index-pattern.png and b/docs/management/images/management-rollup-index-pattern.png differ
diff --git a/docs/management/images/management_create_rollup_job.png b/docs/management/images/management_create_rollup_job.png
index c3139c9f8df1a..f1dd1580c3a86 100644
Binary files a/docs/management/images/management_create_rollup_job.png and b/docs/management/images/management_create_rollup_job.png differ
diff --git a/docs/management/images/management_rollup_job_dashboard.png b/docs/management/images/management_rollup_job_dashboard.png
index d3c394183cad8..9573ab7a863e8 100644
Binary files a/docs/management/images/management_rollup_job_dashboard.png and b/docs/management/images/management_rollup_job_dashboard.png differ
diff --git a/docs/management/images/management_rollup_job_details.png b/docs/management/images/management_rollup_job_details.png
index e6e93a6dae130..5372ba4ad7d13 100644
Binary files a/docs/management/images/management_rollup_job_details.png and b/docs/management/images/management_rollup_job_details.png differ
diff --git a/docs/management/images/management_rollup_job_vis.png b/docs/management/images/management_rollup_job_vis.png
deleted file mode 100644
index e1f46e4db5c0a..0000000000000
Binary files a/docs/management/images/management_rollup_job_vis.png and /dev/null differ
diff --git a/docs/management/images/management_rollup_list.png b/docs/management/images/management_rollup_list.png
index 60e9a35071003..505930bcb9a38 100644
Binary files a/docs/management/images/management_rollup_list.png and b/docs/management/images/management_rollup_list.png differ
diff --git a/docs/management/rollups/create_and_manage_rollups.asciidoc b/docs/management/rollups/create_and_manage_rollups.asciidoc
index bde2ca472b258..51821a935d3f5 100644
--- a/docs/management/rollups/create_and_manage_rollups.asciidoc
+++ b/docs/management/rollups/create_and_manage_rollups.asciidoc
@@ -64,13 +64,16 @@ You can read more at {ref}/rollup-job-config.html[rollup job configuration].
=== Try it: Create and visualize rolled up data
This example creates a rollup job to capture log data from sample web logs.
-To follow along, add the sample web logs data set.
+Before you start, <>.
In this example, you want data that is older than 7 days in the target index pattern `kibana_sample_data_logs`
-to roll up once a day into the index `rollup_logstash`. You’ll bucket the
+to roll up into the `rollup_logstash` index. You’ll bucket the
rolled up data on an hourly basis, using 60m for the time bucket configuration.
This allows for more granular queries, such as 2h and 12h.
+For this example, the job will perform the rollup every minute. However, you'd
+typically roll up less frequently in production.
+
[float]
==== Create the rollup job
@@ -80,7 +83,7 @@ As you walk through the *Create rollup job* UI, enter the data:
|*Field* |*Value*
|Name
-|logs_job
+|`logs_job`
|Index pattern
|`kibana_sample_data_logs`
@@ -89,12 +92,13 @@ As you walk through the *Create rollup job* UI, enter the data:
|`rollup_logstash`
|Frequency
-|Every day at midnight
+|Every minute
|Page size
|1000
-|Delay (latency buffer)|7d
+|Latency buffer
+|7d
|Date field
|@timestamp
@@ -118,6 +122,8 @@ As you walk through the *Create rollup job* UI, enter the data:
|bytes (average)
|===
+On the **Review and save** page, click **Start job now** and **Save**.
+
The terms, histogram, and metrics fields reflect
the key information to retain in the rolled up data: where visitors are from (geo.src),
what operating system they are using (machine.os.keyword),
@@ -133,7 +139,6 @@ rollup index, or you can remove or archive it using < Index Patterns*.
. Click *Create index pattern*, and select *Rollup index pattern* from the dropdown.
@@ -149,7 +154,11 @@ is `rollup_logstash,kibana_sample_data_logs`. In this index pattern, `rollup_log
matches the rolled up index pattern and `kibana_sample_data_logs` matches the index
pattern for raw data.
-. Open the main menu, click *Dashboard*, then create and add a vertical bar chart.
+. Open the main menu, click *Dashboard*, then *Create dashboard*.
+
+. Set the <> to *Last 90 days*.
+
+. On the dashboard, click *Create visualization*.
. Choose `rollup_logstash,kibana_sample_data_logs`
as your source to see both the raw and rolled up data.
@@ -157,13 +166,15 @@ as your source to see both the raw and rolled up data.
[role="screenshot"]
image::images/management-create-rollup-bar-chart.png[][Create visualization of rolled up data]
-. Look at the data in your visualization.
-+
-[role="screenshot"]
-image::images/management_rollup_job_vis.png[][Visualization of rolled up data]
+. Select *Bar vertical stacked* in the chart type dropdown.
-. Optionally, create a dashboard that contains visualizations of the rolled up
-data, raw data, or both.
+. Add the `@timestamp` field to the *Horizontal axis*.
+
+. Add the `bytes` field to the *Vertical axis*, defaulting to an `Average of
+bytes`.
++
+{kib} creates a vertical bar chart of your data. Select a section of the chart
+to zoom in.
+
[role="screenshot"]
image::images/management_rollup_job_dashboard.png[][Dashboard with rolled up data]
diff --git a/docs/management/snapshot-restore/images/create-policy-example.png b/docs/management/snapshot-restore/images/create-policy-example.png
old mode 100755
new mode 100644
index e871c925f5fd5..4ab5e438b306b
Binary files a/docs/management/snapshot-restore/images/create-policy-example.png and b/docs/management/snapshot-restore/images/create-policy-example.png differ
diff --git a/docs/management/snapshot-restore/images/create-policy.png b/docs/management/snapshot-restore/images/create-policy.png
old mode 100755
new mode 100644
index d9a0dce0f4190..3ba33e2522bd5
Binary files a/docs/management/snapshot-restore/images/create-policy.png and b/docs/management/snapshot-restore/images/create-policy.png differ
diff --git a/docs/management/snapshot-restore/images/create_snapshot.png b/docs/management/snapshot-restore/images/create_snapshot.png
deleted file mode 100644
index 14c1229a23ce1..0000000000000
Binary files a/docs/management/snapshot-restore/images/create_snapshot.png and /dev/null differ
diff --git a/docs/management/snapshot-restore/images/register_repo.png b/docs/management/snapshot-restore/images/register_repo.png
old mode 100755
new mode 100644
index 9e7ee9db4ce91..c742028ce108c
Binary files a/docs/management/snapshot-restore/images/register_repo.png and b/docs/management/snapshot-restore/images/register_repo.png differ
diff --git a/docs/management/snapshot-restore/images/repository_list.png b/docs/management/snapshot-restore/images/repository_list.png
old mode 100755
new mode 100644
index a4678e87bfb2c..c4eb4fc1a3d1a
Binary files a/docs/management/snapshot-restore/images/repository_list.png and b/docs/management/snapshot-restore/images/repository_list.png differ
diff --git a/docs/management/snapshot-restore/images/restore-status.png b/docs/management/snapshot-restore/images/restore-status.png
deleted file mode 100755
index fa48e32d2fef3..0000000000000
Binary files a/docs/management/snapshot-restore/images/restore-status.png and /dev/null differ
diff --git a/docs/management/snapshot-restore/images/snapshot-restore.png b/docs/management/snapshot-restore/images/snapshot-restore.png
old mode 100755
new mode 100644
index 41a292f97c853..8ca5dc95e5892
Binary files a/docs/management/snapshot-restore/images/snapshot-restore.png and b/docs/management/snapshot-restore/images/snapshot-restore.png differ
diff --git a/docs/management/snapshot-restore/images/snapshot-retention.png b/docs/management/snapshot-restore/images/snapshot-retention.png
old mode 100755
new mode 100644
index 7b390357a21b6..44dfecc1a3321
Binary files a/docs/management/snapshot-restore/images/snapshot-retention.png and b/docs/management/snapshot-restore/images/snapshot-retention.png differ
diff --git a/docs/management/snapshot-restore/images/snapshot_details.png b/docs/management/snapshot-restore/images/snapshot_details.png
old mode 100755
new mode 100644
index 2bd226eecd84e..e6c463d7acb7f
Binary files a/docs/management/snapshot-restore/images/snapshot_details.png and b/docs/management/snapshot-restore/images/snapshot_details.png differ
diff --git a/docs/management/snapshot-restore/images/snapshot_list.png b/docs/management/snapshot-restore/images/snapshot_list.png
old mode 100755
new mode 100644
index dcbb43ec2ab84..f844bfddac4be
Binary files a/docs/management/snapshot-restore/images/snapshot_list.png and b/docs/management/snapshot-restore/images/snapshot_list.png differ
diff --git a/docs/management/snapshot-restore/images/snapshot_permissions.png b/docs/management/snapshot-restore/images/snapshot_permissions.png
deleted file mode 100644
index 463d4d6e389c6..0000000000000
Binary files a/docs/management/snapshot-restore/images/snapshot_permissions.png and /dev/null differ
diff --git a/docs/management/snapshot-restore/index.asciidoc b/docs/management/snapshot-restore/index.asciidoc
index 62633441ef161..b041bd0873a05 100644
--- a/docs/management/snapshot-restore/index.asciidoc
+++ b/docs/management/snapshot-restore/index.asciidoc
@@ -2,8 +2,8 @@
[[snapshot-repositories]]
== Snapshot and Restore
-*Snapshot and Restore* enables you to backup your {es}
-indices and clusters using data and state snapshots.
+*Snapshot and Restore* lets you back up a running {es}
+cluster using data and state snapshots.
Snapshots are important because they provide a copy of your data in case
something goes wrong. If you need to roll back to an older version of your data,
you can restore a snapshot from the repository.
@@ -34,17 +34,12 @@ The minimum required permissions to access *Snapshot and Restore* include:
To add privileges, open the main menu, then click *Stack Management > Roles*.
-[role="screenshot"]
-image:management/snapshot-restore/images/snapshot_permissions.png["Edit Role"]
-
[float]
[[kib-snapshot-register-repository]]
=== Register a repository
A repository is where your snapshots live. You must register a snapshot
repository before you can perform snapshot and restore operations.
-If you don't have a repository, Kibana walks you through the process of
-registering one.
{kib} supports three repository types
out of the box: shared file system, read-only URL, and source-only.
For more information on these repositories and their settings,
@@ -52,11 +47,9 @@ see {ref}/snapshots-register-repository.html[Repositories].
To use other repositories, such as S3, see
{ref}/snapshots-register-repository.html#snapshots-repository-plugins[Repository plugins].
-
-Once you create a repository, it is listed in the *Repositories*
-view.
-Click a repository name to view its type, number of snapshots, and settings,
-and to verify status.
+The *Repositories* view displays a list of registered repositories. Click a
+repository name to view information about the repository, verify its status, or
+clean it up.
[role="screenshot"]
image:management/snapshot-restore/images/repository_list.png["Repository list"]
@@ -73,15 +66,8 @@ into each snapshot for further investigation.
[role="screenshot"]
image:management/snapshot-restore/images/snapshot_details.png["Snapshot details"]
-If you don’t have any snapshots, you can create them from the {kib} <>. The
-{ref}/snapshots-take-snapshot.html[snapshot API]
-takes the current state and data in your index or cluster, and then saves it to a
-shared repository.
-
-The snapshot process is "smart." Your first snapshot is a complete copy of
-the data in your index or cluster.
-All subsequent snapshots save the changes between the existing snapshots and
-the new data.
+If you don’t have any snapshots, you can create them using the
+{ref}/create-snapshot-api.html[create snapshot API].
[float]
[[kib-restore-snapshot]]
@@ -93,14 +79,14 @@ restore a snapshot made from one cluster to another cluster. You might
use the restore operation to:
* Recover data lost due to a failure
-* Migrate a current Elasticsearch cluster to a new version
+* Migrate an {es} cluster to a new version
* Move data from one cluster to another cluster
To get started, go to the *Snapshots* view, find the
snapshot, and click the restore icon in the *Actions* column.
The Restore wizard presents
options for the restore operation, including which
-indices to restore and whether to modify the index settings.
+data streams and indices to restore and whether to change index settings.
You can restore an existing index only if it’s closed and has the same
number of shards as the index in the snapshot.
@@ -119,7 +105,7 @@ Use a {ref}/snapshot-lifecycle-management-api.html[snapshot lifecycle policy]
to automate the creation and deletion
of cluster snapshots. Taking automatic snapshots:
-* Ensures your {es} indices and clusters are backed up on a regular basis
+* Ensures your {es} data is backed up on a regular basis
* Ensures a recent and relevant snapshot is available if a situation
arises where a cluster needs to be recovered
* Allows you to manage your snapshots in {kib}, instead of using a
@@ -138,8 +124,8 @@ You can drill down into each policy to examine its settings and last successful
You can perform the following actions on a snapshot policy:
-* *Run* a policy immediately without waiting for the scheduled time.
-This action is useful before an upgrade or before performing maintenance on indices.
+* *Run* a policy immediately without waiting for the scheduled time. This action
+is useful before an upgrade or before performing maintenance.
* *Edit* a policy and immediately apply changes to the schedule.
* *Delete* a policy to prevent any future snapshots from being taken.
This action does not cancel any currently ongoing snapshots or remove any previously taken snapshots.
@@ -160,7 +146,7 @@ and then click *Delete snapshots*.
[role="xpack"]
[[snapshot-restore-tutorial]]
-=== Tutorial: Snapshot and Restore
+=== Tutorial: Snapshot and Restore
Ready to try *Snapshot and Restore*? In this tutorial, you'll learn to:
@@ -174,15 +160,12 @@ Ready to try *Snapshot and Restore*? In this tutorial, you'll learn to:
This example shows you how to register a shared file system repository
and store snapshots.
-Before you begin, you must register the location of the repository in the
-{ref}/snapshots-register-repository.html#snapshots-filesystem-repository[path.repo] setting on
-your master and data nodes. You can do this in one of two ways:
-* Edit your `elasticsearch.yml` to include the `path.repo` setting.
-
-* Pass the `path.repo` setting when you start Elasticsearch.
-+
-`bin/elasticsearch -E path.repo=/tmp/es-backups`
+Before you begin, you must first mount the file system to the same location on
+all master and data nodes. Then add the file system’s path or parent directory
+to the
+{ref}/snapshots-register-repository.html#snapshots-filesystem-repository[`path.repo`]
+setting in `elasticsearch.yml` for each master and data node.
[float]
[[register-repo-example]]
@@ -216,13 +199,10 @@ Use the {ref}/snapshots-take-snapshot.html[snapshot API] to create a snapshot.
. Create the snapshot:
+
[source,js]
-PUT /_snapshot/my_backup/2019-04-25_snapshot?wait_for_completion=true
+PUT /_snapshot/my_backup/2099-04-25_snapshot?wait_for_completion=true
+
-In this example, the snapshot name is `2019-04-25_snapshot`. You can also
+In this example, the snapshot name is `2099-04-25_snapshot`. You can also
use {ref}/date-math-index-names.html[date math expression] for the snapshot name.
-+
-[role="screenshot"]
-image:management/snapshot-restore/images/create_snapshot.png["Create snapshot"]
. Return to *Snapshot and Restore*.
+
@@ -251,16 +231,17 @@ image:management/snapshot-restore/images/create-policy-example.png["Create polic
|Snapshot name
|``
-|Schedule
-|Every day at 1:30 a.m.
-
|Repository
|`my_backup`
+|Schedule
+|Every day at 1:30 a.m.
+
|*Snapshot settings* |
-|Indices
-|Select the indices to back up. By default, all indices, including system indices, are backed up.
+|Data streams and indices
+|Select the data streams and indices to back up. By default, all data streams
+and indices, including system indices, are backed up.
|All other settings
|Use the defaults.
@@ -280,20 +261,22 @@ Your new policy is listed in the *Policies* view, and you see a summary of its d
[[restore-snapshot-example]]
==== Restore a snapshot
-Finally, you'll restore indices from an existing snapshot.
+Finally, you'll restore data streams and indices from an existing snapshot.
-. In the *Snapshots* view, find the snapshot you want to restore, for example `2019-04-25_snapshot`.
+. In the *Snapshots* view, find the snapshot you want to restore, for example `2099-04-25_snapshot`.
. Click the restore icon in the *Actions* column.
. As you walk through the wizard, enter the following values:
+
|===
|*Logistics* |
-|Indices
-|Toggle to choose specific indices to restore, or leave in place to restore all indices.
+|Data streams and indices
+|Toggle to choose specific data streams and indices to restore. Use the default
+to restore all data streams and indices in the snapshot.
-|Rename indices
-|Toggle to give your restored indices new names, or leave in place to restore under original index names.
+|Rename data streams and indices
+|Toggle to give your restored data streams and indices new names. Use the
+default to restore the original data stream and index names.
|All other fields
|Use the defaults.
@@ -313,4 +296,4 @@ or leave in place to keep existing settings.
+
The operation loads for a few seconds,
and then you’re navigated to *Restore Status*,
-where you can monitor the status of your restored indices.
+where you can monitor the status of your restored data streams and indices.
diff --git a/docs/maps/images/gs_dashboard_with_map.png b/docs/maps/images/gs_dashboard_with_map.png
index 49b71c16c12b2..a4bf95948edf0 100644
Binary files a/docs/maps/images/gs_dashboard_with_map.png and b/docs/maps/images/gs_dashboard_with_map.png differ
diff --git a/docs/maps/images/gs_dashboard_with_terms_filter.png b/docs/maps/images/gs_dashboard_with_terms_filter.png
index 21b5c044cb35d..bf84b2ee371af 100644
Binary files a/docs/maps/images/gs_dashboard_with_terms_filter.png and b/docs/maps/images/gs_dashboard_with_terms_filter.png differ
diff --git a/docs/maps/images/layer_search.png b/docs/maps/images/layer_search.png
index 8e0e8ff628953..d3828ed5f4551 100644
Binary files a/docs/maps/images/layer_search.png and b/docs/maps/images/layer_search.png differ
diff --git a/docs/maps/images/quantitative_data_driven_styling.png b/docs/maps/images/quantitative_data_driven_styling.png
index a7852ed202016..03dc22f433eee 100644
Binary files a/docs/maps/images/quantitative_data_driven_styling.png and b/docs/maps/images/quantitative_data_driven_styling.png differ
diff --git a/docs/maps/images/sample_data_ecommerce.png b/docs/maps/images/sample_data_ecommerce.png
index 5b261bb535022..7fba3da608d15 100644
Binary files a/docs/maps/images/sample_data_ecommerce.png and b/docs/maps/images/sample_data_ecommerce.png differ
diff --git a/docs/maps/images/sample_data_web_logs.png b/docs/maps/images/sample_data_web_logs.png
index f4f4de88f1992..e4902c3e89610 100644
Binary files a/docs/maps/images/sample_data_web_logs.png and b/docs/maps/images/sample_data_web_logs.png differ
diff --git a/docs/maps/images/vector_style_class.png b/docs/maps/images/vector_style_class.png
index 8c685dfcf0ab6..69549b9f5f2d8 100644
Binary files a/docs/maps/images/vector_style_class.png and b/docs/maps/images/vector_style_class.png differ
diff --git a/docs/maps/images/vector_style_dynamic.png b/docs/maps/images/vector_style_dynamic.png
index aeaef412b5220..3032e74180afa 100644
Binary files a/docs/maps/images/vector_style_dynamic.png and b/docs/maps/images/vector_style_dynamic.png differ
diff --git a/docs/maps/images/vector_style_static.png b/docs/maps/images/vector_style_static.png
index 47d9c3b21fcb6..34908aa02fac7 100644
Binary files a/docs/maps/images/vector_style_static.png and b/docs/maps/images/vector_style_static.png differ
diff --git a/docs/maps/vector-layer.asciidoc b/docs/maps/vector-layer.asciidoc
index 2115c16a889c6..5017ecf91dffd 100644
--- a/docs/maps/vector-layer.asciidoc
+++ b/docs/maps/vector-layer.asciidoc
@@ -20,10 +20,10 @@ The index must contain at least one field mapped as {ref}/geo-point.html[geo_poi
Results are limited to the `index.max_result_window` index setting, which defaults to 10000.
Select the appropriate *Scaling* option for your use case.
+
-* *Limit results to 10000.* The layer displays features from the first `index.max_result_window` documents.
+* *Limit results to 10,000* The layer displays features from the first `index.max_result_window` documents.
Results exceeding `index.max_result_window` are not displayed.
-* *Show clusters when results exceed 10000.* When results exceed `index.max_result_window`, the layer uses {ref}/search-aggregations-bucket-geotilegrid-aggregation.html[GeoTile grid aggregation] to group your documents into clusters and displays metrics for each cluster. When results are less then `index.max_result_window`, the layer displays features from individual documents.
+* *Show clusters when results exceed 10,000* When results exceed `index.max_result_window`, the layer uses {ref}/search-aggregations-bucket-geotilegrid-aggregation.html[GeoTile grid aggregation] to group your documents into clusters and displays metrics for each cluster. When results are less then `index.max_result_window`, the layer displays features from individual documents.
* *Use vector tiles.* Vector tiles partition your map into 6 to 8 tiles.
Each tile request is limited to the `index.max_result_window` index setting.
diff --git a/docs/user/management.asciidoc b/docs/user/management.asciidoc
index b86fa82c30381..2f9f1fe371dc3 100644
--- a/docs/user/management.asciidoc
+++ b/docs/user/management.asciidoc
@@ -82,9 +82,10 @@ connectors>> for triggering actions.
| Monitor the generation of reports—PDF, PNG, and CSV—and download reports that you previously generated.
A report can contain a dashboard, visualization, saved search, or Canvas workpad.
-| {ml-docs}/ml-jobs.html[Machine Learning Jobs]
-| View your {anomaly-jobs} and {dfanalytics-jobs}. Open the Single Metric
-Viewer or Anomaly Explorer to see your {ml} results.
+| Machine Learning Jobs
+| View your <> and
+<> jobs. Open the Single Metric
+Viewer or Anomaly Explorer to see your {anomaly-detect} results.
| <>
| Detect changes in your data by creating, managing, and monitoring alerts.
diff --git a/docs/user/ml/index.asciidoc b/docs/user/ml/index.asciidoc
index b3606b122d750..a05ff1eeec4a6 100644
--- a/docs/user/ml/index.asciidoc
+++ b/docs/user/ml/index.asciidoc
@@ -48,8 +48,9 @@ pane:
image::user/ml/images/ml-job-management.png[Job Management]
You can use the *Settings* pane to create and edit
-{ml-docs}/ml-calendars.html[calendars] and the filters that are used in
-{ml-docs}/ml-rules.html[custom rules]:
+{ml-docs}/ml-ad-finding-anomalies.html#ml-ad-calendars[calendars] and the
+filters that are used in
+{ml-docs}/ml-ad-finding-anomalies.html#ml-ad-rules[custom rules]:
[role="screenshot"]
image::user/ml/images/ml-settings.png[Calendar Management]
diff --git a/docs/user/production-considerations/task-manager-health-monitoring.asciidoc b/docs/user/production-considerations/task-manager-health-monitoring.asciidoc
index 8f2c8d106c77c..3321a9d0c02a1 100644
--- a/docs/user/production-considerations/task-manager-health-monitoring.asciidoc
+++ b/docs/user/production-considerations/task-manager-health-monitoring.asciidoc
@@ -57,8 +57,12 @@ xpack.task_manager.monitored_task_execution_thresholds:
The health API is best consumed by via the `/api/task_manager/_health` endpoint.
-Additionally, the metrics are logged in the {kib} `DEBUG` logger at a regular cadence.
-To enable Task Manager DEBUG logging in your {kib} instance, add the following to your `kibana.yml`:
+Additionally, there are two ways to consume these metrics:
+
+*Debug logging*
+
+The metrics are logged in the {kib} `DEBUG` logger at a regular cadence.
+To enable Task Manager debug logging in your {kib} instance, add the following to your `kibana.yml`:
[source,yml]
----
@@ -69,7 +73,22 @@ logging:
level: debug
----
-These stats are logged based the number of milliseconds set in your <> setting, which means it could add substantial noise to your logs. Only enable this level of logging temporarily.
+These stats are logged based on the number of milliseconds set in your <> setting, which could add substantial noise to your logs. Only enable this level of logging temporarily.
+
+*Automatic logging*
+
+By default, the health API runs at a regular cadence, and each time it runs, it attempts to self evaluate its performance. If this self evaluation yields a potential problem,
+a message will log to the {kib} server log. In addition, the health API will look at how long tasks have waited to start (from when they were scheduled to start). If this number exceeds a configurable threshold (<>), the same message as above will log to the {kib} server log.
+
+This message looks like:
+
+[source,log]
+----
+Detected potential performance issue with Task Manager. Set 'xpack.task_manager.monitored_stats_health_verbose_log.enabled: true' in your Kibana.yml to enable debug logging`
+----
+
+
+If this message appears, set <> to `true` in your `kibana.yml`. This will start logging the health metrics at either a `warn` or `error` log level, depending on the detected severity of the potential problem.
[float]
[[making-sense-of-task-manager-health-stats]]
diff --git a/jest.config.integration.js b/jest.config.integration.js
index 8ff142714eebf..e2b2afaa715ee 100644
--- a/jest.config.integration.js
+++ b/jest.config.integration.js
@@ -6,24 +6,8 @@
* Side Public License, v 1.
*/
-const preset = require('@kbn/test/jest-preset');
-
module.exports = {
- preset: '@kbn/test',
+ preset: '@kbn/test/jest_integration',
rootDir: '.',
roots: ['/src', '/packages'],
- testMatch: ['**/integration_tests**/*.test.{js,mjs,ts,tsx}'],
- testPathIgnorePatterns: preset.testPathIgnorePatterns.filter(
- (pattern) => !pattern.includes('integration_tests')
- ),
- setupFilesAfterEnv: [
- '/node_modules/@kbn/test/target_node/jest/setup/after_env.integration.js',
- ],
- reporters: [
- 'default',
- ['@kbn/test/target_node/jest/junit_reporter', { reportName: 'Jest Integration Tests' }],
- ],
- coverageReporters: !!process.env.CI
- ? [['json', { file: 'jest-integration.json' }]]
- : ['html', 'text'],
};
diff --git a/package.json b/package.json
index 5cf72e2110982..2ee46c031fbf0 100644
--- a/package.json
+++ b/package.json
@@ -83,10 +83,8 @@
"**/minimist": "^1.2.5",
"**/node-jose/node-forge": "^0.10.0",
"**/pdfkit/crypto-js": "4.0.0",
- "**/prismjs": "1.24.0",
"**/react-syntax-highlighter": "^15.3.1",
"**/react-syntax-highlighter/**/highlight.js": "^10.4.1",
- "**/refractor": "^3.3.1",
"**/request": "^2.88.2",
"**/trim": "1.0.1",
"**/typescript": "4.1.3",
@@ -103,7 +101,7 @@
"@elastic/datemath": "link:bazel-bin/packages/elastic-datemath",
"@elastic/elasticsearch": "npm:@elastic/elasticsearch-canary@^8.0.0-canary.13",
"@elastic/ems-client": "7.14.0",
- "@elastic/eui": "34.5.2",
+ "@elastic/eui": "35.0.0",
"@elastic/filesaver": "1.1.2",
"@elastic/good": "^9.0.1-kibana3",
"@elastic/maki": "6.3.0",
@@ -155,6 +153,7 @@
"@kbn/securitysolution-utils": "link:bazel-bin/packages/kbn-securitysolution-utils",
"@kbn/server-http-tools": "link:bazel-bin/packages/kbn-server-http-tools",
"@kbn/server-route-repository": "link:bazel-bin/packages/kbn-server-route-repository",
+ "@kbn/typed-react-router-config": "link:bazel-bin/packages/kbn-typed-react-router-config",
"@kbn/std": "link:bazel-bin/packages/kbn-std",
"@kbn/tinymath": "link:bazel-bin/packages/kbn-tinymath",
"@kbn/ui-framework": "link:bazel-bin/packages/kbn-ui-framework",
@@ -179,6 +178,7 @@
"@turf/distance": "6.0.1",
"@turf/helpers": "6.0.1",
"@turf/length": "^6.0.2",
+ "@types/react-router-config": "^5.0.2",
"@types/redux-logger": "^3.0.8",
"JSONStream": "1.3.5",
"abort-controller": "^3.0.0",
@@ -358,6 +358,7 @@
"react-resize-detector": "^4.2.0",
"react-reverse-portal": "^1.0.4",
"react-router": "^5.2.0",
+ "react-router-config": "^5.1.1",
"react-router-dom": "^5.2.0",
"react-router-redux": "^4.0.8",
"react-shortcuts": "^2.0.0",
diff --git a/packages/BUILD.bazel b/packages/BUILD.bazel
index de7a27fd51276..938afdc205a44 100644
--- a/packages/BUILD.bazel
+++ b/packages/BUILD.bazel
@@ -56,6 +56,7 @@ filegroup(
"//packages/kbn-test:build",
"//packages/kbn-test-subj-selector:build",
"//packages/kbn-tinymath:build",
+ "//packages/kbn-typed-react-router-config:build",
"//packages/kbn-ui-framework:build",
"//packages/kbn-ui-shared-deps:build",
"//packages/kbn-utility-types:build",
diff --git a/packages/kbn-io-ts-utils/src/deep_exact_rt/index.test.ts b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.test.ts
new file mode 100644
index 0000000000000..fe09fb442799c
--- /dev/null
+++ b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.test.ts
@@ -0,0 +1,73 @@
+/*
+ * 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 * as t from 'io-ts';
+import { deepExactRt } from '.';
+import { mergeRt } from '../merge_rt';
+
+describe('deepExactRt', () => {
+ it('recursively wraps partial/interface types in t.exact', () => {
+ const a = t.type({
+ path: t.type({
+ serviceName: t.string,
+ }),
+ query: t.type({
+ foo: t.string,
+ }),
+ });
+
+ const b = t.type({
+ path: t.type({
+ transactionType: t.string,
+ }),
+ });
+
+ const merged = mergeRt(a, b);
+
+ expect(
+ deepExactRt(a).decode({
+ path: {
+ serviceName: '',
+ transactionType: '',
+ },
+ query: {
+ foo: '',
+ bar: '',
+ },
+ // @ts-ignore
+ }).right
+ ).toEqual({ path: { serviceName: '' }, query: { foo: '' } });
+
+ expect(
+ deepExactRt(b).decode({
+ path: {
+ serviceName: '',
+ transactionType: '',
+ },
+ query: {
+ foo: '',
+ bar: '',
+ },
+ // @ts-ignore
+ }).right
+ ).toEqual({ path: { transactionType: '' } });
+
+ expect(
+ deepExactRt(merged).decode({
+ path: {
+ serviceName: '',
+ transactionType: '',
+ },
+ query: {
+ foo: '',
+ bar: '',
+ },
+ // @ts-ignore
+ }).right
+ ).toEqual({ path: { serviceName: '', transactionType: '' }, query: { foo: '' } });
+ });
+});
diff --git a/packages/kbn-io-ts-utils/src/deep_exact_rt/index.ts b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.ts
new file mode 100644
index 0000000000000..8ebb9bbdd52f9
--- /dev/null
+++ b/packages/kbn-io-ts-utils/src/deep_exact_rt/index.ts
@@ -0,0 +1,45 @@
+/*
+ * 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 * as t from 'io-ts';
+import { mapValues } from 'lodash';
+import { mergeRt } from '../merge_rt';
+import { isParsableType, ParseableType } from '../parseable_types';
+
+export function deepExactRt | ParseableType>(type: T): T;
+
+export function deepExactRt(type: t.Type | ParseableType) {
+ if (!isParsableType(type)) {
+ return type;
+ }
+
+ switch (type._tag) {
+ case 'ArrayType':
+ return t.array(deepExactRt(type.type));
+
+ case 'DictionaryType':
+ return t.dictionary(type.domain, deepExactRt(type.codomain));
+
+ case 'InterfaceType':
+ return t.exact(t.interface(mapValues(type.props, deepExactRt)));
+
+ case 'PartialType':
+ return t.exact(t.partial(mapValues(type.props, deepExactRt)));
+
+ case 'IntersectionType':
+ return t.intersection(type.types.map(deepExactRt) as any);
+
+ case 'UnionType':
+ return t.union(type.types.map(deepExactRt) as any);
+
+ case 'MergeType':
+ return mergeRt(deepExactRt(type.types[0]), deepExactRt(type.types[1]));
+
+ default:
+ return type;
+ }
+}
diff --git a/packages/kbn-io-ts-utils/src/parseable_types/index.ts b/packages/kbn-io-ts-utils/src/parseable_types/index.ts
new file mode 100644
index 0000000000000..089717ad8891b
--- /dev/null
+++ b/packages/kbn-io-ts-utils/src/parseable_types/index.ts
@@ -0,0 +1,39 @@
+/*
+ * 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 * as t from 'io-ts';
+import { MergeType } from '../merge_rt';
+
+export type ParseableType =
+ | t.StringType
+ | t.NumberType
+ | t.BooleanType
+ | t.ArrayType
+ | t.RecordC
+ | t.DictionaryType
+ | t.InterfaceType
+ | t.PartialType
+ | t.UnionType
+ | t.IntersectionType
+ | MergeType;
+
+const parseableTags = [
+ 'StringType',
+ 'NumberType',
+ 'BooleanType',
+ 'ArrayType',
+ 'DictionaryType',
+ 'InterfaceType',
+ 'PartialType',
+ 'UnionType',
+ 'IntersectionType',
+ 'MergeType',
+];
+
+export const isParsableType = (type: t.Type | ParseableType): type is ParseableType => {
+ return '_tag' in type && parseableTags.includes(type._tag);
+};
diff --git a/packages/kbn-io-ts-utils/src/to_json_schema/index.ts b/packages/kbn-io-ts-utils/src/to_json_schema/index.ts
index fc196a7c3123e..702c0150d07f7 100644
--- a/packages/kbn-io-ts-utils/src/to_json_schema/index.ts
+++ b/packages/kbn-io-ts-utils/src/to_json_schema/index.ts
@@ -7,35 +7,7 @@
*/
import * as t from 'io-ts';
import { mapValues } from 'lodash';
-
-type JSONSchemableValueType =
- | t.StringType
- | t.NumberType
- | t.BooleanType
- | t.ArrayType
- | t.RecordC
- | t.DictionaryType
- | t.InterfaceType
- | t.PartialType
- | t.UnionType
- | t.IntersectionType;
-
-const tags = [
- 'StringType',
- 'NumberType',
- 'BooleanType',
- 'ArrayType',
- 'DictionaryType',
- 'InterfaceType',
- 'PartialType',
- 'UnionType',
- 'IntersectionType',
-];
-
-const isSchemableValueType = (type: t.Mixed): type is JSONSchemableValueType => {
- // @ts-ignore
- return tags.includes(type._tag);
-};
+import { isParsableType } from '../parseable_types';
interface JSONSchemaObject {
type: 'object';
@@ -74,7 +46,7 @@ type JSONSchema =
| JSONSchemaAnyOf;
export const toJsonSchema = (type: t.Mixed): JSONSchema => {
- if (isSchemableValueType(type)) {
+ if (isParsableType(type)) {
switch (type._tag) {
case 'ArrayType':
return { type: 'array', items: toJsonSchema(type.type) };
diff --git a/packages/kbn-optimizer/limits.yml b/packages/kbn-optimizer/limits.yml
index 4524cbe8912cf..d38c3aa346147 100644
--- a/packages/kbn-optimizer/limits.yml
+++ b/packages/kbn-optimizer/limits.yml
@@ -112,4 +112,5 @@ pageLoadAssetSize:
visTypePie: 35583
expressionRevealImage: 25675
cases: 144442
+ expressionError: 22127
userSetup: 18532
diff --git a/packages/kbn-rule-data-utils/package.json b/packages/kbn-rule-data-utils/package.json
index 6f0b8439ec891..42223e51ec2d6 100644
--- a/packages/kbn-rule-data-utils/package.json
+++ b/packages/kbn-rule-data-utils/package.json
@@ -4,10 +4,5 @@
"types": "./target/index.d.ts",
"version": "1.0.0",
"license": "SSPL-1.0 OR Elastic License 2.0",
- "private": true,
- "scripts": {
- "build": "../../node_modules/.bin/tsc",
- "kbn:bootstrap": "yarn build",
- "kbn:watch": "yarn build --watch"
- }
+ "private": true
}
diff --git a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts b/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts
index 967cebc360f61..051c359dc4612 100644
--- a/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts
+++ b/packages/kbn-securitysolution-list-utils/src/autocomplete_operators/index.ts
@@ -11,8 +11,7 @@ import {
ListOperatorEnum as OperatorEnum,
ListOperatorTypeEnum as OperatorTypeEnum,
} from '@kbn/securitysolution-io-ts-list-types';
-
-import { OperatorOption } from './types';
+import { OperatorOption } from '../types';
export const isOperator: OperatorOption = {
message: i18n.translate('lists.exceptions.isOperatorLabel', {
diff --git a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts
index d208624b69fc5..38446b2a08ec0 100644
--- a/packages/kbn-securitysolution-list-utils/src/helpers/index.ts
+++ b/packages/kbn-securitysolution-list-utils/src/helpers/index.ts
@@ -43,7 +43,6 @@ import {
isOneOfOperator,
isOperator,
} from '../autocomplete_operators';
-import { OperatorOption } from '../autocomplete_operators/types';
import {
BuilderEntry,
@@ -52,6 +51,7 @@ import {
EmptyNestedEntry,
ExceptionsBuilderExceptionItem,
FormattedBuilderEntry,
+ OperatorOption,
} from '../types';
export const isEntryNested = (item: BuilderEntry): item is EntryNested => {
diff --git a/packages/kbn-securitysolution-list-utils/src/types/index.ts b/packages/kbn-securitysolution-list-utils/src/types/index.ts
index faf68ca157981..537ac06a49f34 100644
--- a/packages/kbn-securitysolution-list-utils/src/types/index.ts
+++ b/packages/kbn-securitysolution-list-utils/src/types/index.ts
@@ -23,7 +23,12 @@ import {
EXCEPTION_LIST_NAMESPACE_AGNOSTIC,
} from '@kbn/securitysolution-list-constants';
-import type { OperatorOption } from '../autocomplete_operators/types';
+export interface OperatorOption {
+ message: string;
+ value: string;
+ operator: OperatorEnum;
+ type: OperatorTypeEnum;
+}
/**
* @deprecated Use the one from core once it is in its own package which will be from:
diff --git a/packages/kbn-spec-to-console/README.md b/packages/kbn-spec-to-console/README.md
index 0328dec791320..a0e654713f61b 100644
--- a/packages/kbn-spec-to-console/README.md
+++ b/packages/kbn-spec-to-console/README.md
@@ -18,15 +18,10 @@ git pull --depth=1 origin master
### Usage
-You need to run the command twice: once for the **OSS** specs and once for the **X-Pack** specs
At the root of the Kibana repository, run the following commands:
```sh
-# OSS
yarn spec_to_console -g "/rest-api-spec/src/main/resources/rest-api-spec/api/*" -d "src/plugins/console/server/lib/spec_definitions/json/generated"
-
-# X-pack
-yarn spec_to_console -g "/x-pack/plugin/src/test/resources/rest-api-spec/api/*" -d "x-pack/plugins/console_extensions/server/lib/spec_definitions/json/generated"
```
### Information used in Console that is not available in the REST spec
diff --git a/packages/kbn-spec-to-console/lib/convert/params.js b/packages/kbn-spec-to-console/lib/convert/params.js
index e5365b4d7311e..1aa89be11c76d 100644
--- a/packages/kbn-spec-to-console/lib/convert/params.js
+++ b/packages/kbn-spec-to-console/lib/convert/params.js
@@ -37,6 +37,7 @@ module.exports = (params) => {
case 'string':
case 'number':
case 'number|string':
+ case 'boolean|long':
result[param] = defaultValue || '';
break;
case 'list':
diff --git a/packages/kbn-test/BUILD.bazel b/packages/kbn-test/BUILD.bazel
index 28c42a4c47684..432778f888e7a 100644
--- a/packages/kbn-test/BUILD.bazel
+++ b/packages/kbn-test/BUILD.bazel
@@ -28,6 +28,7 @@ filegroup(
NPM_MODULE_EXTRA_FILES = [
"jest/package.json",
"jest-preset.js",
+ "jest_integration/jest-preset.js",
"jest.config.js",
"README.md",
"package.json",
diff --git a/packages/kbn-test/jest_integration/jest-preset.js b/packages/kbn-test/jest_integration/jest-preset.js
new file mode 100644
index 0000000000000..7504dec9e7a20
--- /dev/null
+++ b/packages/kbn-test/jest_integration/jest-preset.js
@@ -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 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.
+ */
+
+const preset = require('../jest-preset');
+
+module.exports = {
+ ...preset,
+ testMatch: ['**/integration_tests**/*.test.{js,mjs,ts,tsx}'],
+ testPathIgnorePatterns: preset.testPathIgnorePatterns.filter(
+ (pattern) => !pattern.includes('integration_tests')
+ ),
+ setupFilesAfterEnv: [
+ '/node_modules/@kbn/test/target_node/jest/setup/after_env.integration.js',
+ '/node_modules/@kbn/test/target_node/jest/setup/mocks.js',
+ ],
+ reporters: [
+ 'default',
+ ['@kbn/test/target_node/jest/junit_reporter', { reportName: 'Jest Integration Tests' }],
+ ],
+ coverageReporters: !!process.env.CI
+ ? [['json', { file: 'jest-integration.json' }]]
+ : ['html', 'text'],
+};
diff --git a/packages/kbn-typed-react-router-config/BUILD.bazel b/packages/kbn-typed-react-router-config/BUILD.bazel
new file mode 100644
index 0000000000000..90f1acf43d3e7
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/BUILD.bazel
@@ -0,0 +1,113 @@
+load("@npm//@bazel/typescript:index.bzl", "ts_config", "ts_project")
+load("@build_bazel_rules_nodejs//:index.bzl", "js_library", "pkg_npm")
+
+PKG_BASE_NAME = "kbn-typed-react-router-config"
+PKG_REQUIRE_NAME = "@kbn/typed-react-router-config"
+
+SOURCE_FILES = glob(
+ [
+ "src/**/*.ts",
+ "src/**/*.tsx",
+ ],
+ exclude = [
+ "**/*.test.*",
+ ]
+)
+
+SRCS = SOURCE_FILES
+
+filegroup(
+ name = "srcs",
+ srcs = SRCS,
+)
+
+NPM_MODULE_EXTRA_FILES = [
+ "package.json",
+]
+
+SRC_DEPS = [
+ "@npm//tslib",
+ "@npm//utility-types",
+ "@npm//io-ts",
+ "@npm//query-string",
+ "@npm//react-router-config",
+ "@npm//react-router-dom",
+ "//packages/kbn-io-ts-utils",
+]
+
+TYPES_DEPS = [
+ "@npm//@types/jest",
+ "@npm//@types/node",
+ "@npm//@types/react-router-config",
+ "@npm//@types/react-router-dom",
+]
+
+DEPS = SRC_DEPS + TYPES_DEPS
+
+ts_config(
+ name = "tsconfig",
+ src = "tsconfig.json",
+ deps = [
+ "//:tsconfig.base.json",
+ ],
+)
+
+ts_config(
+ name = "tsconfig_browser",
+ src = "tsconfig.browser.json",
+ deps = [
+ "//:tsconfig.base.json",
+ "//:tsconfig.browser.json",
+ ],
+)
+
+ts_project(
+ name = "tsc",
+ args = ['--pretty'],
+ srcs = SRCS,
+ deps = DEPS,
+ declaration = True,
+ declaration_dir = "target_types",
+ declaration_map = True,
+ incremental = True,
+ out_dir = "target_node",
+ source_map = True,
+ root_dir = "src",
+ tsconfig = ":tsconfig",
+)
+
+ts_project(
+ name = "tsc_browser",
+ args = ['--pretty'],
+ srcs = SRCS,
+ deps = DEPS,
+ declaration = False,
+ incremental = True,
+ out_dir = "target_web",
+ source_map = True,
+ root_dir = "src",
+ tsconfig = ":tsconfig_browser",
+)
+
+js_library(
+ name = PKG_BASE_NAME,
+ srcs = NPM_MODULE_EXTRA_FILES,
+ deps = DEPS + [":tsc", ":tsc_browser"],
+ package_name = PKG_REQUIRE_NAME,
+ visibility = ["//visibility:public"],
+)
+
+pkg_npm(
+ name = "npm_module",
+ deps = [
+ ":%s" % PKG_BASE_NAME,
+ ]
+)
+
+filegroup(
+ name = "build",
+ srcs = [
+ ":npm_module",
+ ],
+ visibility = ["//visibility:public"],
+)
diff --git a/packages/kbn-typed-react-router-config/jest.config.js b/packages/kbn-typed-react-router-config/jest.config.js
new file mode 100644
index 0000000000000..3a6b09c5677db
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/jest.config.js
@@ -0,0 +1,13 @@
+/*
+ * 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.
+ */
+
+module.exports = {
+ preset: '@kbn/test',
+ rootDir: '../..',
+ roots: ['/packages/kbn-typed-react-router-config'],
+};
diff --git a/packages/kbn-typed-react-router-config/package.json b/packages/kbn-typed-react-router-config/package.json
new file mode 100644
index 0000000000000..50c2e4b5d7e89
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/package.json
@@ -0,0 +1,9 @@
+{
+ "name": "@kbn/typed-react-router-config",
+ "main": "target_node/index.js",
+ "types": "target_types/index.d.ts",
+ "browser": "target_web/index.js",
+ "version": "1.0.0",
+ "license": "SSPL-1.0 OR Elastic License 2.0",
+ "private": true
+}
diff --git a/packages/kbn-typed-react-router-config/src/create_router.test.tsx b/packages/kbn-typed-react-router-config/src/create_router.test.tsx
new file mode 100644
index 0000000000000..49f6961fa3a85
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/create_router.test.tsx
@@ -0,0 +1,239 @@
+/*
+ * 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 React from 'react';
+import * as t from 'io-ts';
+import { toNumberRt } from '@kbn/io-ts-utils/target/to_number_rt';
+import { createRouter } from './create_router';
+import { createMemoryHistory } from 'history';
+import { route } from './route';
+
+describe('createRouter', () => {
+ const routes = route([
+ {
+ path: '/',
+ element: <>>,
+ children: [
+ {
+ path: '/',
+ element: <>>,
+ params: t.type({
+ query: t.type({
+ rangeFrom: t.string,
+ rangeTo: t.string,
+ }),
+ }),
+ children: [
+ {
+ path: '/services',
+ element: <>>,
+ params: t.type({
+ query: t.type({
+ transactionType: t.string,
+ }),
+ }),
+ },
+ {
+ path: '/services/:serviceName',
+ element: <>>,
+ params: t.type({
+ path: t.type({
+ serviceName: t.string,
+ }),
+ query: t.type({
+ transactionType: t.string,
+ environment: t.string,
+ }),
+ }),
+ },
+ {
+ path: '/traces',
+ element: <>>,
+ params: t.type({
+ query: t.type({
+ aggregationType: t.string,
+ }),
+ }),
+ },
+ {
+ path: '/service-map',
+ element: <>>,
+ params: t.type({
+ query: t.type({
+ maxNumNodes: t.string.pipe(toNumberRt as any),
+ }),
+ }),
+ },
+ ],
+ },
+ ],
+ },
+ ] as const);
+
+ let history = createMemoryHistory();
+ const router = createRouter(routes);
+
+ beforeEach(() => {
+ history = createMemoryHistory();
+ });
+
+ describe('getParams', () => {
+ it('returns parameters for routes matching the path only', () => {
+ history.push('/services?rangeFrom=now-15m&rangeTo=now&transactionType=request');
+ const topLevelParams = router.getParams('/', history.location);
+
+ expect(topLevelParams).toEqual({
+ path: {},
+ query: {
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ },
+ });
+
+ history.push('/services?rangeFrom=now-15m&rangeTo=now&transactionType=request');
+
+ const inventoryParams = router.getParams('/services', history.location);
+
+ expect(inventoryParams).toEqual({
+ path: {},
+ query: {
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ transactionType: 'request',
+ },
+ });
+
+ history.push('/traces?rangeFrom=now-15m&rangeTo=now&aggregationType=avg');
+
+ const topTracesParams = router.getParams('/traces', history.location);
+
+ expect(topTracesParams).toEqual({
+ path: {},
+ query: {
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ aggregationType: 'avg',
+ },
+ });
+
+ history.push(
+ '/services/opbeans-java?rangeFrom=now-15m&rangeTo=now&environment=production&transactionType=request'
+ );
+
+ const serviceOverviewParams = router.getParams('/services/:serviceName', history.location);
+
+ expect(serviceOverviewParams).toEqual({
+ path: {
+ serviceName: 'opbeans-java',
+ },
+ query: {
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ environment: 'production',
+ transactionType: 'request',
+ },
+ });
+ });
+
+ it('decodes the path and query parameters based on the route type', () => {
+ history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3');
+ const topServiceMapParams = router.getParams('/service-map', history.location);
+
+ expect(topServiceMapParams).toEqual({
+ path: {},
+ query: {
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ maxNumNodes: 3,
+ },
+ });
+ });
+
+ it('throws an error if the given path does not match any routes', () => {
+ expect(() => {
+ router.getParams('/service-map', history.location);
+ }).toThrowError('No matching route found for /service-map');
+ });
+ });
+
+ describe('matchRoutes', () => {
+ it('returns only the routes matching the path', () => {
+ history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3');
+
+ expect(router.matchRoutes('/', history.location).length).toEqual(2);
+ expect(router.matchRoutes('/service-map', history.location).length).toEqual(3);
+ });
+
+ it('throws an error if the given path does not match any routes', () => {
+ history.push('/service-map?rangeFrom=now-15m&rangeTo=now&maxNumNodes=3');
+
+ expect(() => {
+ router.matchRoutes('/traces', history.location);
+ }).toThrowError('No matching route found for /traces');
+ });
+ });
+
+ describe('link', () => {
+ it('returns a link for the given route', () => {
+ const serviceOverviewLink = router.link('/services/:serviceName', {
+ path: { serviceName: 'opbeans-java' },
+ query: {
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ environment: 'production',
+ transactionType: 'request',
+ },
+ });
+
+ expect(serviceOverviewLink).toEqual(
+ '/services/opbeans-java?environment=production&rangeFrom=now-15m&rangeTo=now&transactionType=request'
+ );
+
+ const servicesLink = router.link('/services', {
+ query: {
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ transactionType: 'request',
+ },
+ });
+
+ expect(servicesLink).toEqual(
+ '/services?rangeFrom=now-15m&rangeTo=now&transactionType=request'
+ );
+
+ const serviceMapLink = router.link('/service-map', {
+ query: {
+ maxNumNodes: '3',
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ },
+ });
+
+ expect(serviceMapLink).toEqual('/service-map?maxNumNodes=3&rangeFrom=now-15m&rangeTo=now');
+ });
+
+ it('validates the parameters needed for the route', () => {
+ expect(() => {
+ router.link('/traces', {
+ query: {
+ rangeFrom: {},
+ },
+ } as any);
+ }).toThrowError();
+
+ expect(() => {
+ router.link('/service-map', {
+ query: {
+ maxNumNodes: 3,
+ rangeFrom: 'now-15m',
+ rangeTo: 'now',
+ },
+ } as any);
+ }).toThrowError();
+ });
+ });
+});
diff --git a/packages/kbn-typed-react-router-config/src/create_router.ts b/packages/kbn-typed-react-router-config/src/create_router.ts
new file mode 100644
index 0000000000000..51b6e2a2f5692
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/create_router.ts
@@ -0,0 +1,158 @@
+/*
+ * 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 { isLeft } from 'fp-ts/lib/Either';
+import { Location } from 'history';
+import { PathReporter } from 'io-ts/lib/PathReporter';
+import {
+ matchRoutes as matchRoutesConfig,
+ RouteConfig as ReactRouterConfig,
+} from 'react-router-config';
+import qs from 'query-string';
+import { findLastIndex, merge, compact } from 'lodash';
+import { deepExactRt } from '@kbn/io-ts-utils/target/deep_exact_rt';
+import { mergeRt } from '@kbn/io-ts-utils/target/merge_rt';
+import { Route, Router } from './types';
+
+export function createRouter(routes: TRoutes): Router {
+ const routesByReactRouterConfig = new Map();
+ const reactRouterConfigsByRoute = new Map();
+
+ const reactRouterConfigs = routes.map((route) => toReactRouterConfigRoute(route));
+
+ function toReactRouterConfigRoute(route: Route, prefix: string = ''): ReactRouterConfig {
+ const path = `${prefix}${route.path}`.replace(/\/{2,}/g, '/').replace(/\/$/, '') || '/';
+ const reactRouterConfig: ReactRouterConfig = {
+ component: () => route.element,
+ routes:
+ (route.children as Route[] | undefined)?.map((child) =>
+ toReactRouterConfigRoute(child, path)
+ ) ?? [],
+ exact: !route.children?.length,
+ path,
+ };
+
+ routesByReactRouterConfig.set(reactRouterConfig, route);
+ reactRouterConfigsByRoute.set(route, reactRouterConfig);
+
+ return reactRouterConfig;
+ }
+
+ const matchRoutes = (...args: any[]) => {
+ let path: string = args[0];
+ let location: Location = args[1];
+
+ if (args.length === 1) {
+ location = args[0] as Location;
+ path = location.pathname;
+ }
+
+ const greedy = path.endsWith('/*') || args.length === 1;
+
+ if (!path) {
+ path = '/';
+ }
+
+ const matches = matchRoutesConfig(reactRouterConfigs, location.pathname);
+
+ const matchIndex = greedy
+ ? matches.length - 1
+ : findLastIndex(matches, (match) => match.route.path === path);
+
+ if (matchIndex === -1) {
+ throw new Error(`No matching route found for ${path}`);
+ }
+
+ return matches.slice(0, matchIndex + 1).map((matchedRoute) => {
+ const route = routesByReactRouterConfig.get(matchedRoute.route);
+
+ if (route?.params) {
+ const decoded = deepExactRt(route.params).decode({
+ path: matchedRoute.match.params,
+ query: qs.parse(location.search),
+ });
+
+ if (isLeft(decoded)) {
+ throw new Error(PathReporter.report(decoded).join('\n'));
+ }
+
+ return {
+ match: {
+ ...matchedRoute.match,
+ params: decoded.right,
+ },
+ route,
+ };
+ }
+
+ return {
+ match: {
+ ...matchedRoute.match,
+ params: {
+ path: {},
+ query: {},
+ },
+ },
+ route,
+ };
+ });
+ };
+
+ const link = (path: string, ...args: any[]) => {
+ const params: { path?: Record; query?: Record } | undefined = args[0];
+
+ const paramsWithDefaults = merge({ path: {}, query: {} }, params);
+
+ path = path
+ .split('/')
+ .map((part) => {
+ return part.startsWith(':') ? paramsWithDefaults.path[part.split(':')[1]] : part;
+ })
+ .join('/');
+
+ const matches = matchRoutesConfig(reactRouterConfigs, path);
+
+ if (!matches.length) {
+ throw new Error(`No matching route found for ${path}`);
+ }
+
+ const validationType = mergeRt(
+ ...(compact(
+ matches.map((match) => {
+ return routesByReactRouterConfig.get(match.route)?.params;
+ })
+ ) as [any, any])
+ );
+
+ const validation = validationType.decode(paramsWithDefaults);
+
+ if (isLeft(validation)) {
+ throw new Error(PathReporter.report(validation).join('\n'));
+ }
+
+ return qs.stringifyUrl({
+ url: path,
+ query: paramsWithDefaults.query,
+ });
+ };
+
+ return {
+ link: (path, ...args) => {
+ return link(path, ...args);
+ },
+ getParams: (path, location) => {
+ const matches = matchRoutes(path, location);
+ return merge({ path: {}, query: {} }, ...matches.map((match) => match.match.params));
+ },
+ matchRoutes: (...args: any[]) => {
+ return matchRoutes(...args) as any;
+ },
+ getRoutePath: (route) => {
+ return reactRouterConfigsByRoute.get(route)!.path as string;
+ },
+ };
+}
diff --git a/packages/kbn-typed-react-router-config/src/index.ts b/packages/kbn-typed-react-router-config/src/index.ts
new file mode 100644
index 0000000000000..b58c70998901c
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/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 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 * from './create_router';
+export * from './types';
+export * from './outlet';
+export * from './route';
+export * from './route_renderer';
+export * from './router_provider';
+export * from './unconst';
+export * from './use_current_route';
+export * from './use_match_routes';
+export * from './use_params';
+export * from './use_router';
+export * from './use_route_path';
diff --git a/packages/kbn-typed-react-router-config/src/outlet.tsx b/packages/kbn-typed-react-router-config/src/outlet.tsx
new file mode 100644
index 0000000000000..696085489abee
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/outlet.tsx
@@ -0,0 +1,13 @@
+/*
+ * 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 { useCurrentRoute } from './use_current_route';
+
+export function Outlet() {
+ const { element } = useCurrentRoute();
+ return element;
+}
diff --git a/packages/kbn-typed-react-router-config/src/route.ts b/packages/kbn-typed-react-router-config/src/route.ts
new file mode 100644
index 0000000000000..b9b228d1009e2
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/route.ts
@@ -0,0 +1,15 @@
+/*
+ * 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 { Route } from './types';
+import { Unconst, unconst } from './unconst';
+
+export function route(
+ r: TRoute
+): Unconst {
+ return unconst(r);
+}
diff --git a/packages/kbn-typed-react-router-config/src/route_renderer.tsx b/packages/kbn-typed-react-router-config/src/route_renderer.tsx
new file mode 100644
index 0000000000000..e7a39aa7d5d16
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/route_renderer.tsx
@@ -0,0 +1,27 @@
+/*
+ * 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 React from 'react';
+import { CurrentRouteContextProvider } from './use_current_route';
+import { RouteMatch } from './types';
+import { useMatchRoutes } from './use_match_routes';
+
+export function RouteRenderer() {
+ const matches: RouteMatch[] = useMatchRoutes();
+
+ return matches
+ .concat()
+ .reverse()
+ .reduce((prev, match) => {
+ const { element } = match.route;
+ return (
+
+ {element}
+
+ );
+ }, <>>);
+}
diff --git a/packages/kbn-typed-react-router-config/src/router_provider.tsx b/packages/kbn-typed-react-router-config/src/router_provider.tsx
new file mode 100644
index 0000000000000..d2512ba8fe426
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/router_provider.tsx
@@ -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 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 { History } from 'history';
+import React from 'react';
+import { Router as ReactRouter } from 'react-router-dom';
+import { Route, Router } from './types';
+import { RouterContextProvider } from './use_router';
+
+export function RouterProvider({
+ children,
+ router,
+ history,
+}: {
+ router: Router;
+ history: History;
+ children: React.ReactElement;
+}) {
+ return (
+
+ {children}
+
+ );
+}
diff --git a/packages/kbn-typed-react-router-config/src/types/index.ts b/packages/kbn-typed-react-router-config/src/types/index.ts
new file mode 100644
index 0000000000000..dfd9893966491
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/types/index.ts
@@ -0,0 +1,421 @@
+/*
+ * 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 { Location } from 'history';
+import * as t from 'io-ts';
+import { ReactElement } from 'react';
+import { RequiredKeys, ValuesType } from 'utility-types';
+// import { unconst } from '../unconst';
+import { NormalizePath } from './utils';
+
+export type PathsOf = keyof MapRoutes & string;
+
+export interface RouteMatch {
+ route: TRoute;
+ match: {
+ isExact: boolean;
+ path: string;
+ url: string;
+ params: TRoute extends {
+ params: t.Type;
+ }
+ ? t.OutputOf
+ : {};
+ };
+}
+
+type ToRouteMatch = TRoutes extends []
+ ? []
+ : TRoutes extends [Route]
+ ? [RouteMatch]
+ : TRoutes extends [Route, ...infer TTail]
+ ? TTail extends Route[]
+ ? [RouteMatch, ...ToRouteMatch]
+ : []
+ : [];
+
+type UnwrapRouteMap = TRoute extends {
+ parents: Route[];
+}
+ ? ToRouteMatch<[...TRoute['parents'], Omit]>
+ : ToRouteMatch<[Omit]>;
+
+export type Match = MapRoutes extends {
+ [key in TPath]: Route;
+}
+ ? UnwrapRouteMap[TPath]>
+ : [];
+
+interface PlainRoute {
+ path: string;
+ element: ReactElement;
+ children?: PlainRoute[];
+ params?: t.Type;
+}
+
+interface ReadonlyPlainRoute {
+ readonly path: string;
+ readonly element: ReactElement;
+ readonly children?: readonly ReadonlyPlainRoute[];
+ readonly params?: t.Type;
+}
+
+export type Route = PlainRoute | ReadonlyPlainRoute;
+
+interface DefaultOutput {
+ path: {};
+ query: {};
+}
+
+type OutputOfRouteMatch = TRouteMatch extends {
+ route: { params: t.Type };
+}
+ ? t.OutputOf
+ : DefaultOutput;
+
+type OutputOfMatches = TRouteMatches extends [RouteMatch]
+ ? OutputOfRouteMatch
+ : TRouteMatches extends [RouteMatch, ...infer TNextRouteMatches]
+ ? OutputOfRouteMatch &
+ (TNextRouteMatches extends RouteMatch[] ? OutputOfMatches : DefaultOutput)
+ : TRouteMatches extends RouteMatch[]
+ ? OutputOfRouteMatch>
+ : DefaultOutput;
+
+export type OutputOf> = OutputOfMatches<
+ Match
+> &
+ DefaultOutput;
+
+type TypeOfRouteMatch = TRouteMatch extends {
+ route: { params: t.Type };
+}
+ ? t.TypeOf
+ : {};
+
+type TypeOfMatches = TRouteMatches extends [RouteMatch]
+ ? TypeOfRouteMatch
+ : TRouteMatches extends [RouteMatch, ...infer TNextRouteMatches]
+ ? TypeOfRouteMatch &
+ (TNextRouteMatches extends RouteMatch[] ? TypeOfMatches : {})
+ : {};
+
+export type TypeOf> = TypeOfMatches<
+ Match
+>;
+
+export type TypeAsArgs = keyof TObject extends never
+ ? []
+ : RequiredKeys extends never
+ ? [TObject] | []
+ : [TObject];
+
+export interface Router {
+ matchRoutes>(
+ path: TPath,
+ location: Location
+ ): Match;
+ matchRoutes(location: Location): Match>;
+ getParams>(
+ path: TPath,
+ location: Location
+ ): OutputOf;
+ link>(
+ path: TPath,
+ ...args: TypeAsArgs>
+ ): string;
+ getRoutePath(route: Route): string;
+}
+
+type AppendPath<
+ TPrefix extends string,
+ TPath extends string
+> = NormalizePath<`${TPrefix}${NormalizePath<`/${TPath}`>}`>;
+
+type MaybeUnion, U extends Record> = Omit &
+ {
+ [key in keyof U]: key extends keyof T ? T[key] | U[key] : U[key];
+ };
+
+type MapRoute<
+ TRoute extends Route,
+ TPrefix extends string,
+ TParents extends Route[] = []
+> = TRoute extends Route
+ ? MaybeUnion<
+ {
+ [key in AppendPath]: TRoute & { parents: TParents };
+ },
+ TRoute extends { children: Route[] }
+ ? MaybeUnion<
+ MapRoutes<
+ TRoute['children'],
+ AppendPath,
+ [...TParents, TRoute]
+ >,
+ {
+ [key in AppendPath>]: ValuesType<
+ MapRoutes<
+ TRoute['children'],
+ AppendPath,
+ [...TParents, TRoute]
+ >
+ >;
+ }
+ >
+ : {}
+ >
+ : {};
+
+type MapRoutes<
+ TRoutes,
+ TPrefix extends string = '',
+ TParents extends Route[] = []
+> = TRoutes extends [Route]
+ ? MapRoute
+ : TRoutes extends [Route, Route]
+ ? MapRoute & MapRoute
+ : TRoutes extends [Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute
+ : TRoutes extends [Route, Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute
+ : TRoutes extends [Route, Route, Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute
+ : TRoutes extends [Route, Route, Route, Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute
+ : TRoutes extends [Route, Route, Route, Route, Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute
+ : TRoutes extends [Route, Route, Route, Route, Route, Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute
+ : TRoutes extends [Route, Route, Route, Route, Route, Route, Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute
+ : TRoutes extends [Route, Route, Route, Route, Route, Route, Route, Route, Route, Route]
+ ? MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute &
+ MapRoute
+ : {};
+
+// const element = null as any;
+
+// const routes = unconst([
+// {
+// path: '/',
+// element,
+// children: [
+// {
+// path: '/settings',
+// element,
+// children: [
+// {
+// path: '/agent-configuration',
+// element,
+// },
+// {
+// path: '/agent-configuration/create',
+// element,
+// params: t.partial({
+// query: t.partial({
+// pageStep: t.string,
+// }),
+// }),
+// },
+// {
+// path: '/agent-configuration/edit',
+// element,
+// params: t.partial({
+// query: t.partial({
+// pageStep: t.string,
+// }),
+// }),
+// },
+// {
+// path: '/apm-indices',
+// element,
+// },
+// {
+// path: '/customize-ui',
+// element,
+// },
+// {
+// path: '/schema',
+// element,
+// },
+// {
+// path: '/anomaly-detection',
+// element,
+// },
+// {
+// path: '/',
+// element,
+// },
+// ],
+// },
+// {
+// path: '/services/:serviceName',
+// element,
+// params: t.intersection([
+// t.type({
+// path: t.type({
+// serviceName: t.string,
+// }),
+// }),
+// t.partial({
+// query: t.partial({
+// environment: t.string,
+// rangeFrom: t.string,
+// rangeTo: t.string,
+// comparisonEnabled: t.string,
+// comparisonType: t.string,
+// latencyAggregationType: t.string,
+// transactionType: t.string,
+// kuery: t.string,
+// }),
+// }),
+// ]),
+// children: [
+// {
+// path: '/overview',
+// element,
+// },
+// {
+// path: '/transactions',
+// element,
+// },
+// {
+// path: '/errors',
+// element,
+// children: [
+// {
+// path: '/:groupId',
+// element,
+// params: t.type({
+// path: t.type({
+// groupId: t.string,
+// }),
+// }),
+// },
+// {
+// path: '/',
+// element,
+// params: t.partial({
+// query: t.partial({
+// sortDirection: t.string,
+// sortField: t.string,
+// pageSize: t.string,
+// page: t.string,
+// }),
+// }),
+// },
+// ],
+// },
+// {
+// path: '/foo',
+// element,
+// },
+// {
+// path: '/bar',
+// element,
+// },
+// {
+// path: '/baz',
+// element,
+// },
+// {
+// path: '/',
+// element,
+// },
+// ],
+// },
+// {
+// path: '/',
+// element,
+// params: t.partial({
+// query: t.partial({
+// rangeFrom: t.string,
+// rangeTo: t.string,
+// }),
+// }),
+// children: [
+// {
+// path: '/services',
+// element,
+// },
+// {
+// path: '/traces',
+// element,
+// },
+// {
+// path: '/service-map',
+// element,
+// },
+// {
+// path: '/',
+// element,
+// },
+// ],
+// },
+// ],
+// },
+// ] as const);
+
+// type Routes = typeof routes;
+
+// type Mapped = keyof MapRoutes;
+
+// type Bar = ValuesType>['route']['path'];
+// type Foo = OutputOf;
+
+// const { path }: Foo = {} as any;
+
+// function _useApmParams>(p: TPath): OutputOf {
+// return {} as any;
+// }
+
+// const params = _useApmParams('/*');
diff --git a/packages/kbn-typed-react-router-config/src/types/utils.ts b/packages/kbn-typed-react-router-config/src/types/utils.ts
new file mode 100644
index 0000000000000..38b23c5118d0f
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/types/utils.ts
@@ -0,0 +1,31 @@
+/*
+ * 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 * as t from 'io-ts';
+
+export type MaybeOutputOf = T extends t.Type ? [t.OutputOf] : [];
+export type NormalizePath = T extends `//${infer TRest}`
+ ? NormalizePath<`/${TRest}`>
+ : T extends '/'
+ ? T
+ : T extends `${infer TRest}/`
+ ? TRest
+ : T;
+export type DeeplyMutableRoutes = T extends React.ReactElement
+ ? T
+ : T extends t.Type
+ ? T
+ : T extends readonly [infer U]
+ ? [DeeplyMutableRoutes]
+ : T extends readonly [infer U, ...infer V]
+ ? [DeeplyMutableRoutes, ...DeeplyMutableRoutes]
+ : T extends Record
+ ? {
+ -readonly [key in keyof T]: DeeplyMutableRoutes;
+ }
+ : T;
diff --git a/packages/kbn-typed-react-router-config/src/unconst.ts b/packages/kbn-typed-react-router-config/src/unconst.ts
new file mode 100644
index 0000000000000..d10c8290e20e9
--- /dev/null
+++ b/packages/kbn-typed-react-router-config/src/unconst.ts
@@ -0,0 +1,91 @@
+/*
+ * 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 * as t from 'io-ts';
+import { DeepReadonly } from 'utility-types';
+
+export type MaybeConst = TObject extends [object]
+ ? [DeepReadonly | TObject]
+ : TObject extends [object, ...infer TTail]
+ ? [DeepReadonly | TObject, ...(TTail extends object[] ? MaybeConst : [])]
+ : TObject extends object[]
+ ? DeepReadonly
+ : TObject extends object
+ ? [DeepReadonly | TObject]
+ : [];
+
+export type Unconst = T extends React.ReactElement
+ ? React.ReactElement
+ : T extends t.Type
+ ? T
+ : T extends readonly [any]
+ ? [Unconst]
+ : T extends readonly [any, any]
+ ? [Unconst, Unconst]
+ : T extends readonly [any, any, any]
+ ? [Unconst, Unconst, Unconst]
+ : T extends readonly [any, any, any, any]
+ ? [Unconst, Unconst, Unconst, Unconst]
+ : T extends readonly [any, any, any, any, any]
+ ? [Unconst, Unconst, Unconst, Unconst, Unconst]
+ : T extends readonly [any, any, any, any, any, any]
+ ? [Unconst, Unconst, Unconst, Unconst, Unconst, Unconst]
+ : T extends readonly [any, any, any, any, any, any, any]
+ ? [
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst
+ ]
+ : T extends readonly [any, any, any, any, any, any, any, any]
+ ? [
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst
+ ]
+ : T extends readonly [any, any, any, any, any, any, any, any, any]
+ ? [
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst
+ ]
+ : T extends readonly [any, any, any, any, any, any, any, any, any, any]
+ ? [
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst,
+ Unconst