From 9ed2003acdb044f436a0360e2fe647fbb960e8a0 Mon Sep 17 00:00:00 2001 From: snyk-bot Date: Thu, 4 Jun 2026 08:00:15 +0000 Subject: [PATCH 01/19] fix: upgrade @opentelemetry/exporter-trace-otlp-http from 0.215.0 to 0.218.0 Snyk has created this PR to upgrade @opentelemetry/exporter-trace-otlp-http from 0.215.0 to 0.218.0. See this package in npm: @opentelemetry/exporter-trace-otlp-http See this project in Snyk: https://app.snyk.io/org/docs-wmk/project/69782e43-c85b-4c27-afd1-ad863be7a38a?utm_source=github&utm_medium=referral&page=upgrade-pr --- .../package-lock.json | 96 +++++++++++++++---- src/Elastic.Documentation.Site/package.json | 2 +- 2 files changed, 77 insertions(+), 21 deletions(-) diff --git a/src/Elastic.Documentation.Site/package-lock.json b/src/Elastic.Documentation.Site/package-lock.json index 4b2f71b02..6bdff551e 100644 --- a/src/Elastic.Documentation.Site/package-lock.json +++ b/src/Elastic.Documentation.Site/package-lock.json @@ -18,7 +18,7 @@ "@opentelemetry/context-zone": "^2.7.1", "@opentelemetry/core": "^2.7.1", "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", "@opentelemetry/instrumentation": "^0.217.0", "@opentelemetry/instrumentation-fetch": "^0.215.0", "@opentelemetry/otlp-exporter-base": "^0.215.0", @@ -30,7 +30,7 @@ "@tanstack/react-query": "^5.100.9", "@theletterf/beautiful-mermaid": "0.1.5", "@uidotdev/usehooks": "2.4.1", - "dompurify": "^3.4.2", + "dompurify": "3.4.2", "highlight.js": "11.11.1", "htmx-ext-head-support": "2.0.5", "htmx-ext-preload": "2.1.2", @@ -4821,16 +4821,16 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.215.0.tgz", - "integrity": "sha512-k4J9ISeGpb0Bm/wCrlcrbroMFTkiWMrdhNxQGrlktxLy127Yzd4/7nrTawn5d/ApktYTknvdixsE6++34Qfi1w==", + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.218.0.tgz", + "integrity": "sha512-8dqezsmPhtKitIK/eTipZhYl9EX2/gNQ5zUMhaz3uxEURwfkNf8IPvo6yNfrzbxdtpAOybS/+h7wmIWYqFSpiw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0" + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4839,28 +4839,84 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", - "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.218.0.tgz", + "integrity": "sha512-ZwqpkNL5W7RyGJPDZ9g06DvKp8KFTWPJPN12anpMQYSKpTSU0z3EIZuPq9vPGpS8siFyOqDYDAuCwlNO9FqgbA==", "license": "Apache-2.0", "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-transformer": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.218.0.tgz", + "integrity": "sha512-CFaKH87WAzjuJ4awowTTLzUvMfaRfiOFG5+qm5S5ncyalRtN4ecQ+YmuANJSCrVPuvZFEkUgKhBPBndxi3rHsQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.218.0.tgz", + "integrity": "sha512-QvnNdugatFTVCJXH0Mcu7GOOJSylA9j127kIezOE4YwTI4YbowRons2K4WZTv5FMS8T4q9P0NdaRHdkSmeAIag==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", - "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { diff --git a/src/Elastic.Documentation.Site/package.json b/src/Elastic.Documentation.Site/package.json index a1a421ea6..995ba694d 100644 --- a/src/Elastic.Documentation.Site/package.json +++ b/src/Elastic.Documentation.Site/package.json @@ -108,7 +108,7 @@ "@opentelemetry/context-zone": "^2.7.1", "@opentelemetry/core": "^2.7.1", "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", "@opentelemetry/instrumentation": "^0.217.0", "@opentelemetry/instrumentation-fetch": "^0.215.0", "@opentelemetry/otlp-exporter-base": "^0.215.0", From d11528585bd9439f5794a6ba8db9b1ffc29c7483 Mon Sep 17 00:00:00 2001 From: Copilot <198982749+Copilot@users.noreply.github.com> Date: Thu, 4 Jun 2026 21:12:26 -0300 Subject: [PATCH 02/19] Fix: files with `_snippets` in their name incorrectly treated as snippet-only files (#2743) * Initial plan * Fix: treat only _snippets directories as special, not filenames containing _snippets Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: reakaleek <16325797+reakaleek@users.noreply.github.com> Co-authored-by: Felipe Cotti --- .../IO/MarkdownFileFactory.cs | 3 +- .../Myst/Directives/Include/IncludeBlock.cs | 2 +- .../MockFileSystemExtensions.cs | 2 +- .../OutputDirectoryTests.cs | 29 +++++++++++++++++++ 4 files changed, 33 insertions(+), 3 deletions(-) diff --git a/src/Elastic.Markdown/IO/MarkdownFileFactory.cs b/src/Elastic.Markdown/IO/MarkdownFileFactory.cs index e27b89203..cd246c804 100644 --- a/src/Elastic.Markdown/IO/MarkdownFileFactory.cs +++ b/src/Elastic.Markdown/IO/MarkdownFileFactory.cs @@ -117,7 +117,8 @@ private DocumentationFile CreateMarkDownFile(IFileInfo file, BuildContext contex if (context.Configuration.IsExcluded(relativePath)) return new ExcludedFile(file, sourceDirectory, context.Git.RepositoryName); - if (relativePath.Contains("_snippets")) + if (relativePath.Contains($"{Path.DirectorySeparatorChar}_snippets{Path.DirectorySeparatorChar}") + || relativePath.StartsWith($"_snippets{Path.DirectorySeparatorChar}")) return new SnippetFile(file, sourceDirectory, context.Git.RepositoryName); // we ignore files in folders that start with an underscore diff --git a/src/Elastic.Markdown/Myst/Directives/Include/IncludeBlock.cs b/src/Elastic.Markdown/Myst/Directives/Include/IncludeBlock.cs index 3f5a4e81d..9948c4e35 100644 --- a/src/Elastic.Markdown/Myst/Directives/Include/IncludeBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/Include/IncludeBlock.cs @@ -93,7 +93,7 @@ private void ExtractInclusionPath(ParserContext context) if (Literal) return; - if (file.Directory != null && file.Directory.FullName.IndexOf("_snippets", StringComparison.Ordinal) < 0) + if (file.Directory != null && !file.Directory.FullName.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Contains("_snippets")) { this.EmitError($"{{include}} only supports including snippets from `_snippet` folders. `{IncludePath}` is not a snippet"); Found = false; diff --git a/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs b/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs index 68b12f5b3..511cb70bd 100644 --- a/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs +++ b/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs @@ -34,7 +34,7 @@ public static void GenerateDocSetYaml( .EnumerateFiles(root.FullName, "*.md", SearchOption.AllDirectories); foreach (var markdownFile in markdownFiles) { - if (markdownFile.Contains("_snippet")) + if (markdownFile.Contains($"{Path.DirectorySeparatorChar}_snippets{Path.DirectorySeparatorChar}")) continue; var relative = fileSystem.Path.GetRelativePath(root.FullName, markdownFile); yaml.WriteLine($" - file: {relative}"); diff --git a/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs b/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs index 82a69f20a..d6dfa1295 100644 --- a/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs +++ b/tests/Elastic.Markdown.Tests/OutputDirectoryTests.cs @@ -44,6 +44,35 @@ public async Task CreatesDefaultOutputDirectory() fileSystem.Directory.Exists(".artifacts").Should().BeTrue(); } + [Fact] + public void FilesWithSnippetsInNameNotTreatedAsSnippets() + { + var logger = new TestLoggerFactory(output); + var fileSystem = new MockFileSystem(new Dictionary + { + { "docs/docset.yml", + //language=yaml + new MockFileData(""" +project: test +toc: +- file: index.md +- file: top_snippets.md +""") }, + { "docs/index.md", new MockFileData("# Test") }, + { "docs/top_snippets.md", new MockFileData("# Top Snippets") } + }, new MockFileSystemOptions + { + CurrentDirectory = Paths.WorkingDirectoryRoot.FullName + }); + var collector = new TestDiagnosticsCollector(output); + var configurationContext = TestHelpers.CreateConfigurationContext(fileSystem); + var context = new BuildContext(collector, FileSystemFactory.ScopeCurrentWorkingDirectory(fileSystem), configurationContext); + var linkResolver = new TestCrossLinkResolver(); + var set = new DocumentationSet(context, logger, linkResolver); + + set.MarkdownFiles.Should().Contain(f => f.RelativePath.EndsWith("top_snippets.md")); + } + [Theory] [MemberData(nameof(ValidFileNames))] public void OutputFileValidationValidNames(string fileName) From 50bea38d07a4e7d46e4daf68e3ad56635cd8f7da Mon Sep 17 00:00:00 2001 From: Martijn Laarman Date: Fri, 5 Jun 2026 13:57:02 +0200 Subject: [PATCH 03/19] Enable nav search override on staging (#3474) --- src/Elastic.Markdown/_Layout.cshtml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Elastic.Markdown/_Layout.cshtml b/src/Elastic.Markdown/_Layout.cshtml index 88b1df244..c590f02d7 100644 --- a/src/Elastic.Markdown/_Layout.cshtml +++ b/src/Elastic.Markdown/_Layout.cshtml @@ -98,7 +98,7 @@ @if (Model.Features.WebsiteSearchEnabled && Model.Features.WebsiteSearchScriptUrl is not null) { From 3b812711c7860b6f3622a73c2e2464a71c77794e Mon Sep 17 00:00:00 2001 From: "elastic-observability-automation[bot]" <180520183+elastic-observability-automation[bot]@users.noreply.github.com> Date: Fri, 5 Jun 2026 14:00:42 +0200 Subject: [PATCH 04/19] chore: Update config/versions.yml elasticsearch-client-ruby 9.4.1 (#3473) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made with ❤️️ by updatecli Co-authored-by: elastic-observability-automation[bot] <180520183+elastic-observability-automation[bot]@users.noreply.github.com> --- config/versions.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config/versions.yml b/config/versions.yml index 7c878335b..580a6be89 100644 --- a/config/versions.yml +++ b/config/versions.yml @@ -167,7 +167,7 @@ versioning_systems: current: 9.4.0 elasticsearch-client-ruby: base: 9.0 - current: 9.4.0 + current: 9.4.1 elasticsearch-client-rust: base: 9.0 current: 9.1.0-alpha.1 From b86c5eefac841f5a19bd6cccbeecf6edace7c9fa Mon Sep 17 00:00:00 2001 From: Liam Thompson Date: Fri, 5 Jun 2026 14:05:20 +0200 Subject: [PATCH 05/19] Add quick reference to applies_to syntax page (#3475) * Add quick reference to applies_to syntax page Simplify the intro, add a quick-reference table so docs contributors can match their situation to the right annotation type, and link to the applies-to-tagging skill. Co-Authored-By: Claude Opus 4.6 * Fix lead-in sentence grammar in quick reference Co-Authored-By: Claude Opus 4.6 --------- Co-authored-by: Claude Opus 4.6 --- docs/syntax/applies.md | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/docs/syntax/applies.md b/docs/syntax/applies.md index e5c4120f0..1c5895607 100644 --- a/docs/syntax/applies.md +++ b/docs/syntax/applies.md @@ -1,17 +1,29 @@ # Applies to -Starting with Elastic Stack 9.0, ECE 4.0, and ECK 3.0, documentation follows a [cumulative approach](https://www.elastic.co/docs/contribute-docs/how-to/cumulative-docs): instead of creating separate pages for each product and release, we update a single page with product- and version-specific details over time. +Elastic documentation on [elastic.co/docs](https://www.elastic.co/docs) follows a [cumulative](https://www.elastic.co/docs/contribute-docs/how-to/cumulative-docs) model. We don't maintain versioned branches or separate copies of a page for each release. Instead, every page covers all supported versions, and writers use `applies_to` metadata to tag content with the versions, products, deployment models, and lifecycle states it applies to. -To support this, source files use a tagging system to indicate: +:::{tip} +You can use the [applies-to-tagging](https://github.com/elastic/elastic-docs-skills/tree/main/skills/authoring/applies-to-tagging) skill in Claude Code to validate and generate `applies_to` tags. [**Learn more**](https://github.com/elastic/elastic-docs-skills/tree/main#elastic-docs-skills) +::: -* Which Elastic products and deployment models the content applies to. -* When a feature changes state relative to the base version. +## Quick reference -This is what the `applies_to` metadata is for. It can be used at the [page](#page-level), [section](#section-level), or [inline](#inline-level) level to specify applicability with precision. +When documenting a new feature or behavior, answer these two questions: -:::{note} -For detailed guidance, refer to [Write cumulative documentation](https://www.elastic.co/docs/contribute-docs/how-to/cumulative-docs). -::: +1. **Where should I add `applies_to` metadata?** Find where your changes sit on the page and match to a type below. +2. **What keys and values do I use?** Refer to the [key-value reference](#key-value-reference) for the full list of valid keys, lifecycles, and version formats. + +### Where should I put the `applies_to` metadata? + +| When | Use | +|------|-----| +| You're creating a new page | [Frontmatter](#page-level) | +| You're adding a new section to an existing page | [Section-level annotation](#section-level) | +| You're adding a single new item to a list or definition term | [Inline annotation](#inline-level) | +| You're marking a single item as a technical preview | [Preview shorthand](#inline-level) | +| You need to show entirely different content for each variant, not just tag the same content | [Versioned tabs](applies-switch.md) | +| You're adding a version-specific note, tip, or warning | [Admonition annotation](admonitions.md) | +| You're adding a version-specific dropdown | [Dropdown annotation](dropdowns.md) | ## Syntax reference From 182938f86851246d4159b10e7bf4a929e38a9d71 Mon Sep 17 00:00:00 2001 From: Clint Andrew Hall Date: Mon, 8 Jun 2026 11:04:18 -0400 Subject: [PATCH 06/19] [storybook] Storybook directive, for inline story rendering (#2910) Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> --- docs/_docset.yml | 4 + docs/syntax/directives.md | 3 +- docs/syntax/index.md | 4 +- docs/syntax/storybook.md | 84 +++++ .../Builder/ConfigurationFile.cs | 5 + .../Serialization/YamlStaticContext.cs | 1 + .../Toc/DocumentationSetFile.cs | 10 + src/Elastic.Documentation.Site/Assets/main.ts | 16 +- .../Assets/markdown/storybook.css | 32 ++ .../Assets/styles.css | 1 + .../StorybookStoryComponent.tsx | 297 ++++++++++++++++++ .../Myst/Directives/DirectiveBlockParser.cs | 4 + .../Myst/Directives/DirectiveHtmlRenderer.cs | 23 ++ .../Directives/Storybook/StorybookBlock.cs | 297 ++++++++++++++++++ .../Directives/Storybook/StorybookRegistry.cs | 90 ++++++ .../Directives/Storybook/StorybookView.cshtml | 26 ++ .../Storybook/StorybookViewModel.cs | 26 ++ .../LlmMarkdown/LlmBlockRenderers.cs | 26 ++ .../Directives/DirectiveBaseTests.cs | 4 +- .../Directives/StorybookTests.cs | 246 +++++++++++++++ .../MockFileSystemExtensions.cs | 6 +- tests/authoring/Blocks/Storybook.fs | 26 ++ tests/authoring/authoring.fsproj | 1 + 23 files changed, 1225 insertions(+), 7 deletions(-) create mode 100644 docs/syntax/storybook.md create mode 100644 src/Elastic.Documentation.Site/Assets/markdown/storybook.css create mode 100644 src/Elastic.Documentation.Site/Assets/web-components/StorybookStory/StorybookStoryComponent.tsx create mode 100644 src/Elastic.Markdown/Myst/Directives/Storybook/StorybookBlock.cs create mode 100644 src/Elastic.Markdown/Myst/Directives/Storybook/StorybookRegistry.cs create mode 100644 src/Elastic.Markdown/Myst/Directives/Storybook/StorybookView.cshtml create mode 100644 src/Elastic.Markdown/Myst/Directives/Storybook/StorybookViewModel.cs create mode 100644 tests/Elastic.Markdown.Tests/Directives/StorybookTests.cs create mode 100644 tests/authoring/Blocks/Storybook.fs diff --git a/docs/_docset.yml b/docs/_docset.yml index 0dd5a42d6..aed40e521 100644 --- a/docs/_docset.yml +++ b/docs/_docset.yml @@ -41,6 +41,9 @@ subs: features: primary-nav: false + +storybook: + registry: https://ci-artifacts.kibana.dev/storybooks/pr-272388/storybook-docs/docs_registry.json suppress: - AutolinkElasticCoDocs @@ -149,6 +152,7 @@ toc: - file: lists.md - file: task-lists.md - file: line_breaks.md + - file: storybook.md - file: links.md - file: list-sub-pages.md - file: page-card.md diff --git a/docs/syntax/directives.md b/docs/syntax/directives.md index b3754b6c6..07ae223f6 100644 --- a/docs/syntax/directives.md +++ b/docs/syntax/directives.md @@ -77,7 +77,8 @@ The following directives are available: - [Math](math.md) - Mathematical expressions and equations - [Page cards](page-card.md) - Full-width clickable navigation rows - [Settings](automated_settings.md) - Configuration blocks +- [Storybook](storybook.md) - Embedded Storybook stories - [Stepper](stepper.md) - Step-by-step content - [Tabs](tabs.md) - Tabbed content organization - [Tables](tables.md) - Data tables -- [Version blocks](version-variables.md) - API version information \ No newline at end of file +- [Version blocks](version-variables.md) - API version information diff --git a/docs/syntax/index.md b/docs/syntax/index.md index de6b664fa..c049b30a3 100644 --- a/docs/syntax/index.md +++ b/docs/syntax/index.md @@ -12,7 +12,7 @@ Elastic Docs V3 uses a custom implementation of [MyST](https://mystmd.org/) (Mar If you know [Markdown](https://commonmark.org), you already know most of what you need. If not, the CommonMark project offers a [10-minute tutorial](https://commonmark.org/help/). -When you need more than basic Markdown, you can use _directives_ to add features like callouts, tabs, and diagrams. To learn how directives work in general, including how to add options and arguments and nest multiple directives, refer to [How directives work](directives.md). For a full list of available directives, refer to the sidebar. +When you need more than basic Markdown, you can use _directives_ to add features like callouts, tabs, diagrams, and embedded Storybook stories. To learn how directives work in general, including how to add options and arguments and nest multiple directives, refer to [How directives work](directives.md). For a full list of available directives, refer to the sidebar. ## GitHub Flavored Markdown support @@ -25,4 +25,4 @@ V3 supports some GitHub Flavored Markdown extensions: **Not supported:** - Automatic URL linking: https://www.elastic.co - Links must use standard Markdown syntax: [Elastic](https://www.elastic.co) -- Using a subset of HTML \ No newline at end of file +- Using a subset of HTML diff --git a/docs/syntax/storybook.md b/docs/syntax/storybook.md new file mode 100644 index 000000000..71cf58436 --- /dev/null +++ b/docs/syntax/storybook.md @@ -0,0 +1,84 @@ +# Storybook + +The `{storybook}` directive embeds a Storybook story from a Kibana `docs_registry.json`. The registry supplies the Storybook runtime ID, inline module entry, bootstrap assets, and iframe fallback URL. + +Configure the registry URL in `docset.yml`: + +```yaml +storybook: + registry: https://example.com/storybook-docs/docs_registry.json +``` + +This page uses the Kibana Storybook artifact registry from PR 272388 so docs-builder preview builds can exercise inline module loading from `docs-v3-preview.elastic.dev`. + +For local Kibana testing, `yarn storybook_docs shared_ux --serve` serves the registry at: + +```text +http://127.0.0.1:6007/storybook-docs/docs_registry.json +``` + +## Usage + +Use a registry ID directly: + +```markdown +:::{storybook} +:id: kibana:shared_ux:ai-components-aibutton--default +:height: 300 +:title: AI button default story +::: +``` + +:::{storybook} +:id: kibana:shared_ux:ai-components-aibutton--default +:height: 300 +:title: AI button default story +::: + +Or use structured properties: + +```markdown +:::{storybook} +:project: kibana +:storybook: shared_ux +:component: ai-components-aibutton +:story: default +::: +``` + +:::{storybook} +:project: kibana +:storybook: shared_ux +:component: ai-components-aibutton +:story: default +::: + +If a bare `:id:` is used, it must match exactly one story in the configured registry: + +```markdown +:::{storybook} +:id: ai-components-aibutton--default +::: +``` + +In the PR 272388 registry this bare ID is ambiguous — it resolves to both `kibana:presentation:ai-components-aibutton--default` and `kibana:shared_ux:ai-components-aibutton--default` — so it is not rendered live here. Use the fully-qualified `project:storybook:docsId` form when a docs ID is not unique. + +## Properties + +| Property | Required | Description | +|---|---|---| +| `:id:` | Yes* | Full registry ID such as `kibana:shared_ux:ai-components-aibutton--default`, or a bare docs/storybook ID that matches exactly one configured story. | +| `:project:` | Yes* | Registry project prefix, for example `kibana`. Required when `:id:` is omitted. | +| `:storybook:` | Yes* | Storybook alias, for example `shared_ux`. Required when `:id:` is omitted. | +| `:component:` | No | Component ID used with `:story:` to form `{component}--{story}`. | +| `:story:` | Yes* | Story name or docs ID. Required when `:id:` is omitted. | +| `:height:` | No | Iframe fallback height in pixels. Defaults to the registry story height when present, otherwise `400`. | +| `:title:` | No | Accessible title for the iframe fallback. Defaults to `Storybook story`. | + +## Rendering + +If the registry story has `renderMode: inline` and an `inline` entry, docs-builder renders a `` element. The browser loads the registry bootstrap styles and scripts, then imports `inline.entry` and calls `mountStory(story.storybookId, container)`. + +If the story has `renderMode: iframe`, or no inline entry, docs-builder renders `iframe.url`. + +The registry, inline module, bootstrap assets, and iframe fallback can live on different paths. docs-builder uses the URLs from the registry directly; those assets must allow browser access from the docs site. diff --git a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs index f20055f83..8a94b087b 100644 --- a/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs +++ b/src/Elastic.Documentation.Configuration/Builder/ConfigurationFile.cs @@ -59,6 +59,8 @@ public record ConfigurationFile public IReadOnlyDictionary? OpenApiSpecifications { get; } + public string? StorybookRegistry { get; } + /// /// Resolved API configurations with template and specification file information. /// @@ -245,6 +247,9 @@ public ConfigurationFile(DocumentationSetFile docSetFile, IDocumentationSetConte ApiConfigurations = apiConfigs.Count > 0 ? apiConfigs : null; } + if (docSetFile.Storybook is not null) + StorybookRegistry = docSetFile.Storybook.Registry?.Trim(); + // Process products from docset - resolve ProductLinks to Product objects if (docSetFile.Products.Count > 0) { diff --git a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs index 21cab1086..7fc6e8f61 100644 --- a/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs +++ b/src/Elastic.Documentation.Configuration/Serialization/YamlStaticContext.cs @@ -39,6 +39,7 @@ namespace Elastic.Documentation.Configuration.Serialization; // Table of contents [YamlSerializable(typeof(DocumentationSetFile))] [YamlSerializable(typeof(DocumentationSetFeatures))] +[YamlSerializable(typeof(DocumentationSetStorybook))] [YamlSerializable(typeof(CodexDocSetMetadata))] [YamlSerializable(typeof(TableOfContentsFile))] [YamlSerializable(typeof(SiteNavigationFile))] diff --git a/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs b/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs index 120a83960..5b94ab7f7 100644 --- a/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs +++ b/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs @@ -73,6 +73,9 @@ public class DocumentationSetFile : TableOfContentsFile [YamlMember(Alias = "branding")] public BrandingConfiguration? Branding { get; set; } + [YamlMember(Alias = "storybook")] + public DocumentationSetStorybook? Storybook { get; set; } + public static FileRef[] GetFileRefs(ITableOfContentsItem item) { if (item is FileRef fileRef) @@ -740,6 +743,13 @@ public class DocumentationSetFeatures public bool? DisableGithubEditLink { get; set; } } +[YamlSerializable] +public class DocumentationSetStorybook +{ + [YamlMember(Alias = "registry")] + public string? Registry { get; set; } +} + /// /// Codex-specific metadata. Only contains group for navigation grouping in a codex environment. /// diff --git a/src/Elastic.Documentation.Site/Assets/main.ts b/src/Elastic.Documentation.Site/Assets/main.ts index ad795a20a..4f6a935bb 100644 --- a/src/Elastic.Documentation.Site/Assets/main.ts +++ b/src/Elastic.Documentation.Site/Assets/main.ts @@ -41,6 +41,7 @@ import('./web-components/VersionDropdown') import('./web-components/AppliesToPopover') import('./web-components/FullPageSearch/FullPageSearchComponent') import('./web-components/Diagnostics/DiagnosticsComponent') +import('./web-components/StorybookStory/StorybookStoryComponent') if (config.buildType === 'isolated' || config.airGapped) { import('./isolated') @@ -134,8 +135,19 @@ document.addEventListener('htmx:load', function () { document.addEventListener( 'htmx:removingHeadElement', function (event: HtmxEvent) { - const tagName = event.detail.headElement.tagName - if (tagName === 'STYLE') { + const headElement = event.detail.headElement + if (headElement.tagName === 'STYLE') { + event.preventDefault() + return + } + // Keep the Storybook bootstrap assets that injects into + // ; htmx would otherwise strip them on navigation, which both breaks an + // in-flight first load (the stylesheet's onload never fires) and leaves the + // module-level load caches pointing at elements that no longer exist. + if ( + headElement.dataset?.storybookScript !== undefined || + headElement.dataset?.storybookStyle !== undefined + ) { event.preventDefault() } } diff --git a/src/Elastic.Documentation.Site/Assets/markdown/storybook.css b/src/Elastic.Documentation.Site/Assets/markdown/storybook.css new file mode 100644 index 000000000..cee94b9cc --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/markdown/storybook.css @@ -0,0 +1,32 @@ +.storybook-embed { + border: 1px solid #e3e8f2; + border-radius: 6px; + margin-block: 1.5rem; + overflow: hidden; +} + +.storybook-embed iframe { + display: block; +} + +.storybook-embed storybook-story { + display: block; + padding: 24px; +} + +.storybook-embed-body { + padding: 0.75rem 1.5rem; + border-top: 1px solid #e3e8f2; + background: #f7f8fc; +} + +.storybook-embed-error { + padding: 1rem; + color: #bd271e; + font-size: 14px; +} + +.storybook-embed-error strong { + display: block; + margin-bottom: 0.25rem; +} diff --git a/src/Elastic.Documentation.Site/Assets/styles.css b/src/Elastic.Documentation.Site/Assets/styles.css index ebb52e75c..d4e0b9113 100644 --- a/src/Elastic.Documentation.Site/Assets/styles.css +++ b/src/Elastic.Documentation.Site/Assets/styles.css @@ -29,6 +29,7 @@ @import './markdown/agent-skill.css'; @import './markdown/cli-modifiers.css'; @import './markdown/contributors.css'; +@import './markdown/storybook.css'; @import './api-docs.css'; @import 'tippy.js/dist/tippy.css'; diff --git a/src/Elastic.Documentation.Site/Assets/web-components/StorybookStory/StorybookStoryComponent.tsx b/src/Elastic.Documentation.Site/Assets/web-components/StorybookStory/StorybookStoryComponent.tsx new file mode 100644 index 000000000..8487a7e59 --- /dev/null +++ b/src/Elastic.Documentation.Site/Assets/web-components/StorybookStory/StorybookStoryComponent.tsx @@ -0,0 +1,297 @@ +export {} + +type MountFn = (storyId: string, container: HTMLElement) => Promise +type UnmountFn = (container: HTMLElement) => void +type KibanaPublicPath = Record + +declare global { + interface Window { + __kbnPublicPath__?: KibanaPublicPath + __kbnHardenPrototypes__?: boolean + } +} + +interface StoryEntry { + mountStory: MountFn + unmountStory: UnmountFn +} + +interface StoryBootstrap { + publicPath?: string + scripts?: string[] + styles?: string[] +} + +class StorybookLoadError extends Error { + constructor( + public readonly title: string, + public readonly detail: string + ) { + super(detail) + } +} + +const entryCache = new Map>() +const bootstrapCache = new Map>() +const scriptCache = new Map>() +const styleCache = new Map>() + +const loadEntry = (url: string): Promise => { + if (!entryCache.has(url)) { + entryCache.set( + url, + import(/* @vite-ignore */ url).then(validateEntry).catch((err) => { + entryCache.delete(url) + if (err instanceof StorybookLoadError) throw err + throw new StorybookLoadError( + 'Storybook entry module could not be imported.', + `Check that the module exists and CORS allows this docs origin: ${url}` + ) + }) + ) + } + return entryCache.get(url)! +} + +const validateEntry = (entry: unknown): StoryEntry => { + const candidate = entry as Partial + if ( + typeof candidate.mountStory !== 'function' || + typeof candidate.unmountStory !== 'function' + ) { + throw new StorybookLoadError( + 'Storybook entry module has an unsupported contract.', + 'Expected mountStory(storyId, container) and unmountStory(container).' + ) + } + return candidate as StoryEntry +} + +const loadStylesheet = (url: string): Promise => { + if (!styleCache.has(url)) { + styleCache.set( + url, + new Promise((resolve, reject) => { + const existing = Array.from( + document.querySelectorAll( + 'link[data-storybook-style]' + ) + ).find((link) => link.dataset.storybookStyle === url) + if (existing) { + resolve() + return + } + + const link = document.createElement('link') + link.rel = 'stylesheet' + link.href = url + link.dataset.storybookStyle = url + link.onload = () => resolve() + link.onerror = () => { + styleCache.delete(url) + reject( + new StorybookLoadError( + 'Storybook stylesheet could not be loaded.', + `Missing or blocked stylesheet: ${url}` + ) + ) + } + document.head.appendChild(link) + }) + ) + } + return styleCache.get(url)! +} + +const loadScript = (url: string): Promise => { + if (!scriptCache.has(url)) { + scriptCache.set( + url, + new Promise((resolve, reject) => { + const existing = Array.from( + document.querySelectorAll( + 'script[data-storybook-script]' + ) + ).find((script) => script.dataset.storybookScript === url) + if (existing) { + resolve() + return + } + + const script = document.createElement('script') + script.src = url + script.async = false + script.dataset.storybookScript = url + script.onload = () => resolve() + script.onerror = () => { + scriptCache.delete(url) + reject( + new StorybookLoadError( + 'Storybook bootstrap script could not be loaded.', + `Missing or blocked script: ${url}` + ) + ) + } + document.head.appendChild(script) + }) + ) + } + return scriptCache.get(url)! +} + +const parseBootstrap = (value: string | null): StoryBootstrap | null => { + if (!value) return null + + try { + return JSON.parse(value) as StoryBootstrap + } catch { + throw new StorybookLoadError( + 'Storybook bootstrap data is invalid.', + 'The registry provided bootstrap data that is not valid JSON.' + ) + } +} + +const setKibanaGlobals = (publicPath?: string) => { + if (!publicPath) return + + const publicPaths = { + 'kbn-ui-shared-deps-npm': publicPath, + 'kbn-ui-shared-deps-src': publicPath, + 'kbn-monaco': publicPath, + } + + window.__kbnPublicPath__ = publicPaths + window.__kbnHardenPrototypes__ = false + + try { + if (window.top) { + window.top.__kbnPublicPath__ = publicPaths + window.top.__kbnHardenPrototypes__ = false + } + } catch { + // Cross-origin frames cannot access `top`. + } +} + +const loadBootstrap = async (bootstrap: StoryBootstrap | null) => { + if (!bootstrap) return + + setKibanaGlobals(bootstrap.publicPath) + + const cacheKey = JSON.stringify({ + publicPath: bootstrap.publicPath ?? '', + scripts: bootstrap.scripts ?? [], + styles: bootstrap.styles ?? [], + }) + if (!bootstrapCache.has(cacheKey)) { + bootstrapCache.set( + cacheKey, + (async () => { + await Promise.all((bootstrap.styles ?? []).map(loadStylesheet)) + for (const script of bootstrap.scripts ?? []) { + await loadScript(script) + } + })() + ) + } + + await bootstrapCache.get(cacheKey) +} + +class StorybookStoryElement extends HTMLElement { + private container: HTMLDivElement | null = null + private entry: StoryEntry | null = null + private mountVersion = 0 + + static get observedAttributes() { + return ['story-id', 'entry', 'bootstrap'] + } + + connectedCallback() { + if (!this.container) { + this.container = document.createElement('div') + this.appendChild(this.container) + } + this.mount() + } + + disconnectedCallback() { + this.mountVersion++ + this.unmount() + } + + attributeChangedCallback() { + if (this.container) { + this.mount() + } + } + + private unmount() { + if (this.container && this.entry) { + this.entry.unmountStory(this.container) + this.entry = null + } + } + + private async mount() { + const storyId = this.getAttribute('story-id') + const entryUrl = this.getAttribute('entry') + const currentMount = ++this.mountVersion + + if (!storyId || !entryUrl || !this.container) return + + this.unmount() + this.container.replaceChildren() + + try { + await loadBootstrap(parseBootstrap(this.getAttribute('bootstrap'))) + const entry = await loadEntry(entryUrl) + if ( + currentMount !== this.mountVersion || + !this.container || + !this.isConnected + ) + return + + this.entry = entry + try { + await entry.mountStory(storyId, this.container) + } catch (err) { + throw new StorybookLoadError( + 'Storybook story failed while mounting.', + err instanceof Error ? err.message : String(err) + ) + } + } catch (err) { + if (currentMount === this.mountVersion && this.container) { + this.container.replaceChildren(createErrorMessage(err)) + } + } + } +} + +const createErrorMessage = (err: unknown): HTMLDivElement => { + const message = document.createElement('div') + message.className = 'storybook-embed-error' + + const title = document.createElement('strong') + title.textContent = + err instanceof StorybookLoadError + ? err.title + : 'Storybook story failed to load.' + message.appendChild(title) + + const detail = document.createElement('div') + detail.textContent = + err instanceof StorybookLoadError + ? err.detail + : err instanceof Error + ? err.message + : String(err) + message.appendChild(detail) + + return message +} + +customElements.define('storybook-story', StorybookStoryElement) diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs index 100c211c5..5a0bf0dcd 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveBlockParser.cs @@ -16,6 +16,7 @@ using Elastic.Markdown.Myst.Directives.PageCard; using Elastic.Markdown.Myst.Directives.Settings; using Elastic.Markdown.Myst.Directives.Stepper; +using Elastic.Markdown.Myst.Directives.Storybook; using Elastic.Markdown.Myst.Directives.SubPages; using Elastic.Markdown.Myst.Directives.Table; using Elastic.Markdown.Myst.Directives.Tabs; @@ -141,6 +142,9 @@ protected override DirectiveBlock CreateFencedBlock(BlockProcessor processor) if (info.IndexOf("{cli-modifiers}") > 0) return new CliModifiersBlock(this, context); + if (info.IndexOf("{storybook}") > 0) + return new StorybookBlock(this, context); + foreach (var admonition in Admonitions) { if (info.IndexOf(admonition) > 0) diff --git a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs index bb62cf633..efbcb9633 100644 --- a/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs +++ b/src/Elastic.Markdown/Myst/Directives/DirectiveHtmlRenderer.cs @@ -22,6 +22,7 @@ using Elastic.Markdown.Myst.Directives.PageCard; using Elastic.Markdown.Myst.Directives.Settings; using Elastic.Markdown.Myst.Directives.Stepper; +using Elastic.Markdown.Myst.Directives.Storybook; using Elastic.Markdown.Myst.Directives.SubPages; using Elastic.Markdown.Myst.Directives.Table; using Elastic.Markdown.Myst.Directives.Tabs; @@ -129,6 +130,9 @@ protected override void Write(HtmlRenderer renderer, DirectiveBlock directiveBlo case CliModifiersBlock cliModifiersBlock: WriteCliModifiers(renderer, cliModifiersBlock); return; + case StorybookBlock storybookBlock: + WriteStorybook(renderer, storybookBlock); + return; default: // if (!string.IsNullOrEmpty(directiveBlock.Info) && !directiveBlock.Info.StartsWith('{')) // WriteCode(renderer, directiveBlock); @@ -302,6 +306,25 @@ private static void WriteAgentSkill(HtmlRenderer renderer, AgentSkillBlock block RenderRazorSlice(slice, renderer); } + private static void WriteStorybook(HtmlRenderer renderer, StorybookBlock block) + { + if (string.IsNullOrEmpty(block.StoryUrl)) + return; + + var slice = StorybookView.Create(new StorybookViewModel + { + DirectiveBlock = block, + StoryUrl = block.StoryUrl, + StoryId = block.StoryId ?? string.Empty, + Height = block.Height, + IframeTitle = block.IframeTitle, + HasBody = block.Count > 0, + InlineEntry = block.InlineEntry, + InlineBootstrapJson = block.InlineBootstrapJson, + }); + RenderRazorSlice(slice, renderer); + } + private static void WriteFigure(HtmlRenderer renderer, ImageBlock block) { var imageUrl = block.ImageUrl != null && diff --git a/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookBlock.cs b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookBlock.cs new file mode 100644 index 000000000..3b37474c8 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookBlock.cs @@ -0,0 +1,297 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Net; +using System.Text.Json; +using Elastic.Markdown.Diagnostics; + +namespace Elastic.Markdown.Myst.Directives.Storybook; + +public class StorybookBlock(DirectiveBlockParser parser, ParserContext context) : DirectiveBlock(parser, context) +{ + private const string SupportedRegistrySchemaVersion = "1"; + + private static readonly TimeSpan RegistryFetchTimeout = TimeSpan.FromSeconds(30); + + // Shared across all storybook directives to pool connections; PooledConnectionLifetime bounds DNS staleness in long-lived serve/watch runs. + private static readonly HttpClient RegistryHttpClient = new( + new SocketsHttpHandler + { + AutomaticDecompression = DecompressionMethods.All, + PooledConnectionLifetime = TimeSpan.FromMinutes(5) + } + ) + { Timeout = RegistryFetchTimeout }; + + public override string Directive => "storybook"; + + public string? Project { get; private set; } + + public string? Storybook { get; private set; } + + public string? DocsId { get; private set; } + + public string? StoryId { get; private set; } + + public string? StoryUrl { get; private set; } + + public string? InlineEntry { get; private set; } + + public string? InlineBootstrapJson { get; private set; } + + public int Height { get; private set; } = 400; + + public string IframeTitle { get; private set; } = "Storybook story"; + + public bool HasInlineStory => !string.IsNullOrWhiteSpace(InlineEntry); + + public override void FinalizeAndValidate(ParserContext context) + { + if (!string.IsNullOrWhiteSpace(Arguments)) + this.EmitWarning("storybook directive ignores positional arguments. Use properties instead."); + + var reference = ResolveReference(); + if (reference is null) + return; + + if (!TryLoadRegistry(out var registry)) + return; + + var story = FindStory(registry, reference); + if (story is null) + { + this.EmitError($"storybook registry does not contain id '{reference.RawId}'."); + return; + } + + if (string.IsNullOrWhiteSpace(story.RenderMode) || !IsSupportedRenderMode(story.RenderMode)) + { + this.EmitError($"storybook registry id '{reference.RawId}' has unsupported renderMode '{story.RenderMode}'."); + return; + } + + if (string.IsNullOrWhiteSpace(story.DocsId) || string.IsNullOrWhiteSpace(story.StorybookId)) + { + this.EmitError($"storybook registry id '{reference.RawId}' requires docsId and storybookId."); + return; + } + + if (story.Iframe is null || string.IsNullOrWhiteSpace(story.Iframe.Url)) + { + this.EmitError($"storybook registry id '{reference.RawId}' requires iframe.url."); + return; + } + + Project = reference.Project; + Storybook = reference.Storybook ?? story.Alias; + DocsId = story.DocsId; + StoryId = story.StorybookId; + StoryUrl = ResolveRegistryUrl(registry.BaseUrl, story.Iframe.Url); + Height = story.Height ?? Height; + + if (story.RenderMode.Equals("inline", StringComparison.OrdinalIgnoreCase) && !string.IsNullOrWhiteSpace(story.Inline?.Entry)) + { + InlineEntry = ResolveRegistryUrl(registry.BaseUrl, story.Inline.Entry); + if (story.Inline.Bootstrap is not null) + InlineBootstrapJson = SerializeBootstrap(registry.BaseUrl, story.Inline.Bootstrap); + } + + var rawHeight = Prop("height"); + if (!string.IsNullOrWhiteSpace(rawHeight)) + { + if (int.TryParse(rawHeight.Trim(), out var parsedHeight) && parsedHeight > 0) + Height = parsedHeight; + else + this.EmitWarning($"storybook directive :height: must be a positive integer. Got '{rawHeight}', using default {Height}px."); + } + + var rawTitle = Prop("title"); + if (!string.IsNullOrWhiteSpace(rawTitle)) + IframeTitle = rawTitle.Trim(); + } + + private StoryReference? ResolveReference() + { + var rawId = Prop("id")?.Trim(); + var project = Prop("project")?.Trim(); + var storybook = Prop("storybook")?.Trim(); + var component = Prop("component")?.Trim(); + var story = Prop("story")?.Trim(); + + if (!string.IsNullOrWhiteSpace(rawId)) + return StoryReference.FromId(rawId, project, storybook, component, story); + + if (string.IsNullOrWhiteSpace(project)) + { + this.EmitError("storybook directive requires :id: or :project:."); + return null; + } + + if (string.IsNullOrWhiteSpace(storybook)) + { + this.EmitError("storybook directive requires :id: or :storybook:."); + return null; + } + + if (string.IsNullOrWhiteSpace(story)) + { + this.EmitError("storybook directive requires :id: or :story:."); + return null; + } + + var docsId = string.IsNullOrWhiteSpace(component) ? story : $"{component}--{story}"; + return new StoryReference(project, storybook, docsId, component, story, $"{project}:{storybook}:{docsId}"); + } + + private bool TryLoadRegistry(out StorybookRegistry registry) + { + registry = new StorybookRegistry(); + + var rawRegistry = Build.Configuration.StorybookRegistry; + if (string.IsNullOrWhiteSpace(rawRegistry)) + { + this.EmitError("storybook directive requires docset.yml storybook.registry."); + return false; + } + + try + { + var registryJson = ReadRegistry(rawRegistry); + return TryDeserializeRegistry(rawRegistry, registryJson, out registry); + } + catch (Exception e) + { + this.EmitError($"storybook registry could not be read: {rawRegistry}", e); + return false; + } + } + + private string ReadRegistry(string rawRegistry) + { + if (Uri.TryCreate(rawRegistry, UriKind.Absolute, out var uri) && uri.Scheme is "http" or "https") + { + // FinalizeAndValidate is synchronous across all directives, so the fetch is sync-over-async here. + // Bound it with an explicit timeout so an unresponsive registry host can never stall the build. + using var cts = new CancellationTokenSource(RegistryFetchTimeout); + return RegistryHttpClient.GetStringAsync(uri, cts.Token).GetAwaiter().GetResult(); + } + + var registryPath = Path.IsPathRooted(rawRegistry) + ? rawRegistry + : Build.ReadFileSystem.Path.Combine(Build.DocumentationSourceDirectory.FullName, rawRegistry); + return Build.ReadFileSystem.File.ReadAllText(registryPath); + } + + private bool TryDeserializeRegistry(string rawRegistryPath, string registryJson, out StorybookRegistry registry) + { + registry = new StorybookRegistry(); + try + { + registry = JsonSerializer.Deserialize(registryJson, StorybookRegistryJsonContext.Default.StorybookRegistry)!; + } + catch (JsonException e) + { + this.EmitError($"storybook registry could not be parsed: {rawRegistryPath}", e); + return false; + } + + if (registry is null || registry.Stories.Count == 0) + { + this.EmitError($"storybook registry is empty: {rawRegistryPath}"); + return false; + } + + var schemaVersion = RegistrySchemaVersion(registry.SchemaVersion); + if (!schemaVersion.Equals(SupportedRegistrySchemaVersion, StringComparison.Ordinal)) + { + this.EmitError($"storybook registry schemaVersion '{schemaVersion}' is not supported. Expected '{SupportedRegistrySchemaVersion}'."); + return false; + } + + return true; + } + + private static string RegistrySchemaVersion(JsonElement schemaVersion) => + schemaVersion.ValueKind switch + { + JsonValueKind.Number => schemaVersion.GetRawText(), + JsonValueKind.String => schemaVersion.GetString() ?? string.Empty, + _ => string.Empty + }; + + private static StorybookRegistryStory? FindStory(StorybookRegistry registry, StoryReference reference) + { + if (registry.Stories.TryGetValue(reference.RawId, out var rawMatch)) + return rawMatch; + + var namespacedId = $"{reference.Project}:{reference.Storybook}:{reference.DocsId}"; + if (registry.Stories.TryGetValue(namespacedId, out var namespacedMatch)) + return namespacedMatch; + + var matches = registry.Stories + .Where(story => MatchesReferenceScope(story.Key, story.Value, reference)) + .Select(story => story.Value) + .Where(story => + story.DocsId?.Equals(reference.DocsId, StringComparison.OrdinalIgnoreCase) == true + || story.StorybookId?.Equals(reference.DocsId, StringComparison.OrdinalIgnoreCase) == true) + .ToArray(); + + return matches.Length == 1 ? matches[0] : null; + } + + private static bool MatchesReferenceScope(string registryId, StorybookRegistryStory story, StoryReference reference) + { + var parts = registryId.Split(':', 3, StringSplitOptions.TrimEntries); + if (!string.IsNullOrWhiteSpace(reference.Project) && (parts.Length != 3 || !parts[0].Equals(reference.Project, StringComparison.OrdinalIgnoreCase))) + return false; + + if (string.IsNullOrWhiteSpace(reference.Storybook)) + return true; + + var registryStorybook = parts.Length == 3 ? parts[1] : story.Alias; + return registryStorybook?.Equals(reference.Storybook, StringComparison.OrdinalIgnoreCase) == true + || story.Alias?.Equals(reference.Storybook, StringComparison.OrdinalIgnoreCase) == true; + } + + private static bool IsSupportedRenderMode(string renderMode) => + renderMode.Equals("inline", StringComparison.OrdinalIgnoreCase) || renderMode.Equals("iframe", StringComparison.OrdinalIgnoreCase); + + private static string SerializeBootstrap(string? baseUrl, StorybookRegistryBootstrap bootstrap) + { + var resolvedBootstrap = new StorybookRegistryBootstrap + { + PublicPath = ResolveRegistryUrl(baseUrl, bootstrap.PublicPath), + Scripts = bootstrap.Scripts.Select(script => ResolveRegistryUrl(baseUrl, script)!).ToArray(), + Styles = bootstrap.Styles.Select(style => ResolveRegistryUrl(baseUrl, style)!).ToArray() + }; + return JsonSerializer.Serialize(resolvedBootstrap, StorybookRegistryJsonContext.Default.StorybookRegistryBootstrap); + } + + private static string? ResolveRegistryUrl(string? baseUrl, string? rawUrl) + { + if (string.IsNullOrWhiteSpace(rawUrl)) + return rawUrl; + + var trimmed = rawUrl.Trim(); + if (Uri.TryCreate(trimmed, UriKind.Absolute, out _)) + return trimmed; + + if (!string.IsNullOrWhiteSpace(baseUrl) && Uri.TryCreate(baseUrl, UriKind.Absolute, out var baseUri)) + return new Uri(baseUri, trimmed).ToString(); + + return trimmed; + } + + private sealed record StoryReference(string? Project, string? Storybook, string DocsId, string? Component, string? StoryName, string RawId) + { + public static StoryReference FromId(string rawId, string? project, string? storybook, string? component, string? story) + { + var parts = rawId.Split(':', 3, StringSplitOptions.TrimEntries); + if (parts.Length == 3) + return new StoryReference(parts[0], parts[1], parts[2], component, story, rawId); + + return new StoryReference(project, storybook, rawId, component, story, rawId); + } + } +} diff --git a/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookRegistry.cs b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookRegistry.cs new file mode 100644 index 000000000..86939547b --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookRegistry.cs @@ -0,0 +1,90 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Elastic.Markdown.Myst.Directives.Storybook; + +public sealed class StorybookRegistry +{ + [JsonPropertyName("schemaVersion")] + public JsonElement SchemaVersion { get; init; } + + [JsonPropertyName("producer")] + public string? Producer { get; init; } + + [JsonPropertyName("baseUrl")] + public string? BaseUrl { get; init; } + + [JsonPropertyName("build")] + public Dictionary? Build { get; init; } + + [JsonPropertyName("stories")] + public Dictionary Stories { get; init; } = [with(StringComparer.Ordinal)]; +} + +public sealed class StorybookRegistryStory +{ + [JsonPropertyName("alias")] + public string? Alias { get; init; } + + [JsonPropertyName("docsId")] + public string? DocsId { get; init; } + + [JsonPropertyName("storybookId")] + public string? StorybookId { get; init; } + + [JsonPropertyName("title")] + public string? Title { get; init; } + + [JsonPropertyName("name")] + public string? Name { get; init; } + + [JsonPropertyName("height")] + public int? Height { get; init; } + + [JsonPropertyName("renderMode")] + public string? RenderMode { get; init; } + + [JsonPropertyName("inline")] + public StorybookRegistryInline? Inline { get; init; } + + [JsonPropertyName("iframe")] + public StorybookRegistryIframe? Iframe { get; init; } +} + +public sealed class StorybookRegistryIframe +{ + [JsonPropertyName("url")] + public string? Url { get; init; } +} + +public sealed class StorybookRegistryInline +{ + [JsonPropertyName("entry")] + public string? Entry { get; init; } + + [JsonPropertyName("bundleId")] + public string? BundleId { get; init; } + + [JsonPropertyName("bootstrap")] + public StorybookRegistryBootstrap? Bootstrap { get; init; } +} + +public sealed class StorybookRegistryBootstrap +{ + [JsonPropertyName("publicPath")] + public string? PublicPath { get; init; } + + [JsonPropertyName("scripts")] + public IReadOnlyCollection Scripts { get; init; } = []; + + [JsonPropertyName("styles")] + public IReadOnlyCollection Styles { get; init; } = []; +} + +[JsonSerializable(typeof(StorybookRegistry))] +[JsonSerializable(typeof(StorybookRegistryBootstrap))] +internal sealed partial class StorybookRegistryJsonContext : JsonSerializerContext; diff --git a/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookView.cshtml b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookView.cshtml new file mode 100644 index 000000000..086e4064f --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookView.cshtml @@ -0,0 +1,26 @@ +@inherits RazorSlice +
+@if (Model.HasInlineStory) +{ + + +} +else +{ + +} + @if (Model.HasBody) + { +
+ @Model.RenderBlock() +
+ } +
diff --git a/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookViewModel.cs b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookViewModel.cs new file mode 100644 index 000000000..f201bcf75 --- /dev/null +++ b/src/Elastic.Markdown/Myst/Directives/Storybook/StorybookViewModel.cs @@ -0,0 +1,26 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +namespace Elastic.Markdown.Myst.Directives.Storybook; + +public class StorybookViewModel : DirectiveViewModel +{ + public required string StoryUrl { get; init; } + + public required string StoryId { get; init; } + + public required int Height { get; init; } + + public required string IframeTitle { get; init; } + + public bool HasBody { get; init; } + + public string? InlineEntry { get; init; } + + public string? InlineBootstrapJson { get; init; } + + public bool HasInlineStory => !string.IsNullOrWhiteSpace(InlineEntry); + + public string HeightStyle => $"{Height}px"; +} diff --git a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs index 25002d5be..729037689 100644 --- a/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs +++ b/src/Elastic.Markdown/Myst/Renderers/LlmMarkdown/LlmBlockRenderers.cs @@ -15,6 +15,7 @@ using Elastic.Markdown.Myst.Directives.Include; using Elastic.Markdown.Myst.Directives.Math; using Elastic.Markdown.Myst.Directives.Settings; +using Elastic.Markdown.Myst.Directives.Storybook; using Markdig.Extensions.DefinitionLists; using Markdig.Extensions.Tables; using Markdig.Extensions.Yaml; @@ -496,6 +497,9 @@ protected override void Write(LlmMarkdownRenderer renderer, DirectiveBlock obj) case AgentSkillBlock agentSkillBlock: WriteAgentSkillBlock(renderer, agentSkillBlock); return; + case StorybookBlock storybookBlock: + WriteStorybookBlock(renderer, storybookBlock); + return; } // Ensure single empty line before directive @@ -864,6 +868,28 @@ private static void WriteAgentSkillBlock(LlmMarkdownRenderer renderer, AgentSkil renderer.EnsureLine(); } + private static void WriteStorybookBlock(LlmMarkdownRenderer renderer, StorybookBlock block) + { + if (string.IsNullOrEmpty(block.StoryUrl)) + return; + + renderer.EnsureBlockSpacing(); + renderer.Writer.Write(""); + if (block.Count > 0) + WriteChildrenWithIndentation(renderer, block, " "); + renderer.Writer.WriteLine(""); + renderer.EnsureLine(); + } + private static void WriteChildrenWithIndentation(LlmMarkdownRenderer renderer, Block container, string indent) { // Capture output and manually add indentation diff --git a/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs b/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs index 62389d448..48edbc964 100644 --- a/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs +++ b/tests/Elastic.Markdown.Tests/Directives/DirectiveBaseTests.cs @@ -67,7 +67,7 @@ protected DirectiveTest(ITestOutputHelper output, [LanguageInjection("markdown") var root = FileSystem.DirectoryInfo.New(Path.Join(Paths.WorkingDirectoryRoot.FullName, "docs/")); // ReSharper disable once VirtualMemberCallInConstructor - FileSystem.GenerateDocSetYaml(root, products: GetDocsetProducts()); + FileSystem.GenerateDocSetYaml(root, products: GetDocsetProducts(), extraYaml: GetDocsetExtraYaml()); Collector = new TestDiagnosticsCollector(output); var configurationContext = TestHelpers.CreateConfigurationContext(FileSystem); @@ -87,6 +87,8 @@ protected virtual void AddToFileSystem(MockFileSystem fileSystem) { } /// protected virtual IReadOnlyList? GetDocsetProducts() => null; + protected virtual string? GetDocsetExtraYaml() => null; + public virtual async ValueTask InitializeAsync() { _ = Collector.StartAsync(TestContext.Current.CancellationToken); diff --git a/tests/Elastic.Markdown.Tests/Directives/StorybookTests.cs b/tests/Elastic.Markdown.Tests/Directives/StorybookTests.cs new file mode 100644 index 000000000..9180a132a --- /dev/null +++ b/tests/Elastic.Markdown.Tests/Directives/StorybookTests.cs @@ -0,0 +1,246 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using System.IO.Abstractions.TestingHelpers; +using AwesomeAssertions; +using Elastic.Documentation.Diagnostics; +using Elastic.Markdown.Myst.Directives.Storybook; + +namespace Elastic.Markdown.Tests.Directives; + +public abstract class StorybookRegistryTest(ITestOutputHelper output, string content) : DirectiveTest(output, content) +{ + protected override void AddToFileSystem(MockFileSystem fileSystem) => + fileSystem.AddFile("docs/docs_registry.json", new MockFileData(RegistryJson)); + + protected override string? GetDocsetExtraYaml() => +""" +storybook: + registry: docs_registry.json +"""; + + private const string RegistryJson = + /*lang=json,strict*/ + """ + { + "schemaVersion": 1, + "producer": "kibana-storybook", + "baseUrl": "http://127.0.0.1:6007/storybook-docs", + "build": { + "commit": "abc123", + "branch": "storybook-to-docs" + }, + "stories": { + "kibana:shared_ux:ai-components-aibutton--default": { + "alias": "shared_ux", + "docsId": "ai-components-aibutton--default", + "storybookId": "ai-components-aibutton--default", + "title": "ai-components/aibutton", + "name": "Default", + "height": 360, + "type": "story", + "renderMode": "inline", + "inline": { + "entry": "http://127.0.0.1:6007/storybook-docs/shared_ux/registry.js", + "bundleId": "shared_ux", + "bootstrap": { + "publicPath": "http://127.0.0.1:6007/storybook/shared_ux/", + "scripts": [ + "http://127.0.0.1:6007/storybook/shared_ux/kbn-ui-shared-deps-npm.dll.js", + "http://127.0.0.1:6007/storybook/shared_ux/kbn-ui-shared-deps-src.js" + ], + "styles": [ + "http://127.0.0.1:6007/storybook/shared_ux/kbn-ui-shared-deps-src.css", + "https://fonts.googleapis.com/css2?family=Inter:wght@300..700&display=swap" + ] + } + }, + "iframe": { + "url": "http://127.0.0.1:6007/storybook/shared_ux/iframe.html?id=ai-components-aibutton--default&viewMode=story" + } + }, + "kibana:shared_ux:components-callout--info": { + "alias": "shared_ux", + "docsId": "components-callout--info", + "storybookId": "components-callout--info-storybook", + "title": "components-callout", + "name": "Info", + "type": "story", + "renderMode": "iframe", + "iframe": { + "url": "http://127.0.0.1:6007/storybook/shared_ux/iframe.html?id=components-callout--info-storybook&viewMode=story" + } + } + } + } + """; +} + +public class StorybookInlineIdTests(ITestOutputHelper output) : StorybookRegistryTest(output, +""" +:::{storybook} +:id: kibana:shared_ux:ai-components-aibutton--default +:title: AI Button / Default story +::: +""" +) +{ + [Fact] + public void ResolvesStory() + { + Block!.Project.Should().Be("kibana"); + Block.Storybook.Should().Be("shared_ux"); + Block.DocsId.Should().Be("ai-components-aibutton--default"); + Block.StoryId.Should().Be("ai-components-aibutton--default"); + Block.InlineEntry.Should().Be("http://127.0.0.1:6007/storybook-docs/shared_ux/registry.js"); + Block.StoryUrl.Should().Be("http://127.0.0.1:6007/storybook/shared_ux/iframe.html?id=ai-components-aibutton--default&viewMode=story"); + Block.Height.Should().Be(360); + } + + [Fact] + public void RendersInlineStory() + { + Html.Should().Contain(" + Block!.StoryId.Should().Be("ai-components-aibutton--default"); +} + +public class StorybookStructuredReferenceWrongStorybookTests(ITestOutputHelper output) : StorybookRegistryTest(output, +""" +:::{storybook} +:project: kibana +:storybook: content_management +:story: ai-components-aibutton--default +::: +""" +) +{ + [Fact] + public void DoesNotFallbackToAnotherStorybook() => + Collector.Diagnostics.Should().Contain(d => d.Message.Contains("does not contain id 'kibana:content_management:ai-components-aibutton--default'")); +} + +public class StorybookBareIdTests(ITestOutputHelper output) : StorybookRegistryTest(output, +""" +:::{storybook} +:id: ai-components-aibutton--default +::: +""" +) +{ + [Fact] + public void ResolvesFromConfiguredRegistry() => + Block!.StoryId.Should().Be("ai-components-aibutton--default"); +} + +public class StorybookIframeTests(ITestOutputHelper output) : StorybookRegistryTest(output, +""" +:::{storybook} +:id: kibana:shared_ux:components-callout--info +::: +""" +) +{ + [Fact] + public void RendersIframeFallback() + { + Block!.HasInlineStory.Should().BeFalse(); + Html.Should().Contain(" + Html.Should().Contain("Supporting details for this story."); +} + +public class StorybookInvalidHeightTests(ITestOutputHelper output) : StorybookRegistryTest(output, +""" +:::{storybook} +:id: kibana:shared_ux:components-callout--info +:height: tall +::: +""" +) +{ + [Fact] + public void WarnsAndFallsBackToDefaultHeight() + { + Block!.Height.Should().Be(400); + Collector.Diagnostics.Should().ContainSingle(d => + d.Severity == Severity.Warning + && d.Message.Contains(":height: must be a positive integer")); + Html.Should().Contain("height:400px"); + } +} + +public class StorybookMissingRegistryTests(ITestOutputHelper output) : DirectiveTest(output, +""" +:::{storybook} +:id: kibana:shared_ux:ai-components-aibutton--default +::: +""" +) +{ + [Fact] + public void EmitsError() => + Collector.Diagnostics.Should().Contain(d => d.Message.Contains("requires docset.yml storybook.registry")); +} + +public class StorybookMissingIdTests(ITestOutputHelper output) : StorybookRegistryTest(output, +""" +:::{storybook} +::: +""" +) +{ + [Fact] + public void EmitsError() => + Collector.Diagnostics.Should().Contain(d => d.Message.Contains("requires :id: or :project:")); +} + +public class StorybookPositionalArgumentWarningTests(ITestOutputHelper output) : StorybookRegistryTest(output, +""" +:::{storybook} /storybook/ignored +:id: kibana:shared_ux:components-callout--info +::: +""" +) +{ + [Fact] + public void EmitsWarning() => + Collector.Diagnostics.Should().ContainSingle(d => + d.Severity == Severity.Warning + && d.Message.Contains("ignores positional arguments")); +} diff --git a/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs b/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs index 511cb70bd..b98472d61 100644 --- a/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs +++ b/tests/Elastic.Markdown.Tests/MockFileSystemExtensions.cs @@ -13,7 +13,8 @@ public static void GenerateDocSetYaml( this MockFileSystem fileSystem, IDirectoryInfo root, Dictionary? globalVariables = null, - IReadOnlyList? products = null) + IReadOnlyList? products = null, + string? extraYaml = null) { // language=yaml var yaml = new StringWriter(); @@ -47,6 +48,9 @@ public static void GenerateDocSetYaml( yaml.WriteLine($" {key}: {value}"); } + if (!string.IsNullOrWhiteSpace(extraYaml)) + yaml.WriteLine(extraYaml.Trim()); + fileSystem.AddFile(Path.Join(root.FullName, "docset.yml"), new MockFileData(yaml.ToString())); } } diff --git a/tests/authoring/Blocks/Storybook.fs b/tests/authoring/Blocks/Storybook.fs new file mode 100644 index 000000000..2aadbe4fa --- /dev/null +++ b/tests/authoring/Blocks/Storybook.fs @@ -0,0 +1,26 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information +module ``AuthoringTests``.``block elements``.``storybook elements`` + +open Xunit +open authoring + +type ``storybook missing reference`` () = + static let markdown = Setup.Markdown """ +:::{storybook} +::: +""" + + [] + let ``has error`` () = markdown |> hasError "requires :id: or :project:" + +type ``storybook missing registry`` () = + static let markdown = Setup.Markdown """ +:::{storybook} +:id: kibana:shared_ux:components-button--regular +::: +""" + + [] + let ``has error`` () = markdown |> hasError "requires docset.yml storybook.registry" diff --git a/tests/authoring/authoring.fsproj b/tests/authoring/authoring.fsproj index b71096b26..db9aafb70 100644 --- a/tests/authoring/authoring.fsproj +++ b/tests/authoring/authoring.fsproj @@ -51,6 +51,7 @@ + From b015bef50a0637f932969bc5fb6e2c399774d1d9 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 10 Jun 2026 15:06:13 +0200 Subject: [PATCH 07/19] fix(cli-reference): preserve supplemental frontmatter (#3488) Keep YAML frontmatter from CLI supplemental files as metadata while parsing the remaining body with the existing supplemental rules. Co-authored-by: GPT-5.5 Co-authored-by: Cursor --- docs/cli/cli-supplemental-docs.md | 20 ++++++ .../CliReference/CliMarkdownGenerator.cs | 12 ++++ .../CliReference/CliSupplementalDoc.cs | 22 ++++-- .../CliReference/CliSupplementalDocTests.cs | 70 +++++++++++++++++++ 4 files changed, 120 insertions(+), 4 deletions(-) create mode 100644 tests/Elastic.Markdown.Tests/CliReference/CliSupplementalDocTests.cs diff --git a/docs/cli/cli-supplemental-docs.md b/docs/cli/cli-supplemental-docs.md index 14f6bbe84..f2c2cf7f2 100644 --- a/docs/cli/cli-supplemental-docs.md +++ b/docs/cli/cli-supplemental-docs.md @@ -10,6 +10,8 @@ Supplemental files let you enrich any auto-generated CLI reference page with con **Validation is strict.** Any supplemental file whose name does not match a known namespace or command produces a build error, so renamed or removed commands can never leave orphaned docs behind silently. +**Frontmatter is preserved as metadata.** Add YAML frontmatter to set page metadata such as `description`, `applies_to`, or `navigation_title`. It is passed through to the generated page and is not rendered as supplemental description text. + ## File naming Two naming styles are supported and can coexist in the same folder. @@ -48,6 +50,24 @@ cli/ The heading structure of a supplemental file controls what it contributes to the generated page. +### Frontmatter + +Use frontmatter for page metadata: + +```markdown +--- +description: Use the Elastic CLI to call Elasticsearch REST APIs from the command line. +applies_to: + stack: preview +--- + +## Description + +The `elastic stack es` command group exposes Elasticsearch REST APIs as CLI commands. +``` + +The metadata remains metadata. The generated page uses the `## Description` section, or the schema description if the file only contains frontmatter. + ### No headings A file with no `##` headings replaces the auto-generated description entirely: diff --git a/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs b/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs index d4a616814..0a6c1df78 100644 --- a/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs +++ b/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs @@ -13,6 +13,7 @@ internal static partial class CliMarkdownGenerator public static string RootPage(CliSchema schema, CliSupplementalDoc? supplemental) { var sb = new StringBuilder(); + AppendFrontMatter(sb, supplemental); _ = sb.AppendLine($"# {schema.Name}"); _ = sb.AppendLine(); @@ -108,6 +109,7 @@ public static string NamespacePage( List? shortcuts = null) { var sb = new StringBuilder(); + AppendFrontMatter(sb, supplemental); var heading = fullPath is { Length: > 0 } ? string.Join(" ", fullPath) : ns.Segment; _ = sb.AppendLine($"# {heading} cli namespace"); _ = sb.AppendLine(); @@ -198,6 +200,7 @@ public static string CommandPage( List? shortcuts = null) { var sb = new StringBuilder(); + AppendFrontMatter(sb, supplemental); var heading = fullPath is { Length: > 0 } ? string.Join(" ", fullPath) : cmd.Name; _ = sb.AppendLine($"# {heading} cli command"); _ = sb.AppendLine(); @@ -322,6 +325,15 @@ public static string CommandPage( return sb.ToString(); } + private static void AppendFrontMatter(StringBuilder sb, CliSupplementalDoc? supplemental) + { + if (string.IsNullOrWhiteSpace(supplemental?.FrontMatter)) + return; + + _ = sb.AppendLine(supplemental.FrontMatter); + _ = sb.AppendLine(); + } + private static void AppendCommandModifiers(StringBuilder sb, CliCommandSchema cmd) { if (cmd.Deprecated is not null) diff --git a/src/Elastic.Markdown/Extensions/CliReference/CliSupplementalDoc.cs b/src/Elastic.Markdown/Extensions/CliReference/CliSupplementalDoc.cs index ca59d01dd..854ceab23 100644 --- a/src/Elastic.Markdown/Extensions/CliReference/CliSupplementalDoc.cs +++ b/src/Elastic.Markdown/Extensions/CliReference/CliSupplementalDoc.cs @@ -7,6 +7,7 @@ namespace Elastic.Markdown.Extensions.CliReference; internal sealed partial record CliSupplementalDoc( + string? FrontMatter, string? Description, Dictionary OptionOverrides, Dictionary ArgumentOverrides, @@ -18,13 +19,14 @@ internal sealed partial record CliSupplementalDoc( if (raw is null) return null; - var trimmed = raw.Trim(); + var (frontMatter, rawContent) = ExtractFrontMatter(raw); + var trimmed = rawContent.Trim(); if (string.IsNullOrWhiteSpace(trimmed)) - return null; + return string.IsNullOrWhiteSpace(frontMatter) ? null : new CliSupplementalDoc(frontMatter, null, [], [], null); // Backward compat: no ## headings → entire content is description if (!trimmed.Contains("\n## ") && !trimmed.StartsWith("## ", StringComparison.Ordinal)) - return new CliSupplementalDoc(trimmed, [], [], null); + return new CliSupplementalDoc(frontMatter, trimmed, [], [], null); var sections = SplitSections(trimmed); string? description = null; @@ -55,7 +57,16 @@ internal sealed partial record CliSupplementalDoc( } var postContent = postParts.Count > 0 ? string.Join("\n\n", postParts) : null; - return new CliSupplementalDoc(description, optionOverrides, argumentOverrides, postContent); + return new CliSupplementalDoc(frontMatter, description, optionOverrides, argumentOverrides, postContent); + } + + private static (string? FrontMatter, string Content) ExtractFrontMatter(string raw) + { + var match = FrontMatterRegex().Match(raw); + if (!match.Success) + return (null, raw); + + return (match.Value.Trim(), raw[match.Length..]); } private static List<(string? heading, string body)> SplitSections(string text) @@ -124,4 +135,7 @@ private static string NormalizeKey(string raw) // Matches: `: `--flag`` or `: --flag` or `: ` [GeneratedRegex(@"^:\s+(`[^`]+`|--[\w-]+|<[\w-]+>)")] private static partial Regex TermLineRegex(); + + [GeneratedRegex(@"\A---\r?\n[\s\S]*?\r?\n---[ \t]*(?:\r?\n|$)")] + private static partial Regex FrontMatterRegex(); } diff --git a/tests/Elastic.Markdown.Tests/CliReference/CliSupplementalDocTests.cs b/tests/Elastic.Markdown.Tests/CliReference/CliSupplementalDocTests.cs new file mode 100644 index 000000000..5de73f634 --- /dev/null +++ b/tests/Elastic.Markdown.Tests/CliReference/CliSupplementalDocTests.cs @@ -0,0 +1,70 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using AwesomeAssertions; +using Elastic.Documentation.Configuration.Toc.CliReference; +using Elastic.Markdown.Extensions.CliReference; + +namespace Elastic.Markdown.Tests.CliReference; + +public class CliSupplementalDocTests +{ + [Fact] + public void RootPage_PreservesFrontMatterAsMetadata() + { + var schema = CreateSchema(); + const string raw = """ + --- + description: Use the Elastic CLI from the command line. + applies_to: + stack: preview + --- + """; + + var supplemental = CliSupplementalDoc.Parse(raw); + var markdown = CliMarkdownGenerator.RootPage(schema, supplemental).ReplaceLineEndings("\n"); + + var expectedStart = """ + --- + description: Use the Elastic CLI from the command line. + applies_to: + stack: preview + --- + + # elastic + """.ReplaceLineEndings("\n"); + + markdown.Should().StartWith(expectedStart); + markdown.Should().NotContain("description: Use the Elastic CLI from the command line.\n\n"); + } + + [Fact] + public void RootPage_StripsFrontMatterBeforeParsingDescription() + { + var schema = CreateSchema(); + const string raw = """ + --- + description: Metadata description. + --- + + User-facing supplemental description. + """; + + var supplemental = CliSupplementalDoc.Parse(raw); + var markdown = CliMarkdownGenerator.RootPage(schema, supplemental).ReplaceLineEndings("\n"); + + markdown.Should().Contain("\n# elastic\n\nUser-facing supplemental description.\n"); + markdown.Should().NotContain("\nMetadata description.\n"); + } + + private static CliSchema CreateSchema() => new( + SchemaVersion: 1, + Name: "elastic", + Description: "Schema description.", + GlobalOptions: [], + RootDefault: null, + Commands: [], + Namespaces: [] + ); +} From 9c4e40f6ad44786a06b6e2c27a5f49d13a899ae2 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti Date: Wed, 10 Jun 2026 15:54:16 +0200 Subject: [PATCH 08/19] feat(cli-reference): support title overrides (#3487) Allow cli TOC entries to override the generated root page title and navigation label without changing schema command names. Co-authored-by: GPT-5.5 Co-authored-by: Cursor --- docs/cli/cli-reference-how-to.md | 12 +++++ .../Toc/CliReference/CliReferenceRef.cs | 2 + .../Toc/DocumentationSetFile.cs | 2 +- .../Toc/TableOfContentsYamlConverters.cs | 4 +- .../CliReference/CliMarkdownGenerator.cs | 5 +- .../CliReferenceDocsBuilderExtension.cs | 10 ++-- .../Extensions/CliReference/CliRootFile.cs | 16 ++++-- .../PhysicalDocsetTests.cs | 19 +++++++ .../CliReference/CliMarkdownGeneratorTests.cs | 51 +++++++++++++++++++ 9 files changed, 109 insertions(+), 12 deletions(-) create mode 100644 tests/Elastic.Markdown.Tests/CliReference/CliMarkdownGeneratorTests.cs diff --git a/docs/cli/cli-reference-how-to.md b/docs/cli/cli-reference-how-to.md index adfa46bbb..df0cb0aa5 100644 --- a/docs/cli/cli-reference-how-to.md +++ b/docs/cli/cli-reference-how-to.md @@ -62,6 +62,16 @@ toc: folder: cli-reference ``` +Use `title:` to customize the generated CLI root page title, and `navigation_title:` to customize the sidebar and breadcrumb label without changing generated command examples: + +```yaml +toc: + - cli: cli-schema.json + folder: cli-reference + title: Elastic CLI reference + navigation_title: CLI reference +``` + Use `children:` to prepend hand-written pages — installation guides, conceptual overviews, or quick-start tutorials — before the auto-generated reference. All schema-generated pages follow the listed children: ```yaml @@ -101,4 +111,6 @@ Your CLI reference section is live. As your CLI evolves, regenerate the schema a |---|---| | `cli: ` | Path to the schema JSON, relative to `docset.yml` | | `folder: ` | Supplemental docs folder; also sets the URL prefix | +| `title: ` | Optional generated CLI root page title | +| `navigation_title: <title>` | Optional generated CLI root navigation label | | `children:` | Regular toc items prepended before generated pages | diff --git a/src/Elastic.Documentation.Configuration/Toc/CliReference/CliReferenceRef.cs b/src/Elastic.Documentation.Configuration/Toc/CliReference/CliReferenceRef.cs index f67f023a2..e41c661ec 100644 --- a/src/Elastic.Documentation.Configuration/Toc/CliReference/CliReferenceRef.cs +++ b/src/Elastic.Documentation.Configuration/Toc/CliReference/CliReferenceRef.cs @@ -14,6 +14,8 @@ namespace Elastic.Documentation.Configuration.Toc.CliReference; public record CliReferenceRef( string SchemaPath, string? SupplementalFolder, + string? Title, + string? NavigationTitle, string PathRelativeToDocumentationSet, string PathRelativeToContainer, string Context, diff --git a/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs b/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs index 5b94ab7f7..f82f87150 100644 --- a/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs +++ b/src/Elastic.Documentation.Configuration/Toc/DocumentationSetFile.cs @@ -548,7 +548,7 @@ private static ITableOfContentsItem ResolveRuleOverviewReference(IDiagnosticsCol ? ResolveTableOfContents(collector, cliRef.Children, baseDirectory, fileSystem, fullVirtualRoot, containerPath, context) : []; - return new CliReferenceRef(schemaFullPath, cliRef.SupplementalFolder, fullVirtualRoot, pathRelativeToContainer, context, resolvedChildren); + return new CliReferenceRef(schemaFullPath, cliRef.SupplementalFolder, cliRef.Title, cliRef.NavigationTitle, fullVirtualRoot, pathRelativeToContainer, context, resolvedChildren); } /// <summary> diff --git a/src/Elastic.Documentation.Configuration/Toc/TableOfContentsYamlConverters.cs b/src/Elastic.Documentation.Configuration/Toc/TableOfContentsYamlConverters.cs index 664c18085..baee79ca4 100644 --- a/src/Elastic.Documentation.Configuration/Toc/TableOfContentsYamlConverters.cs +++ b/src/Elastic.Documentation.Configuration/Toc/TableOfContentsYamlConverters.cs @@ -117,7 +117,9 @@ public class TocItemYamlConverter : IYamlTypeConverter if (dictionary.TryGetValue("cli", out var cliSchemaPath) && cliSchemaPath is string cliSchema) { var supplementalFolder = dictionary.TryGetValue("folder", out var f) && f is string fStr ? fStr : null; - return new CliReferenceRef(cliSchema, supplementalFolder, cliSchema, cliSchema, placeholderContext, children); + var title = dictionary.TryGetValue("title", out var t) && t is string titleStr ? titleStr : null; + var navigationTitle = dictionary.TryGetValue("navigation_title", out var nt) && nt is string navigationTitleStr ? navigationTitleStr : null; + return new CliReferenceRef(cliSchema, supplementalFolder, title, navigationTitle, cliSchema, cliSchema, placeholderContext, children); } // Check for folder+file combination (e.g., folder: getting-started, file: getting-started.md) diff --git a/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs b/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs index 0a6c1df78..0b718330e 100644 --- a/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs +++ b/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs @@ -10,11 +10,12 @@ namespace Elastic.Markdown.Extensions.CliReference; internal static partial class CliMarkdownGenerator { - public static string RootPage(CliSchema schema, CliSupplementalDoc? supplemental) + public static string RootPage(CliSchema schema, CliSupplementalDoc? supplemental, string? title = null) { var sb = new StringBuilder(); AppendFrontMatter(sb, supplemental); - _ = sb.AppendLine($"# {schema.Name}"); + var pageTitle = string.IsNullOrWhiteSpace(title) ? schema.Name : title.Trim(); + _ = sb.AppendLine($"# {pageTitle}"); _ = sb.AppendLine(); var description = supplemental?.Description ?? schema.Description?.Trim(); diff --git a/src/Elastic.Markdown/Extensions/CliReference/CliReferenceDocsBuilderExtension.cs b/src/Elastic.Markdown/Extensions/CliReference/CliReferenceDocsBuilderExtension.cs index 3bc9e2e01..9204df692 100644 --- a/src/Elastic.Markdown/Extensions/CliReference/CliReferenceDocsBuilderExtension.cs +++ b/src/Elastic.Markdown/Extensions/CliReference/CliReferenceDocsBuilderExtension.cs @@ -24,7 +24,11 @@ internal sealed record CliEntityInfo( /// <summary>Ancestor namespace options ordered from closest to furthest (direct parent first).</summary> IReadOnlyList<(string Segment, List<CliParamSchema>? Options)>? AncestorNamespaceOptions = null, /// <summary>Relative path from this file to the alias target — set for CliShortcutSchema entities only.</summary> - string? AliasCanonicalRelativePath = null + string? AliasCanonicalRelativePath = null, + /// <summary>Display title for the generated CLI root page.</summary> + string? Title = null, + /// <summary>Navigation title for the generated CLI root page.</summary> + string? NavigationTitle = null ); public class CliReferenceDocsBuilderExtension(BuildContext build) : IDocsBuilderExtension @@ -113,7 +117,7 @@ private void EnsureSyntheticFilesBuilt() private MarkdownFile? CreateCliFileFromInfo(IFileInfo sourceFile, MarkdownParser markdownParser, CliEntityInfo info) => info.Entity switch { - CliSchema schema => new CliRootFile(sourceFile, Build.DocumentationSourceDirectory, markdownParser, Build, schema, info.SupplementalDoc), + CliSchema schema => new CliRootFile(sourceFile, Build.DocumentationSourceDirectory, markdownParser, Build, schema, info.SupplementalDoc, info.Title, info.NavigationTitle), CliNamespaceSchema ns => new CliNamespaceFile(sourceFile, Build.DocumentationSourceDirectory, markdownParser, Build, ns, info.SupplementalDoc, info.FullPath ?? [ns.Segment], info.Schema.Name, info.Schema.ReservedMetaCommands, info.Schema.Shortcuts), CliCommandSchema cmd => new CliCommandFile(sourceFile, Build.DocumentationSourceDirectory, markdownParser, Build, cmd, info.SupplementalDoc, info.FullPath ?? [cmd.Name], info.Schema.Name, info.Schema.ReservedMetaCommands, info.AncestorNamespaceOptions, info.Schema.GlobalOptions, info.Schema.Shortcuts), CliShortcutSchema shortcut => new CliAliasFile(sourceFile, Build.DocumentationSourceDirectory, markdownParser, Build, shortcut, info.Schema.Name, info.AliasCanonicalRelativePath ?? "../"), @@ -186,7 +190,7 @@ private List<IFileInfo> BuildSyntheticFiles() var rootSupplemental = FindSupplemental(supplementalDirPath, [], isNamespace: true, matched); var rootSyntheticPath = SyntheticPath(Build.DocumentationSourceDirectory.FullName, virtualRoot, [], isNamespace: true); var rootFileInfo = Build.ReadFileSystem.FileInfo.New(rootSyntheticPath); - var rootInfo = new CliEntityInfo(schema, schema, rootSupplemental, rootFileInfo); + var rootInfo = new CliEntityInfo(schema, schema, rootSupplemental, rootFileInfo, Title: cliRef.Title, NavigationTitle: cliRef.NavigationTitle); _syntheticFiles![rootSyntheticPath] = rootInfo; if (rootSupplemental != null) _supplementalFiles![rootSupplemental.FullName] = rootInfo; diff --git a/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs b/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs index 991622158..1eefadd2b 100644 --- a/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs +++ b/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs @@ -14,6 +14,8 @@ public record CliRootFile : IO.MarkdownFile { private readonly CliSchema _schema; private readonly IFileInfo? _supplementalDoc; + private readonly string _title; + private readonly string _navigationTitle; public CliRootFile( IFileInfo sourceFile, @@ -21,19 +23,23 @@ public CliRootFile( MarkdownParser parser, BuildContext build, CliSchema schema, - IFileInfo? supplementalDoc + IFileInfo? supplementalDoc, + string? title = null, + string? navigationTitle = null ) : base(sourceFile, rootPath, parser, build) { _schema = schema; _supplementalDoc = supplementalDoc; - Title = schema.Name; + _title = string.IsNullOrWhiteSpace(title) ? schema.Name : title; + _navigationTitle = string.IsNullOrWhiteSpace(navigationTitle) ? $"{schema.Name} CLI" : navigationTitle; + Title = _title; } - public override string NavigationTitle => $"{_schema.Name} CLI"; + public override string NavigationTitle => _navigationTitle; protected override Task<MarkdownDocument> GetMinimalParseDocumentAsync(Cancel ctx) { - Title = _schema.Name; + Title = _title; var markdown = BuildMarkdown(); return Task.FromResult(MarkdownParser.MinimalParseStringAsync(markdown, SourceFile, null)); } @@ -50,6 +56,6 @@ private string BuildMarkdown() ? _supplementalDoc.FileSystem.File.ReadAllText(_supplementalDoc.FullName) : null; var supplemental = CliSupplementalDoc.Parse(rawSupplemental); - return CliMarkdownGenerator.RootPage(_schema, supplemental); + return CliMarkdownGenerator.RootPage(_schema, supplemental, _title); } } diff --git a/tests/Elastic.Documentation.Configuration.Tests/PhysicalDocsetTests.cs b/tests/Elastic.Documentation.Configuration.Tests/PhysicalDocsetTests.cs index ffb96d96e..9a7d41b80 100644 --- a/tests/Elastic.Documentation.Configuration.Tests/PhysicalDocsetTests.cs +++ b/tests/Elastic.Documentation.Configuration.Tests/PhysicalDocsetTests.cs @@ -10,6 +10,25 @@ namespace Elastic.Documentation.Configuration.Tests; public class PhysicalDocsetTests { + [Fact] + public void CliReferenceRefReadsTitleOverrides() + { + const string yaml = """ + project: test + toc: + - cli: cli/schema.json + folder: cli + title: Elastic CLI reference + navigation_title: CLI reference + """; + + var docSet = ConfigurationFileProvider.Deserializer.Deserialize<DocumentationSetFile>(yaml); + var cliRef = docSet.TableOfContents.OfType<CliReferenceRef>().Single(); + + cliRef.Title.Should().Be("Elastic CLI reference"); + cliRef.NavigationTitle.Should().Be("CLI reference"); + } + [Fact] public void PhysicalDocsetFileCanBeDeserialized() { diff --git a/tests/Elastic.Markdown.Tests/CliReference/CliMarkdownGeneratorTests.cs b/tests/Elastic.Markdown.Tests/CliReference/CliMarkdownGeneratorTests.cs new file mode 100644 index 000000000..796a3c377 --- /dev/null +++ b/tests/Elastic.Markdown.Tests/CliReference/CliMarkdownGeneratorTests.cs @@ -0,0 +1,51 @@ +// Licensed to Elasticsearch B.V under one or more agreements. +// Elasticsearch B.V licenses this file to you under the Apache 2.0 License. +// See the LICENSE file in the project root for more information + +using AwesomeAssertions; +using Elastic.Documentation.Configuration.Toc.CliReference; +using Elastic.Markdown.Extensions.CliReference; + +namespace Elastic.Markdown.Tests.CliReference; + +public class CliMarkdownGeneratorTests +{ + [Fact] + public void RootPage_UsesTitleOverrideForHeading() + { + var schema = new CliSchema( + SchemaVersion: 1, + Name: "elastic", + Description: "Interact with Elastic from the command line.", + GlobalOptions: [], + RootDefault: null, + Commands: [], + Namespaces: [] + ); + + var markdown = CliMarkdownGenerator.RootPage(schema, null, "Elastic CLI reference"); + + markdown.Should().StartWith("# Elastic CLI reference"); + markdown.Should().Contain("Interact with Elastic from the command line."); + } + + [Theory] + [InlineData("")] + [InlineData(" ")] + public void RootPage_FallsBackToSchemaNameForBlankTitleOverride(string title) + { + var schema = new CliSchema( + SchemaVersion: 1, + Name: "elastic", + Description: "Interact with Elastic from the command line.", + GlobalOptions: [], + RootDefault: null, + Commands: [], + Namespaces: [] + ); + + var markdown = CliMarkdownGenerator.RootPage(schema, null, title); + + markdown.Should().StartWith("# elastic"); + } +} From d11e18d8bab03bd17476e43e3c8e8f3b167c3718 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti <fabri.ferribenedetti@elastic.co> Date: Thu, 11 Jun 2026 14:40:55 +0200 Subject: [PATCH 09/19] fix(cli-reference): use segment path instead of segment/index.md for namespace page cards (#3489) * feat(cli-reference): support title overrides Allow cli TOC entries to override the generated root page title and navigation label without changing schema command names. Co-authored-by: GPT-5.5 <gpt-5.5@openai.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli-reference): ignore blank title overrides Co-authored-by: GPT-5.5 <gpt-5.5@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli-reference): use segment path instead of segment/index.md for namespace page cards Namespace and sub-namespace page card links were generated as ./segment/index.md, which the docs-builder resolved to /cli/.../segment/index (a 404) instead of /cli/.../segment. Drop the /index.md suffix so links resolve to the correct namespace URL. Affects RootPage (top-level namespaces), NamespacePage (sub-namespaces and alias blurb links). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(cli-reference): trim title and navigation_title override values Prevents padded whitespace in non-empty overrides from leaking into nav/file metadata. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: GPT-5.5 <gpt-5.5@openai.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: GPT-5.5 <gpt-5.5@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> --- .../Extensions/CliReference/CliMarkdownGenerator.cs | 6 +++--- src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs b/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs index 0b718330e..d260b30f8 100644 --- a/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs +++ b/src/Elastic.Markdown/Extensions/CliReference/CliMarkdownGenerator.cs @@ -46,7 +46,7 @@ public static string RootPage(CliSchema schema, CliSupplementalDoc? supplemental _ = sb.AppendLine("## Namespaces"); _ = sb.AppendLine(); foreach (var ns in schema.Namespaces) - AppendPageCard(sb, ns.Segment, $"./{ns.Segment}/index.md", ns.Summary); + AppendPageCard(sb, ns.Segment, $"./{ns.Segment}", ns.Summary); } if (schema.Environment?.Variables is { Count: > 0 } envVars) @@ -129,7 +129,7 @@ public static string NamespacePage( { var depth = fullPath?.Length ?? 1; var upPrefix = string.Concat(Enumerable.Repeat("../", depth)); - var links = nsAliases.Select(a => $"[`{binaryName ?? a} {a}`]({upPrefix}{a}/index.md)"); + var links = nsAliases.Select(a => $"[`{binaryName ?? a} {a}`]({upPrefix}{a})"); _ = sb.AppendLine($"Also accessible as {string.Join(", ", links)}."); _ = sb.AppendLine(); } @@ -159,7 +159,7 @@ public static string NamespacePage( _ = sb.AppendLine("## Sub-namespaces"); _ = sb.AppendLine(); foreach (var sub in subNamespaces) - AppendPageCard(sb, sub.Segment, $"./{sub.Segment}/index.md", sub.Summary); + AppendPageCard(sb, sub.Segment, $"./{sub.Segment}", sub.Summary); } var options = ns.Options ?? []; diff --git a/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs b/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs index 1eefadd2b..739409b60 100644 --- a/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs +++ b/src/Elastic.Markdown/Extensions/CliReference/CliRootFile.cs @@ -30,8 +30,8 @@ public CliRootFile( { _schema = schema; _supplementalDoc = supplementalDoc; - _title = string.IsNullOrWhiteSpace(title) ? schema.Name : title; - _navigationTitle = string.IsNullOrWhiteSpace(navigationTitle) ? $"{schema.Name} CLI" : navigationTitle; + _title = string.IsNullOrWhiteSpace(title) ? schema.Name : title.Trim(); + _navigationTitle = string.IsNullOrWhiteSpace(navigationTitle) ? $"{schema.Name} CLI" : navigationTitle.Trim(); Title = _title; } From b75b08b64ede4f534c5f89ccfc025cdcce50e2e6 Mon Sep 17 00:00:00 2001 From: Fabrizio Ferri-Benedetti <fabri.ferribenedetti@elastic.co> Date: Thu, 11 Jun 2026 15:26:03 +0200 Subject: [PATCH 10/19] fix(page-card): apply UrlPathPrefix to resolved URLs (#3490) * feat(cli-reference): support title overrides Allow cli TOC entries to override the generated root page title and navigation label without changing schema command names. Co-authored-by: GPT-5.5 <gpt-5.5@openai.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli-reference): ignore blank title overrides Co-authored-by: GPT-5.5 <gpt-5.5@users.noreply.github.com> Co-authored-by: Cursor <cursoragent@cursor.com> * fix(cli-reference): use segment path instead of segment/index.md for namespace page cards Namespace and sub-namespace page card links were generated as ./segment/index.md, which the docs-builder resolved to /cli/.../segment/index (a 404) instead of /cli/.../segment. Drop the /index.md suffix so links resolve to the correct namespace URL. Affects RootPage (top-level namespaces), NamespacePage (sub-namespaces and alias blurb links). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(cli-reference): trim title and navigation_title override values Prevents padded whitespace in non-empty overrides from leaking into nav/file metadata. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(page-card): apply UrlPathPrefix to resolved URLs Page-card links were generating site-root-relative hrefs (e.g. /cli/stack/es) without the deployment path prefix, causing 404s in preview environments where the site is hosted under a subpath (e.g. /elastic/cli/pull/406/cli/stack/es). Apply the same UrlPathPrefix logic used by DiagnosticLinkInlineParser. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: GPT-5.5 <gpt-5.5@openai.com> Co-authored-by: Cursor <cursoragent@cursor.com> Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: GPT-5.5 <gpt-5.5@users.noreply.github.com> Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com> --- .../Myst/Directives/PageCard/PageCardBlock.cs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/Elastic.Markdown/Myst/Directives/PageCard/PageCardBlock.cs b/src/Elastic.Markdown/Myst/Directives/PageCard/PageCardBlock.cs index 4cdc9db58..810341d6c 100644 --- a/src/Elastic.Markdown/Myst/Directives/PageCard/PageCardBlock.cs +++ b/src/Elastic.Markdown/Myst/Directives/PageCard/PageCardBlock.cs @@ -55,6 +55,11 @@ public override void FinalizeAndValidate(ParserContext context) : relativeToSource; ResolvedUrl = "/" + withoutExtension.Replace('\\', '/'); + + // Apply URL path prefix so links work in preview/sub-path deployments (same logic as DiagnosticLinkInlineParser) + var urlPathPrefix = context.Build.UrlPathPrefix ?? string.Empty; + if (!string.IsNullOrWhiteSpace(urlPathPrefix) && !ResolvedUrl.StartsWith(urlPathPrefix, StringComparison.OrdinalIgnoreCase)) + ResolvedUrl = $"{urlPathPrefix.TrimEnd('/')}{ResolvedUrl}"; } [GeneratedRegex(@"^\[([^\]]+)\]\(([^)]+)\)$")] From 080afb130f7a428a6973cb6d9e60908d7914f74e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 09:57:58 +0200 Subject: [PATCH 11/19] build(deps-dev): bump joi in /src/Elastic.Documentation.Site (#3491) Bumps [joi](https://github.com/hapijs/joi) from 18.1.2 to 18.2.1. - [Commits](https://github.com/hapijs/joi/compare/v18.1.2...v18.2.1) --- updated-dependencies: - dependency-name: joi dependency-version: 18.2.1 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Elastic.Documentation.Site/package-lock.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Elastic.Documentation.Site/package-lock.json b/src/Elastic.Documentation.Site/package-lock.json index 4b2f71b02..8486915a6 100644 --- a/src/Elastic.Documentation.Site/package-lock.json +++ b/src/Elastic.Documentation.Site/package-lock.json @@ -30,7 +30,7 @@ "@tanstack/react-query": "^5.100.9", "@theletterf/beautiful-mermaid": "0.1.5", "@uidotdev/usehooks": "2.4.1", - "dompurify": "^3.4.2", + "dompurify": "3.4.2", "highlight.js": "11.11.1", "htmx-ext-head-support": "2.0.5", "htmx-ext-preload": "2.1.2", @@ -29901,9 +29901,9 @@ } }, "node_modules/joi": { - "version": "18.1.2", - "resolved": "https://registry.npmjs.org/joi/-/joi-18.1.2.tgz", - "integrity": "sha512-rF5MAmps5esSlhCA+N1b6IYHDw9j/btzGaqfgie522jS02Ju/HXBxamlXVlKEHAxoMKQL77HWI8jlqWsFuekZA==", + "version": "18.2.1", + "resolved": "https://registry.npmjs.org/joi/-/joi-18.2.1.tgz", + "integrity": "sha512-2/OKlogiESf2Nh3TFCrRjrr9z1DRHeW0I+KReF67+4J0Ns+8hBtHRmoWAZ2OFU6I5+TWLEe6sVlSdXPjHm5UbQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { From ac42e18111b4121c18811ea910d7bd4f648a7427 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 09:59:41 +0200 Subject: [PATCH 12/19] Bump AWSSDK.S3 from 4.0.23.3 to 4.0.23.4 (#3483) --- updated-dependencies: - dependency-name: AWSSDK.S3 dependency-version: 4.0.23.4 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index a00ac8f6d..d9498f3f5 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -37,7 +37,7 @@ <PackageVersion Include="AWSSDK.Core" Version="4.0.7.3" /> <PackageVersion Include="AWSSDK.DynamoDBv2" Version="4.0.18.4" /> <PackageVersion Include="AWSSDK.SQS" Version="4.0.2.32" /> - <PackageVersion Include="AWSSDK.S3" Version="4.0.23.3" /> + <PackageVersion Include="AWSSDK.S3" Version="4.0.23.4" /> <PackageVersion Include="Elastic.OpenTelemetry" Version="1.3.0" /> <PackageVersion Include="Microsoft.Extensions.Configuration.UserSecrets" Version="10.0.0" /> <PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="10.0.8" /> From 615ae1573ffede7018a6453a6eebd585360dd228 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:00:24 +0200 Subject: [PATCH 13/19] Bump AWSSDK.DynamoDBv2 from 4.0.18.4 to 4.0.18.5 (#3482) --- updated-dependencies: - dependency-name: AWSSDK.DynamoDBv2 dependency-version: 4.0.18.5 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Directory.Packages.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index d9498f3f5..61f9616c6 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -35,7 +35,7 @@ <PackageVersion Include="Aspire.Hosting" Version="13.0.0" /> <PackageVersion Include="Aspire.Hosting.Testing" Version="13.0.0" /> <PackageVersion Include="AWSSDK.Core" Version="4.0.7.3" /> - <PackageVersion Include="AWSSDK.DynamoDBv2" Version="4.0.18.4" /> + <PackageVersion Include="AWSSDK.DynamoDBv2" Version="4.0.18.5" /> <PackageVersion Include="AWSSDK.SQS" Version="4.0.2.32" /> <PackageVersion Include="AWSSDK.S3" Version="4.0.23.4" /> <PackageVersion Include="Elastic.OpenTelemetry" Version="1.3.0" /> From d8afb5d5f6222f53a448b90f74f2c868ae628732 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:00:57 +0200 Subject: [PATCH 14/19] build(deps): bump actions/checkout from 6 to 6.0.2 in /.github/workflows (#3481) Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 6.0.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v6.0.2) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .github/workflows/assembler-preview.yml | 2 +- .../workflows/build-changelog-scrubber-lambda.yml | 2 +- .../workflows/build-link-index-updater-lambda.yml | 2 +- .github/workflows/ci.yml | 12 ++++++------ .github/workflows/create-major-tag.yml | 2 +- .github/workflows/docs-preview-local.yml | 4 ++-- .github/workflows/license.yml | 2 +- .github/workflows/prerelease.yml | 4 ++-- .github/workflows/release.yml | 4 ++-- .github/workflows/required-labels.yml | 2 +- .github/workflows/smoke-test.yml | 4 ++-- .github/workflows/updatecli.yml | 2 +- .github/workflows/zizmor.yml | 2 +- 13 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/assembler-preview.yml b/.github/workflows/assembler-preview.yml index a7b9695b0..79ca0d5e5 100644 --- a/.github/workflows/assembler-preview.yml +++ b/.github/workflows/assembler-preview.yml @@ -57,7 +57,7 @@ jobs: } - name: Checkout - uses: actions/checkout@v6 + uses: actions/checkout@v6.0.2 with: ref: ${{ steps.pr-details.outputs.result || github.event.pull_request.head.sha }} persist-credentials: false diff --git a/.github/workflows/build-changelog-scrubber-lambda.yml b/.github/workflows/build-changelog-scrubber-lambda.yml index 6265d75ce..220f5a65c 100644 --- a/.github/workflows/build-changelog-scrubber-lambda.yml +++ b/.github/workflows/build-changelog-scrubber-lambda.yml @@ -22,7 +22,7 @@ jobs: env: BINARY_PATH: .artifacts/docs-lambda-changelog-scrubber/release_linux-x64/bootstrap steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: ref: ${{ inputs.ref }} persist-credentials: false diff --git a/.github/workflows/build-link-index-updater-lambda.yml b/.github/workflows/build-link-index-updater-lambda.yml index e2008b0a8..a577c6f94 100644 --- a/.github/workflows/build-link-index-updater-lambda.yml +++ b/.github/workflows/build-link-index-updater-lambda.yml @@ -22,7 +22,7 @@ jobs: env: BINARY_PATH: .artifacts/docs-lambda-index-publisher/release_linux-x64/bootstrap steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: ref: ${{ inputs.ref }} persist-credentials: false diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index abc7e57fd..e3a589301 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -20,7 +20,7 @@ jobs: validate-assembler: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -42,7 +42,7 @@ jobs: env: MSBuildNoWarn: IDE0032 steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -77,7 +77,7 @@ jobs: run: working-directory: src/Elastic.Documentation.Site steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -127,7 +127,7 @@ jobs: lint: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false @@ -148,7 +148,7 @@ jobs: - macos-latest - windows-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false - name: 'Windows only, set TEMP to the same drive' @@ -201,7 +201,7 @@ jobs: contents: read id-token: write steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/create-major-tag.yml b/.github/workflows/create-major-tag.yml index 0269fe9b7..343101b56 100644 --- a/.github/workflows/create-major-tag.yml +++ b/.github/workflows/create-major-tag.yml @@ -15,7 +15,7 @@ jobs: create-major-tag: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: true - name: Get major version diff --git a/.github/workflows/docs-preview-local.yml b/.github/workflows/docs-preview-local.yml index cdc931cbb..6268aca03 100644 --- a/.github/workflows/docs-preview-local.yml +++ b/.github/workflows/docs-preview-local.yml @@ -93,7 +93,7 @@ jobs: steps: - name: Checkout if: github.event_name == 'push' - uses: actions/checkout@v6 + uses: actions/checkout@v6.0.2 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} persist-credentials: false @@ -211,7 +211,7 @@ jobs: if: > env.MATCH == 'true' && needs.check.outputs.any_modified != 'false' - uses: actions/checkout@v6 + uses: actions/checkout@v6.0.2 with: ref: ${{ github.event.pull_request.head.sha || github.ref }} persist-credentials: false diff --git a/.github/workflows/license.yml b/.github/workflows/license.yml index 0ae71ac31..4b1e805a5 100644 --- a/.github/workflows/license.yml +++ b/.github/workflows/license.yml @@ -11,7 +11,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/prerelease.yml b/.github/workflows/prerelease.yml index 56244eeb4..b170088ae 100644 --- a/.github/workflows/prerelease.yml +++ b/.github/workflows/prerelease.yml @@ -27,7 +27,7 @@ jobs: steps: - id: repo-basename run: 'echo "value=`basename ${{ github.repository }}`" >> $GITHUB_OUTPUT' - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false - name: Setup Pages @@ -56,7 +56,7 @@ jobs: major-version: ${{ steps.bootstrap.outputs.major-version }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c23904ed2..ade452057 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -53,7 +53,7 @@ jobs: major-version: ${{ steps.bootstrap.outputs.major-version }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: ref: ${{ needs.release-drafter.outputs.tag_name }} persist-credentials: false @@ -188,7 +188,7 @@ jobs: major-version: ${{ steps.bootstrap.outputs.major-version }} steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: ref: ${{ needs.release-drafter.outputs.tag_name }} persist-credentials: false diff --git a/.github/workflows/required-labels.yml b/.github/workflows/required-labels.yml index 13c0bb56c..0d84967ea 100644 --- a/.github/workflows/required-labels.yml +++ b/.github/workflows/required-labels.yml @@ -18,7 +18,7 @@ jobs: pull-requests: write runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false - name: Wait for PR to be ready (if just opened) diff --git a/.github/workflows/smoke-test.yml b/.github/workflows/smoke-test.yml index a2b5b3ae7..473793ac8 100644 --- a/.github/workflows/smoke-test.yml +++ b/.github/workflows/smoke-test.yml @@ -26,13 +26,13 @@ jobs: landing-page-path-output: "" steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false - name: Bootstrap Action Workspace uses: ./.github/actions/bootstrap - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: repository: ${{ matrix.repository }} path: test-repo diff --git a/.github/workflows/updatecli.yml b/.github/workflows/updatecli.yml index e4b286bcb..d0ca2b9cd 100644 --- a/.github/workflows/updatecli.yml +++ b/.github/workflows/updatecli.yml @@ -16,7 +16,7 @@ jobs: bump: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6 + - uses: actions/checkout@v6.0.2 with: persist-credentials: false diff --git a/.github/workflows/zizmor.yml b/.github/workflows/zizmor.yml index 6d3562186..10c743a3c 100644 --- a/.github/workflows/zizmor.yml +++ b/.github/workflows/zizmor.yml @@ -16,7 +16,7 @@ jobs: contents: read steps: - name: Checkout repository - uses: actions/checkout@v6 + uses: actions/checkout@v6.0.2 with: persist-credentials: false From 94eef47fb79828c7f4d3e6819224b0e47d56d517 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:01:36 +0200 Subject: [PATCH 15/19] build(deps): bump zustand in /src/Elastic.Documentation.Site (#3480) Bumps [zustand](https://github.com/pmndrs/zustand) from 5.0.12 to 5.0.13. - [Release notes](https://github.com/pmndrs/zustand/releases) - [Commits](https://github.com/pmndrs/zustand/compare/v5.0.12...v5.0.13) --- updated-dependencies: - dependency-name: zustand dependency-version: 5.0.13 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- src/Elastic.Documentation.Site/package-lock.json | 8 ++++---- src/Elastic.Documentation.Site/package.json | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/Elastic.Documentation.Site/package-lock.json b/src/Elastic.Documentation.Site/package-lock.json index 8486915a6..e0654baad 100644 --- a/src/Elastic.Documentation.Site/package-lock.json +++ b/src/Elastic.Documentation.Site/package-lock.json @@ -45,7 +45,7 @@ "ua-parser-js": "2.0.9", "uuid": "14.0.0", "zod": "4.4.3", - "zustand": "5.0.12" + "zustand": "5.0.13" }, "devDependencies": { "@babel/core": "7.28.4", @@ -34442,9 +34442,9 @@ "license": "MIT" }, "node_modules/zustand": { - "version": "5.0.12", - "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.12.tgz", - "integrity": "sha512-i77ae3aZq4dhMlRhJVCYgMLKuSiZAaUPAct2AksxQ+gOtimhGMdXljRT21P5BNpeT4kXlLIckvkPM029OljD7g==", + "version": "5.0.13", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-5.0.13.tgz", + "integrity": "sha512-efI2tVaVQPqtOh114loML/Z80Y4NP3yc+Ff0fYiZJPauNeWZeIp/bRFD7I9bfmCOYBh/PHxlglQ9+wvlwnPikQ==", "license": "MIT", "engines": { "node": ">=12.20.0" diff --git a/src/Elastic.Documentation.Site/package.json b/src/Elastic.Documentation.Site/package.json index a1a421ea6..76a3c6050 100644 --- a/src/Elastic.Documentation.Site/package.json +++ b/src/Elastic.Documentation.Site/package.json @@ -135,6 +135,6 @@ "ua-parser-js": "2.0.9", "uuid": "14.0.0", "zod": "4.4.3", - "zustand": "5.0.12" + "zustand": "5.0.13" } } From 6fa3f9fc5f00a0595820553eb8b5fa23266e1a03 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:02:02 +0200 Subject: [PATCH 16/19] build(deps): bump @opentelemetry/instrumentation (#3479) Bumps [@opentelemetry/instrumentation](https://github.com/open-telemetry/opentelemetry-js) from 0.217.0 to 0.218.0. - [Release notes](https://github.com/open-telemetry/opentelemetry-js/releases) - [Changelog](https://github.com/open-telemetry/opentelemetry-js/blob/main/CHANGELOG.md) - [Commits](https://github.com/open-telemetry/opentelemetry-js/compare/experimental/v0.217.0...experimental/v0.218.0) --- updated-dependencies: - dependency-name: "@opentelemetry/instrumentation" dependency-version: 0.218.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 22 +++++-------------- src/Elastic.Documentation.Site/package.json | 2 +- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/Elastic.Documentation.Site/package-lock.json b/src/Elastic.Documentation.Site/package-lock.json index e0654baad..4a135ebaf 100644 --- a/src/Elastic.Documentation.Site/package-lock.json +++ b/src/Elastic.Documentation.Site/package-lock.json @@ -19,7 +19,7 @@ "@opentelemetry/core": "^2.7.1", "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", - "@opentelemetry/instrumentation": "^0.217.0", + "@opentelemetry/instrumentation": "^0.218.0", "@opentelemetry/instrumentation-fetch": "^0.215.0", "@opentelemetry/otlp-exporter-base": "^0.215.0", "@opentelemetry/resources": "^2.7.1", @@ -4871,12 +4871,12 @@ } }, "node_modules/@opentelemetry/instrumentation": { - "version": "0.217.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.217.0.tgz", - "integrity": "sha512-24ucQMjz7Y34Kw3trbxL2ZrssbtgWnR+Clpaa+YdeWuuyH3Cvk23Q03PcQvqiZrDvt8AmQmjgg9v6Y9PHoxG7w==", + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/instrumentation/-/instrumentation-0.218.0.tgz", + "integrity": "sha512-mIZil8Es+sYDK5m+DQiwAwF57F14TF2YlEqvIjZ/RQWcxDBwRGsKfdK2Tv65OU9meQKCMzSIFS9mxAcnAb6Bkg==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/api-logs": "0.217.0", + "@opentelemetry/api-logs": "0.218.0", "import-in-the-middle": "^3.0.0", "require-in-the-middle": "^8.0.0" }, @@ -4949,18 +4949,6 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/instrumentation/node_modules/@opentelemetry/api-logs": { - "version": "0.217.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.217.0.tgz", - "integrity": "sha512-Cdq0jW2lknrNfrAm92MyEAvpe2cRsKjdnQLHUL6xRA4IVUnsWx6P65E7NcUO0Y+L4w1Aee5iV8FvjSwd+lrs9A==", - "license": "Apache-2.0", - "dependencies": { - "@opentelemetry/api": "^1.3.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, "node_modules/@opentelemetry/otlp-exporter-base": { "version": "0.215.0", "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.215.0.tgz", diff --git a/src/Elastic.Documentation.Site/package.json b/src/Elastic.Documentation.Site/package.json index 76a3c6050..2883652d2 100644 --- a/src/Elastic.Documentation.Site/package.json +++ b/src/Elastic.Documentation.Site/package.json @@ -109,7 +109,7 @@ "@opentelemetry/core": "^2.7.1", "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", - "@opentelemetry/instrumentation": "^0.217.0", + "@opentelemetry/instrumentation": "^0.218.0", "@opentelemetry/instrumentation-fetch": "^0.215.0", "@opentelemetry/otlp-exporter-base": "^0.215.0", "@opentelemetry/resources": "^2.7.1", From ddeb48d654e5a049397611cdb69d213e960a3d10 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:02:23 +0200 Subject: [PATCH 17/19] build(deps): bump actions/checkout from 6 to 6.0.2 (#3478) Bumps [actions/checkout](https://github.com/actions/checkout) from 6 to 6.0.2. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v6...v6.0.2) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: 6.0.2 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> From d68e69d1b6ae1961396da99583aefbe823eadb2e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Jun 2026 10:03:00 +0200 Subject: [PATCH 18/19] build(deps-dev): bump typescript-eslint (#3477) Bumps the eslint group with 1 update in the /src/Elastic.Documentation.Site directory: [typescript-eslint](https://github.com/typescript-eslint/typescript-eslint/tree/HEAD/packages/typescript-eslint). Updates `typescript-eslint` from 8.59.3 to 8.59.4 - [Release notes](https://github.com/typescript-eslint/typescript-eslint/releases) - [Changelog](https://github.com/typescript-eslint/typescript-eslint/blob/main/packages/typescript-eslint/CHANGELOG.md) - [Commits](https://github.com/typescript-eslint/typescript-eslint/commits/v8.59.4/packages/typescript-eslint) --- updated-dependencies: - dependency-name: typescript-eslint dependency-version: 8.59.4 dependency-type: direct:development update-type: version-update:semver-patch dependency-group: eslint ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- .../package-lock.json | 130 +++++++++--------- src/Elastic.Documentation.Site/package.json | 2 +- 2 files changed, 66 insertions(+), 66 deletions(-) diff --git a/src/Elastic.Documentation.Site/package-lock.json b/src/Elastic.Documentation.Site/package-lock.json index 4a135ebaf..532895ee3 100644 --- a/src/Elastic.Documentation.Site/package-lock.json +++ b/src/Elastic.Documentation.Site/package-lock.json @@ -83,7 +83,7 @@ "svgo": "^4.0.1", "text-diff": "1.0.1", "typescript": "^5.9.3", - "typescript-eslint": "8.59.3", + "typescript-eslint": "8.59.4", "wait-on": "9.0.5" } }, @@ -24715,17 +24715,17 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.3.tgz", - "integrity": "sha512-PwFvSKsXGShKGW6n5bZOhGHEcCZXM8HofLK9fNsEwZXzFRjoY+XT1Vsf1zgyXdwTr0ZYz1/2tkZ0DBTT9jZjhw==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.59.4.tgz", + "integrity": "sha512-PegsU+XfyJJNjd4+u/k6f9yTyp0lEXXiPopUNobZcIAUJFGICFLN+sP0Rb3JehVmiij1Ph0dFGYqODoRo/2+6A==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.12.2", - "@typescript-eslint/scope-manager": "8.59.3", - "@typescript-eslint/type-utils": "8.59.3", - "@typescript-eslint/utils": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/type-utils": "8.59.4", + "@typescript-eslint/utils": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "ignore": "^7.0.5", "natural-compare": "^1.4.0", "ts-api-utils": "^2.5.0" @@ -24738,7 +24738,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.59.3", + "@typescript-eslint/parser": "^8.59.4", "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0", "typescript": ">=4.8.4 <6.1.0" } @@ -24754,16 +24754,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.3.tgz", - "integrity": "sha512-HPwA+hVkfcriajbNvTmZv4VRauibay+cWArYUYq7u7W7PmGShMxbPxLvrwDme55a6d5alG3nrYfhyJ/G28XlLg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.59.4.tgz", + "integrity": "sha512-zORHqO/tuhxY1zWuTvMUqddRxpiFJ72xVfcNoWpqdLjs6lfPbuQBJuW4pk+49/uBMy7Ssr4bzgjiKmmDB1UbZQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.59.3", - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3", + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3" }, "engines": { @@ -24779,14 +24779,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.3.tgz", - "integrity": "sha512-ECiUWa/KYRGDFUqTNehaRgzDshnJfkTABJxVemHk4ko22gcr0ukloKjWvyQ64g8YCV/UI47kN1dbmjf/GaQYng==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.59.4.tgz", + "integrity": "sha512-Ly00Vu4oAacfDeHp2Zg85ioNG6l8HG+tN1D7J+xTHSxu9y0awYKJ2zH1rFBn8ZSfuGK+7FxK3Cgl3uAz0aZZLg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.59.3", - "@typescript-eslint/types": "^8.59.3", + "@typescript-eslint/tsconfig-utils": "^8.59.4", + "@typescript-eslint/types": "^8.59.4", "debug": "^4.4.3" }, "engines": { @@ -24801,14 +24801,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.3.tgz", - "integrity": "sha512-t2LvZnoEfzKtnPjgeEu41xw5gxq9mQVfYy4OoZ4Vlt0sk3JwxmhCca/AR7DwOiHrjWgjAj6as4AhRLKSDfvZIA==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.59.4.tgz", + "integrity": "sha512-mUeR/3H1WrTAddJrwut8OoPjfauaztMQmRwV5fQTUyNVJCLiUXXe4lGEyYIL2oFDpP7UtgbGJXCt72wT0z2S3Q==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3" + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -24819,9 +24819,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.3.tgz", - "integrity": "sha512-PcIJHjmaREXLgIAIzLnSY9VucEzz8FKXsRgFa1DmdGCK/5tJpW03TKJF01Q6VZd1lLdz2sIKPWaDUZN9dp//dw==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.59.4.tgz", + "integrity": "sha512-DLCpnKgD4alVxTBSKulK+gU1KCqOgUXfDRDXh2mZgzokQKa/70ax93I2uVO3m/LLvIAtWZIFoiifudmIqAxpMA==", "dev": true, "license": "MIT", "engines": { @@ -24836,15 +24836,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.3.tgz", - "integrity": "sha512-g71d8QD8UaiHGvrJwyIS1hCX5r63w6Jll+4VEYhEAHXTDIqX1JgxhTAbEHtKntL9kuc4jRo7/GWw5xfCepSccQ==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.59.4.tgz", + "integrity": "sha512-uonTuPAAKr9XaBGqJ3LjYTh72zy5DyGesljO9gtmk/eFW0W1fRHjnwVYKB35Lm8d5Q5CluEW3gPHjTvZTmgrfA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/utils": "8.59.3", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4", "debug": "^4.4.3", "ts-api-utils": "^2.5.0" }, @@ -24861,9 +24861,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.3.tgz", - "integrity": "sha512-ePFoH0g4ludssdRFqqDxQePCxU4WQyRa9+XVwjm7yLn0FKhMeoetC+qBEEI1Eyb1pGSDveTIT09Bvw2WhlGayg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.59.4.tgz", + "integrity": "sha512-F1o7WJcCq+bc8dwcO/YsSEOudAH8RDtaOhM6wcAQhcUsFhnWQl81JKy48q1hoxAU0qrzM89+31GYh1515Zde3Q==", "dev": true, "license": "MIT", "engines": { @@ -24875,16 +24875,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.3.tgz", - "integrity": "sha512-CbRjVRAf7Lr9Kr8RopKcbY45p2VfmmHrm0ygOCYFi7oU8q19m0Fs/6iHS7kNOmwpp+ob07ZVcAqlxUod9lYdmg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.59.4.tgz", + "integrity": "sha512-F+RuOmcDXo4+TPdfd/TCLS3m2nw8gE9XXyZLrA3JBfaA5tz9TtdkyD3YJFmPxulyc2cKbEok/CvFE3MgSLWnag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.59.3", - "@typescript-eslint/tsconfig-utils": "8.59.3", - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/visitor-keys": "8.59.3", + "@typescript-eslint/project-service": "8.59.4", + "@typescript-eslint/tsconfig-utils": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/visitor-keys": "8.59.4", "debug": "^4.4.3", "minimatch": "^10.2.2", "semver": "^7.7.3", @@ -24942,9 +24942,9 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.1.tgz", - "integrity": "sha512-rkVq3IXh+4FDGch+KwzX3aV9W3kO54GyEgpvBzSyctDA6Xtd7RJQV1xmXbeQp5v7+VzLOfVqiutSE6GICgPFvg==", + "version": "7.8.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.2.tgz", + "integrity": "sha512-c8jsqUZm3omBOI66G90z1Dyw5z622G8oLG+omfsHBJf3CWQTlOcwOjvOG6wtiNfW6anKm/eA39LMwMtMez2TiQ==", "dev": true, "license": "ISC", "bin": { @@ -24955,16 +24955,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.3.tgz", - "integrity": "sha512-JAvT14goBzRzzzZyqq3P9BLArIxTtQURUtFgQ/V7FO+eU+Gg6ES+5ymOPP1wRxXcxAYeivCk4uS3jCKWI1K8Zg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.59.4.tgz", + "integrity": "sha512-cYXeNAUsG4lJo5dbc1FcKm+JwIWrj1/UpTORsC6tGMjEZ81DYcvIr9/ueikhMa/Y/gDQYGp+YX9/xQrXje5BJw==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.9.1", - "@typescript-eslint/scope-manager": "8.59.3", - "@typescript-eslint/types": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3" + "@typescript-eslint/scope-manager": "8.59.4", + "@typescript-eslint/types": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -24979,13 +24979,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.3.tgz", - "integrity": "sha512-f1UQF7ggd42YiwI5wGrRaPsa+P0CINBlrkLPmGfpq/u/I/oVtecoEIfFR9ag/oa1sLOsRNZ6xehf6qMZhQGBDg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.59.4.tgz", + "integrity": "sha512-U3gxVaDVnuZKhSspW/MzMxE1kq7zOdc072FcSNoqA1I9p8HyKbBFfEHoWckBAMgNMph4MamwS5iTVzFmrnt8TQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.59.3", + "@typescript-eslint/types": "8.59.4", "eslint-visitor-keys": "^5.0.0" }, "engines": { @@ -33580,16 +33580,16 @@ } }, "node_modules/typescript-eslint": { - "version": "8.59.3", - "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.3.tgz", - "integrity": "sha512-KgusgyDgG4LI8Ih/sWaCtZ06tckLAS5CvT5A4D1Q7bYVoAAyzwiZvE4BmwDHkhRVkvhRBepKeASoFzQetha7Fg==", + "version": "8.59.4", + "resolved": "https://registry.npmjs.org/typescript-eslint/-/typescript-eslint-8.59.4.tgz", + "integrity": "sha512-Rw6+44QNFaXtgHSjPy+Kw8hrJniMYzR85E9yLmOLcfZ91/rz+JXQbDTCmc6ccxMPY6K6PgAq26f0JCBfR7LIPQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/eslint-plugin": "8.59.3", - "@typescript-eslint/parser": "8.59.3", - "@typescript-eslint/typescript-estree": "8.59.3", - "@typescript-eslint/utils": "8.59.3" + "@typescript-eslint/eslint-plugin": "8.59.4", + "@typescript-eslint/parser": "8.59.4", + "@typescript-eslint/typescript-estree": "8.59.4", + "@typescript-eslint/utils": "8.59.4" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" diff --git a/src/Elastic.Documentation.Site/package.json b/src/Elastic.Documentation.Site/package.json index 2883652d2..82f316e3e 100644 --- a/src/Elastic.Documentation.Site/package.json +++ b/src/Elastic.Documentation.Site/package.json @@ -91,7 +91,7 @@ "svgo": "^4.0.1", "text-diff": "1.0.1", "typescript": "^5.9.3", - "typescript-eslint": "8.59.3", + "typescript-eslint": "8.59.4", "wait-on": "9.0.5" }, "browserslist": [ From 716e12c8bb7429c5d594200089419960ca70d30a Mon Sep 17 00:00:00 2001 From: snyk-bot <snyk-bot@snyk.io> Date: Thu, 4 Jun 2026 08:00:15 +0000 Subject: [PATCH 19/19] fix: upgrade @opentelemetry/exporter-trace-otlp-http from 0.215.0 to 0.218.0 Snyk has created this PR to upgrade @opentelemetry/exporter-trace-otlp-http from 0.215.0 to 0.218.0. See this package in npm: @opentelemetry/exporter-trace-otlp-http See this project in Snyk: https://app.snyk.io/org/docs-wmk/project/69782e43-c85b-4c27-afd1-ad863be7a38a?utm_source=github&utm_medium=referral&page=upgrade-pr --- .../package-lock.json | 94 +++++++++++++++---- src/Elastic.Documentation.Site/package.json | 2 +- 2 files changed, 76 insertions(+), 20 deletions(-) diff --git a/src/Elastic.Documentation.Site/package-lock.json b/src/Elastic.Documentation.Site/package-lock.json index 532895ee3..25611a472 100644 --- a/src/Elastic.Documentation.Site/package-lock.json +++ b/src/Elastic.Documentation.Site/package-lock.json @@ -18,7 +18,7 @@ "@opentelemetry/context-zone": "^2.7.1", "@opentelemetry/core": "^2.7.1", "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", "@opentelemetry/instrumentation": "^0.218.0", "@opentelemetry/instrumentation-fetch": "^0.215.0", "@opentelemetry/otlp-exporter-base": "^0.215.0", @@ -4821,16 +4821,16 @@ } }, "node_modules/@opentelemetry/exporter-trace-otlp-http": { - "version": "0.215.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.215.0.tgz", - "integrity": "sha512-k4J9ISeGpb0Bm/wCrlcrbroMFTkiWMrdhNxQGrlktxLy127Yzd4/7nrTawn5d/ApktYTknvdixsE6++34Qfi1w==", + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-trace-otlp-http/-/exporter-trace-otlp-http-0.218.0.tgz", + "integrity": "sha512-8dqezsmPhtKitIK/eTipZhYl9EX2/gNQ5zUMhaz3uxEURwfkNf8IPvo6yNfrzbxdtpAOybS/+h7wmIWYqFSpiw==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", - "@opentelemetry/otlp-exporter-base": "0.215.0", - "@opentelemetry/otlp-transformer": "0.215.0", - "@opentelemetry/resources": "2.7.0", - "@opentelemetry/sdk-trace-base": "2.7.0" + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-exporter-base": "0.218.0", + "@opentelemetry/otlp-transformer": "0.218.0", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -4839,28 +4839,84 @@ "@opentelemetry/api": "^1.3.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/core": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.7.0.tgz", - "integrity": "sha512-DT12SXVwV2eoJrGf4nnsvZojxxeQo+LlNAsoYGRRObPWTeN6APiqZ2+nqDCQDvQX40eLi1AePONS0onoASp3yQ==", + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.218.0.tgz", + "integrity": "sha512-ZwqpkNL5W7RyGJPDZ9g06DvKp8KFTWPJPN12anpMQYSKpTSU0z3EIZuPq9vPGpS8siFyOqDYDAuCwlNO9FqgbA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/otlp-transformer": "0.218.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/otlp-transformer": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.218.0.tgz", + "integrity": "sha512-CFaKH87WAzjuJ4awowTTLzUvMfaRfiOFG5+qm5S5ncyalRtN4ecQ+YmuANJSCrVPuvZFEkUgKhBPBndxi3rHsQ==", "license": "Apache-2.0", "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", + "@opentelemetry/sdk-logs": "0.218.0", + "@opentelemetry/sdk-metrics": "2.7.1", + "@opentelemetry/sdk-trace-base": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-logs": { + "version": "0.218.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.218.0.tgz", + "integrity": "sha512-QvnNdugatFTVCJXH0Mcu7GOOJSylA9j127kIezOE4YwTI4YbowRons2K4WZTv5FMS8T4q9P0NdaRHdkSmeAIag==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.218.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" }, "peerDependencies": { - "@opentelemetry/api": ">=1.0.0 <1.10.0" + "@opentelemetry/api": ">=1.4.0 <1.10.0" } }, - "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/resources": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.7.0.tgz", - "integrity": "sha512-K+oi0hNMv94EpZbnW3eyu2X6SGVpD3O5DhG2NIp65Hc7lhAj9brRXTAVzh3wB82+q3ThakEf7Zd7RsFUqcTc7A==", + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-metrics": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.7.1.tgz", + "integrity": "sha512-MpDJdkiFDs3Pm1RHO3KByuZbuBdJEXEAkiC0+yJdsZGVCdf1RpHR6n+LHDcS7ffmfrt5kVCzJSCfm4z2C7v0uQ==", "license": "Apache-2.0", "dependencies": { - "@opentelemetry/core": "2.7.0", + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-trace-otlp-http/node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.7.1.tgz", + "integrity": "sha512-NAYIlsF8MPUsKqJMiDQJTMPOmlbawC1Iz/omMLygZ1C9am8fTKYjTaI+OZM+WTY3t3Glo0wnOg/6/pac6RGPPw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.7.1", + "@opentelemetry/resources": "2.7.1", "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { diff --git a/src/Elastic.Documentation.Site/package.json b/src/Elastic.Documentation.Site/package.json index 82f316e3e..a4e31a52c 100644 --- a/src/Elastic.Documentation.Site/package.json +++ b/src/Elastic.Documentation.Site/package.json @@ -108,7 +108,7 @@ "@opentelemetry/context-zone": "^2.7.1", "@opentelemetry/core": "^2.7.1", "@opentelemetry/exporter-logs-otlp-http": "^0.215.0", - "@opentelemetry/exporter-trace-otlp-http": "^0.215.0", + "@opentelemetry/exporter-trace-otlp-http": "^0.218.0", "@opentelemetry/instrumentation": "^0.218.0", "@opentelemetry/instrumentation-fetch": "^0.215.0", "@opentelemetry/otlp-exporter-base": "^0.215.0",