From 02d7b301ea0fb7d12899f52d3b0f845d5e4aeaae Mon Sep 17 00:00:00 2001 From: Seth Malaki Date: Wed, 1 Apr 2026 14:59:35 +0100 Subject: [PATCH] feat: add native NGINX Ingress migration tool MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the iframe embed with a native React component from @tigera/ingress-to-gateway-web. The converter runs entirely in-browser — paste NGINX Ingress YAML and get a personalised migration playbook for Envoy Gateway. - Add IngressConverter wrapper component - Configure @tigera npm scope for GitHub Packages - Add NODE_AUTH_TOKEN to CI workflows for package auth - Remove CLI tool section (internal only for now) --- .github/workflows/vale.yml | 3 + .../ingress-gateway/migrate-from-nginx.mdx | 41 ++ .../ingress-gateway/migrate-from-nginx.mdx | 41 ++ .../version-3.31-sidebars.json | 3 +- package.json | 1 + sidebars-calico.js | 3 +- .../components/IngressConverter/index.tsx | 74 ++++ .../IngressConverter/vendor/ing2gw-web.css | 2 + .../IngressConverter/vendor/ing2gw-web.js | 419 ++++++++++++++++++ src/___new___/components/index.ts | 3 +- yarn.lock | 10 + 11 files changed, 597 insertions(+), 3 deletions(-) create mode 100644 calico/networking/ingress-gateway/migrate-from-nginx.mdx create mode 100644 calico_versioned_docs/version-3.31/networking/ingress-gateway/migrate-from-nginx.mdx create mode 100644 src/___new___/components/IngressConverter/index.tsx create mode 100644 src/___new___/components/IngressConverter/vendor/ing2gw-web.css create mode 100644 src/___new___/components/IngressConverter/vendor/ing2gw-web.js diff --git a/.github/workflows/vale.yml b/.github/workflows/vale.yml index 6678db6b09..782c0afd6c 100644 --- a/.github/workflows/vale.yml +++ b/.github/workflows/vale.yml @@ -30,6 +30,9 @@ jobs: with: node-version: '22' cache: yarn + - name: Authenticate GitHub Packages + if: steps.changed.outputs.skip == 'false' + run: yarn config set --home npmScopes.tigera.npmAuthToken "${{ secrets.GITHUB_TOKEN }}" - name: Install dependencies if: steps.changed.outputs.skip == 'false' run: yarn install --immutable diff --git a/calico/networking/ingress-gateway/migrate-from-nginx.mdx b/calico/networking/ingress-gateway/migrate-from-nginx.mdx new file mode 100644 index 0000000000..78b1c5d04c --- /dev/null +++ b/calico/networking/ingress-gateway/migrate-from-nginx.mdx @@ -0,0 +1,41 @@ +--- +description: Migrate your NGINX Ingress resources to Calico Ingress Gateway (Envoy Gateway) with an automated conversion tool and step-by-step playbook. +title: Migrating from NGINX +--- + +import { IngressConverter } from '/src/___new___/components'; + +# Migrating from NGINX Ingress + +NGINX Ingress Controller [retired in March 2026](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/) — no more releases, bugfixes, or security patches. Calico Ingress Gateway, powered by Envoy Gateway, is the recommended replacement. + +This migration tool converts your NGINX Ingress resources (including annotations) into Gateway API equivalents and generates a personalised migration playbook. + +## What it converts + +- **Structural Ingress fields** — hosts, paths, TLS, backends → Gateway, HTTPRoute, ReferenceGrant +- **NGINX annotations** — CORS, timeouts, rate limiting, auth, session affinity, rewrites, and 130+ more → SecurityPolicy, BackendTrafficPolicy, ClientTrafficPolicy +- **Supports both NGINX controllers** — community (`nginx.ingress.kubernetes.io/*`) and NGINX Inc/F5 (`nginx.org/*`, `nginx.com/*`) + +Annotations that can't be auto-converted are flagged with "think in Envoy terms" guidance explaining how to achieve the same behavior in Envoy Gateway. + +## Try it now + +Paste your NGINX Ingress YAML below to generate a conversion and migration playbook — everything runs in your browser, nothing is sent to a server. + + + +## Migration playbook + +The converter generates a 6-section playbook tailored to your resources: + +1. **Pre-Migration Assessment** — what you have, what you'll get, what needs manual attention +2. **Prepare** — install Envoy Gateway, review and apply converted resources +3. **Verify (Parallel Running)** — test routes through Envoy Gateway without disrupting NGINX traffic +4. **Traffic Shift** — DNS-based, weighted, or canary strategies with rollback instructions +5. **Cleanup** — remove Ingress resources and NGINX controller +6. **Post-Migration** — explore Gateway API features, set up observability + +## Need help? + +For complex migrations (hundreds of Ingress resources, custom NGINX snippets, mTLS, NGINX Plus features), [talk to a Tigera engineer](https://www.tigera.io/contact/). diff --git a/calico_versioned_docs/version-3.31/networking/ingress-gateway/migrate-from-nginx.mdx b/calico_versioned_docs/version-3.31/networking/ingress-gateway/migrate-from-nginx.mdx new file mode 100644 index 0000000000..78b1c5d04c --- /dev/null +++ b/calico_versioned_docs/version-3.31/networking/ingress-gateway/migrate-from-nginx.mdx @@ -0,0 +1,41 @@ +--- +description: Migrate your NGINX Ingress resources to Calico Ingress Gateway (Envoy Gateway) with an automated conversion tool and step-by-step playbook. +title: Migrating from NGINX +--- + +import { IngressConverter } from '/src/___new___/components'; + +# Migrating from NGINX Ingress + +NGINX Ingress Controller [retired in March 2026](https://kubernetes.io/blog/2025/11/11/ingress-nginx-retirement/) — no more releases, bugfixes, or security patches. Calico Ingress Gateway, powered by Envoy Gateway, is the recommended replacement. + +This migration tool converts your NGINX Ingress resources (including annotations) into Gateway API equivalents and generates a personalised migration playbook. + +## What it converts + +- **Structural Ingress fields** — hosts, paths, TLS, backends → Gateway, HTTPRoute, ReferenceGrant +- **NGINX annotations** — CORS, timeouts, rate limiting, auth, session affinity, rewrites, and 130+ more → SecurityPolicy, BackendTrafficPolicy, ClientTrafficPolicy +- **Supports both NGINX controllers** — community (`nginx.ingress.kubernetes.io/*`) and NGINX Inc/F5 (`nginx.org/*`, `nginx.com/*`) + +Annotations that can't be auto-converted are flagged with "think in Envoy terms" guidance explaining how to achieve the same behavior in Envoy Gateway. + +## Try it now + +Paste your NGINX Ingress YAML below to generate a conversion and migration playbook — everything runs in your browser, nothing is sent to a server. + + + +## Migration playbook + +The converter generates a 6-section playbook tailored to your resources: + +1. **Pre-Migration Assessment** — what you have, what you'll get, what needs manual attention +2. **Prepare** — install Envoy Gateway, review and apply converted resources +3. **Verify (Parallel Running)** — test routes through Envoy Gateway without disrupting NGINX traffic +4. **Traffic Shift** — DNS-based, weighted, or canary strategies with rollback instructions +5. **Cleanup** — remove Ingress resources and NGINX controller +6. **Post-Migration** — explore Gateway API features, set up observability + +## Need help? + +For complex migrations (hundreds of Ingress resources, custom NGINX snippets, mTLS, NGINX Plus features), [talk to a Tigera engineer](https://www.tigera.io/contact/). diff --git a/calico_versioned_sidebars/version-3.31-sidebars.json b/calico_versioned_sidebars/version-3.31-sidebars.json index 79a308fe31..d3488f0177 100644 --- a/calico_versioned_sidebars/version-3.31-sidebars.json +++ b/calico_versioned_sidebars/version-3.31-sidebars.json @@ -253,7 +253,8 @@ "networking/ingress-gateway/about-calico-ingress-gateway", "networking/ingress-gateway/create-ingress-gateway", "networking/ingress-gateway/customize-ingress-gateway", - "networking/ingress-gateway/tutorial-ingress-gateway-canary" + "networking/ingress-gateway/tutorial-ingress-gateway-canary", + "networking/ingress-gateway/migrate-from-nginx" ] }, { diff --git a/package.json b/package.json index aea229cdc6..2a47bec7ff 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "cheerio": "^1.1.0", "clsx": "^2.1.1", "framer-motion": "^4.1.17", + "marked": "^17.0.5", "prism-react-renderer": "^2.1.0", "react": "19.2.3", "react-dom": "19.2.3", diff --git a/sidebars-calico.js b/sidebars-calico.js index f4845c6b89..6fbc14ec50 100644 --- a/sidebars-calico.js +++ b/sidebars-calico.js @@ -260,7 +260,8 @@ module.exports = { 'networking/ingress-gateway/about-calico-ingress-gateway', 'networking/ingress-gateway/create-ingress-gateway', 'networking/ingress-gateway/customize-ingress-gateway', - 'networking/ingress-gateway/tutorial-ingress-gateway-canary' + 'networking/ingress-gateway/tutorial-ingress-gateway-canary', + 'networking/ingress-gateway/migrate-from-nginx' ], }, { diff --git a/src/___new___/components/IngressConverter/index.tsx b/src/___new___/components/IngressConverter/index.tsx new file mode 100644 index 0000000000..7ca0894004 --- /dev/null +++ b/src/___new___/components/IngressConverter/index.tsx @@ -0,0 +1,74 @@ +// Vendored from @tigera/ingress-to-gateway-web@0.7.0 +// Source: https://github.com/tigera/ing2gw (private) +// To update: copy packages/web/dist/{index.js,styles.css} → vendor/ +import { IngressInput, PlaybookOutput, YamlOutput, useConversion } from './vendor/ing2gw-web'; +import './vendor/ing2gw-web.css'; +import { marked } from 'marked'; +import React, { useCallback, useState } from 'react'; + +function MarkdownRenderer({ children }: { children: string }) { + const html = marked.parse(children, { async: false }) as string; + // biome-ignore lint/security/noDangerouslySetInnerHtml: markdown is generated by our own report engine, not user input + return
; +} + +type Step = 'input' | 'playbook'; + +export default function IngressConverter() { + const conversion = useConversion(); + const [step, setStep] = useState('input'); + + const handleConvert = useCallback(() => { + const result = conversion.handleConvert(); + if (result) { + setStep('playbook'); + } + }, [conversion.handleConvert]); + + const handleReset = useCallback(() => { + conversion.reset(); + setStep('input'); + }, [conversion.reset]); + + if (step === 'playbook' && conversion.output) { + return ( +
+
+ +
+ + +
+ ); + } + + return ( + + ); +} diff --git a/src/___new___/components/IngressConverter/vendor/ing2gw-web.css b/src/___new___/components/IngressConverter/vendor/ing2gw-web.css new file mode 100644 index 0000000000..7d15bfcf92 --- /dev/null +++ b/src/___new___/components/IngressConverter/vendor/ing2gw-web.css @@ -0,0 +1,2 @@ +/*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */ +@layer properties{@supports (((-webkit-hyphens:none)) and (not (margin-trim:inline))) or ((-moz-orient:inline) and (not (color:rgb(from red r g b)))){*,:before,:after,::backdrop{--tw-space-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-inset:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000}}}@layer theme{:root,:host{--font-sans:ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--spacing:.25rem;--container-lg:32rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--font-weight-medium:500;--font-weight-semibold:600;--leading-relaxed:1.625;--radius-md:.375rem;--radius-lg:.5rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--color-tigera-color-neutral-0:#fff;--color-tigera-color-neutral-50:#f5f5f6;--color-tigera-color-neutral-100:#e6e6e7;--color-tigera-color-neutral-200:#cfd0d2;--color-tigera-color-neutral-300:#adafb3;--color-tigera-color-neutral-400:#84878c;--color-tigera-color-neutral-500:#696c71;--color-tigera-color-neutral-600:#595b61;--color-tigera-color-neutral-700:#4c4e52;--color-tigera-color-neutral-800:#424346;--color-tigera-color-neutral-1000:#252627;--color-tigera-color-neutral-1100:#1b1c1d;--color-tigera-color-neutral-1200:#141414;--color-tigera-color-dark-alpha-100:#f5f5f60d;--color-tigera-color-dark-alpha-200:#f5f5f61a;--color-tigera-color-dark-alpha-300:#f5f5f626;--color-tigera-color-light-alpha-100:#1414140d;--color-tigera-color-light-alpha-200:#1414141a;--color-tigera-color-light-alpha-300:#14141426;--color-tigera-color-blue-50:#f0f5fe;--color-tigera-color-blue-100:#dee8fb;--color-tigera-color-blue-200:#c4d9f9;--color-tigera-color-blue-400:#6d9eed;--color-tigera-color-blue-700:#2d4cc8;--color-tigera-color-blue-800:#2a3fa3;--color-tigera-color-blue-900:#273981;--color-tigera-color-medium-gold-100:#fef2d6;--color-tigera-color-medium-gold-200:#fde1ab;--color-tigera-color-medium-gold-300:#fbcb76;--color-tigera-color-medium-gold-400:#f8aa3f;--color-tigera-color-medium-gold-500:#f69320;--color-tigera-color-medium-gold-600:#e7750f;--color-tigera-color-medium-gold-700:#bf580f;--color-tigera-color-medium-gold-800:#984614;--color-tigera-color-medium-gold-1000:#421b08;--color-tigera-color-medium-blue-100:#e1f2fd;--color-tigera-color-medium-blue-200:#bde6fa;--color-tigera-color-medium-blue-300:#82d2f7;--color-tigera-color-medium-blue-400:#3fbcf1;--color-tigera-color-medium-blue-600:#097db6;--color-tigera-color-medium-blue-700:#09699b;--color-tigera-color-medium-blue-800:#0c5980;--color-tigera-color-green-100:#d7ffed;--color-tigera-color-green-200:#b2ffdc;--color-tigera-color-green-300:#76ffc2;--color-tigera-color-green-400:#33f5a1;--color-tigera-color-green-600:#00c771;--color-tigera-color-green-700:#049a5a;--color-tigera-color-green-800:#0a7146;--color-tigera-color-green-1000:#00341f;--color-tigera-color-red-100:#ffe1e1;--color-tigera-color-red-200:#ffcac9;--color-tigera-color-red-300:#fea4a3;--color-tigera-color-red-400:#fa716f;--color-tigera-color-red-500:#f24341;--color-tigera-color-red-600:#e1302e;--color-tigera-color-red-700:#bc1b19;--color-tigera-color-red-800:#9b1a19;--color-tigera-color-red-1000:#460a09;--color-tigera-color-zest-400:#bee846;--color-tigera-color-zest-500:#9acc16;--color-tigera-color-zest-600:#77a30d;--color-tigera-token-white:var(--color-tigera-color-neutral-0);--color-tigera-token-bg-brand:var(--color-tigera-color-blue-900);--color-tigera-token-bg-brand-hovered:var(--color-tigera-color-blue-800);--color-tigera-token-bg-brand-pressed:var(--color-tigera-color-blue-700);--color-tigera-token-bg-brand-subtle:var(--color-tigera-color-blue-50);--color-tigera-token-bg-danger-subtle:var(--color-tigera-color-red-100);--color-tigera-token-bg-empty:transparent;--color-tigera-token-bg-neutral-solid:var(--color-tigera-color-neutral-200);--color-tigera-token-bg-neutral-solid-hovered:var(--color-tigera-color-neutral-300);--color-tigera-token-bg-neutral-solid-pressed:var(--color-tigera-color-neutral-400);--color-tigera-token-border-default:var(--color-tigera-color-neutral-100);--color-tigera-token-border-bold:var(--color-tigera-color-neutral-300);--color-tigera-token-border-brand:var(--color-tigera-color-blue-900);--color-tigera-token-border-danger:var(--color-tigera-color-red-600);--color-tigera-token-elevation-sunken:var(--color-tigera-color-neutral-50);--color-tigera-token-elevation-surface:var(--color-tigera-color-neutral-0);--color-tigera-token-elevation-surface-shadow:0px 0px 8px #1414140d;--color-tigera-token-fg-default:var(--color-tigera-color-neutral-1200);--color-tigera-token-fg-inverted:var(--color-tigera-color-neutral-0);--color-tigera-token-fg-subtle:var(--color-tigera-color-neutral-600);--color-tigera-token-fg-brand:var(--color-tigera-color-blue-900);--color-tigera-token-fg-danger:var(--color-tigera-color-red-600)}}@layer base{*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr:where([title]){-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}:where(select:is([multiple],[size])) optgroup{font-weight:bolder}:where(select:is([multiple],[size])) optgroup option{padding-inline-start:20px}::file-selector-button{margin-inline-end:4px}::placeholder{opacity:1}@supports (not ((-webkit-appearance:-apple-pay-button))) or (contain-intrinsic-size:1px){::placeholder{color:currentColor}@supports (color:color-mix(in lab, red, red)){::placeholder{color:color-mix(in oklab, currentcolor 50%, transparent)}}}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-block:0}::-webkit-datetime-edit-year-field{padding-block:0}::-webkit-datetime-edit-month-field{padding-block:0}::-webkit-datetime-edit-day-field{padding-block:0}::-webkit-datetime-edit-hour-field{padding-block:0}::-webkit-datetime-edit-minute-field{padding-block:0}::-webkit-datetime-edit-second-field{padding-block:0}::-webkit-datetime-edit-millisecond-field{padding-block:0}::-webkit-datetime-edit-meridiem-field{padding-block:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input:where([type=button],[type=reset],[type=submit]){appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:where(:not([hidden=until-found])){display:none!important}}@layer components;@layer utilities{.m-0{margin:calc(var(--spacing) * 0)}.mx-auto{margin-inline:auto}.mt-0\.5{margin-top:calc(var(--spacing) * .5)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.ml-auto{margin-left:auto}.box-border{box-sizing:border-box}.flex{display:flex}.hidden{display:none}.max-h-\[400px\]{max-height:400px}.min-h-\[280px\]{min-height:280px}.w-full{width:100%}.max-w-lg{max-width:var(--container-lg)}.cursor-pointer{cursor:pointer}.resize-y{resize:vertical}.items-center{align-items:center}.justify-between{justify-content:space-between}.justify-end{justify-content:flex-end}.gap-2{gap:calc(var(--spacing) * 2)}.gap-4{gap:calc(var(--spacing) * 4)}:where(.space-y-6>:not(:last-child)){--tw-space-y-reverse:0;margin-block-start:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-block-end:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.overflow-auto{overflow:auto}.overflow-hidden{overflow:hidden}.rounded{border-radius:.25rem}.rounded-lg{border-radius:var(--radius-lg)}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-none{--tw-border-style:none;border-style:none}.border-tigera-token-border-danger{border-color:var(--color-tigera-token-border-danger)}.border-tigera-token-border-default{border-color:var(--color-tigera-token-border-default)}.bg-tigera-token-bg-brand{background-color:var(--color-tigera-token-bg-brand)}.bg-tigera-token-bg-danger-subtle{background-color:var(--color-tigera-token-bg-danger-subtle)}.bg-tigera-token-elevation-sunken{background-color:var(--color-tigera-token-elevation-sunken)}.bg-tigera-token-elevation-surface{background-color:var(--color-tigera-token-elevation-surface)}.bg-transparent{background-color:#0000}.p-3{padding:calc(var(--spacing) * 3)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.px-2{padding-inline:calc(var(--spacing) * 2)}.px-3{padding-inline:calc(var(--spacing) * 3)}.px-6{padding-inline:calc(var(--spacing) * 6)}.px-8{padding-inline:calc(var(--spacing) * 8)}.py-1\.5{padding-block:calc(var(--spacing) * 1.5)}.py-2\.5{padding-block:calc(var(--spacing) * 2.5)}.py-3{padding-block:calc(var(--spacing) * 3)}.py-4{padding-block:calc(var(--spacing) * 4)}.py-8{padding-block:calc(var(--spacing) * 8)}.pl-5{padding-left:calc(var(--spacing) * 5)}.text-center{text-align:center}.font-mono{font-family:var(--font-mono)}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.text-\[0\.8125rem\]{font-size:.8125rem}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.break-words{overflow-wrap:break-word}.whitespace-pre-wrap{white-space:pre-wrap}.text-tigera-token-fg-brand{color:var(--color-tigera-token-fg-brand)}.text-tigera-token-fg-danger{color:var(--color-tigera-token-fg-danger)}.text-tigera-token-fg-default{color:var(--color-tigera-token-fg-default)}.text-tigera-token-fg-inverted{color:var(--color-tigera-token-fg-inverted)}.text-tigera-token-fg-subtle{color:var(--color-tigera-token-fg-subtle)}.no-underline{text-decoration-line:none}.shadow-\[var\(--color-tigera-token-elevation-surface-shadow\)\]{--tw-shadow:var(--color-tigera-token-elevation-surface-shadow);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.outline-none{--tw-outline-style:none;outline-style:none}.placeholder\:text-tigera-token-fg-subtle::placeholder{color:var(--color-tigera-token-fg-subtle)}@media (hover:hover){.hover\:border-tigera-token-border-bold:hover{border-color:var(--color-tigera-token-border-bold)}.hover\:border-tigera-token-border-danger:hover{border-color:var(--color-tigera-token-border-danger)}.hover\:bg-tigera-token-bg-brand-hovered:hover{background-color:var(--color-tigera-token-bg-brand-hovered)}.hover\:text-tigera-token-fg-danger:hover{color:var(--color-tigera-token-fg-danger)}.hover\:text-tigera-token-fg-default:hover{color:var(--color-tigera-token-fg-default)}.hover\:underline:hover{text-decoration-line:underline}}.focus\:border-tigera-token-border-brand:focus{border-color:var(--color-tigera-token-border-brand)}.disabled\:cursor-not-allowed:disabled{cursor:not-allowed}.disabled\:opacity-50:disabled{opacity:.5}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-brand:var(--color-tigera-color-medium-gold-400);--color-tigera-token-bg-brand-hovered:var(--color-tigera-color-medium-gold-300);--color-tigera-token-bg-brand-pressed:var(--color-tigera-color-medium-gold-200);--color-tigera-token-bg-brand-subtle:#f8aa3f2b}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-bg-brand-subtle:color-mix(in srgb, var(--color-tigera-color-medium-gold-400) 17%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-brand-subtle-hovered:#fbcb762b}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-bg-brand-subtle-hovered:color-mix(in srgb, var(--color-tigera-color-medium-gold-300) 17%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-brand-subtle-pressed:#fde1ab2b}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-bg-brand-subtle-pressed:color-mix(in srgb, var(--color-tigera-color-medium-gold-200) 17%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-brand-accent:var(--color-tigera-color-medium-gold-200);--color-tigera-token-bg-on-brand:var(--color-tigera-token-bg-neutral-solid);--color-tigera-token-bg-on-brand-hovered:var(--color-tigera-token-bg-neutral-solid-hovered);--color-tigera-token-bg-on-brand-pressed:var(--color-tigera-token-bg-neutral-solid-pressed);--color-tigera-token-bg-brand-light:var(--color-tigera-color-medium-gold-200);--color-tigera-token-on-brand-light:var(--color-tigera-token-fg-inverted);--color-tigera-token-bg-warning:var(--color-tigera-color-medium-gold-400);--color-tigera-token-bg-warning-hovered:var(--color-tigera-color-medium-gold-300);--color-tigera-token-bg-warning-pressed:var(--color-tigera-color-medium-gold-500);--color-tigera-token-bg-warning-subtle:#f6932033}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-bg-warning-subtle:color-mix(in srgb, var(--color-tigera-color-medium-gold-500) 20%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-info:var(--color-tigera-color-medium-blue-300);--color-tigera-token-bg-info-hovered:var(--color-tigera-color-medium-blue-200);--color-tigera-token-bg-info-pressed:var(--color-tigera-color-medium-blue-100);--color-tigera-token-bg-info-subtle:#3fbcf133}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-bg-info-subtle:color-mix(in srgb, var(--color-tigera-color-medium-blue-400) 20%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-danger:var(--color-tigera-color-red-400);--color-tigera-token-bg-danger-hovered:var(--color-tigera-color-red-300);--color-tigera-token-bg-danger-pressed:var(--color-tigera-color-red-200);--color-tigera-token-bg-danger-subtle:#f2434133}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-bg-danger-subtle:color-mix(in srgb, var(--color-tigera-color-red-500) 20%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-success:var(--color-tigera-color-green-400);--color-tigera-token-bg-success-hovered:var(--color-tigera-color-green-300);--color-tigera-token-bg-success-pressed:var(--color-tigera-color-green-200);--color-tigera-token-bg-success-subtle:#00c77133}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-bg-success-subtle:color-mix(in srgb, var(--color-tigera-color-green-600) 20%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-selected:transparent;--color-tigera-token-bg-selected-hovered:transparent;--color-tigera-token-bg-selected-pressed:transparent;--color-tigera-token-bg-empty:transparent;--color-tigera-token-bg-neutral-base:var(--color-tigera-color-neutral-1200);--color-tigera-token-bg-neutral-subtle:var(--color-tigera-token-bg-empty);--color-tigera-token-bg-neutral-subtle-hovered:var(--color-tigera-color-dark-alpha-100);--color-tigera-token-bg-neutral-subtle-pressed:var(--color-tigera-color-dark-alpha-200);--color-tigera-token-bg-neutral:var(--color-tigera-color-dark-alpha-100);--color-tigera-token-bg-neutral-hovered:var(--color-tigera-color-dark-alpha-200);--color-tigera-token-bg-neutral-pressed:var(--color-tigera-color-dark-alpha-300);--color-tigera-token-bg-neutral-solid:var(--color-tigera-color-neutral-500);--color-tigera-token-bg-neutral-solid-hovered:var(--color-tigera-color-neutral-600);--color-tigera-token-bg-neutral-solid-pressed:var(--color-tigera-color-neutral-700);--color-tigera-token-bg-input:var(--color-tigera-color-neutral-1200);--color-tigera-token-border-default:var(--color-tigera-color-neutral-800);--color-tigera-token-border-default-hovered:var(--color-tigera-color-neutral-700);--color-tigera-token-border-bold:var(--color-tigera-color-neutral-500);--color-tigera-token-border-selected:var(--color-tigera-color-medium-gold-300);--color-tigera-token-border-brand:var(--color-tigera-color-medium-gold-400);--color-tigera-token-border-brand-hovered:var(--color-tigera-color-medium-gold-400);--color-tigera-token-border-brand-pressed:var(--color-tigera-color-medium-gold-200);--color-tigera-token-border-warning:var(--color-tigera-color-medium-gold-400);--color-tigera-token-border-info:var(--color-tigera-color-medium-blue-300);--color-tigera-token-border-danger:var(--color-tigera-color-red-600);--color-tigera-token-border-success:var(--color-tigera-color-green-400);--color-tigera-token-table-selected:#e7750f59}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-table-selected:color-mix(in srgb, var(--color-tigera-color-medium-gold-600) 35%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-table-selected-hovered:#bf580f59}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-table-selected-hovered:color-mix(in srgb, var(--color-tigera-color-medium-gold-700) 35%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-table-selected-pressed:#98461459}@supports (color:color-mix(in lab, red, red)){:is(.dark,[data-theme=dark]){--color-tigera-token-table-selected-pressed:color-mix(in srgb, var(--color-tigera-color-medium-gold-800) 35%, transparent)}}:is(.dark,[data-theme=dark]){--color-tigera-token-bg-table-expanded:var(--color-tigera-token-elevation-sunken);--color-tigera-token-bg-table-header:#1f1f20;--color-tigera-token-on-table-selected:var(--color-tigera-token-fg-default);--color-tigera-token-tabs-selected:var(--color-tigera-token-bg-brand);--color-tigera-token-bg-nav-menu:var(--color-tigera-color-neutral-950);--color-tigera-token-bg-nav-menu-active:transparent;--color-tigera-token-bg-nav-menu-hovered:var(--color-tigera-color-medium-gold-500);--color-tigera-token-on-nav-menu:var(--color-tigera-token-white);--color-tigera-token-on-nav-menu-active:var(--color-tigera-color-medium-gold-500);--color-tigera-token-on-nav-menu-hovered:var(--color-tigera-color-neutral-950);--color-tigera-token-elevation-sunken:var(--color-tigera-color-neutral-1200);--color-tigera-token-elevation-page:var(--color-tigera-color-neutral-1100);--color-tigera-token-elevation-surface:var(--color-tigera-color-neutral-1000);--color-tigera-token-elevation-surface-shadow:0px 0px 8px #2526271a;--color-tigera-token-elevation-overlay:var(--color-tigera-color-neutral-1000);--color-tigera-token-elevation-overlay-shadow:0px 0px 8px #2526271a;--color-tigera-token-elevation-overlay-inverted:var(--color-tigera-color-medium-gold-200);--color-tigera-token-fg-default:var(--color-tigera-color-neutral-50);--color-tigera-token-fg-inverted:var(--color-tigera-color-neutral-1200);--color-tigera-token-fg-support:var(--color-tigera-color-neutral-200);--color-tigera-token-fg-subtle:var(--color-tigera-color-neutral-300);--color-tigera-token-fg-brand:var(--color-tigera-color-medium-gold-500);--color-tigera-token-fg-brand-hovered:var(--color-tigera-color-medium-gold-300);--color-tigera-token-fg-brand-pressed:var(--color-tigera-color-medium-gold-200);--color-tigera-token-fg-warning:var(--color-tigera-color-medium-gold-400);--color-tigera-token-fg-info:var(--color-tigera-color-medium-blue-300);--color-tigera-token-fg-danger:var(--color-tigera-color-red-400);--color-tigera-token-fg-success:var(--color-tigera-color-green-400);--color-tigera-token-on-bg-brand:var(--color-tigera-token-fg-inverted);--color-tigera-token-on-bg-warning:var(--color-tigera-token-fg-inverted);--color-tigera-token-on-bg-warning-subtle:var(--color-tigera-color-medium-gold-100);--color-tigera-token-on-bg-info:var(--color-tigera-token-fg-inverted);--color-tigera-token-on-bg-info-subtle:var(--color-tigera-color-medium-blue-100);--color-tigera-token-on-bg-danger:var(--color-tigera-token-fg-inverted);--color-tigera-token-on-bg-danger-subtle:var(--color-tigera-color-red-200);--color-tigera-token-on-bg-success:var(--color-tigera-color-neutral-1200);--color-tigera-token-on-bg-success-subtle:var(--color-tigera-color-green-200);--color-tigera-token-link:var(--color-tigera-color-medium-gold-400);--color-tigera-token-link-hovered:var(--color-tigera-color-medium-gold-300);--color-tigera-token-link-pressed:var(--color-tigera-color-medium-gold-200)}.i2g-playbook-prose{color:var(--color-tigera-token-fg-default);font-size:.9375rem}.i2g-playbook-prose h1{color:var(--color-tigera-token-fg-default);margin:0 0 .5rem;font-size:1.75rem;font-weight:700}.i2g-playbook-prose h2{border-bottom:1px solid var(--color-tigera-token-border-default);color:var(--color-tigera-token-fg-default);margin:2rem 0 .75rem;padding-bottom:.5rem;font-size:1.35rem;font-weight:600}.i2g-playbook-prose h3{color:var(--color-tigera-token-fg-default);margin:1.5rem 0 .5rem;font-size:1.1rem;font-weight:600}.i2g-playbook-prose p{color:var(--color-tigera-token-fg-default);margin:.5rem 0;line-height:1.65}.i2g-playbook-prose blockquote{border-left:3px solid var(--color-tigera-token-border-brand);color:var(--color-tigera-token-fg-subtle);margin:.75rem 0;padding:.5rem 1rem;font-size:.875rem}.i2g-playbook-prose ul,.i2g-playbook-prose ol{margin:.5rem 0;padding-left:1.5rem}.i2g-playbook-prose li{color:var(--color-tigera-token-fg-default);margin:.25rem 0;line-height:1.65}.i2g-playbook-prose li::marker{color:var(--color-tigera-token-fg-subtle)}.i2g-playbook-prose pre{background:var(--color-tigera-token-elevation-sunken);border:1px solid var(--color-tigera-token-border-default);color:var(--color-tigera-token-fg-default);border-radius:.375rem;margin:.75rem 0;padding:1rem;font-size:.8125rem;line-height:1.5;overflow-x:auto}.i2g-playbook-prose code{color:var(--color-tigera-token-fg-default);font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,monospace;font-size:.85em}.i2g-playbook-prose :not(pre)>code{background:var(--color-tigera-token-elevation-sunken);border-radius:.25rem;padding:.125rem .375rem}.i2g-playbook-prose table{border-collapse:collapse;width:100%;margin:.75rem 0;font-size:.875rem}.i2g-playbook-prose th,.i2g-playbook-prose td{border:1px solid var(--color-tigera-token-border-default);text-align:left;color:var(--color-tigera-token-fg-default);padding:.5rem .75rem}.i2g-playbook-prose th{background:var(--color-tigera-token-elevation-sunken);font-weight:600}.i2g-playbook-prose hr{border:none;border-top:1px solid var(--color-tigera-token-border-default);margin:2rem 0}.i2g-playbook-prose a{color:var(--color-tigera-token-fg-brand);text-decoration:none}.i2g-playbook-prose a:hover{text-decoration:underline}.i2g-playbook-prose strong{font-weight:600}.i2g-playbook-prose input[type=checkbox]{margin-right:.5rem}@keyframes i2g-flash{0%{background-color:var(--color-tigera-token-bg-brand-subtle,#2739811a)}to{background-color:#0000}}.i2g-flash{animation:.6s ease-out i2g-flash}@property --tw-space-y-reverse{syntax:"*";inherits:false;initial-value:0}@property --tw-border-style{syntax:"*";inherits:false;initial-value:solid}@property --tw-leading{syntax:"*";inherits:false}@property --tw-font-weight{syntax:"*";inherits:false}@property --tw-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-shadow-color{syntax:"*";inherits:false}@property --tw-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-inset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-shadow-color{syntax:"*";inherits:false}@property --tw-inset-shadow-alpha{syntax:"";inherits:false;initial-value:100%}@property --tw-ring-color{syntax:"*";inherits:false}@property --tw-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-inset-ring-color{syntax:"*";inherits:false}@property --tw-inset-ring-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000}@property --tw-ring-inset{syntax:"*";inherits:false}@property --tw-ring-offset-width{syntax:"";inherits:false;initial-value:0}@property --tw-ring-offset-color{syntax:"*";inherits:false;initial-value:#fff}@property --tw-ring-offset-shadow{syntax:"*";inherits:false;initial-value:0 0 #0000} \ No newline at end of file diff --git a/src/___new___/components/IngressConverter/vendor/ing2gw-web.js b/src/___new___/components/IngressConverter/vendor/ing2gw-web.js new file mode 100644 index 0000000000..3a6f1cee73 --- /dev/null +++ b/src/___new___/components/IngressConverter/vendor/ing2gw-web.js @@ -0,0 +1,419 @@ +var $8={simple:{label:"Simple (TLS + redirect)",yaml:`apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: web-app + namespace: default + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-read-timeout: "30" +spec: + ingressClassName: nginx + tls: + - hosts: + - app.example.com + secretName: app-tls + rules: + - host: app.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: web-service + port: + number: 80`},full:{label:"Full-featured (CORS, auth, rate limit)",yaml:`apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: online-store + namespace: production + annotations: + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-origin: "https://store.example.com,https://admin.example.com" + nginx.ingress.kubernetes.io/cors-allow-methods: "GET,POST,PUT,DELETE,OPTIONS" + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-read-timeout: "120" + nginx.ingress.kubernetes.io/proxy-body-size: "50m" + nginx.ingress.kubernetes.io/limit-rps: "100" + nginx.ingress.kubernetes.io/whitelist-source-range: "10.0.0.0/8,172.16.0.0/12" + nginx.ingress.kubernetes.io/auth-url: "http://auth-service.production.svc.cluster.local:8080/verify" + nginx.ingress.kubernetes.io/auth-response-headers: "X-User-ID,X-User-Role" + nginx.ingress.kubernetes.io/affinity: "cookie" + nginx.ingress.kubernetes.io/session-cookie-name: "STORE_SESSION" + nginx.ingress.kubernetes.io/session-cookie-max-age: "3600" + nginx.ingress.kubernetes.io/upstream-vhost: "api-internal.example.com" + nginx.ingress.kubernetes.io/configuration-snippet: | + more_set_headers "X-Frame-Options: DENY"; +spec: + ingressClassName: nginx + tls: + - hosts: + - store.example.com + secretName: store-tls-cert + rules: + - host: store.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: storefront + port: + number: 443 + - path: /api + pathType: Prefix + backend: + service: + name: api-gateway + port: + number: 8080`},"nginxinc-simple":{label:"NGINX Inc — Simple",yaml:`apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: web-app + namespace: default + annotations: + nginx.org/redirect-to-https: "true" + nginx.org/proxy-read-timeout: "30s" + nginx.org/client-max-body-size: "10m" +spec: + ingressClassName: nginx + tls: + - hosts: + - app.example.com + secretName: app-tls + rules: + - host: app.example.com + http: + paths: + - path: / + pathType: Prefix + backend: + service: + name: web-service + port: + number: 80`},multi:{label:"Multi-service (2 namespaces)",yaml:`--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: frontend + namespace: web + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/enable-cors: "true" + nginx.ingress.kubernetes.io/cors-allow-origin: "*" + nginx.ingress.kubernetes.io/rewrite-target: /$1 + nginx.ingress.kubernetes.io/use-regex: "true" +spec: + ingressClassName: nginx + tls: + - hosts: + - app.example.com + secretName: frontend-tls + rules: + - host: app.example.com + http: + paths: + - path: /(.*) + pathType: ImplementationSpecific + backend: + service: + name: frontend-svc + port: + number: 3000 +--- +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: api + namespace: backend + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" + nginx.ingress.kubernetes.io/proxy-body-size: "100m" + nginx.ingress.kubernetes.io/proxy-read-timeout: "300" + nginx.ingress.kubernetes.io/limit-rps: "50" + nginx.ingress.kubernetes.io/auth-type: "basic" + nginx.ingress.kubernetes.io/auth-secret: "api-basic-auth" + nginx.ingress.kubernetes.io/backend-protocol: "HTTPS" +spec: + ingressClassName: nginx + tls: + - hosts: + - api.example.com + secretName: api-tls + rules: + - host: api.example.com + http: + paths: + - path: /v1 + pathType: Prefix + backend: + service: + name: api-v1 + port: + number: 8443 + - path: /v2 + pathType: Prefix + backend: + service: + name: api-v2 + port: + number: 8443`}};import{useCallback as g8,useRef as J$,useState as U$}from"react";import{jsx as w,jsxs as i}from"react/jsx-runtime";function X8({value:$,onChange:X,onConvert:Z,provider:G,onProviderChange:J,errors:U=[],examples:Q=$8,showHeader:z=!0}){let[W,V]=U$(!1),H=J$(null),Y=g8(()=>{H.current?.click()},[]),_=g8((F)=>{let q=F.target.files?.[0];if(!q)return;let I=new FileReader;I.onload=(D)=>{let L=D.target?.result;if(typeof L==="string")X(L)},I.readAsText(q),F.target.value=""},[X]);return i("div",{className:"rounded-lg border border-tigera-token-border-default bg-tigera-token-elevation-surface overflow-hidden shadow-[var(--color-tigera-token-elevation-surface-shadow)]",children:[z&&i("div",{className:"px-6 py-8 text-center border-b border-tigera-token-border-default bg-tigera-token-elevation-sunken",children:[w("h2",{className:"m-0 mb-2 text-2xl font-semibold text-tigera-token-fg-default",children:"NGINX Ingress to Gateway API"}),w("p",{className:"m-0 mb-3 text-sm text-tigera-token-fg-subtle max-w-lg mx-auto",children:"Paste your NGINX Ingress YAML below and get a personalised, step-by-step migration playbook for Envoy Gateway — in seconds."}),i("p",{className:"m-0 text-xs text-tigera-token-fg-subtle",children:["Supports NGINX Ingress (community) and NGINX Inc (F5) annotations. Using a different controller?"," ",w("a",{href:"https://tigera.io/contact",target:"_blank",rel:"noopener noreferrer",className:"text-tigera-token-fg-brand no-underline hover:underline",children:"Let us know"})," ","— more providers are on the roadmap."]})]}),i("div",{className:"p-6",children:[i("div",{className:"flex items-center gap-2 mb-2",children:[w("label",{htmlFor:"i2g-yaml-input",className:"text-sm font-semibold text-tigera-token-fg-default",children:"Ingress YAML"}),i("div",{className:"ml-auto flex items-center gap-2",children:[i("select",{className:"px-2 py-1.5 text-xs text-tigera-token-fg-subtle bg-tigera-token-elevation-surface border border-tigera-token-border-default rounded cursor-pointer hover:border-tigera-token-border-bold",value:G,onChange:(F)=>J(F.target.value),children:[w("option",{value:"auto",children:"Auto-detect provider"}),w("option",{value:"ingress-nginx",children:"NGINX Ingress (community)"}),w("option",{value:"nginxinc",children:"NGINX Inc (F5)"})]}),i("select",{className:"px-2 py-1.5 text-xs text-tigera-token-fg-subtle bg-tigera-token-elevation-surface border border-tigera-token-border-default rounded cursor-pointer hover:border-tigera-token-border-bold",value:"",onChange:(F)=>{let q=Q[F.target.value];if(q)X(q.yaml),V(!0),setTimeout(()=>V(!1),600),F.target.value=""},children:[w("option",{value:"",disabled:!0,children:"Try an example..."}),Object.entries(Q).map(([F,q])=>w("option",{value:F,children:q.label},F))]}),w("span",{className:"text-xs text-tigera-token-fg-subtle",children:"or"}),w("button",{type:"button",onClick:Y,className:"px-3 py-1.5 text-xs text-tigera-token-fg-subtle bg-tigera-token-elevation-surface border border-tigera-token-border-default rounded cursor-pointer hover:text-tigera-token-fg-default hover:border-tigera-token-border-bold",children:"Upload file"})]}),w("input",{ref:H,type:"file",accept:".yaml,.yml",className:"hidden",onChange:_})]}),w("textarea",{id:"i2g-yaml-input",className:`w-full min-h-[280px] p-4 font-mono text-[0.8125rem] leading-relaxed text-tigera-token-fg-default bg-tigera-token-elevation-sunken border border-tigera-token-border-default rounded-md outline-none resize-y box-border placeholder:text-tigera-token-fg-subtle focus:border-tigera-token-border-brand ${W?"i2g-flash":""}`,value:$,onChange:(F)=>X(F.target.value),placeholder:`# Paste your NGINX Ingress YAML here +apiVersion: networking.k8s.io/v1 +kind: Ingress +metadata: + name: my-app + annotations: + nginx.ingress.kubernetes.io/ssl-redirect: "true" + ...`,spellCheck:!1}),U.length>0&&w("div",{className:"mt-3 p-3 bg-tigera-token-bg-danger-subtle border border-tigera-token-border-danger rounded-md",children:w("ul",{className:"m-0 pl-5",children:U.map((F)=>w("li",{className:"text-tigera-token-fg-danger text-[0.8125rem] leading-relaxed",children:F},F))})}),w("div",{className:"mt-4 flex items-center gap-4",children:w("button",{type:"button",onClick:Z,disabled:!$.trim(),className:"px-8 py-2.5 text-sm font-semibold text-tigera-token-fg-inverted bg-tigera-token-bg-brand border-none rounded-md cursor-pointer transition-colors hover:bg-tigera-token-bg-brand-hovered disabled:opacity-50 disabled:cursor-not-allowed",children:"Generate Playbook"})})]})]})}import{useCallback as Q$,useState as z$}from"react";import{jsx as L0,jsxs as W$}from"react/jsx-runtime";function V$({children:$}){return L0("pre",{className:"m-0 whitespace-pre-wrap break-words font-mono text-[0.8125rem] leading-relaxed text-tigera-token-fg-default",children:$})}function Z8({markdown:$,markdownRenderer:X=V$}){let[Z,G]=z$(!1),J=Q$(()=>{navigator.clipboard.writeText($),G(!0),setTimeout(()=>G(!1),2000)},[$]);return W$("div",{className:"rounded-lg border border-tigera-token-border-default bg-tigera-token-elevation-surface overflow-hidden shadow-[var(--color-tigera-token-elevation-surface-shadow)]",children:[L0("div",{className:"px-6 py-3 flex items-center justify-end border-b border-tigera-token-border-default bg-tigera-token-elevation-sunken",children:L0("button",{type:"button",onClick:J,className:"px-3 py-1.5 text-xs font-medium text-tigera-token-fg-subtle bg-tigera-token-elevation-surface border border-tigera-token-border-default rounded cursor-pointer hover:text-tigera-token-fg-default hover:border-tigera-token-border-bold",children:Z?"Copied!":"Copy as Markdown"})}),L0("div",{className:"px-8 py-8 i2g-playbook-prose",children:L0(X,{children:$})})]})}import{useCallback as v8,useState as Y$}from"react";import{jsx as O0,jsxs as E0}from"react/jsx-runtime";function G8({yaml:$}){let[X,Z]=Y$(!1),G=v8(()=>{navigator.clipboard.writeText($),Z(!0),setTimeout(()=>Z(!1),2000)},[$]),J=v8(()=>{let U=new Blob([$],{type:"text/yaml"}),Q=URL.createObjectURL(U),z=document.createElement("a");z.href=Q,z.download="gateway.yaml",z.click(),URL.revokeObjectURL(Q)},[$]);return E0("div",{className:"rounded-lg border border-tigera-token-border-default bg-tigera-token-elevation-surface overflow-hidden shadow-[var(--color-tigera-token-elevation-surface-shadow)]",children:[E0("div",{className:"px-6 py-4 flex items-center justify-between border-b border-tigera-token-border-default bg-tigera-token-elevation-sunken",children:[E0("div",{children:[O0("h3",{className:"m-0 text-base font-semibold text-tigera-token-fg-default",children:"Gateway API Resources"}),O0("p",{className:"m-0 text-xs text-tigera-token-fg-subtle mt-0.5",children:"Ready to apply to your cluster"})]}),E0("div",{className:"flex gap-2",children:[O0("button",{type:"button",onClick:G,className:"px-3 py-1.5 text-xs font-medium text-tigera-token-fg-subtle bg-tigera-token-elevation-surface border border-tigera-token-border-default rounded cursor-pointer hover:text-tigera-token-fg-default hover:border-tigera-token-border-bold",children:X?"Copied!":"Copy YAML"}),O0("button",{type:"button",onClick:J,className:"px-3 py-1.5 text-xs font-medium text-tigera-token-fg-inverted bg-tigera-token-bg-brand border-none rounded cursor-pointer hover:bg-tigera-token-bg-brand-hovered",children:"Download gateway.yaml"})]})]}),O0("pre",{className:"m-0 p-4 font-mono text-[0.8125rem] leading-relaxed text-tigera-token-fg-default bg-tigera-token-elevation-sunken max-h-[400px] overflow-auto whitespace-pre-wrap break-words",children:$})]})}var O8=Symbol.for("yaml.alias"),H8=Symbol.for("yaml.document"),n=Symbol.for("yaml.map"),V6=Symbol.for("yaml.pair"),d=Symbol.for("yaml.scalar"),_0=Symbol.for("yaml.seq"),v=Symbol.for("yaml.node.type"),e=($)=>!!$&&typeof $==="object"&&$[v]===O8,F0=($)=>!!$&&typeof $==="object"&&$[v]===H8,A0=($)=>!!$&&typeof $==="object"&&$[v]===n,N=($)=>!!$&&typeof $==="object"&&$[v]===V6,P=($)=>!!$&&typeof $==="object"&&$[v]===d,P0=($)=>!!$&&typeof $==="object"&&$[v]===_0;function C($){if($&&typeof $==="object")switch($[v]){case n:case _0:return!0}return!1}function S($){if($&&typeof $==="object")switch($[v]){case O8:case n:case d:case _0:return!0}return!1}var W6=($)=>(P($)||C($))&&!!$.anchor,y=Symbol("break visit"),Y6=Symbol("skip children"),u=Symbol("remove node");function I0($,X){let Z=H6(X);if(F0($)){if(G0(null,$.contents,Z,Object.freeze([$]))===u)$.contents=null}else G0(null,$,Z,Object.freeze([]))}I0.BREAK=y;I0.SKIP=Y6;I0.REMOVE=u;function G0($,X,Z,G){let J=_6($,X,Z,G);if(S(J)||N(J))return F6($,G,J),G0($,J,Z,G);if(typeof J!=="symbol"){if(C(X)){G=Object.freeze(G.concat(X));for(let U=0;U$.replace(/[!,[\]{}]/g,(X)=>H$[X]);class k{constructor($,X){this.docStart=null,this.docEnd=!1,this.yaml=Object.assign({},k.defaultYaml,$),this.tags=Object.assign({},k.defaultTags,X)}clone(){let $=new k(this.yaml,this.tags);return $.docStart=this.docStart,$}atDocument(){let $=new k(this.yaml,this.tags);switch(this.yaml.version){case"1.1":this.atNextDocument=!0;break;case"1.2":this.atNextDocument=!1,this.yaml={explicit:k.defaultYaml.explicit,version:"1.2"},this.tags=Object.assign({},k.defaultTags);break}return $}add($,X){if(this.atNextDocument)this.yaml={explicit:k.defaultYaml.explicit,version:"1.1"},this.tags=Object.assign({},k.defaultTags),this.atNextDocument=!1;let Z=$.trim().split(/[ \t]+/),G=Z.shift();switch(G){case"%TAG":{if(Z.length!==2){if(X(0,"%TAG directive should contain exactly two parts"),Z.length<2)return!1}let[J,U]=Z;return this.tags[J]=U,!0}case"%YAML":{if(this.yaml.explicit=!0,Z.length!==1)return X(0,"%YAML directive should contain exactly one part"),!1;let[J]=Z;if(J==="1.1"||J==="1.2")return this.yaml.version=J,!0;else{let U=/^\d+\.\d+$/.test(J);return X(6,`Unsupported YAML version ${J}`,U),!1}}default:return X(0,`Unknown directive ${G}`,!0),!1}}tagName($,X){if($==="!")return"!";if($[0]!=="!")return X(`Not a valid tag: ${$}`),null;if($[1]==="<"){let U=$.slice(2,-1);if(U==="!"||U==="!!")return X(`Verbatim tags aren't resolved, so ${$} is invalid.`),null;if($[$.length-1]!==">")X("Verbatim tags must end with a >");return U}let[,Z,G]=$.match(/^(.*!)([^!]*)$/s);if(!G)X(`The ${$} tag has no suffix`);let J=this.tags[Z];if(J)try{return J+decodeURIComponent(G)}catch(U){return X(String(U)),null}if(Z==="!")return $;return X(`Could not resolve tag: ${$}`),null}tagString($){for(let[X,Z]of Object.entries(this.tags))if($.startsWith(Z))return X+_$($.substring(Z.length));return $[0]==="!"?$:`!<${$}>`}toString($){let X=this.yaml.explicit?[`%YAML ${this.yaml.version||"1.2"}`]:[],Z=Object.entries(this.tags),G;if($&&Z.length>0&&S($.contents)){let J={};I0($.contents,(U,Q)=>{if(S(Q)&&Q.tag)J[Q.tag]=!0}),G=Object.keys(J)}else G=[];for(let[J,U]of Z){if(J==="!!"&&U==="tag:yaml.org,2002:")continue;if(!$||G.some((Q)=>Q.startsWith(U)))X.push(`%TAG ${J} ${U}`)}return X.join(` +`)}}k.defaultYaml={explicit:!1,version:"1.2"};k.defaultTags={"!!":"tag:yaml.org,2002:"};function I6($){if(/[\x00-\x19\s,[\]{}]/.test($)){let Z=`Anchor must not contain whitespace or control characters: ${JSON.stringify($)}`;throw Error(Z)}return!0}function D6($){let X=new Set;return I0($,{Value(Z,G){if(G.anchor)X.add(G.anchor)}}),X}function q6($,X){for(let Z=1;;++Z){let G=`${$}${Z}`;if(!X.has(G))return G}}function F$($,X){let Z=[],G=new Map,J=null;return{onAnchor:(U)=>{Z.push(U),J??(J=D6($));let Q=q6(X,J);return J.add(Q),Q},setAnchors:()=>{for(let U of Z){let Q=G.get(U);if(typeof Q==="object"&&Q.anchor&&(P(Q.node)||C(Q.node)))Q.node.anchor=Q.anchor;else{let z=Error("Failed to resolve repeated object (this should not happen)");throw z.source=U,z}}},sourceObjects:G}}function U0($,X,Z,G){if(G&&typeof G==="object")if(Array.isArray(G))for(let J=0,U=G.length;Jg(G,String(J),Z));if($&&typeof $.toJSON==="function"){if(!Z||!W6($))return $.toJSON(X,Z);let G={aliasCount:0,count:1,res:void 0};Z.anchors.set($,G),Z.onCreate=(U)=>{G.res=U,delete Z.onCreate};let J=$.toJSON(X,Z);if(Z.onCreate)Z.onCreate(J);return J}if(typeof $==="bigint"&&!Z?.keep)return Number($);return $}class p0{constructor($){Object.defineProperty(this,v,{value:$})}clone(){let $=Object.create(Object.getPrototypeOf(this),Object.getOwnPropertyDescriptors(this));if(this.range)$.range=this.range.slice();return $}toJS($,{mapAsMap:X,maxAliasCount:Z,onAnchor:G,reviver:J}={}){if(!F0($))throw TypeError("A document argument is required");let U={anchors:new Map,doc:$,keep:!0,mapAsMap:X===!0,mapKeyWarned:!1,maxAliasCount:typeof Z==="number"?Z:100},Q=g(this,"",U);if(typeof G==="function")for(let{count:z,res:W}of U.anchors.values())G(W,z);return typeof J==="function"?U0(J,{"":Q},"",Q):Q}}class l0 extends p0{constructor($){super(O8);this.source=$,Object.defineProperty(this,"tag",{set(){throw Error("Alias nodes cannot have tags")}})}resolve($,X){let Z;if(X?.aliasResolveCache)Z=X.aliasResolveCache;else if(Z=[],I0($,{Node:(J,U)=>{if(e(U)||W6(U))Z.push(U)}}),X)X.aliasResolveCache=Z;let G=void 0;for(let J of Z){if(J===this)break;if(J.anchor===this.source)G=J}return G}toJSON($,X){if(!X)return{source:this.source};let{anchors:Z,doc:G,maxAliasCount:J}=X,U=this.resolve(G,X);if(!U){let z=`Unresolved alias (the anchor must be set before the alias): ${this.source}`;throw ReferenceError(z)}let Q=Z.get(U);if(!Q)g(U,null,X),Q=Z.get(U);if(Q?.res===void 0)throw ReferenceError("This should not happen: Alias anchor was not resolved?");if(J>=0){if(Q.count+=1,Q.aliasCount===0)Q.aliasCount=v0(G,U,Z);if(Q.count*Q.aliasCount>J)throw ReferenceError("Excessive alias count indicates a resource exhaustion attack")}return Q.res}toString($,X,Z){let G=`*${this.source}`;if($){if(I6(this.source),$.options.verifyAliasOrder&&!$.anchors.has(this.source)){let J=`Unresolved alias (the anchor must be set before the alias): ${this.source}`;throw Error(J)}if($.implicitKey)return`${G} `}return G}}function v0($,X,Z){if(e(X)){let G=X.resolve($),J=Z&&G&&Z.get(G);return J?J.count*J.aliasCount:0}else if(C(X)){let G=0;for(let J of X.items){let U=v0($,J,Z);if(U>G)G=U}return G}else if(N(X)){let G=v0($,X.key,Z),J=v0($,X.value,Z);return Math.max(G,J)}return 1}var L6=($)=>!$||typeof $!=="function"&&typeof $!=="object";class R extends p0{constructor($){super(d);this.value=$}toJSON($,X){return X?.keep?this.value:g(this.value,$,X)}toString(){return String(this.value)}}R.BLOCK_FOLDED="BLOCK_FOLDED";R.BLOCK_LITERAL="BLOCK_LITERAL";R.PLAIN="PLAIN";R.QUOTE_DOUBLE="QUOTE_DOUBLE";R.QUOTE_SINGLE="QUOTE_SINGLE";var I$="tag:yaml.org,2002:";function D$($,X,Z){if(X){let G=Z.filter((U)=>U.tag===X),J=G.find((U)=>!U.format)??G[0];if(!J)throw Error(`Tag ${X} not found`);return J}return Z.find((G)=>G.identify?.($)&&!G.format)}function T0($,X,Z){if(F0($))$=$.contents;if(S($))return $;if(N($)){let Y=Z.schema[n].createNode?.(Z.schema,null,Z);return Y.items.push($),Y}if($ instanceof String||$ instanceof Number||$ instanceof Boolean||typeof BigInt<"u"&&$ instanceof BigInt)$=$.valueOf();let{aliasDuplicateObjects:G,onAnchor:J,onTagObj:U,schema:Q,sourceObjects:z}=Z,W=void 0;if(G&&$&&typeof $==="object")if(W=z.get($),W)return W.anchor??(W.anchor=J($)),new l0(W.anchor);else W={anchor:null,node:null},z.set($,W);if(X?.startsWith("!!"))X=I$+X.slice(2);let V=D$($,X,Q.tags);if(!V){if($&&typeof $.toJSON==="function")$=$.toJSON();if(!$||typeof $!=="object"){let Y=new R($);if(W)W.node=Y;return Y}V=$ instanceof Map?Q[n]:(Symbol.iterator in Object($))?Q[_0]:Q[n]}if(U)U(V),delete Z.onTagObj;let H=V?.createNode?V.createNode(Z.schema,$,Z):typeof V?.nodeClass?.from==="function"?V.nodeClass.from(Z.schema,$,Z):new R($);if(X)H.tag=X;else if(!V.default)H.tag=V.tag;if(W)W.node=H;return H}function u0($,X,Z){let G=Z;for(let J=X.length-1;J>=0;--J){let U=X[J];if(typeof U==="number"&&Number.isInteger(U)&&U>=0){let Q=[];Q[U]=G,G=Q}else G=new Map([[U,G]])}return T0(G,void 0,{aliasDuplicateObjects:!1,keepUndefined:!1,onAnchor:()=>{throw Error("This should not happen, please report a bug.")},schema:$,sourceObjects:new Map})}var K0=($)=>$==null||typeof $==="object"&&!!$[Symbol.iterator]().next().done;class K8 extends p0{constructor($,X){super($);Object.defineProperty(this,"schema",{value:X,configurable:!0,enumerable:!1,writable:!0})}clone($){let X=Object.create(Object.getPrototypeOf(this),Object.getOwnPropertyDescriptors(this));if($)X.schema=$;if(X.items=X.items.map((Z)=>S(Z)||N(Z)?Z.clone($):Z),this.range)X.range=this.range.slice();return X}addIn($,X){if(K0($))this.add(X);else{let[Z,...G]=$,J=this.get(Z,!0);if(C(J))J.addIn(G,X);else if(J===void 0&&this.schema)this.set(Z,u0(this.schema,G,X));else throw Error(`Expected YAML collection at ${Z}. Remaining path: ${G}`)}}deleteIn($){let[X,...Z]=$;if(Z.length===0)return this.delete(X);let G=this.get(X,!0);if(C(G))return G.deleteIn(Z);else throw Error(`Expected YAML collection at ${X}. Remaining path: ${Z}`)}getIn($,X){let[Z,...G]=$,J=this.get(Z,!0);if(G.length===0)return!X&&P(J)?J.value:J;else return C(J)?J.getIn(G,X):void 0}hasAllNullValues($){return this.items.every((X)=>{if(!N(X))return!1;let Z=X.value;return Z==null||$&&P(Z)&&Z.value==null&&!Z.commentBefore&&!Z.comment&&!Z.tag})}hasIn($){let[X,...Z]=$;if(Z.length===0)return this.has(X);let G=this.get(X,!0);return C(G)?G.hasIn(Z):!1}setIn($,X){let[Z,...G]=$;if(G.length===0)this.set(Z,X);else{let J=this.get(Z,!0);if(C(J))J.setIn(G,X);else if(J===void 0&&this.schema)this.set(Z,u0(this.schema,G,X));else throw Error(`Expected YAML collection at ${Z}. Remaining path: ${G}`)}}}var q$=($)=>$.replace(/^(?!$)(?: $)?/gm,"#");function p($,X){if(/^\n+$/.test($))return $.substring(1);return X?$.replace(/^(?! *$)/gm,X):$}var r=($,X,Z)=>$.endsWith(` +`)?p(Z,X):Z.includes(` +`)?` +`+p(Z,X):($.endsWith(" ")?"":" ")+Z,O6="flow",_8="block",x0="quoted";function c0($,X,Z="flow",{indentAtStart:G,lineWidth:J=80,minContentWidth:U=20,onFold:Q,onOverflow:z}={}){if(!J||J<0)return $;if(JJ-Math.max(2,U))V.push(0);else Y=J-G;let _=void 0,F=void 0,q=!1,I=-1,D=-1,L=-1;if(Z===_8){if(I=x8($,I,X.length),I!==-1)Y=I+W}for(let K;K=$[I+=1];){if(Z===x0&&K==="\\"){switch(D=I,$[I+1]){case"x":I+=3;break;case"u":I+=5;break;case"U":I+=9;break;default:I+=1}L=I}if(K===` +`){if(Z===_8)I=x8($,I,X.length);Y=I+X.length+W,_=void 0}else{if(K===" "&&F&&F!==" "&&F!==` +`&&F!=="\t"){let j=$[I+1];if(j&&j!==" "&&j!==` +`&&j!=="\t")_=I}if(I>=Y)if(_)V.push(_),Y=_+W,_=void 0;else if(Z===x0){while(F===" "||F==="\t")F=K,K=$[I+=1],q=!0;let j=I>L+1?I-2:D-1;if(H[j])return $;V.push(j),H[j]=!0,Y=j+W,_=void 0}else q=!0}F=K}if(q&&z)z();if(V.length===0)return $;if(Q)Q();let B=$.slice(0,V[0]);for(let K=0;K({indentAtStart:X?$.indent.length:$.indentAtStart,lineWidth:$.options.lineWidth,minContentWidth:$.options.minContentWidth}),s0=($)=>/^(%|---|\.\.\.)/m.test($);function L$($,X,Z){if(!X||X<0)return!1;let G=X-Z,J=$.length;if(J<=G)return!1;for(let U=0,Q=0;UG)return!0;if(Q=U+1,J-Q<=G)return!1}return!0}function M0($,X){let Z=JSON.stringify($);if(X.options.doubleQuotedAsJSON)return Z;let{implicitKey:G}=X,J=X.options.doubleQuotedMinMultiLineLength,U=X.indent||(s0($)?" ":""),Q="",z=0;for(let W=0,V=Z[W];V;V=Z[++W]){if(V===" "&&Z[W+1]==="\\"&&Z[W+2]==="n")Q+=Z.slice(z,W)+"\\ ",W+=1,z=W,V="\\";if(V==="\\")switch(Z[W+1]){case"u":{Q+=Z.slice(z,W);let H=Z.substr(W+2,4);switch(H){case"0000":Q+="\\0";break;case"0007":Q+="\\a";break;case"000b":Q+="\\v";break;case"001b":Q+="\\e";break;case"0085":Q+="\\N";break;case"00a0":Q+="\\_";break;case"2028":Q+="\\L";break;case"2029":Q+="\\P";break;default:if(H.substr(0,2)==="00")Q+="\\x"+H.substr(2);else Q+=Z.substr(W,6)}W+=5,z=W+1}break;case"n":if(G||Z[W+2]==='"'||Z.length +`;let Y,_;for(_=Z.length;_>0;--_){let M=Z[_-1];if(M!==` +`&&M!=="\t"&&M!==" ")break}let F=Z.substring(_),q=F.indexOf(` +`);if(q===-1)Y="-";else if(Z===F||q!==F.length-1){if(Y="+",U)U()}else Y="";if(F){if(Z=Z.slice(0,-F.length),F[F.length-1]===` +`)F=F.slice(0,-1);F=F.replace(I8,`$&${V}`)}let I=!1,D,L=-1;for(D=0;D{T=!0};let O=c0(`${B}${M}${F}`,V,_8,A);if(!T)return`>${j} +${V}${O}`}return Z=Z.replace(/\n+/g,`$&${V}`),`|${j} +${V}${B}${Z}${F}`}function O$($,X,Z,G){let{type:J,value:U}=$,{actualString:Q,implicitKey:z,indent:W,indentStep:V,inFlow:H}=X;if(z&&U.includes(` +`)||H&&/[[\]{},]/.test(U))return Q0(U,X);if(/^[\n\t ,[\]{}#&*!|>'"%@`]|^[?-]$|^[?-][ \t]|[\n:][ \t]|[ \t]\n|[\n\t ]#|[\n\t :]$/.test(U))return z||H||!U.includes(` +`)?Q0(U,X):m0($,X,Z,G);if(!z&&!H&&J!==R.PLAIN&&U.includes(` +`))return m0($,X,Z,G);if(s0(U)){if(W==="")return X.forceBlockIndent=!0,m0($,X,Z,G);else if(z&&W===V)return Q0(U,X)}let Y=U.replace(/\n+/g,`$& +${W}`);if(Q){let _=(I)=>I.default&&I.tag!=="tag:yaml.org,2002:str"&&I.test?.test(Y),{compat:F,tags:q}=X.doc.schema;if(q.some(_)||F?.some(_))return Q0(U,X)}return z?Y:c0(Y,W,O6,i0(X,!1))}function j8($,X,Z,G){let{implicitKey:J,inFlow:U}=X,Q=typeof $.value==="string"?$:Object.assign({},$,{value:String($.value)}),{type:z}=$;if(z!==R.QUOTE_DOUBLE){if(/[\x00-\x08\x0b-\x1f\x7f-\x9f\u{D800}-\u{DFFF}]/u.test(Q.value))z=R.QUOTE_DOUBLE}let W=(H)=>{switch(H){case R.BLOCK_FOLDED:case R.BLOCK_LITERAL:return J||U?Q0(Q.value,X):m0(Q,X,Z,G);case R.QUOTE_DOUBLE:return M0(Q.value,X);case R.QUOTE_SINGLE:return F8(Q.value,X);case R.PLAIN:return O$(Q,X,Z,G);default:return null}},V=W(z);if(V===null){let{defaultKeyType:H,defaultStringType:Y}=X.options,_=J&&H||Y;if(V=W(_),V===null)throw Error(`Unsupported default string type ${_}`)}return V}function B6($,X){let Z=Object.assign({blockQuote:!0,commentString:q$,defaultKeyType:null,defaultStringType:"PLAIN",directives:null,doubleQuotedAsJSON:!1,doubleQuotedMinMultiLineLength:40,falseStr:"false",flowCollectionPadding:!0,indentSeq:!0,lineWidth:80,minContentWidth:20,nullStr:"null",simpleKeys:!1,singleQuote:null,trailingComma:!1,trueStr:"true",verifyAliasOrder:!0},$.schema.toStringOptions,X),G;switch(Z.collectionStyle){case"block":G=!1;break;case"flow":G=!0;break;default:G=null}return{anchors:new Set,doc:$,flowCollectionPadding:Z.flowCollectionPadding?" ":"",indent:"",indentStep:typeof Z.indent==="number"?" ".repeat(Z.indent):" ",inFlow:G,options:Z}}function B$($,X){if(X.tag){let J=$.filter((U)=>U.tag===X.tag);if(J.length>0)return J.find((U)=>U.format===X.format)??J[0]}let Z=void 0,G;if(P(X)){G=X.value;let J=$.filter((U)=>U.identify?.(G));if(J.length>1){let U=J.filter((Q)=>Q.test);if(U.length>0)J=U}Z=J.find((U)=>U.format===X.format)??J.find((U)=>!U.format)}else G=X,Z=$.find((J)=>J.nodeClass&&G instanceof J.nodeClass);if(!Z){let J=G?.constructor?.name??(G===null?"null":typeof G);throw Error(`Tag not resolved for ${J} value`)}return Z}function K$($,X,{anchors:Z,doc:G}){if(!G.directives)return"";let J=[],U=(P($)||C($))&&$.anchor;if(U&&I6(U))Z.add(U),J.push(`&${U}`);let Q=$.tag??(X.default?null:X.tag);if(Q)J.push(G.directives.tagString(Q));return J.join(" ")}function W0($,X,Z,G){if(N($))return $.toString(X,Z,G);if(e($)){if(X.doc.directives)return $.toString(X);if(X.resolvedAliases?.has($))throw TypeError("Cannot stringify circular structure without alias nodes");else{if(X.resolvedAliases)X.resolvedAliases.add($);else X.resolvedAliases=new Set([$]);$=$.resolve(X.doc)}}let J=void 0,U=S($)?$:X.doc.createNode($,{onTagObj:(W)=>J=W});J??(J=B$(X.doc.schema.tags,U));let Q=K$(U,J,X);if(Q.length>0)X.indentAtStart=(X.indentAtStart??0)+Q.length+1;let z=typeof J.stringify==="function"?J.stringify(U,X,Z,G):P(U)?j8(U,X,Z,G):U.toString(X,Z,G);if(!Q)return z;return P(U)||z[0]==="{"||z[0]==="["?`${Q} ${z}`:`${Q} +${X.indent}${z}`}function j$({key:$,value:X},Z,G,J){let{allNullValues:U,doc:Q,indent:z,indentStep:W,options:{commentString:V,indentSeq:H,simpleKeys:Y}}=Z,_=S($)&&$.comment||null;if(Y){if(_)throw Error("With simple keys, key nodes cannot have comments");if(C($)||!S($)&&typeof $==="object")throw Error("With simple keys, collection cannot be used as a key value")}let F=!Y&&(!$||_&&X==null&&!Z.inFlow||C($)||(P($)?$.type===R.BLOCK_FOLDED||$.type===R.BLOCK_LITERAL:typeof $==="object"));Z=Object.assign({},Z,{allNullValues:!1,implicitKey:!F&&(Y||!U),indent:z+W});let q=!1,I=!1,D=W0($,Z,()=>q=!0,()=>I=!0);if(!F&&!Z.inFlow&&D.length>1024){if(Y)throw Error("With simple keys, single line scalar must not span more than 1024 characters");F=!0}if(Z.inFlow){if(U||X==null){if(q&&G)G();return D===""?"?":F?`? ${D}`:D}}else if(U&&!Y||X==null&&F){if(D=`? ${D}`,_&&!q)D+=r(D,Z.indent,V(_));else if(I&&J)J();return D}if(q)_=null;if(F){if(_)D+=r(D,Z.indent,V(_));D=`? ${D} +${z}:`}else if(D=`${D}:`,_)D+=r(D,Z.indent,V(_));let L,B,K;if(S(X))L=!!X.spaceBefore,B=X.commentBefore,K=X.comment;else if(L=!1,B=null,K=null,X&&typeof X==="object")X=Q.createNode(X);if(Z.implicitKey=!1,!F&&!_&&P(X))Z.indentAtStart=D.length+1;if(I=!1,!H&&W.length>=2&&!Z.inFlow&&!F&&P0(X)&&!X.flow&&!X.tag&&!X.anchor)Z.indent=Z.indent.substring(2);let j=!1,M=W0(X,Z,()=>j=!0,()=>I=!0),T=" ";if(_||L||B){if(T=L?` +`:"",B){let A=V(B);T+=` +${p(A,Z.indent)}`}if(M===""&&!Z.inFlow){if(T===` +`&&K)T=` + +`}else T+=` +${Z.indent}`}else if(!F&&C(X)){let A=M[0],O=M.indexOf(` +`),b=O!==-1,c=Z.inFlow??X.flow??X.items.length===0;if(b||!c){let $0=!1;if(b&&(A==="&"||A==="!")){let E=M.indexOf(" ");if(A==="&"&&E!==-1&&E$===w0||typeof $==="symbol"&&$.description===w0,default:"key",tag:"tag:yaml.org,2002:merge",test:/^<<$/,resolve:()=>Object.assign(new R(Symbol(w0)),{addToJSMap:K6}),stringify:()=>w0},T$=($,X)=>(l.identify(X)||P(X)&&(!X.type||X.type===R.PLAIN)&&l.identify(X.value))&&$?.doc.schema.tags.some((Z)=>Z.tag===l.tag&&Z.default);function K6($,X,Z){if(Z=$&&e(Z)?Z.resolve($.doc):Z,P0(Z))for(let G of Z.items)J8($,X,G);else if(Array.isArray(Z))for(let G of Z)J8($,X,G);else J8($,X,Z)}function J8($,X,Z){let G=$&&e(Z)?Z.resolve($.doc):Z;if(!A0(G))throw Error("Merge sources must be maps or map aliases");let J=G.toJSON(null,$,Map);for(let[U,Q]of J)if(X instanceof Map){if(!X.has(U))X.set(U,Q)}else if(X instanceof Set)X.add(U);else if(!Object.prototype.hasOwnProperty.call(X,U))Object.defineProperty(X,U,{value:Q,writable:!0,enumerable:!0,configurable:!0});return X}function j6($,X,{key:Z,value:G}){if(S(Z)&&Z.addToJSMap)Z.addToJSMap($,X,G);else if(T$($,Z))K6($,X,G);else{let J=g(Z,"",$);if(X instanceof Map)X.set(J,g(G,J,$));else if(X instanceof Set)X.add(J);else{let U=R$(Z,J,$),Q=g(G,U,$);if(U in X)Object.defineProperty(X,U,{value:Q,writable:!0,enumerable:!0,configurable:!0});else X[U]=Q}}return X}function R$($,X,Z){if(X===null)return"";if(typeof X!=="object")return String(X);if(S($)&&Z?.doc){let G=B6(Z.doc,{});G.anchors=new Set;for(let U of Z.anchors.keys())G.anchors.add(U.anchor);G.inFlow=!0,G.inStringifyKey=!0;let J=$.toString(G);if(!Z.mapKeyWarned){let U=JSON.stringify(J);if(U.length>40)U=U.substring(0,36)+'..."';M$(Z.doc.options.logLevel,`Keys with collection values will be stringified due to JS Object restrictions: ${U}. Set mapAsMap: true to use object keys.`),Z.mapKeyWarned=!0}return J}return JSON.stringify(X)}function M8($,X,Z){let G=T0($,void 0,Z),J=T0(X,void 0,Z);return new f(G,J)}class f{constructor($,X=null){Object.defineProperty(this,v,{value:V6}),this.key=$,this.value=X}clone($){let{key:X,value:Z}=this;if(S(X))X=X.clone($);if(S(Z))Z=Z.clone($);return new f(X,Z)}toJSON($,X){let Z=X?.mapAsMap?new Map:{};return j6(X,Z,this)}toString($,X,Z){return $?.doc?j$(this,$,X,Z):JSON.stringify(this)}}function M6($,X,Z){return(X.inFlow??$.flow?P$:A$)($,X,Z)}function A$({comment:$,items:X},Z,{blockItemPrefix:G,flowChars:J,itemIndent:U,onChompKeep:Q,onComment:z}){let{indent:W,options:{commentString:V}}=Z,H=Object.assign({},Z,{indent:U,type:null}),Y=!1,_=[];for(let q=0;qD=null,()=>Y=!0);if(D)L+=r(L,U,V(D));if(Y&&D)Y=!1;_.push(G+L)}let F;if(_.length===0)F=J.start+J.end;else{F=_[0];for(let q=1;q<_.length;++q){let I=_[q];F+=I?` +${W}${I}`:` +`}}if($){if(F+=` +`+p(V($),W),z)z()}else if(Y&&Q)Q();return F}function P$({items:$},X,{flowChars:Z,itemIndent:G}){let{indent:J,indentStep:U,flowCollectionPadding:Q,options:{commentString:z}}=X;G+=U;let W=Object.assign({},X,{indent:G,inFlow:!0,type:null}),V=!1,H=0,Y=[];for(let q=0;q<$.length;++q){let I=$[q],D=null;if(S(I)){if(I.spaceBefore)Y.push("");if(d0(X,Y,I.commentBefore,!1),I.comment)D=I.comment}else if(N(I)){let B=S(I.key)?I.key:null;if(B){if(B.spaceBefore)Y.push("");if(d0(X,Y,B.commentBefore,!1),B.comment)V=!0}let K=S(I.value)?I.value:null;if(K){if(K.comment)D=K.comment;if(K.commentBefore)V=!0}else if(I.value==null&&B?.comment)D=B.comment}if(D)V=!0;let L=W0(I,W,()=>D=null);if(V||(V=Y.length>H||L.includes(` +`)),q<$.length-1)L+=",";else if(X.options.trailingComma){if(X.options.lineWidth>0)V||(V=Y.reduce((B,K)=>B+K.length+2,2)+(L.length+2)>X.options.lineWidth);if(V)L+=","}if(D)L+=r(L,G,z(D));Y.push(L),H=Y.length}let{start:_,end:F}=Z;if(Y.length===0)return _+F;else{if(!V){let q=Y.reduce((I,D)=>I+D.length+2,2);V=X.options.lineWidth>0&&q>X.options.lineWidth}if(V){let q=_;for(let I of Y)q+=I?` +${U}${J}${I}`:` +`;return`${q} +${J}${F}`}else return`${_}${Q}${Y.join(" ")}${Q}${F}`}}function d0({indent:$,options:{commentString:X}},Z,G,J){if(G&&J)G=G.replace(/^\n+/,"");if(G){let U=p(X(G),$);Z.push(U.trimStart())}}function o($,X){let Z=P(X)?X.value:X;for(let G of $)if(N(G)){if(G.key===X||G.key===Z)return G;if(P(G.key)&&G.key.value===Z)return G}return}class h extends K8{static get tagName(){return"tag:yaml.org,2002:map"}constructor($){super(n,$);this.items=[]}static from($,X,Z){let{keepUndefined:G,replacer:J}=Z,U=new this($),Q=(z,W)=>{if(typeof J==="function")W=J.call(X,z,W);else if(Array.isArray(J)&&!J.includes(z))return;if(W!==void 0||G)U.items.push(M8(z,W,Z))};if(X instanceof Map)for(let[z,W]of X)Q(z,W);else if(X&&typeof X==="object")for(let z of Object.keys(X))Q(z,X[z]);if(typeof $.sortMapEntries==="function")U.items.sort($.sortMapEntries);return U}add($,X){let Z;if(N($))Z=$;else if(!$||typeof $!=="object"||!("key"in $))Z=new f($,$?.value);else Z=new f($.key,$.value);let G=o(this.items,Z.key),J=this.schema?.sortMapEntries;if(G){if(!X)throw Error(`Key ${Z.key} already set`);if(P(G.value)&&L6(Z.value))G.value.value=Z.value;else G.value=Z.value}else if(J){let U=this.items.findIndex((Q)=>J(Z,Q)<0);if(U===-1)this.items.push(Z);else this.items.splice(U,0,Z)}else this.items.push(Z)}delete($){let X=o(this.items,$);if(!X)return!1;return this.items.splice(this.items.indexOf(X),1).length>0}get($,X){let G=o(this.items,$)?.value;return(!X&&P(G)?G.value:G)??void 0}has($){return!!o(this.items,$)}set($,X){this.add(new f($,X),!0)}toJSON($,X,Z){let G=Z?new Z:X?.mapAsMap?new Map:{};if(X?.onCreate)X.onCreate(G);for(let J of this.items)j6(X,G,J);return G}toString($,X,Z){if(!$)return JSON.stringify(this);for(let G of this.items)if(!N(G))throw Error(`Map items must all be pairs; found ${JSON.stringify(G)} instead`);if(!$.allNullValues&&this.hasAllNullValues(!1))$=Object.assign({},$,{allNullValues:!0});return M6(this,$,{blockItemPrefix:"",flowChars:{start:"{",end:"}"},itemIndent:$.indent||"",onChompKeep:Z,onComment:X})}}var D0={collection:"map",default:!0,nodeClass:h,tag:"tag:yaml.org,2002:map",resolve($,X){if(!A0($))X("Expected a mapping for this tag");return $},createNode:($,X,Z)=>h.from($,X,Z)};class a extends K8{static get tagName(){return"tag:yaml.org,2002:seq"}constructor($){super(_0,$);this.items=[]}add($){this.items.push($)}delete($){let X=k0($);if(typeof X!=="number")return!1;return this.items.splice(X,1).length>0}get($,X){let Z=k0($);if(typeof Z!=="number")return;let G=this.items[Z];return!X&&P(G)?G.value:G}has($){let X=k0($);return typeof X==="number"&&X=0?X:null}var q0={collection:"seq",default:!0,nodeClass:a,tag:"tag:yaml.org,2002:seq",resolve($,X){if(!P0($))X("Expected a sequence for this tag");return $},createNode:($,X,Z)=>a.from($,X,Z)},n0={identify:($)=>typeof $==="string",default:!0,tag:"tag:yaml.org,2002:str",resolve:($)=>$,stringify($,X,Z,G){return X=Object.assign({actualString:!0},X),j8($,X,Z,G)}},a0={identify:($)=>$==null,createNode:()=>new R(null),default:!0,tag:"tag:yaml.org,2002:null",test:/^(?:~|[Nn]ull|NULL)?$/,resolve:()=>new R(null),stringify:({source:$},X)=>typeof $==="string"&&a0.test.test($)?$:X.options.nullStr},T8={identify:($)=>typeof $==="boolean",default:!0,tag:"tag:yaml.org,2002:bool",test:/^(?:[Tt]rue|TRUE|[Ff]alse|FALSE)$/,resolve:($)=>new R($[0]==="t"||$[0]==="T"),stringify({source:$,value:X},Z){if($&&T8.test.test($)){let G=$[0]==="t"||$[0]==="T";if(X===G)return $}return X?Z.options.trueStr:Z.options.falseStr}};function m({format:$,minFractionDigits:X,tag:Z,value:G}){if(typeof G==="bigint")return String(G);let J=typeof G==="number"?G:Number(G);if(!isFinite(J))return isNaN(J)?".nan":J<0?"-.inf":".inf";let U=Object.is(G,-0)?"-0":JSON.stringify(G);if(!$&&X&&(!Z||Z==="tag:yaml.org,2002:float")&&/^\d/.test(U)){let Q=U.indexOf(".");if(Q<0)Q=U.length,U+=".";let z=X-(U.length-Q-1);while(z-- >0)U+="0"}return U}var T6={identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",test:/^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/,resolve:($)=>$.slice(-3).toLowerCase()==="nan"?NaN:$[0]==="-"?Number.NEGATIVE_INFINITY:Number.POSITIVE_INFINITY,stringify:m},R6={identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",format:"EXP",test:/^[-+]?(?:\.[0-9]+|[0-9]+(?:\.[0-9]*)?)[eE][-+]?[0-9]+$/,resolve:($)=>parseFloat($),stringify($){let X=Number($.value);return isFinite(X)?X.toExponential():m($)}},A6={identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",test:/^[-+]?(?:\.[0-9]+|[0-9]+\.[0-9]*)$/,resolve($){let X=new R(parseFloat($)),Z=$.indexOf(".");if(Z!==-1&&$[$.length-1]==="0")X.minFractionDigits=$.length-Z-1;return X},stringify:m},r0=($)=>typeof $==="bigint"||Number.isInteger($),R8=($,X,Z,{intAsBigInt:G})=>G?BigInt($):parseInt($.substring(X),Z);function P6($,X,Z){let{value:G}=$;if(r0(G)&&G>=0)return Z+G.toString(X);return m($)}var b6={identify:($)=>r0($)&&$>=0,default:!0,tag:"tag:yaml.org,2002:int",format:"OCT",test:/^0o[0-7]+$/,resolve:($,X,Z)=>R8($,2,8,Z),stringify:($)=>P6($,8,"0o")},N6={identify:r0,default:!0,tag:"tag:yaml.org,2002:int",test:/^[-+]?[0-9]+$/,resolve:($,X,Z)=>R8($,0,10,Z),stringify:m},C6={identify:($)=>r0($)&&$>=0,default:!0,tag:"tag:yaml.org,2002:int",format:"HEX",test:/^0x[0-9a-fA-F]+$/,resolve:($,X,Z)=>R8($,2,16,Z),stringify:($)=>P6($,16,"0x")},b$=[D0,q0,n0,a0,T8,b6,N6,C6,T6,R6,A6];function m8($){return typeof $==="bigint"||Number.isInteger($)}var f0=({value:$})=>JSON.stringify($),N$=[{identify:($)=>typeof $==="string",default:!0,tag:"tag:yaml.org,2002:str",resolve:($)=>$,stringify:f0},{identify:($)=>$==null,createNode:()=>new R(null),default:!0,tag:"tag:yaml.org,2002:null",test:/^null$/,resolve:()=>null,stringify:f0},{identify:($)=>typeof $==="boolean",default:!0,tag:"tag:yaml.org,2002:bool",test:/^true$|^false$/,resolve:($)=>$==="true",stringify:f0},{identify:m8,default:!0,tag:"tag:yaml.org,2002:int",test:/^-?(?:0|[1-9][0-9]*)$/,resolve:($,X,{intAsBigInt:Z})=>Z?BigInt($):parseInt($,10),stringify:({value:$})=>m8($)?$.toString():JSON.stringify($)},{identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",test:/^-?(?:0|[1-9][0-9]*)(?:\.[0-9]*)?(?:[eE][-+]?[0-9]+)?$/,resolve:($)=>parseFloat($),stringify:f0}],C$={default:!0,tag:"",test:/^/,resolve($,X){return X(`Unresolved plain scalar ${JSON.stringify($)}`),$}},S$=[D0,q0].concat(N$,C$),A8={identify:($)=>$ instanceof Uint8Array,default:!1,tag:"tag:yaml.org,2002:binary",resolve($,X){if(typeof atob==="function"){let Z=atob($.replace(/[\n\r]/g,"")),G=new Uint8Array(Z.length);for(let J=0;J1)X("Each pair must have its own sequence indicator");let J=G.items[0]||new f(new R(null));if(G.commentBefore)J.key.commentBefore=J.key.commentBefore?`${G.commentBefore} +${J.key.commentBefore}`:G.commentBefore;if(G.comment){let U=J.value??J.key;U.comment=U.comment?`${G.comment} +${U.comment}`:G.comment}G=J}$.items[Z]=N(G)?G:new f(G)}else X("Expected a sequence for this tag");return $}function E6($,X,Z){let{replacer:G}=Z,J=new a($);J.tag="tag:yaml.org,2002:pairs";let U=0;if(X&&Symbol.iterator in Object(X))for(let Q of X){if(typeof G==="function")Q=G.call(X,String(U++),Q);let z,W;if(Array.isArray(Q))if(Q.length===2)z=Q[0],W=Q[1];else throw TypeError(`Expected [key, value] tuple: ${Q}`);else if(Q&&Q instanceof Object){let V=Object.keys(Q);if(V.length===1)z=V[0],W=Q[z];else throw TypeError(`Expected tuple with one key, not ${V.length} keys`)}else z=Q;J.items.push(M8(z,W,Z))}return J}var P8={collection:"seq",default:!1,tag:"tag:yaml.org,2002:pairs",resolve:S6,createNode:E6};class z0 extends a{constructor(){super();this.add=h.prototype.add.bind(this),this.delete=h.prototype.delete.bind(this),this.get=h.prototype.get.bind(this),this.has=h.prototype.has.bind(this),this.set=h.prototype.set.bind(this),this.tag=z0.tag}toJSON($,X){if(!X)return super.toJSON($);let Z=new Map;if(X?.onCreate)X.onCreate(Z);for(let G of this.items){let J,U;if(N(G))J=g(G.key,"",X),U=g(G.value,J,X);else J=g(G,"",X);if(Z.has(J))throw Error("Ordered maps must not include duplicate keys");Z.set(J,U)}return Z}static from($,X,Z){let G=E6($,X,Z),J=new this;return J.items=G.items,J}}z0.tag="tag:yaml.org,2002:omap";var b8={collection:"seq",identify:($)=>$ instanceof Map,nodeClass:z0,default:!1,tag:"tag:yaml.org,2002:omap",resolve($,X){let Z=S6($,X),G=[];for(let{key:J}of Z.items)if(P(J))if(G.includes(J.value))X(`Ordered maps must not include duplicate keys: ${J.value}`);else G.push(J.value);return Object.assign(new z0,Z)},createNode:($,X,Z)=>z0.from($,X,Z)};function w6({value:$,source:X},Z){if(X&&($?k6:f6).test.test(X))return X;return $?Z.options.trueStr:Z.options.falseStr}var k6={identify:($)=>$===!0,default:!0,tag:"tag:yaml.org,2002:bool",test:/^(?:Y|y|[Yy]es|YES|[Tt]rue|TRUE|[Oo]n|ON)$/,resolve:()=>new R(!0),stringify:w6},f6={identify:($)=>$===!1,default:!0,tag:"tag:yaml.org,2002:bool",test:/^(?:N|n|[Nn]o|NO|[Ff]alse|FALSE|[Oo]ff|OFF)$/,resolve:()=>new R(!1),stringify:w6},E$={identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",test:/^(?:[-+]?\.(?:inf|Inf|INF)|\.nan|\.NaN|\.NAN)$/,resolve:($)=>$.slice(-3).toLowerCase()==="nan"?NaN:$[0]==="-"?Number.NEGATIVE_INFINITY:Number.POSITIVE_INFINITY,stringify:m},w$={identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",format:"EXP",test:/^[-+]?(?:[0-9][0-9_]*)?(?:\.[0-9_]*)?[eE][-+]?[0-9]+$/,resolve:($)=>parseFloat($.replace(/_/g,"")),stringify($){let X=Number($.value);return isFinite(X)?X.toExponential():m($)}},k$={identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",test:/^[-+]?(?:[0-9][0-9_]*)?\.[0-9_]*$/,resolve($){let X=new R(parseFloat($.replace(/_/g,""))),Z=$.indexOf(".");if(Z!==-1){let G=$.substring(Z+1).replace(/_/g,"");if(G[G.length-1]==="0")X.minFractionDigits=G.length}return X},stringify:m},b0=($)=>typeof $==="bigint"||Number.isInteger($);function o0($,X,Z,{intAsBigInt:G}){let J=$[0];if(J==="-"||J==="+")X+=1;if($=$.substring(X).replace(/_/g,""),G){switch(Z){case 2:$=`0b${$}`;break;case 8:$=`0o${$}`;break;case 16:$=`0x${$}`;break}let Q=BigInt($);return J==="-"?BigInt(-1)*Q:Q}let U=parseInt($,Z);return J==="-"?-1*U:U}function N8($,X,Z){let{value:G}=$;if(b0(G)){let J=G.toString(X);return G<0?"-"+Z+J.substr(1):Z+J}return m($)}var f$={identify:b0,default:!0,tag:"tag:yaml.org,2002:int",format:"BIN",test:/^[-+]?0b[0-1_]+$/,resolve:($,X,Z)=>o0($,2,2,Z),stringify:($)=>N8($,2,"0b")},y$={identify:b0,default:!0,tag:"tag:yaml.org,2002:int",format:"OCT",test:/^[-+]?0[0-7_]+$/,resolve:($,X,Z)=>o0($,1,8,Z),stringify:($)=>N8($,8,"0")},h$={identify:b0,default:!0,tag:"tag:yaml.org,2002:int",test:/^[-+]?[0-9][0-9_]*$/,resolve:($,X,Z)=>o0($,0,10,Z),stringify:m},g$={identify:b0,default:!0,tag:"tag:yaml.org,2002:int",format:"HEX",test:/^[-+]?0x[0-9a-fA-F_]+$/,resolve:($,X,Z)=>o0($,2,16,Z),stringify:($)=>N8($,16,"0x")};class V0 extends h{constructor($){super($);this.tag=V0.tag}add($){let X;if(N($))X=$;else if($&&typeof $==="object"&&"key"in $&&"value"in $&&$.value===null)X=new f($.key,null);else X=new f($,null);if(!o(this.items,X.key))this.items.push(X)}get($,X){let Z=o(this.items,$);return!X&&N(Z)?P(Z.key)?Z.key.value:Z.key:Z}set($,X){if(typeof X!=="boolean")throw Error(`Expected boolean value for set(key, value) in a YAML set, not ${typeof X}`);let Z=o(this.items,$);if(Z&&!X)this.items.splice(this.items.indexOf(Z),1);else if(!Z&&X)this.items.push(new f($))}toJSON($,X){return super.toJSON($,X,Set)}toString($,X,Z){if(!$)return JSON.stringify(this);if(this.hasAllNullValues(!0))return super.toString(Object.assign({},$,{allNullValues:!0}),X,Z);else throw Error("Set items must all have null values")}static from($,X,Z){let{replacer:G}=Z,J=new this($);if(X&&Symbol.iterator in Object(X))for(let U of X){if(typeof G==="function")U=G.call(X,U,U);J.items.push(M8(U,null,Z))}return J}}V0.tag="tag:yaml.org,2002:set";var C8={collection:"map",identify:($)=>$ instanceof Set,nodeClass:V0,default:!1,tag:"tag:yaml.org,2002:set",createNode:($,X,Z)=>V0.from($,X,Z),resolve($,X){if(A0($))if($.hasAllNullValues(!0))return Object.assign(new V0,$);else X("Set items must all have null values");else X("Expected a mapping for this tag");return $}};function S8($,X){let Z=$[0],G=Z==="-"||Z==="+"?$.substring(1):$,J=(Q)=>X?BigInt(Q):Number(Q),U=G.replace(/_/g,"").split(":").reduce((Q,z)=>Q*J(60)+J(z),J(0));return Z==="-"?J(-1)*U:U}function y6($){let{value:X}=$,Z=(Q)=>Q;if(typeof X==="bigint")Z=(Q)=>BigInt(Q);else if(isNaN(X)||!isFinite(X))return m($);let G="";if(X<0)G="-",X*=Z(-1);let J=Z(60),U=[X%J];if(X<60)U.unshift(0);else if(X=(X-U[0])/J,U.unshift(X%J),X>=60)X=(X-U[0])/J,U.unshift(X);return G+U.map((Q)=>String(Q).padStart(2,"0")).join(":").replace(/000000\d*$/,"")}var h6={identify:($)=>typeof $==="bigint"||Number.isInteger($),default:!0,tag:"tag:yaml.org,2002:int",format:"TIME",test:/^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+$/,resolve:($,X,{intAsBigInt:Z})=>S8($,Z),stringify:y6},g6={identify:($)=>typeof $==="number",default:!0,tag:"tag:yaml.org,2002:float",format:"TIME",test:/^[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\.[0-9_]*$/,resolve:($)=>S8($,!1),stringify:y6},t0={identify:($)=>$ instanceof Date,default:!0,tag:"tag:yaml.org,2002:timestamp",test:RegExp("^([0-9]{4})-([0-9]{1,2})-([0-9]{1,2})(?:(?:t|T|[ \\t]+)([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2}(\\.[0-9]+)?)(?:[ \\t]*(Z|[-+][012]?[0-9](?::[0-9]{2})?))?)?$"),resolve($){let X=$.match(t0.test);if(!X)throw Error("!!timestamp expects a date, starting with yyyy-mm-dd");let[,Z,G,J,U,Q,z]=X.map(Number),W=X[7]?Number((X[7]+"00").substr(1,3)):0,V=Date.UTC(Z,G-1,J,U||0,Q||0,z||0,W),H=X[8];if(H&&H!=="Z"){let Y=S8(H,!1);if(Math.abs(Y)<30)Y*=60;V-=60000*Y}return new Date(V)},stringify:({value:$})=>$?.toISOString().replace(/(T00:00:00)?\.000Z$/,"")??""},u8=[D0,q0,n0,a0,k6,f6,f$,y$,h$,g$,E$,w$,k$,A8,l,b8,P8,C8,h6,g6,t0],d8=new Map([["core",b$],["failsafe",[D0,q0,n0]],["json",S$],["yaml11",u8],["yaml-1.1",u8]]),p8={binary:A8,bool:T8,float:A6,floatExp:R6,floatNaN:T6,floatTime:g6,int:N6,intHex:C6,intOct:b6,intTime:h6,map:D0,merge:l,null:a0,omap:b8,pairs:P8,seq:q0,set:C8,timestamp:t0},v$={"tag:yaml.org,2002:binary":A8,"tag:yaml.org,2002:merge":l,"tag:yaml.org,2002:omap":b8,"tag:yaml.org,2002:pairs":P8,"tag:yaml.org,2002:set":C8,"tag:yaml.org,2002:timestamp":t0};function U8($,X,Z){let G=d8.get(X);if(G&&!$)return Z&&!G.includes(l)?G.concat(l):G.slice();let J=G;if(!J)if(Array.isArray($))J=[];else{let U=Array.from(d8.keys()).filter((Q)=>Q!=="yaml11").map((Q)=>JSON.stringify(Q)).join(", ");throw Error(`Unknown schema "${X}"; use one of ${U} or define customTags array`)}if(Array.isArray($))for(let U of $)J=J.concat(U);else if(typeof $==="function")J=$(J.slice());if(Z)J=J.concat(l);return J.reduce((U,Q)=>{let z=typeof Q==="string"?p8[Q]:Q;if(!z){let W=JSON.stringify(Q),V=Object.keys(p8).map((H)=>JSON.stringify(H)).join(", ");throw Error(`Unknown custom tag ${W}; use one of ${V}`)}if(!U.includes(z))U.push(z);return U},[])}var x$=($,X)=>$.keyX.key?1:0;class E8{constructor({compat:$,customTags:X,merge:Z,resolveKnownTags:G,schema:J,sortMapEntries:U,toStringDefaults:Q}){this.compat=Array.isArray($)?U8($,"compat"):$?U8(null,$):null,this.name=typeof J==="string"&&J||"core",this.knownTags=G?v$:{},this.tags=U8(X,this.name,Z),this.toStringOptions=Q??null,Object.defineProperty(this,n,{value:D0}),Object.defineProperty(this,d,{value:n0}),Object.defineProperty(this,_0,{value:q0}),this.sortMapEntries=typeof U==="function"?U:U===!0?x$:null}clone(){let $=Object.create(E8.prototype,Object.getOwnPropertyDescriptors(this));return $.tags=this.tags.slice(),$}}function m$($,X){let Z=[],G=X.directives===!0;if(X.directives!==!1&&$.directives){let W=$.directives.toString($);if(W)Z.push(W),G=!0;else if($.directives.docStart)G=!0}if(G)Z.push("---");let J=B6($,X),{commentString:U}=J.options;if($.commentBefore){if(Z.length!==1)Z.unshift("");let W=U($.commentBefore);Z.unshift(p(W,""))}let Q=!1,z=null;if($.contents){if(S($.contents)){if($.contents.spaceBefore&&G)Z.push("");if($.contents.commentBefore){let H=U($.contents.commentBefore);Z.push(p(H,""))}J.forceBlockIndent=!!$.comment,z=$.contents.comment}let W=z?void 0:()=>Q=!0,V=W0($.contents,J,()=>z=null,W);if(z)V+=r(V,"",U(z));if((V[0]==="|"||V[0]===">")&&Z[Z.length-1]==="---")Z[Z.length-1]=`--- ${V}`;else Z.push(V)}else Z.push(W0($.contents,J));if($.directives?.docEnd)if($.comment){let W=U($.comment);if(W.includes(` +`))Z.push("..."),Z.push(p(W,""));else Z.push(`... ${W}`)}else Z.push("...");else{let W=$.comment;if(W&&Q)W=W.replace(/^\n+/,"");if(W){if((!Q||z)&&Z[Z.length-1]!=="")Z.push("");Z.push(p(U(W),""))}}return Z.join(` +`)+` +`}class N0{constructor($,X,Z){this.commentBefore=null,this.comment=null,this.errors=[],this.warnings=[],Object.defineProperty(this,v,{value:H8});let G=null;if(typeof X==="function"||Array.isArray(X))G=X;else if(Z===void 0&&X)Z=X,X=void 0;let J=Object.assign({intAsBigInt:!1,keepSourceTokens:!1,logLevel:"warn",prettyErrors:!0,strict:!0,stringKeys:!1,uniqueKeys:!0,version:"1.2"},Z);this.options=J;let{version:U}=J;if(Z?._directives){if(this.directives=Z._directives.atDocument(),this.directives.yaml.explicit)U=this.directives.yaml.version}else this.directives=new k({version:U});this.setSchema(U,Z),this.contents=$===void 0?null:this.createNode($,G,Z)}clone(){let $=Object.create(N0.prototype,{[v]:{value:H8}});if($.commentBefore=this.commentBefore,$.comment=this.comment,$.errors=this.errors.slice(),$.warnings=this.warnings.slice(),$.options=Object.assign({},this.options),this.directives)$.directives=this.directives.clone();if($.schema=this.schema.clone(),$.contents=S(this.contents)?this.contents.clone($.schema):this.contents,this.range)$.range=this.range.slice();return $}add($){if(X0(this.contents))this.contents.add($)}addIn($,X){if(X0(this.contents))this.contents.addIn($,X)}createAlias($,X){if(!$.anchor){let Z=D6(this);$.anchor=!X||Z.has(X)?q6(X||"a",Z):X}return new l0($.anchor)}createNode($,X,Z){let G=void 0;if(typeof X==="function")$=X.call({"":$},"",$),G=X;else if(Array.isArray(X)){let I=(L)=>typeof L==="number"||L instanceof String||L instanceof Number,D=X.filter(I).map(String);if(D.length>0)X=X.concat(D);G=X}else if(Z===void 0&&X)Z=X,X=void 0;let{aliasDuplicateObjects:J,anchorPrefix:U,flow:Q,keepUndefined:z,onTagObj:W,tag:V}=Z??{},{onAnchor:H,setAnchors:Y,sourceObjects:_}=F$(this,U||"a"),F={aliasDuplicateObjects:J??!0,keepUndefined:z??!1,onAnchor:H,onTagObj:W,replacer:G,schema:this.schema,sourceObjects:_},q=T0($,V,F);if(Q&&C(q))q.flow=!0;return Y(),q}createPair($,X,Z={}){let G=this.createNode($,null,Z),J=this.createNode(X,null,Z);return new f(G,J)}delete($){return X0(this.contents)?this.contents.delete($):!1}deleteIn($){if(K0($)){if(this.contents==null)return!1;return this.contents=null,!0}return X0(this.contents)?this.contents.deleteIn($):!1}get($,X){return C(this.contents)?this.contents.get($,X):void 0}getIn($,X){if(K0($))return!X&&P(this.contents)?this.contents.value:this.contents;return C(this.contents)?this.contents.getIn($,X):void 0}has($){return C(this.contents)?this.contents.has($):!1}hasIn($){if(K0($))return this.contents!==void 0;return C(this.contents)?this.contents.hasIn($):!1}set($,X){if(this.contents==null)this.contents=u0(this.schema,[$],X);else if(X0(this.contents))this.contents.set($,X)}setIn($,X){if(K0($))this.contents=X;else if(this.contents==null)this.contents=u0(this.schema,Array.from($),X);else if(X0(this.contents))this.contents.setIn($,X)}setSchema($,X={}){if(typeof $==="number")$=String($);let Z;switch($){case"1.1":if(this.directives)this.directives.yaml.version="1.1";else this.directives=new k({version:"1.1"});Z={resolveKnownTags:!1,schema:"yaml-1.1"};break;case"1.2":case"next":if(this.directives)this.directives.yaml.version=$;else this.directives=new k({version:$});Z={resolveKnownTags:!0,schema:"core"};break;case null:if(this.directives)delete this.directives;Z=null;break;default:{let G=JSON.stringify($);throw Error(`Expected '1.1', '1.2' or null as first argument, but found: ${G}`)}}if(X.schema instanceof Object)this.schema=X.schema;else if(Z)this.schema=new E8(Object.assign(Z,X));else throw Error("With a null YAML version, the { schema: Schema } option is required")}toJS({json:$,jsonArg:X,mapAsMap:Z,maxAliasCount:G,onAnchor:J,reviver:U}={}){let Q={anchors:new Map,doc:this,keep:!$,mapAsMap:Z===!0,mapKeyWarned:!1,maxAliasCount:typeof G==="number"?G:100},z=g(this.contents,X??"",Q);if(typeof J==="function")for(let{count:W,res:V}of Q.anchors.values())J(V,W);return typeof U==="function"?U0(U,{"":z},"",z):z}toJSON($,X){return this.toJS({json:!0,jsonArg:$,mapAsMap:!1,onAnchor:X})}toString($={}){if(this.errors.length>0)throw Error("Document with errors cannot be stringified");if("indent"in $&&(!Number.isInteger($.indent)||Number($.indent)<=0)){let X=JSON.stringify($.indent);throw Error(`"indent" option must be a positive integer, not ${X}`)}return m$(this,$)}}function X0($){if(C($))return!0;throw Error("Expected a YAML collection as document contents")}class w8 extends Error{constructor($,X,Z,G){super();this.name=$,this.code=Z,this.message=G,this.pos=X}}class j0 extends w8{constructor($,X,Z){super("YAMLParseError",$,X,Z)}}class v6 extends w8{constructor($,X,Z){super("YAMLWarning",$,X,Z)}}var l8=($,X)=>(Z)=>{if(Z.pos[0]===-1)return;Z.linePos=Z.pos.map((z)=>X.linePos(z));let{line:G,col:J}=Z.linePos[0];Z.message+=` at line ${G}, column ${J}`;let U=J-1,Q=$.substring(X.lineStarts[G-1],X.lineStarts[G]).replace(/[\n\r]+$/,"");if(U>=60&&Q.length>80){let z=Math.min(U-39,Q.length-79);Q="…"+Q.substring(z),U-=z-1}if(Q.length>80)Q=Q.substring(0,79)+"…";if(G>1&&/^ *$/.test(Q.substring(0,U))){let z=$.substring(X.lineStarts[G-2],X.lineStarts[G-1]);if(z.length>80)z=z.substring(0,79)+`… +`;Q=z+Q}if(/[^ ]/.test(Q)){let z=1,W=Z.linePos[1];if(W?.line===G&&W.col>J)z=Math.max(1,Math.min(W.col-J,80-U));let V=" ".repeat(U)+"^".repeat(z);Z.message+=`: + +${Q} +${V} +`}};function Y0($,{flow:X,indicator:Z,next:G,offset:J,onError:U,parentIndent:Q,startOnNewline:z}){let W=!1,V=z,H=z,Y="",_="",F=!1,q=!1,I=null,D=null,L=null,B=null,K=null,j=null,M=null;for(let O of $){if(q){if(O.type!=="space"&&O.type!=="newline"&&O.type!=="comma")U(O.offset,"MISSING_CHAR","Tags and anchors must be separated from the next token by white space");q=!1}if(I){if(V&&O.type!=="comment"&&O.type!=="newline")U(I,"TAB_AS_INDENT","Tabs are not allowed as indentation");I=null}switch(O.type){case"space":if(!X&&(Z!=="doc-start"||G?.type!=="flow-collection")&&O.source.includes("\t"))I=O;H=!0;break;case"comment":{if(!H)U(O,"MISSING_CHAR","Comments must be separated from other tokens by white space characters");let b=O.source.substring(1)||" ";if(!Y)Y=b;else Y+=_+b;_="",V=!1;break}case"newline":if(V){if(Y)Y+=O.source;else if(!j||Z!=="seq-item-ind")W=!0}else _+=O.source;if(V=!0,F=!0,D||L)B=O;H=!0;break;case"anchor":if(D)U(O,"MULTIPLE_ANCHORS","A node can have at most one anchor");if(O.source.endsWith(":"))U(O.offset+O.source.length-1,"BAD_ALIAS","Anchor ending in : is ambiguous",!0);D=O,M??(M=O.offset),V=!1,H=!1,q=!0;break;case"tag":{if(L)U(O,"MULTIPLE_TAGS","A node can have at most one tag");L=O,M??(M=O.offset),V=!1,H=!1,q=!0;break}case Z:if(D||L)U(O,"BAD_PROP_ORDER",`Anchors and tags must be after the ${O.source} indicator`);if(j)U(O,"UNEXPECTED_TOKEN",`Unexpected ${O.source} in ${X??"collection"}`);j=O,V=Z==="seq-item-ind"||Z==="explicit-key-ind",H=!1;break;case"comma":if(X){if(K)U(O,"UNEXPECTED_TOKEN",`Unexpected , in ${X}`);K=O,V=!1,H=!1;break}default:U(O,"UNEXPECTED_TOKEN",`Unexpected ${O.type} token`),V=!1,H=!1}}let T=$[$.length-1],A=T?T.offset+T.source.length:J;if(q&&G&&G.type!=="space"&&G.type!=="newline"&&G.type!=="comma"&&(G.type!=="scalar"||G.source!==""))U(G.offset,"MISSING_CHAR","Tags and anchors must be separated from the next token by white space");if(I&&(V&&I.indent<=Q||G?.type==="block-map"||G?.type==="block-seq"))U(I,"TAB_AS_INDENT","Tabs are not allowed as indentation");return{comma:K,found:j,spaceBefore:W,comment:Y,hasNewline:F,anchor:D,tag:L,newlineAfterProp:B,end:A,start:M??A}}function R0($){if(!$)return null;switch($.type){case"alias":case"scalar":case"double-quoted-scalar":case"single-quoted-scalar":if($.source.includes(` +`))return!0;if($.end){for(let X of $.end)if(X.type==="newline")return!0}return!1;case"flow-collection":for(let X of $.items){for(let Z of X.start)if(Z.type==="newline")return!0;if(X.sep){for(let Z of X.sep)if(Z.type==="newline")return!0}if(R0(X.key)||R0(X.value))return!0}return!1;default:return!0}}function D8($,X,Z){if(X?.type==="flow-collection"){let G=X.end[0];if(G.indent===$&&(G.source==="]"||G.source==="}")&&R0(X))Z(G,"BAD_INDENT","Flow end indicator should be more indented than parent",!0)}}function x6($,X,Z){let{uniqueKeys:G}=$.options;if(G===!1)return!1;let J=typeof G==="function"?G:(U,Q)=>U===Q||P(U)&&P(Q)&&U.value===Q.value;return X.some((U)=>J(U.key,Z))}var c8="All mapping items must start at the same column";function u$({composeNode:$,composeEmptyNode:X},Z,G,J,U){let z=new(U?.nodeClass??h)(Z.schema);if(Z.atRoot)Z.atRoot=!1;let W=G.offset,V=null;for(let H of G.items){let{start:Y,key:_,sep:F,value:q}=H,I=Y0(Y,{indicator:"explicit-key-ind",next:_??F?.[0],offset:W,onError:J,parentIndent:G.indent,startOnNewline:!0}),D=!I.found;if(D){if(_){if(_.type==="block-seq")J(W,"BLOCK_AS_IMPLICIT_KEY","A block sequence may not be used as an implicit map key");else if("indent"in _&&_.indent!==G.indent)J(W,"BAD_INDENT",c8)}if(!I.anchor&&!I.tag&&!F){if(V=I.end,I.comment)if(z.comment)z.comment+=` +`+I.comment;else z.comment=I.comment;continue}if(I.newlineAfterProp||R0(_))J(_??Y[Y.length-1],"MULTILINE_IMPLICIT_KEY","Implicit keys need to be on a single line")}else if(I.found?.indent!==G.indent)J(W,"BAD_INDENT",c8);Z.atKey=!0;let L=I.end,B=_?$(Z,_,I,J):X(Z,L,Y,null,I,J);if(Z.schema.compat)D8(G.indent,_,J);if(Z.atKey=!1,x6(Z,z.items,B))J(L,"DUPLICATE_KEY","Map keys must be unique");let K=Y0(F??[],{indicator:"map-value-ind",next:q,offset:B.range[2],onError:J,parentIndent:G.indent,startOnNewline:!_||_.type==="block-scalar"});if(W=K.end,K.found){if(D){if(q?.type==="block-map"&&!K.hasNewline)J(W,"BLOCK_AS_IMPLICIT_KEY","Nested mappings are not allowed in compact mappings");if(Z.options.strict&&I.start$&&($.type==="block-map"||$.type==="block-seq");function p$({composeNode:$,composeEmptyNode:X},Z,G,J,U){let Q=G.start.source==="{",z=Q?"flow map":"flow sequence",V=new(U?.nodeClass??(Q?h:a))(Z.schema);V.flow=!0;let H=Z.atRoot;if(H)Z.atRoot=!1;if(Z.atKey)Z.atKey=!1;let Y=G.offset+G.start.source.length;for(let D=0;D0){let D=C0(q,I,Z.options.strict,J);if(D.comment)if(V.comment)V.comment+=` +`+D.comment;else V.comment=D.comment;V.range=[G.offset,I,D.offset]}else V.range=[G.offset,I,I];return V}function V8($,X,Z,G,J,U){let Q=Z.type==="block-map"?u$($,X,Z,G,U):Z.type==="block-seq"?d$($,X,Z,G,U):p$($,X,Z,G,U),z=Q.constructor;if(J==="!"||J===z.tagName)return Q.tag=z.tagName,Q;if(J)Q.tag=J;return Q}function l$($,X,Z,G,J){let U=G.tag,Q=!U?null:X.directives.tagName(U.source,(_)=>J(U,"TAG_RESOLVE_FAILED",_));if(Z.type==="block-seq"){let{anchor:_,newlineAfterProp:F}=G,q=_&&U?_.offset>U.offset?_:U:_??U;if(q&&(!F||F.offset_.tag===Q&&_.collection===z);if(!W){let _=X.schema.knownTags[Q];if(_?.collection===z)X.schema.tags.push(Object.assign({},_,{default:!1})),W=_;else{if(_)J(U,"BAD_COLLECTION_TYPE",`${_.tag} used for ${z} collection, but expects ${_.collection??"scalar"}`,!0);else J(U,"TAG_RESOLVE_FAILED",`Unresolved tag: ${Q}`,!0);return V8($,X,Z,J,Q)}}let V=V8($,X,Z,J,Q,W),H=W.resolve?.(V,(_)=>J(U,"TAG_RESOLVE_FAILED",_),X.options)??V,Y=S(H)?H:new R(H);if(Y.range=V.range,Y.tag=Q,W?.format)Y.format=W.format;return Y}function c$($,X,Z){let G=X.offset,J=i$(X,$.options.strict,Z);if(!J)return{value:"",type:null,comment:"",range:[G,G,G]};let U=J.mode===">"?R.BLOCK_FOLDED:R.BLOCK_LITERAL,Q=X.source?s$(X.source):[],z=Q.length;for(let I=Q.length-1;I>=0;--I){let D=Q[I][1];if(D===""||D==="\r")z=I;else break}if(z===0){let I=J.chomp==="+"&&Q.length>0?` +`.repeat(Math.max(1,Q.length-1)):"",D=G+J.length;if(X.source)D+=X.source.length;return{value:I,type:U,comment:J.comment,range:[G,D,D]}}let W=X.indent+J.indent,V=X.offset+J.length,H=0;for(let I=0;IW)W=D.length}else{if(D.length=z;--I)if(Q[I][0].length>W)z=I+1;let Y="",_="",F=!1;for(let I=0;IW||L[0]==="\t"){if(_===" ")_=` +`;else if(!F&&_===` +`)_=` + +`;Y+=_+D.slice(W)+L,_=` +`,F=!0}else if(L==="")if(_===` +`)Y+=` +`;else _=` +`;else Y+=_+L,_=" ",F=!1}switch(J.chomp){case"-":break;case"+":for(let I=z;IZ(G+_,F,q);switch(J){case"scalar":z=R.PLAIN,W=a$(U,V);break;case"single-quoted-scalar":z=R.QUOTE_SINGLE,W=r$(U,V);break;case"double-quoted-scalar":z=R.QUOTE_DOUBLE,W=o$(U,V);break;default:return Z($,"UNEXPECTED_TOKEN",`Expected a flow scalar value, but found: ${J}`),{value:"",type:null,comment:"",range:[G,G+U.length,G+U.length]}}let H=G+U.length,Y=C0(Q,H,X,Z);return{value:W,type:z,comment:Y.comment,range:[G,H,Y.offset]}}function a$($,X){let Z="";switch($[0]){case"\t":Z="a tab character";break;case",":Z="flow indicator character ,";break;case"%":Z="directive indicator character %";break;case"|":case">":{Z=`block scalar indicator ${$[0]}`;break}case"@":case"`":{Z=`reserved character ${$[0]}`;break}}if(Z)X(0,"BAD_SCALAR_START",`Plain value cannot start with ${Z}`);return m6($)}function r$($,X){if($[$.length-1]!=="'"||$.length===1)X($.length,"MISSING_CHAR","Missing closing 'quote");return m6($.slice(1,-1)).replace(/''/g,"'")}function m6($){let X,Z;try{X=new RegExp(`(.*?)(?U?$.slice(U,G+1):J}else Z+=J}if($[$.length-1]!=='"'||$.length===1)X($.length,"MISSING_CHAR",'Missing closing "quote');return Z}function t$($,X){let Z="",G=$[X+1];while(G===" "||G==="\t"||G===` +`||G==="\r"){if(G==="\r"&&$[X+2]!==` +`)break;if(G===` +`)Z+=` +`;X+=1,G=$[X+1]}if(!Z)Z=" ";return{fold:Z,offset:X}}var e$={"0":"\x00",a:"\x07",b:"\b",e:"\x1B",f:"\f",n:` +`,r:"\r",t:"\t",v:"\v",N:"…",_:" ",L:"\u2028",P:"\u2029"," ":" ",'"':'"',"/":"/","\\":"\\","\t":"\t"};function $X($,X,Z,G){let J=$.substr(X,Z),Q=J.length===Z&&/^[0-9a-fA-F]+$/.test(J)?parseInt(J,16):NaN;if(isNaN(Q)){let z=$.substr(X-2,Z+2);return G(X-2,"BAD_DQ_ESCAPE",`Invalid escape sequence ${z}`),z}return String.fromCodePoint(Q)}function u6($,X,Z,G){let{value:J,type:U,comment:Q,range:z}=X.type==="block-scalar"?c$($,X,G):n$(X,$.options.strict,G),W=Z?$.directives.tagName(Z.source,(Y)=>G(Z,"TAG_RESOLVE_FAILED",Y)):null,V;if($.options.stringKeys&&$.atKey)V=$.schema[d];else if(W)V=XX($.schema,J,W,Z,G);else if(X.type==="scalar")V=ZX($,J,X,G);else V=$.schema[d];let H;try{let Y=V.resolve(J,(_)=>G(Z??X,"TAG_RESOLVE_FAILED",_),$.options);H=P(Y)?Y:new R(Y)}catch(Y){let _=Y instanceof Error?Y.message:String(Y);G(Z??X,"TAG_RESOLVE_FAILED",_),H=new R(J)}if(H.range=z,H.source=J,U)H.type=U;if(W)H.tag=W;if(V.format)H.format=V.format;if(Q)H.comment=Q;return H}function XX($,X,Z,G,J){if(Z==="!")return $[d];let U=[];for(let z of $.tags)if(!z.collection&&z.tag===Z)if(z.default&&z.test)U.push(z);else return z;for(let z of U)if(z.test?.test(X))return z;let Q=$.knownTags[Z];if(Q&&!Q.collection)return $.tags.push(Object.assign({},Q,{default:!1,test:void 0})),Q;return J(G,"TAG_RESOLVE_FAILED",`Unresolved tag: ${Z}`,Z!=="tag:yaml.org,2002:str"),$[d]}function ZX({atKey:$,directives:X,schema:Z},G,J,U){let Q=Z.tags.find((z)=>(z.default===!0||$&&z.default==="key")&&z.test?.test(G))||Z[d];if(Z.compat){let z=Z.compat.find((W)=>W.default&&W.test?.test(G))??Z[d];if(Q.tag!==z.tag){let W=X.tagString(Q.tag),V=X.tagString(z.tag),H=`Value may be parsed as either ${W} or ${V}`;U(J,"TAG_RESOLVE_FAILED",H,!0)}}return Q}function GX($,X,Z){if(X){Z??(Z=X.length);for(let G=Z-1;G>=0;--G){let J=X[G];switch(J.type){case"space":case"comment":case"newline":$-=J.source.length;continue}J=X[++G];while(J?.type==="space")$+=J.source.length,J=X[++G];break}}return $}var JX={composeNode:d6,composeEmptyNode:k8};function d6($,X,Z,G){let J=$.atKey,{spaceBefore:U,comment:Q,anchor:z,tag:W}=Z,V,H=!0;switch(X.type){case"alias":if(V=UX($,X,G),z||W)G(X,"ALIAS_PROPS","An alias node must not specify any properties");break;case"scalar":case"single-quoted-scalar":case"double-quoted-scalar":case"block-scalar":if(V=u6($,X,W,G),z)V.anchor=z.source.substring(1);break;case"block-map":case"block-seq":case"flow-collection":try{if(V=l$(JX,$,X,Z,G),z)V.anchor=z.source.substring(1)}catch(Y){let _=Y instanceof Error?Y.message:String(Y);G(X,"RESOURCE_EXHAUSTION",_)}break;default:{let Y=X.type==="error"?X.message:`Unsupported token (type: ${X.type})`;G(X,"UNEXPECTED_TOKEN",Y),H=!1}}if(V??(V=k8($,X.offset,void 0,null,Z,G)),z&&V.anchor==="")G(z,"BAD_ALIAS","Anchor cannot be an empty string");if(J&&$.options.stringKeys&&(!P(V)||typeof V.value!=="string"||V.tag&&V.tag!=="tag:yaml.org,2002:str"))G(W??X,"NON_STRING_KEY","With stringKeys, all keys must be strings");if(U)V.spaceBefore=!0;if(Q)if(X.type==="scalar"&&X.source==="")V.comment=Q;else V.commentBefore=Q;if($.options.keepSourceTokens&&H)V.srcToken=X;return V}function k8($,X,Z,G,{spaceBefore:J,comment:U,anchor:Q,tag:z,end:W},V){let H={type:"scalar",offset:GX(X,Z,G),indent:-1,source:""},Y=u6($,H,z,V);if(Q){if(Y.anchor=Q.source.substring(1),Y.anchor==="")V(Q,"BAD_ALIAS","Anchor cannot be an empty string")}if(J)Y.spaceBefore=!0;if(U)Y.comment=U,Y.range[2]=W;return Y}function UX({options:$},{offset:X,source:Z,end:G},J){let U=new l0(Z.substring(1));if(U.source==="")J(X,"BAD_ALIAS","Alias cannot be an empty string");if(U.source.endsWith(":"))J(X+Z.length-1,"BAD_ALIAS","Alias ending in : is ambiguous",!0);let Q=X+Z.length,z=C0(G,Q,$.strict,J);if(U.range=[X,Q,z.offset],z.comment)U.comment=z.comment;return U}function QX($,X,{offset:Z,start:G,value:J,end:U},Q){let z=Object.assign({_directives:X},$),W=new N0(void 0,z),V={atKey:!1,atRoot:!0,directives:W.directives,options:W.options,schema:W.schema},H=Y0(G,{indicator:"doc-start",next:J??U?.[0],offset:Z,onError:Q,parentIndent:0,startOnNewline:!0});if(H.found){if(W.directives.docStart=!0,J&&(J.type==="block-map"||J.type==="block-seq")&&!H.hasNewline)Q(H.end,"MISSING_CHAR","Block collection cannot start on same line with directives-end marker")}W.contents=J?d6(V,J,H,Q):k8(V,H.end,G,null,H,Q);let Y=W.contents.range[2],_=C0(U,Y,!1,Q);if(_.comment)W.comment=_.comment;return W.range=[Z,Y,_.offset],W}function B0($){if(typeof $==="number")return[$,$+1];if(Array.isArray($))return $.length===2?$:[$[0],$[1]];let{offset:X,source:Z}=$;return[X,X+(typeof Z==="string"?Z.length:1)]}function i8($){let X="",Z=!1,G=!1;for(let J=0;J<$.length;++J){let U=$[J];switch(U[0]){case"#":X+=(X===""?"":G?` + +`:` +`)+(U.substring(1)||" "),Z=!0,G=!1;break;case"%":if($[J+1]?.[0]!=="#")J+=1;Z=!1;break;default:if(!Z)G=!0;Z=!1}}return{comment:X,afterEmptyLine:G}}class p6{constructor($={}){this.doc=null,this.atDirectives=!1,this.prelude=[],this.errors=[],this.warnings=[],this.onError=(X,Z,G,J)=>{let U=B0(X);if(J)this.warnings.push(new v6(U,Z,G));else this.errors.push(new j0(U,Z,G))},this.directives=new k({version:$.version||"1.2"}),this.options=$}decorate($,X){let{comment:Z,afterEmptyLine:G}=i8(this.prelude);if(Z){let J=$.contents;if(X)$.comment=$.comment?`${$.comment} +${Z}`:Z;else if(G||$.directives.docStart||!J)$.commentBefore=Z;else if(C(J)&&!J.flow&&J.items.length>0){let U=J.items[0];if(N(U))U=U.key;let Q=U.commentBefore;U.commentBefore=Q?`${Z} +${Q}`:Z}else{let U=J.commentBefore;J.commentBefore=U?`${Z} +${U}`:Z}}if(X)Array.prototype.push.apply($.errors,this.errors),Array.prototype.push.apply($.warnings,this.warnings);else $.errors=this.errors,$.warnings=this.warnings;this.prelude=[],this.errors=[],this.warnings=[]}streamInfo(){return{comment:i8(this.prelude).comment,directives:this.directives,errors:this.errors,warnings:this.warnings}}*compose($,X=!1,Z=-1){for(let G of $)yield*this.next(G);yield*this.end(X,Z)}*next($){switch($.type){case"directive":this.directives.add($.source,(X,Z,G)=>{let J=B0($);J[0]+=X,this.onError(J,"BAD_DIRECTIVE",Z,G)}),this.prelude.push($.source),this.atDirectives=!0;break;case"document":{let X=QX(this.options,this.directives,$,this.onError);if(this.atDirectives&&!X.directives.docStart)this.onError($,"MISSING_CHAR","Missing directives-end/doc-start indicator line");if(this.decorate(X,!1),this.doc)yield this.doc;this.doc=X,this.atDirectives=!1;break}case"byte-order-mark":case"space":break;case"comment":case"newline":this.prelude.push($.source);break;case"error":{let X=$.source?`${$.message}: ${JSON.stringify($.source)}`:$.message,Z=new j0(B0($),"UNEXPECTED_TOKEN",X);if(this.atDirectives||!this.doc)this.errors.push(Z);else this.doc.errors.push(Z);break}case"doc-end":{if(!this.doc){this.errors.push(new j0(B0($),"UNEXPECTED_TOKEN","Unexpected doc-end without preceding document"));break}this.doc.directives.docEnd=!0;let X=C0($.end,$.offset+$.source.length,this.doc.options.strict,this.onError);if(this.decorate(this.doc,!0),X.comment){let Z=this.doc.comment;this.doc.comment=Z?`${Z} +${X.comment}`:X.comment}this.doc.range[2]=X.offset;break}default:this.errors.push(new j0(B0($),"UNEXPECTED_TOKEN",`Unsupported token ${$.type}`))}}*end($=!1,X=-1){if(this.doc)this.decorate(this.doc,!0),yield this.doc,this.doc=null;else if($){let Z=Object.assign({_directives:this.directives},this.options),G=new N0(void 0,Z);if(this.atDirectives)this.onError(X,"MISSING_CHAR","Missing directives-end indicator line");G.range=[0,X,X],this.decorate(G,!1),yield G}}}var q8=Symbol("break visit"),zX=Symbol("skip children"),l6=Symbol("remove item");function H0($,X){if("type"in $&&$.type==="document")$={start:$.start,value:$.value};c6(Object.freeze([]),$,X)}H0.BREAK=q8;H0.SKIP=zX;H0.REMOVE=l6;H0.itemAtPath=($,X)=>{let Z=$;for(let[G,J]of X){let U=Z?.[G];if(U&&"items"in U)Z=U.items[J];else return}return Z};H0.parentCollection=($,X)=>{let Z=H0.itemAtPath($,X.slice(0,-1)),G=X[X.length-1][0],J=Z?.[G];if(J&&"items"in J)return J;throw Error("Parent collection not found")};function c6($,X,Z){let G=Z(X,$);if(typeof G==="symbol")return G;for(let J of["key","value"]){let U=X[J];if(U&&"items"in U){for(let Q=0;Q":return"block-scalar-header"}return null}function x($){switch($){case void 0:case" ":case` +`:case"\r":case"\t":return!0;default:return!1}}var s8=new Set("0123456789ABCDEFabcdef"),WX=new Set("0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-#;/?:@&=+$_.!~*'()"),y0=new Set(",[]{}"),YX=new Set(` ,[]{} +\r `),W8=($)=>!$||YX.has($);class a6{constructor(){this.atEnd=!1,this.blockScalarIndent=-1,this.blockScalarKeep=!1,this.buffer="",this.flowKey=!1,this.flowLevel=0,this.indentNext=0,this.indentValue=0,this.lineEndPos=null,this.next=null,this.pos=0}*lex($,X=!1){if($){if(typeof $!=="string")throw TypeError("source is not a string");this.buffer=this.buffer?this.buffer+$:$,this.lineEndPos=null}this.atEnd=!X;let Z=this.next??"stream";while(Z&&(X||this.hasChars(1)))Z=yield*this.parseNext(Z)}atLineEnd(){let $=this.pos,X=this.buffer[$];while(X===" "||X==="\t")X=this.buffer[++$];if(!X||X==="#"||X===` +`)return!0;if(X==="\r")return this.buffer[$+1]===` +`;return!1}charAt($){return this.buffer[this.pos+$]}continueScalar($){let X=this.buffer[$];if(this.indentNext>0){let Z=0;while(X===" ")X=this.buffer[++Z+$];if(X==="\r"){let G=this.buffer[Z+$+1];if(G===` +`||!G&&!this.atEnd)return $+Z+1}return X===` +`||Z>=this.indentNext||!X&&!this.atEnd?$+Z:-1}if(X==="-"||X==="."){let Z=this.buffer.substr($,3);if((Z==="---"||Z==="...")&&x(this.buffer[$+3]))return-1}return $}getLine(){let $=this.lineEndPos;if(typeof $!=="number"||$!==-1&&$this.indentValue&&!x(this.charAt(1)))this.indentNext=this.indentValue;return yield*this.parseBlockStart()}*parseBlockStart(){let[$,X]=this.peek(2);if(!X&&!this.atEnd)return this.setNext("block-start");if(($==="-"||$==="?"||$===":")&&x(X)){let Z=(yield*this.pushCount(1))+(yield*this.pushSpaces(!0));return this.indentNext=this.indentValue+1,this.indentValue+=Z,yield*this.parseBlockStart()}return"doc"}*parseDocument(){yield*this.pushSpaces(!0);let $=this.getLine();if($===null)return this.setNext("doc");let X=yield*this.pushIndicators();switch($[X]){case"#":yield*this.pushCount($.length-X);case void 0:return yield*this.pushNewline(),yield*this.parseLineStart();case"{":case"[":return yield*this.pushCount(1),this.flowKey=!1,this.flowLevel=1,"flow";case"}":case"]":return yield*this.pushCount(1),"doc";case"*":return yield*this.pushUntil(W8),"doc";case'"':case"'":return yield*this.parseQuotedScalar();case"|":case">":return X+=yield*this.parseBlockScalarHeader(),X+=yield*this.pushSpaces(!0),yield*this.pushCount($.length-X),yield*this.pushNewline(),yield*this.parseBlockScalar();default:return yield*this.parsePlainScalar()}}*parseFlowCollection(){let $,X,Z=-1;do{if($=yield*this.pushNewline(),$>0)X=yield*this.pushSpaces(!1),this.indentValue=Z=X;else X=0;X+=yield*this.pushSpaces(!0)}while($+X>0);let G=this.getLine();if(G===null)return this.setNext("flow");if(Z!==-1&&Z"0"&&X<="9")this.blockScalarIndent=Number(X)-1;else if(X!=="-")break}return yield*this.pushUntil((X)=>x(X)||X==="#")}*parseBlockScalar(){let $=this.pos-1,X=0,Z;$:for(let J=this.pos;Z=this.buffer[J];++J)switch(Z){case" ":X+=1;break;case` +`:$=J,X=0;break;case"\r":{let U=this.buffer[J+1];if(!U&&!this.atEnd)return this.setNext("block-scalar");if(U===` +`)break}default:break $}if(!Z&&!this.atEnd)return this.setNext("block-scalar");if(X>=this.indentNext){if(this.blockScalarIndent===-1)this.indentNext=X;else this.indentNext=this.blockScalarIndent+(this.indentNext===0?1:this.indentNext);do{let J=this.continueScalar($+1);if(J===-1)break;$=this.buffer.indexOf(` +`,J)}while($!==-1);if($===-1){if(!this.atEnd)return this.setNext("block-scalar");$=this.buffer.length}}let G=$+1;Z=this.buffer[G];while(Z===" ")Z=this.buffer[++G];if(Z==="\t"){while(Z==="\t"||Z===" "||Z==="\r"||Z===` +`)Z=this.buffer[++G];$=G-1}else if(!this.blockScalarKeep)do{let J=$-1,U=this.buffer[J];if(U==="\r")U=this.buffer[--J];let Q=J;while(U===" ")U=this.buffer[--J];if(U===` +`&&J>=this.pos&&J+1+X>Q)$=J;else break}while(!0);return yield L8,yield*this.pushToIndex($+1,!0),yield*this.parseLineStart()}*parsePlainScalar(){let $=this.flowLevel>0,X=this.pos-1,Z=this.pos-1,G;while(G=this.buffer[++Z])if(G===":"){let J=this.buffer[Z+1];if(x(J)||$&&y0.has(J))break;X=Z}else if(x(G)){let J=this.buffer[Z+1];if(G==="\r")if(J===` +`)Z+=1,G=` +`,J=this.buffer[Z+1];else X=Z;if(J==="#"||$&&y0.has(J))break;if(G===` +`){let U=this.continueScalar(Z+1);if(U===-1)break;Z=Math.max(Z,U-2)}}else{if($&&y0.has(G))break;X=Z}if(!G&&!this.atEnd)return this.setNext("plain-scalar");return yield L8,yield*this.pushToIndex(X+1,!0),$?"flow":"doc"}*pushCount($){if($>0)return yield this.buffer.substr(this.pos,$),this.pos+=$,$;return 0}*pushToIndex($,X){let Z=this.buffer.slice(this.pos,$);if(Z)return yield Z,this.pos+=Z.length,Z.length;else if(X)yield"";return 0}*pushIndicators(){switch(this.charAt(0)){case"!":return(yield*this.pushTag())+(yield*this.pushSpaces(!0))+(yield*this.pushIndicators());case"&":return(yield*this.pushUntil(W8))+(yield*this.pushSpaces(!0))+(yield*this.pushIndicators());case"-":case"?":case":":{let $=this.flowLevel>0,X=this.charAt(1);if(x(X)||$&&y0.has(X)){if(!$)this.indentNext=this.indentValue+1;else if(this.flowKey)this.flowKey=!1;return(yield*this.pushCount(1))+(yield*this.pushSpaces(!0))+(yield*this.pushIndicators())}}}return 0}*pushTag(){if(this.charAt(1)==="<"){let $=this.pos+2,X=this.buffer[$];while(!x(X)&&X!==">")X=this.buffer[++$];return yield*this.pushToIndex(X===">"?$+1:$,!1)}else{let $=this.pos+1,X=this.buffer[$];while(X)if(WX.has(X))X=this.buffer[++$];else if(X==="%"&&s8.has(this.buffer[$+1])&&s8.has(this.buffer[$+2]))X=this.buffer[$+=3];else break;return yield*this.pushToIndex($,!1)}}*pushNewline(){let $=this.buffer[this.pos];if($===` +`)return yield*this.pushCount(1);else if($==="\r"&&this.charAt(1)===` +`)return yield*this.pushCount(2);else return 0}*pushSpaces($){let X=this.pos-1,Z;do Z=this.buffer[++X];while(Z===" "||$&&Z==="\t");let G=X-this.pos;if(G>0)yield this.buffer.substr(this.pos,G),this.pos=X;return G}*pushUntil($){let X=this.pos,Z=this.buffer[X];while(!$(Z))Z=this.buffer[++X];return yield*this.pushToIndex(X,!1)}}class r6{constructor(){this.lineStarts=[],this.addNewLine=($)=>this.lineStarts.push($),this.linePos=($)=>{let X=0,Z=this.lineStarts.length;while(X>1;if(this.lineStarts[J]<$)X=J+1;else Z=J}if(this.lineStarts[X]===$)return{line:X+1,col:1};if(X===0)return{line:0,col:$};let G=this.lineStarts[X-1];return{line:X,col:$-G+1}}}}function s($,X){for(let Z=0;Z<$.length;++Z)if($[Z].type===X)return!0;return!1}function n8($){for(let X=0;X<$.length;++X)switch($[X].type){case"space":case"comment":case"newline":break;default:return X}return-1}function o6($){switch($?.type){case"alias":case"scalar":case"single-quoted-scalar":case"double-quoted-scalar":case"flow-collection":return!0;default:return!1}}function h0($){switch($.type){case"document":return $.start;case"block-map":{let X=$.items[$.items.length-1];return X.sep??X.start}case"block-seq":return $.items[$.items.length-1].start;default:return[]}}function Z0($){if($.length===0)return[];let X=$.length;$:while(--X>=0)switch($[X].type){case"doc-start":case"explicit-key-ind":case"map-value-ind":case"seq-item-ind":case"newline":break $}while($[++X]?.type==="space");return $.splice(X,$.length)}function a8($){if($.start.type==="flow-seq-start"){for(let X of $.items)if(X.sep&&!X.value&&!s(X.start,"explicit-key-ind")&&!s(X.sep,"map-value-ind")){if(X.key)X.value=X.key;if(delete X.key,o6(X.value))if(X.value.end)Array.prototype.push.apply(X.value.end,X.sep);else X.value.end=X.sep;else Array.prototype.push.apply(X.start,X.sep);delete X.sep}}}class t6{constructor($){this.atNewLine=!0,this.atScalar=!1,this.indent=0,this.offset=0,this.onKeyLine=!1,this.stack=[],this.source="",this.type="",this.lexer=new a6,this.onNewLine=$}*parse($,X=!1){if(this.onNewLine&&this.offset===0)this.onNewLine(0);for(let Z of this.lexer.lex($,X))yield*this.next(Z);if(!X)yield*this.end()}*next($){if(this.source=$,this.atScalar){this.atScalar=!1,yield*this.step(),this.offset+=$.length;return}let X=VX($);if(!X){let Z=`Not a YAML token: ${$}`;yield*this.pop({type:"error",offset:this.offset,message:Z,source:$}),this.offset+=$.length}else if(X==="scalar")this.atNewLine=!1,this.atScalar=!0,this.type="scalar";else{switch(this.type=X,yield*this.step(),X){case"newline":if(this.atNewLine=!0,this.indent=0,this.onNewLine)this.onNewLine(this.offset+$.length);break;case"space":if(this.atNewLine&&$[0]===" ")this.indent+=$.length;break;case"explicit-key-ind":case"map-value-ind":case"seq-item-ind":if(this.atNewLine)this.indent+=$.length;break;case"doc-mode":case"flow-error-end":return;default:this.atNewLine=!1}this.offset+=$.length}}*end(){while(this.stack.length>0)yield*this.pop()}get sourceToken(){return{type:this.type,offset:this.offset,indent:this.indent,source:this.source}}*step(){let $=this.peek(1);if(this.type==="doc-end"&&$?.type!=="doc-end"){while(this.stack.length>0)yield*this.pop();this.stack.push({type:"doc-end",offset:this.offset,source:this.source});return}if(!$)return yield*this.stream();switch($.type){case"document":return yield*this.document($);case"alias":case"scalar":case"single-quoted-scalar":case"double-quoted-scalar":return yield*this.scalar($);case"block-scalar":return yield*this.blockScalar($);case"block-map":return yield*this.blockMap($);case"block-seq":return yield*this.blockSequence($);case"flow-collection":return yield*this.flowCollection($);case"doc-end":return yield*this.documentEnd($)}yield*this.pop()}peek($){return this.stack[this.stack.length-$]}*pop($){let X=$??this.stack.pop();if(!X)yield{type:"error",offset:this.offset,source:"",message:"Tried to pop an empty stack"};else if(this.stack.length===0)yield X;else{let Z=this.peek(1);if(X.type==="block-scalar")X.indent="indent"in Z?Z.indent:0;else if(X.type==="flow-collection"&&Z.type==="document")X.indent=0;if(X.type==="flow-collection")a8(X);switch(Z.type){case"document":Z.value=X;break;case"block-scalar":Z.props.push(X);break;case"block-map":{let G=Z.items[Z.items.length-1];if(G.value){Z.items.push({start:[],key:X,sep:[]}),this.onKeyLine=!0;return}else if(G.sep)G.value=X;else{Object.assign(G,{key:X,sep:[]}),this.onKeyLine=!G.explicitKey;return}break}case"block-seq":{let G=Z.items[Z.items.length-1];if(G.value)Z.items.push({start:[],value:X});else G.value=X;break}case"flow-collection":{let G=Z.items[Z.items.length-1];if(!G||G.value)Z.items.push({start:[],key:X,sep:[]});else if(G.sep)G.value=X;else Object.assign(G,{key:X,sep:[]});return}default:yield*this.pop(),yield*this.pop(X)}if((Z.type==="document"||Z.type==="block-map"||Z.type==="block-seq")&&(X.type==="block-map"||X.type==="block-seq")){let G=X.items[X.items.length-1];if(G&&!G.sep&&!G.value&&G.start.length>0&&n8(G.start)===-1&&(X.indent===0||G.start.every((J)=>J.type!=="comment"||J.indent=$.indent){let Z=!this.onKeyLine&&this.indent===$.indent,G=Z&&(X.sep||X.explicitKey)&&this.type!=="seq-item-ind",J=[];if(G&&X.sep&&!X.value){let U=[];for(let Q=0;Q$.indent)U.length=0;break;default:U.length=0}}if(U.length>=2)J=X.sep.splice(U[1])}switch(this.type){case"anchor":case"tag":if(G||X.value)J.push(this.sourceToken),$.items.push({start:J}),this.onKeyLine=!0;else if(X.sep)X.sep.push(this.sourceToken);else X.start.push(this.sourceToken);return;case"explicit-key-ind":if(!X.sep&&!X.explicitKey)X.start.push(this.sourceToken),X.explicitKey=!0;else if(G||X.value)J.push(this.sourceToken),$.items.push({start:J,explicitKey:!0});else this.stack.push({type:"block-map",offset:this.offset,indent:this.indent,items:[{start:[this.sourceToken],explicitKey:!0}]});this.onKeyLine=!0;return;case"map-value-ind":if(X.explicitKey)if(!X.sep)if(s(X.start,"newline"))Object.assign(X,{key:null,sep:[this.sourceToken]});else{let U=Z0(X.start);this.stack.push({type:"block-map",offset:this.offset,indent:this.indent,items:[{start:U,key:null,sep:[this.sourceToken]}]})}else if(X.value)$.items.push({start:[],key:null,sep:[this.sourceToken]});else if(s(X.sep,"map-value-ind"))this.stack.push({type:"block-map",offset:this.offset,indent:this.indent,items:[{start:J,key:null,sep:[this.sourceToken]}]});else if(o6(X.key)&&!s(X.sep,"newline")){let U=Z0(X.start),Q=X.key,z=X.sep;z.push(this.sourceToken),delete X.key,delete X.sep,this.stack.push({type:"block-map",offset:this.offset,indent:this.indent,items:[{start:U,key:Q,sep:z}]})}else if(J.length>0)X.sep=X.sep.concat(J,this.sourceToken);else X.sep.push(this.sourceToken);else if(!X.sep)Object.assign(X,{key:null,sep:[this.sourceToken]});else if(X.value||G)$.items.push({start:J,key:null,sep:[this.sourceToken]});else if(s(X.sep,"map-value-ind"))this.stack.push({type:"block-map",offset:this.offset,indent:this.indent,items:[{start:[],key:null,sep:[this.sourceToken]}]});else X.sep.push(this.sourceToken);this.onKeyLine=!0;return;case"alias":case"scalar":case"single-quoted-scalar":case"double-quoted-scalar":{let U=this.flowScalar(this.type);if(G||X.value)$.items.push({start:J,key:U,sep:[]}),this.onKeyLine=!0;else if(X.sep)this.stack.push(U);else Object.assign(X,{key:U,sep:[]}),this.onKeyLine=!0;return}default:{let U=this.startBlockValue($);if(U){if(U.type==="block-seq"){if(!X.explicitKey&&X.sep&&!s(X.sep,"newline")){yield*this.pop({type:"error",offset:this.offset,message:"Unexpected block-seq-ind on same line with key",source:this.source});return}}else if(Z)$.items.push({start:J});this.stack.push(U);return}}}}yield*this.pop(),yield*this.step()}*blockSequence($){let X=$.items[$.items.length-1];switch(this.type){case"newline":if(X.value){let Z="end"in X.value?X.value.end:void 0;if((Array.isArray(Z)?Z[Z.length-1]:void 0)?.type==="comment")Z?.push(this.sourceToken);else $.items.push({start:[this.sourceToken]})}else X.start.push(this.sourceToken);return;case"space":case"comment":if(X.value)$.items.push({start:[this.sourceToken]});else{if(this.atIndentedComment(X.start,$.indent)){let G=$.items[$.items.length-2]?.value?.end;if(Array.isArray(G)){Array.prototype.push.apply(G,X.start),G.push(this.sourceToken),$.items.pop();return}}X.start.push(this.sourceToken)}return;case"anchor":case"tag":if(X.value||this.indent<=$.indent)break;X.start.push(this.sourceToken);return;case"seq-item-ind":if(this.indent!==$.indent)break;if(X.value||s(X.start,"seq-item-ind"))$.items.push({start:[this.sourceToken]});else X.start.push(this.sourceToken);return}if(this.indent>$.indent){let Z=this.startBlockValue($);if(Z){this.stack.push(Z);return}}yield*this.pop(),yield*this.step()}*flowCollection($){let X=$.items[$.items.length-1];if(this.type==="flow-error-end"){let Z;do yield*this.pop(),Z=this.peek(1);while(Z?.type==="flow-collection")}else if($.end.length===0){switch(this.type){case"comma":case"explicit-key-ind":if(!X||X.sep)$.items.push({start:[this.sourceToken]});else X.start.push(this.sourceToken);return;case"map-value-ind":if(!X||X.value)$.items.push({start:[],key:null,sep:[this.sourceToken]});else if(X.sep)X.sep.push(this.sourceToken);else Object.assign(X,{key:null,sep:[this.sourceToken]});return;case"space":case"comment":case"newline":case"anchor":case"tag":if(!X||X.value)$.items.push({start:[this.sourceToken]});else if(X.sep)X.sep.push(this.sourceToken);else X.start.push(this.sourceToken);return;case"alias":case"scalar":case"single-quoted-scalar":case"double-quoted-scalar":{let G=this.flowScalar(this.type);if(!X||X.value)$.items.push({start:[],key:G,sep:[]});else if(X.sep)this.stack.push(G);else Object.assign(X,{key:G,sep:[]});return}case"flow-map-end":case"flow-seq-end":$.end.push(this.sourceToken);return}let Z=this.startBlockValue($);if(Z)this.stack.push(Z);else yield*this.pop(),yield*this.step()}else{let Z=this.peek(2);if(Z.type==="block-map"&&(this.type==="map-value-ind"&&Z.indent===$.indent||this.type==="newline"&&!Z.items[Z.items.length-1].sep))yield*this.pop(),yield*this.step();else if(this.type==="map-value-ind"&&Z.type!=="flow-collection"){let G=h0(Z),J=Z0(G);a8($);let U=$.end.splice(1,$.end.length);U.push(this.sourceToken);let Q={type:"block-map",offset:$.offset,indent:$.indent,items:[{start:J,key:$,sep:U}]};this.onKeyLine=!0,this.stack[this.stack.length-1]=Q}else yield*this.lineEnd($)}}flowScalar($){if(this.onNewLine){let X=this.source.indexOf(` +`)+1;while(X!==0)this.onNewLine(this.offset+X),X=this.source.indexOf(` +`,X)+1}return{type:$,offset:this.offset,indent:this.indent,source:this.source}}startBlockValue($){switch(this.type){case"alias":case"scalar":case"single-quoted-scalar":case"double-quoted-scalar":return this.flowScalar(this.type);case"block-scalar-header":return{type:"block-scalar",offset:this.offset,indent:this.indent,props:[this.sourceToken],source:""};case"flow-map-start":case"flow-seq-start":return{type:"flow-collection",offset:this.offset,indent:this.indent,start:this.sourceToken,items:[],end:[]};case"seq-item-ind":return{type:"block-seq",offset:this.offset,indent:this.indent,items:[{start:[this.sourceToken]}]};case"explicit-key-ind":{this.onKeyLine=!0;let X=h0($),Z=Z0(X);return Z.push(this.sourceToken),{type:"block-map",offset:this.offset,indent:this.indent,items:[{start:Z,explicitKey:!0}]}}case"map-value-ind":{this.onKeyLine=!0;let X=h0($),Z=Z0(X);return{type:"block-map",offset:this.offset,indent:this.indent,items:[{start:Z,key:null,sep:[this.sourceToken]}]}}}return null}atIndentedComment($,X){if(this.type!=="comment")return!1;if(this.indent<=X)return!1;return $.every((Z)=>Z.type==="newline"||Z.type==="space")}*documentEnd($){if(this.type!=="doc-mode"){if($.end)$.end.push(this.sourceToken);else $.end=[this.sourceToken];if(this.type==="newline")yield*this.pop()}}*lineEnd($){switch(this.type){case"comma":case"doc-start":case"doc-end":case"flow-seq-end":case"flow-map-end":case"map-value-ind":yield*this.pop(),yield*this.step();break;case"newline":this.onKeyLine=!1;case"space":case"comment":default:if($.end)$.end.push(this.sourceToken);else $.end=[this.sourceToken];if(this.type==="newline")yield*this.pop()}}}function HX($){let X=$.prettyErrors!==!1;return{lineCounter:$.lineCounter||X&&new r6||null,prettyErrors:X}}function _X($,X={}){let{lineCounter:Z,prettyErrors:G}=HX(X),J=new t6(Z?.addNewLine),U=new p6(X),Q=Array.from(U.compose(J.parse($)));if(G&&Z)for(let z of Q)z.errors.forEach(l8($,Z)),z.warnings.forEach(l8($,Z));if(Q.length>0)return Q;return Object.assign([],{empty:!0},U.streamInfo())}function FX($,X,Z){let G=null;if(typeof X==="function"||Array.isArray(X))G=X;else if(Z===void 0&&X)Z=X;if(typeof Z==="string")Z=Z.length;if(typeof Z==="number"){let J=Math.round(Z);Z=J<1?void 0:J>8?{indent:8}:{indent:J}}if($===void 0){let{keepUndefined:J}=Z??X??{};if(!J)return}if(F0($)&&!G)return $.toString(Z);return new N0($,G,Z).toString(Z)}function IX($){let X=[],Z=[],G=[],J;try{J=_X($)}catch(U){return G.push({message:`Failed to parse YAML: ${U instanceof Error?U.message:String(U)}`}),{ingresses:X,passthrough:Z,errors:G}}for(let U=0;U0){for(let W of Q.errors)G.push({message:`YAML parse error: ${W.message}`,document:U,line:W.linePos?.[0]?.line});continue}let z=Q.toJSON();if(z==null)continue;if(z.kind==="IngressList"||z.kind==="List"){let W=Array.isArray(z.items)?z.items:[];for(let V of W)r8(V,U,X,Z,G);continue}r8(z,U,X,Z,G)}return{ingresses:X,passthrough:Z,errors:G}}function r8($,X,Z,G,J){if($.apiVersion==="networking.k8s.io/v1"&&$.kind==="Ingress"){let U=$.metadata;if(!U?.name){J.push({message:"Ingress is missing required field: metadata.name",document:X});return}let Q={...$,metadata:{...U,namespace:U.namespace??"default"}};Z.push(Q)}else if(typeof $==="object"&&$!==null)G.push($)}class f8{handlers=[];prefixes;constructor($){this.prefixes=$}register($){this.handlers.push($)}process($,X){let Z=$.metadata.annotations??{},G={};for(let[V,H]of Object.entries(Z))if(this.prefixes.some((Y)=>V.startsWith(Y)))G[V]=H;let J=new Set,U=$.metadata.namespace??"default",Q=$.metadata.name,z={ingress:$,annotations:Z,namespace:U,name:Q,prefix:this.prefixes[0],prefixes:this.prefixes};for(let V of this.handlers){let H=[];for(let _ of V.annotations)for(let F of this.prefixes){let q=`${F}${_}`;if(q in G){H.push({suffix:_,fullKey:q});break}}if(H.length===0)continue;let Y=V.convert(z);if(Y.policies)X.policies.push(...Y.policies);if(Y.referenceGrants)X.referenceGrants.push(...Y.referenceGrants);if(Y.routeFilters)X.routeFilters.push(...Y.routeFilters);if(Y.warnings)X.warnings.push(...Y.warnings);if(Y.unsupported)X.unsupported.push(...Y.unsupported);for(let{fullKey:_}of H)J.add(_),X.convertedAnnotations.push({annotation:_,value:G[_],handler:V.category,outputKind:DX(Y)})}for(let V of Object.keys(G))if(!J.has(V))X.unknown.push(V);let W=new Set(["keep-alive","keep-alive-requests","proxy-connect-timeout","proxy-read-timeout","proxy-send-timeout","upstream-keepalive-connections","upstream-keepalive-requests","upstream-keepalive-time","upstream-keepalive-timeout","proxy-buffer-size","proxy-buffers-number","worker-processes","max-worker-connections"]);for(let[V]of Object.entries(Z))if(W.has(V))X.warnings.push({annotation:V,message:`"${V}" appears to be an NGINX controller ConfigMap key, not a per-Ingress annotation. ConfigMap keys apply globally to the NGINX controller and are not converted per-route. In Envoy Gateway, equivalent global settings are configured via the EnvoyProxy custom resource.`,severity:"info"})}}function DX($){if($.policies&&$.policies.length>0)return $.policies[0].kind;if($.routeFilters&&$.routeFilters.length>0)return"HTTPRouteFilter";if($.unsupported&&$.unsupported.length>0)return"Unsupported";if($.warnings&&$.warnings.length>0)return"Warning";return"Unknown"}var qX={annotations:["app-root"],category:"app-root",convert($){let{annotations:X,name:Z}=$,G=X[`${$.prefix}app-root`];if(!G)return{};return{routeFilters:[{type:"RequestRedirect",filter:{type:"RequestRedirect",requestRedirect:{path:{type:"ReplaceFullPath",value:G},statusCode:302}},targetRouteName:Z}]}}},LX={annotations:["auth-type","auth-secret","auth-secret-type","auth-realm"],category:"auth-basic",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=X[`${$.prefix}auth-type`],z=X[`${$.prefix}auth-secret`],W=X[`${$.prefix}auth-secret-type`],V=X[`${$.prefix}auth-realm`];if(!Q)return{};let H=Q.toLowerCase();if(H==="digest")return U.push({annotation:`${$.prefix}auth-type`,value:Q,reason:"Digest authentication has no Gateway API or Envoy Gateway equivalent.",guidance:"Consider migrating to basic authentication over HTTPS, or implement a custom ExtAuth service that handles digest authentication."}),{unsupported:U};if(H!=="basic")return{};if(V)J.push({annotation:`${$.prefix}auth-realm`,message:`In NGINX, auth-realm sets the WWW-Authenticate realm string ("${V}") in the 401 response. Envoy Gateway's BasicAuth does not support custom realm strings — the realm is determined by the auth implementation.`,severity:"warning"});if(W)J.push({annotation:`${$.prefix}auth-secret-type`,message:`auth-secret-type ("${W}") noted. Envoy Gateway BasicAuth expects a htpasswd-formatted Secret. Ensure the Secret "${z??"unknown"}" uses the correct format.`,severity:"info"});if(!z)return J.push({annotation:`${$.prefix}auth-type`,message:'auth-type is "basic" but no auth-secret annotation found. Cannot generate SecurityPolicy without a Secret reference.',severity:"warning"}),{warnings:J.length>0?J:void 0};return J.push({annotation:`${$.prefix}auth-secret`,message:`The NGINX basic auth secret "${z}" must be adapted for Envoy Gateway. Two changes needed: (1) The secret key must be '.htpasswd' (NGINX uses 'auth'). `+"(2) Password hashes must be in {SHA} format — NGINX supports APR1/bcrypt ($apr1$, $2y$) "+"but Envoy Gateway only supports {SHA} (SHA-1 base64). Regenerate with: htpasswd -s -nb ",severity:"warning"}),{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"SecurityPolicy",metadata:{name:`${G}-basic-auth`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},basicAuth:{users:{name:z}}}}],warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}},OX={annotations:["auth-url","auth-cache-key","auth-cache-duration","auth-proxy-set-headers","auth-snippet","enable-global-auth","auth-response-headers"],category:"auth-external",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=X[`${$.prefix}auth-url`],z=X[`${$.prefix}auth-cache-key`],W=X[`${$.prefix}auth-cache-duration`],V=X[`${$.prefix}auth-snippet`],H=X[`${$.prefix}enable-global-auth`],Y=X[`${$.prefix}auth-response-headers`];if(z)J.push({annotation:`${$.prefix}auth-cache-key`,message:"In NGINX, auth-cache-key caches external auth responses to reduce latency. Envoy Gateway's ExtAuth does not natively support response caching. For high-traffic services, consider implementing caching in the external auth service itself.",severity:"warning"});if(W)J.push({annotation:`${$.prefix}auth-cache-duration`,message:"In NGINX, auth-cache-duration controls how long cached external auth responses are valid. Envoy Gateway's ExtAuth does not natively support response caching. For high-traffic services, consider implementing caching in the external auth service itself.",severity:"warning"});if(V)U.push({annotation:`${$.prefix}auth-snippet`,value:V,reason:"Raw NGINX configuration snippets cannot be converted to Gateway API resources.",guidance:"Review the snippet and manually implement equivalent behavior using Envoy Gateway filters or EnvoyPatchPolicy."});if(H)J.push({annotation:`${$.prefix}enable-global-auth`,message:"In NGINX, enable-global-auth applies the auth configuration to all routes. In Gateway API, policies are scoped per-route or per-Gateway. To apply auth globally, attach the SecurityPolicy to the Gateway rather than individual HTTPRoutes.",severity:"warning"});if(!Q)return{warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0};let _=KX(Q),F=["Authorization"];if(Y){let D=Y.split(",").map((L)=>L.trim()).filter((L)=>L.length>0);for(let L of D)if(!F.includes(L))F.push(L)}if(!_)return J.push({annotation:`${$.prefix}auth-url`,message:`auth-url "${Q}" could not be parsed as a Kubernetes service URL. For Envoy Gateway ExtAuth, use a backendRef pointing to a Kubernetes Service, or configure an external HTTP endpoint manually in the SecurityPolicy.`,severity:"warning"}),{warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0};if("external"in _)return U.push({annotation:`${$.prefix}auth-url`,value:Q,reason:`auth-url points to an external service (${_.hostname}). In NGINX, auth-url can point to any HTTP endpoint. In Envoy Gateway, extAuth requires a Kubernetes Service as the backend.`,guidance:"To use an external auth endpoint, create a Kubernetes ExternalName Service pointing to the external host, then reference that Service in the SecurityPolicy."}),{warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0};let q={backendRef:{name:_.serviceName,port:_.port},headersToBackend:F};if(_.path&&_.path!=="/")q.path=_.path;return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"SecurityPolicy",metadata:{name:`${G}-ext-auth`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},extAuth:{http:q}}}],warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}};function BX($){let X=$.split(".");if(X.length===1)return!0;return X.includes("svc")}function KX($){try{let X=new URL($),Z=X.hostname;if(!Z)return null;if(!BX(Z))return{external:!0,hostname:Z};let J=Z.split(".")[0];if(!J)return null;let U;if(X.port)U=Number.parseInt(X.port,10);else U=X.protocol==="https:"?443:80;let Q=X.pathname||"/";return{serviceName:J,port:U,path:Q}}catch{return null}}var jX={annotations:["auth-tls-secret","auth-tls-verify-client","auth-tls-verify-depth","auth-tls-pass-certificate-to-upstream","auth-tls-error-page","auth-tls-match-cn"],category:"auth-tls",convert($){let{annotations:X,namespace:Z,name:G,ingress:J}=$,U=[],Q=[],z=[],W=X[`${$.prefix}auth-tls-verify-client`],V=X[`${$.prefix}auth-tls-secret`],H=X[`${$.prefix}auth-tls-pass-certificate-to-upstream`],Y=X[`${$.prefix}auth-tls-verify-depth`],_=X[`${$.prefix}auth-tls-error-page`],F=X[`${$.prefix}auth-tls-match-cn`];if(H&&H.toLowerCase()==="true")U.push({annotation:`${$.prefix}auth-tls-pass-certificate-to-upstream`,message:"Envoy natively populates the X-Forwarded-Client-Cert (XFCC) header when mTLS is configured. No manual conversion needed.",severity:"info"});if(Y)Q.push({annotation:`${$.prefix}auth-tls-verify-depth`,value:Y,reason:"Envoy Gateway ClientTrafficPolicy does not support certificate chain verify depth.",guidance:"Recommended workaround (verify manually): Use EnvoyPatchPolicy to set max_verify_depth on the DownstreamTlsContext. Test thoroughly in non-production first."});if(_)Q.push({annotation:`${$.prefix}auth-tls-error-page`,value:_,reason:"Envoy Gateway ClientTrafficPolicy does not support custom error pages for client certificate failures.",guidance:"Recommended workaround (verify manually): Set clientValidation.optional to true, then add a Lua filter via EnvoyPatchPolicy that inspects the XFCC header and returns a 302 redirect if the client certificate is missing or invalid. Test thoroughly in non-production first."});if(F)Q.push({annotation:`${$.prefix}auth-tls-match-cn`,value:F,reason:"Envoy Gateway ClientTrafficPolicy does not support CN/SAN matching on client certificates.",guidance:"Recommended workaround (verify manually): Use EnvoyPatchPolicy to configure SAN matchers on the DownstreamTlsContext. Test thoroughly in non-production first."});if(!W)return{warnings:U.length>0?U:void 0,unsupported:Q.length>0?Q:void 0};if(W==="off")return U.push({annotation:`${$.prefix}auth-tls-verify-client`,message:'auth-tls-verify-client is "off". Client certificate verification is disabled; no SecurityPolicy will be generated.',severity:"info"}),{warnings:U,unsupported:Q.length>0?Q:void 0};let q=W==="optional"||W==="optional_no_ca",I=W==="optional_no_ca",D={optional:q};if(!I&&V){let K=V.indexOf("/");if(K!==-1){let j=V.substring(0,K),M=V.substring(K+1);if(D.caCertificateRefs=[{group:"",kind:"Secret",name:M,namespace:j}],j!==Z)z.push({apiVersion:"gateway.networking.k8s.io/v1beta1",kind:"ReferenceGrant",metadata:{name:`allow-${Z}-to-${j}-secret`,namespace:j},spec:{from:[{group:"gateway.envoyproxy.io",kind:"ClientTrafficPolicy",namespace:Z}],to:[{group:"",kind:"Secret",name:M}]}}),U.push({annotation:`${$.prefix}auth-tls-secret`,message:`auth-tls-secret references a cross-namespace Secret "${V}". A ReferenceGrant has been generated, but verify that the target namespace permits this access.`,severity:"warning"})}else D.caCertificateRefs=[{group:"",kind:"Secret",name:V}]}if(I)U.push({annotation:`${$.prefix}auth-tls-verify-client`,message:'auth-tls-verify-client is "optional_no_ca". In NGINX, this requests a certificate without verifying its CA. In Envoy Gateway, clientValidation.optional=true without caCertificateRefs achieves similar behavior, but the exact semantics may differ. Verify client certificate handling in your environment.',severity:"warning"});let L=J.spec.ingressClassName||"default-gateway";return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"ClientTrafficPolicy",metadata:{name:`${G}-client-tls`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"Gateway",name:L},tls:{clientValidation:D}}}],referenceGrants:z.length>0?z:void 0,warnings:U.length>0?U:void 0,unsupported:Q.length>0?Q:void 0}}},MX={annotations:["backend-protocol"],category:"backend-protocol",convert($){let{annotations:X,namespace:Z,name:G,ingress:J}=$,U=[],Q=[],z=X[`${$.prefix}backend-protocol`];if(!z)return{};let W=z.toUpperCase();if(W==="HTTP")return{};if(W==="GRPC"||W==="GRPCS")return U.push({annotation:`${$.prefix}backend-protocol`,message:`In NGINX, backend-protocol: ${z} routes gRPC traffic through the Ingress. In Gateway API, gRPC has first-class support via GRPCRoute — consider using GRPCRoute instead of HTTPRoute for gRPC services.`,severity:"warning"}),{warnings:U};if(W==="AJP"||W==="FCGI")return Q.push({annotation:`${$.prefix}backend-protocol`,value:z,reason:`Backend protocol "${z}" is not supported by Gateway API or Envoy Gateway.`,guidance:W==="AJP"?"AJP is an Apache-specific protocol. Consider migrating your backend to serve HTTP/HTTPS, or use a sidecar proxy that translates AJP to HTTP.":"FastCGI is not supported by Envoy. Consider placing an HTTP-to-FastCGI adapter (e.g., an nginx sidecar) in front of the backend."}),{unsupported:Q};if(W==="HTTPS"){let V=TX(J);return{policies:[{apiVersion:"gateway.networking.k8s.io/v1alpha3",kind:"BackendTLSPolicy",metadata:{name:`${G}-backend-tls`,namespace:Z},spec:{targetRefs:[{group:"",kind:"Service",name:V}],validation:{hostname:V,wellKnownCACertificates:"System"}}}]}}return U.push({annotation:`${$.prefix}backend-protocol`,message:`Unrecognized backend-protocol value "${z}". Expected one of: HTTP, HTTPS, GRPC, GRPCS, AJP, FCGI.`,severity:"warning"}),{warnings:U}}};function TX($){let X=$.spec.rules;if(X&&X.length>0){let G=X[0].http;if(G&&G.paths.length>0){let J=G.paths[0].backend.service;if(J)return J.name}}let Z=$.spec.defaultBackend;if(Z?.service)return Z.service.name;return"unknown-service"}var RX={annotations:["proxy-ssl-secret","proxy-ssl-ciphers","proxy-ssl-name","proxy-ssl-protocols","proxy-ssl-verify","proxy-ssl-server-name"],category:"backend-tls",convert($){let{annotations:X,namespace:Z,name:G,ingress:J}=$,U=[],Q=X[`${$.prefix}proxy-ssl-secret`],z=X[`${$.prefix}proxy-ssl-ciphers`],W=X[`${$.prefix}proxy-ssl-name`],V=X[`${$.prefix}proxy-ssl-protocols`],H=X[`${$.prefix}proxy-ssl-verify`],Y=X[`${$.prefix}proxy-ssl-server-name`];if(z)U.push({annotation:`${$.prefix}proxy-ssl-ciphers`,message:`proxy-ssl-ciphers ("${z}") cannot be converted. BackendTLSPolicy doesn't support cipher selection.`,severity:"warning"});if(V)U.push({annotation:`${$.prefix}proxy-ssl-protocols`,message:`proxy-ssl-protocols ("${V}") cannot be converted. Protocol version is not configurable via BackendTLSPolicy.`,severity:"warning"});if(Y&&Y.toLowerCase()==="on")U.push({annotation:`${$.prefix}proxy-ssl-server-name`,message:"proxy-ssl-server-name is enabled. SNI is the default behavior in Envoy, so no additional configuration is needed.",severity:"info"});let _=AX(J),F=W||_,q={};if(H&&H.toLowerCase()==="off")q.wellKnownCACertificates="System",q.hostname=F;else if(Q)q.caCertificateRefs=[{name:Q,group:"",kind:"Secret"}],q.hostname=F;else q.wellKnownCACertificates="System",q.hostname=F;return{policies:[{apiVersion:"gateway.networking.k8s.io/v1alpha3",kind:"BackendTLSPolicy",metadata:{name:`${G}-backend-tls`,namespace:Z},spec:{targetRefs:[{group:"",kind:"Service",name:_}],validation:q}}],warnings:U.length>0?U:void 0}}};function AX($){let X=$.spec.rules;if(X&&X.length>0){let G=X[0].http;if(G&&G.paths.length>0){let J=G.paths[0].backend.service;if(J)return J.name}}let Z=$.spec.defaultBackend;if(Z?.service)return Z.service.name;return"unknown-service"}var PX={annotations:["proxy-body-size","client-body-buffer-size"],category:"body-size",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=X[`${$.prefix}proxy-body-size`],z=X[`${$.prefix}client-body-buffer-size`];if(z)U.push({annotation:`${$.prefix}client-body-buffer-size`,value:z,reason:"No direct Envoy Gateway equivalent for client-body-buffer-size.",guidance:"Envoy Gateway does not expose a client body buffer size setting. If buffering behavior is critical, consider configuring the EnvoyProxy custom resource directly or adjusting Envoy's per_connection_buffer_limit."});if(!Q)return{warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0};if(Q==="0")return J.push({annotation:`${$.prefix}proxy-body-size`,message:'proxy-body-size is "0" (unlimited). No BackendTrafficPolicy limit will be set.',severity:"info"}),{warnings:J,unsupported:U.length>0?U:void 0};let W=NX(Q);if(W===null)return J.push({annotation:`${$.prefix}proxy-body-size`,message:`Could not parse proxy-body-size value "${Q}". Expected format: "10m", "1g", "1024k", or raw bytes.`,severity:"warning"}),{warnings:J,unsupported:U.length>0?U:void 0};let V=bX(W),H={apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-body-size`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},requestBuffer:{limit:V}}},_=($.ingress.spec.ingressClassName??"")||"default-gateway",F={apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"ClientTrafficPolicy",metadata:{name:`${G}-client-body`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"Gateway",name:_},connection:{bufferLimit:V}}};return J.push({annotation:`${$.prefix}proxy-body-size`,message:`proxy-body-size mapped to BackendTrafficPolicy requestBuffer.limit: ${V} with a matching ClientTrafficPolicy connection.bufferLimit: ${V}. The requestBuffer enforces a per-request size limit (HTTP 413 if exceeded). The ClientTrafficPolicy connection buffer must be at least as large to avoid premature rejection.`,severity:"warning"}),{policies:[H,F],warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}};function bX($){if($>=1073741824&&$%1073741824===0)return`${$/1073741824}Gi`;if($>=1048576&&$%1048576===0)return`${$/1048576}Mi`;if($>=1024&&$%1024===0)return`${$/1024}Ki`;return`${$}`}function NX($){let Z=$.trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*([kmg]?)$/);if(!Z)return null;let G=Number.parseFloat(Z[1]);switch(Z[2]){case"k":return G*1024;case"m":return G*1024*1024;case"g":return G*1024*1024*1024;default:return G}}var CX={annotations:["canary","canary-by-header","canary-by-header-value","canary-weight","canary-weight-total"],category:"canary",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}canary`],J=X[`${$.prefix}canary-by-header`],U=X[`${$.prefix}canary-by-header-value`],Q=X[`${$.prefix}canary-weight`],z=X[`${$.prefix}canary-weight-total`];if(G!=="true")return Z.push({annotation:`${$.prefix}canary`,message:'Canary annotations present but canary is not "true". No conversion performed.',severity:"info"}),{warnings:Z};if(J){let W=U?`header "${J}" with value "${U}"`:`header "${J}"`;Z.push({annotation:`${$.prefix}canary-by-header`,message:`In NGINX, canary annotations split traffic between two Ingress resources using header matching (${W}). In Gateway API, traffic splitting is native via HTTPRoute header matches and backendRefs weights. Create a single HTTPRoute with a header match rule routing to the canary backend.`,severity:"warning"})}if(Q){let W=Number.parseInt(Q,10),V=z?Number.parseInt(z,10):100,H=V-W;Z.push({annotation:`${$.prefix}canary-weight`,message:`In NGINX, canary-weight splits traffic between two Ingress resources by percentage. In Gateway API, traffic splitting is native via HTTPRoute backendRefs weights. Create a single HTTPRoute with weight: ${W} on canary backend and weight: ${H} on main backend (total: ${V}).`,severity:"warning"})}return{warnings:Z.length>0?Z:void 0}}},SX={annotations:["enable-cors","cors-allow-origin","cors-allow-methods","cors-allow-headers","cors-expose-headers","cors-allow-credentials","cors-max-age"],category:"cors",convert($){let{annotations:X,namespace:Z,name:G}=$;if(X[`${$.prefix}enable-cors`]!=="true")return{warnings:[{annotation:`${$.prefix}enable-cors`,message:'CORS annotations present but enable-cors is not "true". No SecurityPolicy generated.',severity:"info"}]};let U={},Q=X[`${$.prefix}cors-allow-origin`];if(Q)U.allowOrigins=g0(Q);let z=X[`${$.prefix}cors-allow-methods`];if(z)U.allowMethods=g0(z);let W=X[`${$.prefix}cors-allow-headers`];if(W)U.allowHeaders=g0(W);let V=X[`${$.prefix}cors-expose-headers`];if(V)U.exposeHeaders=g0(V);let H=X[`${$.prefix}cors-allow-credentials`];if(H)U.allowCredentials=H==="true";let Y=X[`${$.prefix}cors-max-age`];if(Y)U.maxAge=Y.endsWith("s")?Y:`${Y}s`;return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"SecurityPolicy",metadata:{name:`${G}-cors`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},cors:U}}]}}};function g0($){return $.split(",").map((X)=>X.trim()).filter((X)=>X.length>0)}var EX={annotations:["custom-headers","upstream-vhost","x-forwarded-prefix"],category:"headers",convert($){let{annotations:X,name:Z}=$,G=[],J=[],U=[],Q=X[`${$.prefix}custom-headers`],z=X[`${$.prefix}upstream-vhost`],W=X[`${$.prefix}x-forwarded-prefix`];if(Q)U.push({annotation:`${$.prefix}custom-headers`,value:Q,reason:"In NGINX, custom-headers references a ConfigMap containing header key-value pairs to add to upstream requests. In Gateway API, headers are set via HTTPRoute RequestHeaderModifier filters.",guidance:"Manually translate the ConfigMap entries to individual header set operations in an HTTPRoute RequestHeaderModifier filter."});if(z){if(/\$\w+/.test(z))J.push({annotation:`${$.prefix}upstream-vhost`,message:`upstream-vhost value "${z}" contains an NGINX variable. In Envoy, urlRewrite.hostname is a static string — NGINX variables like $host are not evaluated. Replace with the actual hostname value.`,severity:"warning"});G.push({type:"URLRewrite",filter:{type:"URLRewrite",urlRewrite:{hostname:z}},targetRouteName:Z})}if(W)G.push({type:"RequestHeaderModifier",filter:{type:"RequestHeaderModifier",requestHeaderModifier:{set:[{name:"X-Forwarded-Prefix",value:W}]}},targetRouteName:Z});return{routeFilters:G.length>0?G:void 0,warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}},wX={annotations:["whitelist-source-range","denylist-source-range"],category:"ip-access",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}whitelist-source-range`],Q=X[`${$.prefix}denylist-source-range`];if(!U&&!Q)return{};if(U&&Q)J.push({annotation:`${$.prefix}whitelist-source-range`,message:"Both whitelist-source-range and denylist-source-range are set. In NGINX Ingress, whitelist takes precedence. The generated SecurityPolicy includes both rule sets; review and adjust precedence manually.",severity:"warning"});let z=[],W="Allow";if(U){W="Deny";let H=o8(U);z.push({action:"Allow",principal:{clientCIDRs:H}})}if(Q){if(!U)W="Allow";let H=o8(Q);z.push({action:"Deny",principal:{clientCIDRs:H}})}return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"SecurityPolicy",metadata:{name:`${G}-ip-access`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},authorization:{defaultAction:W,rules:z}}}],warnings:J.length>0?J:void 0}}};function o8($){return $.split(",").map((X)=>X.trim()).filter((X)=>X.length>0)}var kX={annotations:["upstream-hash-by","load-balance"],category:"load-balancing",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}upstream-hash-by`],Q=X[`${$.prefix}load-balance`],z;if(U){J.push({annotation:`${$.prefix}upstream-hash-by`,message:"In NGINX, upstream-hash-by uses NGINX variables for hash keys. In Envoy, consistent hashing supports SourceIP, Cookie, and Header-based hashing.",severity:"info"});let V=fX(U,$.prefix,J);if(V)z={type:"ConsistentHash",consistentHash:V}}if(Q){let V=yX(Q,$.prefix,J);if(V){if(!z)z=V}}if(!z)return{warnings:J.length>0?J:void 0};return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-load-balancing`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},loadBalancer:z}}],warnings:J.length>0?J:void 0}}};function fX($,X,Z){if($==="$remote_addr")return{type:"SourceIP"};if($==="$request_uri")return Z.push({annotation:`${X}upstream-hash-by`,message:"In NGINX, $request_uri hashes the full request URI. In Envoy, the closest consistent hash option is SourceIP. For URI-based hashing, use EnvoyPatchPolicy to configure a custom hash policy on the route.",severity:"warning"}),{type:"SourceIP"};let G=$.match(/^\$cookie_(.+)$/);if(G)return{type:"Cookie",cookie:{name:G[1]}};Z.push({annotation:`${X}upstream-hash-by`,message:`upstream-hash-by variable "${$}" has no direct Envoy equivalent. Envoy consistent hashing supports SourceIP, Cookie, and Header. Use EnvoyPatchPolicy to configure a custom hash policy.`,severity:"warning"});return}function yX($,X,Z){switch($){case"round_robin":return{type:"RoundRobin"};case"least_conn":return{type:"LeastRequest"};case"ip_hash":return{type:"ConsistentHash",consistentHash:{type:"SourceIP"}};case"ewma":return Z.push({annotation:`${X}load-balance`,message:"In NGINX, ewma (Exponentially Weighted Moving Average) selects the backend with lowest latency. In Envoy, the closest equivalent is LeastRequest which considers active request count, not latency. For latency-based routing, use EnvoyPatchPolicy.",severity:"warning"}),{type:"LeastRequest"};default:Z.push({annotation:`${X}load-balance`,message:`load-balance algorithm "${$}" is not recognized. Supported values: round_robin, least_conn, ip_hash, ewma.`,severity:"warning"});return}}var hX={annotations:["mirror-target","mirror-request-body","mirror-host"],category:"mirror",convert($){let{annotations:X,name:Z}=$,G=[],J=[],U=X[`${$.prefix}mirror-target`],Q=X[`${$.prefix}mirror-request-body`],z=X[`${$.prefix}mirror-host`];if(U){let W=vX(U);if(W&&"external"in W)G.push({annotation:`${$.prefix}mirror-target`,message:`mirror-target "${U}" points to an external host (${W.hostname}). In Gateway API, RequestMirror requires a Kubernetes Service as the backendRef. Create a Kubernetes ExternalName Service pointing to the external host, then reference that Service.`,severity:"warning"});else if(W)J.push({type:"RequestMirror",filter:{type:"RequestMirror",requestMirror:{backendRef:{name:W.serviceName,port:W.port}}},targetRouteName:Z});else G.push({annotation:`${$.prefix}mirror-target`,message:`mirror-target "${U}" could not be parsed as a valid URL. Expected format: http(s)://service.namespace.svc.cluster.local:port/path`,severity:"warning"})}if(Q?.toLowerCase()==="false")G.push({annotation:`${$.prefix}mirror-request-body`,message:"In NGINX, mirror-request-body: false mirrors only headers without the body. Gateway API RequestMirror always includes the full request body. There is no option to exclude the body.",severity:"warning"});if(z)G.push({annotation:`${$.prefix}mirror-host`,message:"In NGINX, mirror-host overrides the Host header for mirrored traffic. In Gateway API, RequestMirror uses the same Host header as the original request. To override, configure the mirror backend to accept any Host.",severity:"warning"});return{routeFilters:J.length>0?J:void 0,warnings:G.length>0?G:void 0}}};function gX($){let X=$.split(".");if(X.length===1)return!0;return X.includes("svc")}function vX($){try{let X=new URL($),Z=X.hostname;if(!Z)return null;if(!gX(Z))return{external:!0,hostname:Z};let J=Z.split(".")[0];if(!J)return null;let U;if(X.port)U=Number.parseInt(X.port,10);else U=X.protocol==="https:"?443:80;return{serviceName:J,port:U}}catch{return null}}var xX={annotations:["http2-push-preload","satisfy","from-to-www-redirect","service-upstream","server-alias"],category:"niche",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}http2-push-preload`],J=X[`${$.prefix}satisfy`],U=X[`${$.prefix}from-to-www-redirect`],Q=X[`${$.prefix}service-upstream`],z=X[`${$.prefix}server-alias`];if(G)Z.push({annotation:`${$.prefix}http2-push-preload`,value:G,reason:"In NGINX, this enables HTTP/2 server push for preload links. HTTP/2 server push has been deprecated in most browsers (Chrome removed it in 2022). In Envoy, HTTP/2 push is not supported. No conversion needed.",guidance:"HTTP/2 server push is deprecated. Remove this annotation — modern browsers use 103 Early Hints instead."});if(J)Z.push({annotation:`${$.prefix}satisfy`,value:J,reason:"In NGINX, satisfy controls whether all or any access restrictions must pass (satisfy all vs satisfy any). In Envoy, access control policies are evaluated independently.",guidance:"Configure equivalent logic using SecurityPolicy or ExtAuth. For 'satisfy any' behavior, implement the logic in a custom ExtAuth service."});if(U)Z.push({annotation:`${$.prefix}from-to-www-redirect`,value:U,reason:"In NGINX, this redirects from non-www to www (or vice versa). In Gateway API, create an additional HTTPRoute with a RequestRedirect filter for the redirect hostname.",guidance:"Create a separate HTTPRoute matching the non-www hostname with a RequestRedirect filter that sets the hostname to the www variant."});if(Q)Z.push({annotation:`${$.prefix}service-upstream`,value:Q,reason:"In NGINX, service-upstream forces routing to the Service ClusterIP rather than individual pod endpoints. In Envoy Gateway, this is the default behavior when using Service backends in HTTPRoute.",guidance:"No action needed — Envoy Gateway routes to Service ClusterIP by default. Remove this annotation."});if(z)Z.push({annotation:`${$.prefix}server-alias`,value:z,reason:"In NGINX, server-alias adds additional hostnames to the server block. In Gateway API, add additional entries to the HTTPRoute's hostnames array or add listeners to the Gateway.",guidance:"Add the alias hostnames to the HTTPRoute's spec.hostnames array and ensure matching Gateway listeners exist."});return{unsupported:Z.length>0?Z:void 0}}},mX={annotations:["enable-access-log","enable-opentelemetry","opentelemetry-trust-incoming-span","enable-rewrite-log"],category:"observability",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}enable-access-log`],J=X[`${$.prefix}enable-opentelemetry`],U=X[`${$.prefix}opentelemetry-trust-incoming-span`],Q=X[`${$.prefix}enable-rewrite-log`];if(G)Z.push({annotation:`${$.prefix}enable-access-log`,value:G,reason:"In NGINX, enable-access-log controls per-Ingress access logging. In Envoy Gateway, access logging is configured globally via the EnvoyProxy resource or per-route via BackendTrafficPolicy telemetry settings.",guidance:"Enable access logging via the EnvoyProxy resource's telemetry.accessLog configuration."});if(J)Z.push({annotation:`${$.prefix}enable-opentelemetry`,value:J,reason:"In NGINX, this enables the OpenTelemetry module per Ingress. In Envoy Gateway, OpenTelemetry tracing is configured via the EnvoyProxy resource's telemetry.tracing configuration. Envoy has native OpenTelemetry support — no module required.",guidance:"Configure tracing in the EnvoyProxy resource's telemetry.tracing section with your OpenTelemetry collector endpoint."});if(U)Z.push({annotation:`${$.prefix}opentelemetry-trust-incoming-span`,value:U,reason:"In NGINX, this controls whether incoming trace context is trusted. In Envoy, trace context propagation is configured in the tracing provider settings. Envoy trusts incoming context by default.",guidance:"No action needed — Envoy trusts incoming trace context by default. To change this behavior, configure the tracing provider in the EnvoyProxy resource."});if(Q)Z.push({annotation:`${$.prefix}enable-rewrite-log`,value:Q,reason:"In NGINX, this logs URL rewrite operations for debugging. In Envoy, equivalent debugging is available via access log filters and debug-level logging.",guidance:"Configure access log filters in the EnvoyProxy resource to capture rewrite operations, or enable debug-level logging for the router filter."});return{unsupported:Z.length>0?Z:void 0}}},uX={annotations:["proxy-buffering","proxy-buffer-size","proxy-buffers-number","proxy-busy-buffers-size"],category:"proxy-buffering",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}proxy-buffering`],J=X[`${$.prefix}proxy-buffer-size`],U=X[`${$.prefix}proxy-buffers-number`],Q=X[`${$.prefix}proxy-busy-buffers-size`];if(G)if(G.toLowerCase()==="off")Z.push({annotation:`${$.prefix}proxy-buffering`,message:"In NGINX, proxy-buffering controls response buffering (memory allocation, disk spillover). In Envoy, response streaming is the default behavior — responses are forwarded as received. To enable response buffering, use EnvoyPatchPolicy to configure the buffer filter. Note: Envoy buffers in memory only, not to disk.",severity:"warning"});else Z.push({annotation:`${$.prefix}proxy-buffering`,message:"In NGINX, proxy-buffering enables response buffering. In Envoy, response streaming is the default — responses are forwarded as received. No additional configuration needed.",severity:"info"});if(J)Z.push({annotation:`${$.prefix}proxy-buffer-size`,message:`In NGINX, proxy-buffer-size ("${J}") sets the buffer for reading the first part of the backend response (headers). In Envoy, there is no separate header buffer setting — use EnvoyPatchPolicy to configure per_connection_buffer_limit or the buffer filter.`,severity:"warning"});if(U)Z.push({annotation:`${$.prefix}proxy-buffers-number`,message:`In NGINX, proxy-buffers-number ("${U}") controls the number of buffers for reading backend responses. In Envoy, buffering is controlled by a single buffer limit rather than a count of fixed-size buffers. Use EnvoyPatchPolicy to configure the buffer filter.`,severity:"warning"});if(Q)Z.push({annotation:`${$.prefix}proxy-busy-buffers-size`,message:`In NGINX, proxy-busy-buffers-size ("${Q}") limits how much buffered data can be busy-sending to the client. Envoy does not have a separate busy-buffer concept — it streams data as it arrives. Use EnvoyPatchPolicy to configure the buffer filter if needed.`,severity:"warning"});return{warnings:Z.length>0?Z:void 0}}},dX={annotations:["proxy-cookie-domain","proxy-cookie-path","proxy-redirect-from","proxy-redirect-to","proxy-http-version","proxy-request-buffering"],category:"proxy-cookies",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}proxy-cookie-domain`],J=X[`${$.prefix}proxy-cookie-path`],U=X[`${$.prefix}proxy-redirect-from`],Q=X[`${$.prefix}proxy-redirect-to`],z=X[`${$.prefix}proxy-http-version`],W=X[`${$.prefix}proxy-request-buffering`];if(G)Z.push({annotation:`${$.prefix}proxy-cookie-domain`,value:G,reason:"In NGINX, proxy-cookie-domain rewrites cookie domain values in Set-Cookie headers from the backend. In Envoy, cookie manipulation is not natively supported in Gateway API.",guidance:"Use a Lua filter via EnvoyPatchPolicy to modify Set-Cookie headers, or handle cookie domain in the backend application."});if(J)Z.push({annotation:`${$.prefix}proxy-cookie-path`,value:J,reason:"In NGINX, proxy-cookie-path rewrites cookie path values in Set-Cookie headers from the backend. In Envoy, cookie manipulation is not natively supported in Gateway API.",guidance:"Use a Lua filter via EnvoyPatchPolicy to modify Set-Cookie headers, or handle cookie path in the backend application."});if(U||Q){let V=U?`${$.prefix}proxy-redirect-from`:`${$.prefix}proxy-redirect-to`,H=U||Q||"";Z.push({annotation:V,value:H,reason:"In NGINX, proxy-redirect-from/to rewrite Location and Refresh headers from the backend. In Envoy, response header manipulation is available via ResponseHeaderModifier, but regex-based rewriting requires a Lua filter via EnvoyPatchPolicy.",guidance:"For simple replacements, use ResponseHeaderModifier on the HTTPRoute. For regex-based rewrites, use a Lua filter via EnvoyPatchPolicy."})}if(z)Z.push({annotation:`${$.prefix}proxy-http-version`,value:z,reason:"In NGINX, proxy-http-version controls the HTTP version used for upstream connections (1.0 or 1.1). In Envoy, upstream HTTP version is auto-negotiated by default.",guidance:"To force HTTP/1.1, configure via BackendTrafficPolicy or EnvoyPatchPolicy cluster settings."});if(W)Z.push({annotation:`${$.prefix}proxy-request-buffering`,value:W,reason:"In NGINX, proxy-request-buffering controls whether the full request body is buffered before forwarding to upstream. In Envoy, request buffering is controlled via BackendTrafficPolicy requestBuffer.",guidance:"When disabled ('off'), Envoy streams the request body. Configure request buffering via BackendTrafficPolicy or the buffer filter via EnvoyPatchPolicy."});return{unsupported:Z.length>0?Z:void 0}}},pX={annotations:["limit-rps","limit-connections"],category:"rate-limit",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}limit-rps`],Q=X[`${$.prefix}limit-connections`];if(Q)J.push({annotation:`${$.prefix}limit-connections`,message:`limit-connections (${Q}) cannot be directly converted. Envoy Gateway rate limiting is request-based, not connection-based. Consider using limit-rps instead.`,severity:"warning"});if(!U)return{warnings:J.length>0?J:void 0};let z=Number.parseInt(U,10);if(Number.isNaN(z)||z<=0)return J.push({annotation:`${$.prefix}limit-rps`,message:`Could not parse limit-rps value "${U}". Expected a positive integer.`,severity:"warning"}),{warnings:J.length>0?J:void 0};return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-rate-limit`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},rateLimit:{type:"Local",local:{rules:[{limit:{requests:z,unit:"Second"}}]}}}}],warnings:J.length>0?J:void 0}}},lX={annotations:["permanent-redirect","permanent-redirect-code","temporal-redirect","ssl-redirect","force-ssl-redirect"],category:"redirects",convert($){let{annotations:X,name:Z}=$,G=[],J=X[`${$.prefix}permanent-redirect`],U=X[`${$.prefix}permanent-redirect-code`],Q=X[`${$.prefix}temporal-redirect`],z=X[`${$.prefix}ssl-redirect`],W=X[`${$.prefix}force-ssl-redirect`];if(J){let V=U?Number.parseInt(U,10):301,H=t8(J);H.statusCode=V,G.push({type:"RequestRedirect",filter:{type:"RequestRedirect",requestRedirect:H},targetRouteName:Z})}if(Q){let V=t8(Q);V.statusCode=302,G.push({type:"RequestRedirect",filter:{type:"RequestRedirect",requestRedirect:V},targetRouteName:Z})}if(z==="true"||W==="true"){if(!J&&!Q)G.push({type:"RequestRedirect",filter:{type:"RequestRedirect",requestRedirect:{scheme:"https",statusCode:301}},targetRouteName:Z})}return{routeFilters:G.length>0?G:void 0}}};function t8($){let X={};try{let Z=new URL($);if(Z.protocol)X.scheme=Z.protocol.replace(":","");if(Z.hostname)X.hostname=Z.hostname;if(Z.port)X.port=Number.parseInt(Z.port,10);if(Z.pathname&&Z.pathname!=="/")X.path={type:"ReplaceFullPath",replaceFullPath:Z.pathname}}catch{X.path={type:"ReplaceFullPath",replaceFullPath:$}}return X}var cX={annotations:["rewrite-target","use-regex"],category:"rewrites",convert($){let{annotations:X,name:Z}=$,G=[],J=X[`${$.prefix}rewrite-target`];if(!J)return{};let U=X[`${$.prefix}use-regex`]==="true",Q=/\$\d/.test(J);if(U&&Q)G.push({annotation:`${$.prefix}rewrite-target`,message:"In NGINX, rewrite-target supports capture groups ($1, $2) from regex path matches. "+"In Gateway API, URLRewrite does not support capture groups — the replacement is a static path. "+"For dynamic rewrites, consider using EnvoyPatchPolicy with Envoy's header/path manipulation filters.",severity:"warning"});if(/\$[a-zA-Z_]\w*/.test(J)&&!Q)G.push({annotation:`${$.prefix}rewrite-target`,message:`rewrite-target value "${J}" contains an NGINX variable. In Envoy, URLRewrite paths are static strings — NGINX variables are not evaluated. Replace with the actual path value or use EnvoyPatchPolicy for dynamic path manipulation.`,severity:"warning"});let W=U?"ReplaceFullPath":"ReplacePrefixMatch",V={type:W};if(W==="ReplaceFullPath")V.replaceFullPath=J;else V.replacePrefixMatch=J;return{routeFilters:[{type:"URLRewrite",filter:{type:"URLRewrite",urlRewrite:{path:V}},targetRouteName:Z}],warnings:G.length>0?G:void 0}}},iX={annotations:["affinity","affinity-mode","session-cookie-name","session-cookie-path","session-cookie-max-age","session-cookie-expires","session-cookie-samesite","session-cookie-change-on-failure"],category:"session-affinity",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=X[`${$.prefix}affinity`],z=X[`${$.prefix}affinity-mode`],W=X[`${$.prefix}session-cookie-name`],V=X[`${$.prefix}session-cookie-path`],H=X[`${$.prefix}session-cookie-max-age`]||X[`${$.prefix}session-cookie-expires`],Y=X[`${$.prefix}session-cookie-samesite`],_=X[`${$.prefix}session-cookie-change-on-failure`];if(!Q)return{};if(Q!=="cookie")return U.push({annotation:`${$.prefix}affinity`,value:Q,reason:`Affinity type "${Q}" is not supported by Envoy Gateway. Only cookie-based session affinity can be converted.`,guidance:"Envoy Gateway supports cookie-based session persistence via BackendTrafficPolicy. Consider switching to cookie-based affinity."}),{unsupported:U};if(z&&z.toLowerCase()==="balanced")J.push({annotation:`${$.prefix}affinity-mode`,message:"In NGINX, affinity-mode 'balanced' allows the load balancer to redistribute sessions when new backends are added. In Envoy, ConsistentHash naturally handles this — when backends change, only a fraction of sessions are redistributed.",severity:"warning"});if(_)J.push({annotation:`${$.prefix}session-cookie-change-on-failure`,message:"In NGINX, session-cookie-change-on-failure reassigns the session to a new backend when the current one fails. In Envoy, failed backends are removed from the hash ring via outlier detection — sessions are automatically redistributed without a specific cookie change mechanism.",severity:"warning"});let F={name:W||"INGRESSCOOKIE",attributes:{path:V||"/",sameSite:Y||"Lax"}};if(H)F.ttl=H.endsWith("s")?H:`${H}s`;return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-session-affinity`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},loadBalancer:{type:"ConsistentHash",consistentHash:{type:"Cookie",cookie:F}}}}],warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}},sX=/proxy_set_header\s+(\S+)\s+(.+?)\s*;/,nX=/add_header\s+(\S+)\s+(.+?)(?:\s+always)?\s*;/,aX=/more_set_headers\s+"([^:]+):\s*(.+?)"\s*;/,rX=/proxy_hide_header\s+(\S+)\s*;/,e8=/\$\w+/;function $6($){if($.startsWith('"')&&$.endsWith('"')||$.startsWith("'")&&$.endsWith("'"))return $.slice(1,-1);return $}function oX($,X){return $.toLowerCase().replace(/_/g,"-")==="x-forwarded-client-cert"&&(X.includes("$ssl_client_escaped_cert")||X.includes("$ssl_client_cert"))}function tX($,X,Z){let G=[],J=[],U=[],Q=$.split(` +`);for(let W of Q){let V=W.trim();if(!V||V.startsWith("#"))continue;let H=!1,Y=V.match(sX);if(Y){H=!0;let I=Y[1],D=$6(Y[2]);if(oX(I,D))J.push({annotation:X,message:"Envoy natively populates the X-Forwarded-Client-Cert (XFCC) header when mTLS is configured. No manual conversion needed.",severity:"info"});else{if(e8.test(D))J.push({annotation:X,message:"NGINX variable detected in header value. Envoy uses different variable syntax — verify the header value is correct.",severity:"warning"});G.push({type:"RequestHeaderModifier",filter:{requestHeaderModifier:{set:[{name:I,value:D}]}},targetRouteName:Z})}continue}let _=V.match(nX);if(_){H=!0;let I=_[1],D=$6(_[2]);G.push({type:"ResponseHeaderModifier",filter:{responseHeaderModifier:{add:[{name:I,value:D}]}},targetRouteName:Z});continue}let F=V.match(aX);if(F){H=!0;let I=F[1],D=F[2];if(e8.test(D))J.push({annotation:X,message:"NGINX variable detected in header value. Envoy uses different variable syntax — verify the header value is correct for your Envoy configuration.",severity:"warning"});G.push({type:"ResponseHeaderModifier",filter:{responseHeaderModifier:{set:[{name:I,value:D}]}},targetRouteName:Z});continue}let q=V.match(rX);if(q){H=!0;let I=q[1];G.push({type:"ResponseHeaderModifier",filter:{responseHeaderModifier:{remove:[I]}},targetRouteName:Z});continue}if(!H)U.push(V)}let z=[];if(U.length>0)z.push({annotation:X,value:U.join(` +`),reason:"These NGINX configuration directives could not be automatically converted.",guidance:"Recommended workaround (verify manually): Use EnvoyPatchPolicy to apply equivalent Envoy configuration. Test thoroughly in a non-production environment before applying."});return{routeFilters:G,warnings:J,unsupported:z}}var eX={annotations:["configuration-snippet","server-snippet","stream-snippet"],category:"snippets",convert($){let{annotations:X,name:Z}=$,G=[],J=[],U=[],Q=X[`${$.prefix}configuration-snippet`],z=X[`${$.prefix}server-snippet`],W=X[`${$.prefix}stream-snippet`];for(let[V,H]of[["configuration-snippet",Q],["server-snippet",z]])if(H){let Y=tX(H,`${$.prefix}${V}`,Z);G.push(...Y.routeFilters),J.push(...Y.warnings),U.push(...Y.unsupported)}if(W)U.push({annotation:`${$.prefix}stream-snippet`,value:W,reason:"Stream-level NGINX configuration has no Gateway API equivalent.",guidance:"Stream-level NGINX configuration has no Gateway API equivalent. Consider TCPRoute or UDPRoute for stream workloads, and EnvoyPatchPolicy for advanced stream configuration."});return{routeFilters:G.length>0?G:void 0,warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}},$Z={annotations:["ssl-passthrough"],category:"ssl-passthrough",convert($){let{annotations:X,namespace:Z,name:G,ingress:J}=$;if(X[`${$.prefix}ssl-passthrough`]!=="true")return{};let Q=[],z=J.spec.rules?.map((F)=>F.host).filter(Boolean),W=J.spec.ingressClassName||"default-gateway",V=J.spec.rules?.[0]?.http?.paths?.[0],H=V?.backend?.service?.name??"unknown",Y=V?.backend?.service?.port?.number??443,_={apiVersion:"gateway.networking.k8s.io/v1alpha2",kind:"TLSRoute",metadata:{name:`${G}-passthrough`,namespace:Z},spec:{parentRefs:[{group:"gateway.networking.k8s.io",kind:"Gateway",name:W}],hostnames:z,rules:[{backendRefs:[{name:H,port:Y}]}]}};return Q.push({annotation:`${$.prefix}ssl-passthrough`,message:"ssl-passthrough generates a TLSRoute instead of an HTTPRoute. You must also change the Gateway listener for this host to use `tls.mode: Passthrough` "+"(instead of Terminate). The generated HTTPRoute for this Ingress should be removed — "+"only the TLSRoute is needed for passthrough. "+"Note: With passthrough, the Gateway does not terminate TLS — the backend must handle TLS termination.",severity:"warning"}),{policies:[_],warnings:Q}}},XZ={annotations:["proxy-connect-timeout","proxy-send-timeout","proxy-read-timeout","proxy-next-upstream-timeout","proxy-next-upstream-tries"],category:"timeouts",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}proxy-connect-timeout`],Q=X[`${$.prefix}proxy-send-timeout`],z=X[`${$.prefix}proxy-read-timeout`],W={},V={};if(U)V.connectTimeout=Y8(U),J.push({annotation:`${$.prefix}proxy-connect-timeout`,message:"In NGINX, proxy-connect-timeout controls the TCP handshake timeout to the upstream server. In Envoy, this maps to timeout.tcp.connectTimeout (time to establish the upstream connection). "+"Note: Envoy's connectionIdleTimeout is a different concept — it controls how long idle connections are kept alive.",severity:"info"});if(z)W.requestTimeout=Y8(z),J.push({annotation:`${$.prefix}proxy-read-timeout`,message:"In NGINX, proxy-read-timeout resets on each data chunk received from the upstream — a slow but active transfer will not time out. In Envoy, requestTimeout is the total time allowed for the entire response. Long-running transfers (large file downloads, streaming) may be terminated prematurely. Consider increasing the timeout or using per-stream timeouts via EnvoyPatchPolicy for streaming workloads.",severity:"warning"});if(Q)J.push({annotation:`${$.prefix}proxy-send-timeout`,message:`In NGINX, proxy-send-timeout (${Q}s) controls how long to wait between two successive write operations to the backend. In Envoy, timeout behavior is unified under requestTimeout (total request lifecycle). There's no separate send phase — configure BackendTrafficPolicy timeout.http.requestTimeout as an approximation.`,severity:"warning"});let H=X[`${$.prefix}proxy-next-upstream-timeout`],Y=X[`${$.prefix}proxy-next-upstream-tries`],_={};if(H)_.perRetry={timeout:Y8(H)};if(Y){let q=Number.parseInt(Y,10);if(!Number.isNaN(q))_.numRetries=q}if(H||Y)J.push({annotation:`${$.prefix}proxy-next-upstream-timeout`,message:"proxy-next-upstream-timeout/tries mapped to BackendTrafficPolicy retry config. NGINX retries on specific error conditions (error, timeout, invalid_header, etc.) which are configured via proxy-next-upstream annotation (not handled). Envoy retries on 5xx by default. Verify retry behavior matches expectations.",severity:"warning"});if(Object.keys(W).length===0&&Object.keys(V).length===0&&Object.keys(_).length===0)return{warnings:J.length>0?J:void 0};return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-timeouts`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},...Object.keys(W).length>0||Object.keys(V).length>0?{timeout:{...Object.keys(W).length>0?{http:W}:{},...Object.keys(V).length>0?{tcp:V}:{}}}:{},...Object.keys(_).length>0?{retry:_}:{}}}],warnings:J.length>0?J:void 0}}};function Y8($){return $.endsWith("s")?$:`${$}s`}function ZZ(){let $=new f8(["nginx.ingress.kubernetes.io/"]);return $.register(SX),$.register(cX),$.register(lX),$.register(XZ),$.register(PX),$.register(MX),$.register(wX),$.register(EX),$.register(qX),$.register($Z),$.register(pX),$.register(LX),$.register(OX),$.register(jX),$.register(CX),$.register(iX),$.register(RX),$.register(uX),$.register(dX),$.register(hX),$.register(kX),$.register(mX),$.register(xX),$.register(eX),$}var GZ={name:"ingress-nginx",displayName:"NGINX Ingress (community)",annotationPrefixes:["nginx.ingress.kubernetes.io/"],docsUrl:"https://kubernetes.github.io/ingress-nginx/user-guide/nginx-configuration/annotations/",createEngine:ZZ},JZ={annotations:["app-root"],category:"app-root",convert($){let{annotations:X,name:Z}=$,G=X[`${$.prefix}app-root`];if(!G)return{};return{routeFilters:[{type:"RequestRedirect",filter:{type:"RequestRedirect",requestRedirect:{path:{type:"ReplaceFullPath",value:G},statusCode:302}},targetRouteName:Z}]}}},UZ={annotations:["basic-auth-secret","basic-auth-realm"],category:"auth-basic",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}basic-auth-secret`],Q=X[`${$.prefix}basic-auth-realm`];if(!U)return{};if(Q)J.push({annotation:`${$.prefix}basic-auth-realm`,message:`basic-auth-realm ("${Q}") cannot be converted. Envoy Gateway BasicAuth does not support custom realms.`,severity:"warning"});return J.push({annotation:`${$.prefix}basic-auth-secret`,message:`The NGINX basic auth secret "${U}" must be adapted for Envoy Gateway. Two changes needed: (1) The secret key must be '.htpasswd' (NGINX uses 'auth'). `+"(2) Password hashes must be in {SHA} format — NGINX supports APR1/bcrypt ($apr1$, $2y$) "+"but Envoy Gateway only supports {SHA} (SHA-1 base64). Regenerate with: htpasswd -s -nb ",severity:"warning"}),{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"SecurityPolicy",metadata:{name:`${G}-basic-auth`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},basicAuth:{users:{name:U}}}}],warnings:J.length>0?J:void 0}}},QZ={annotations:["max-fails","fail-timeout","max-conns","keepalive"],category:"backend-connection",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=[],z=X[`${$.prefix}max-fails`],W=X[`${$.prefix}fail-timeout`],V=X[`${$.prefix}max-conns`],H=X[`${$.prefix}keepalive`];if(z||W){let Y=[];if(z)Y.push(`max-fails=${z}`);if(W)Y.push(`fail-timeout=${W}`);J.push({annotation:`${$.prefix}${z?"max-fails":"fail-timeout"}`,message:`In NGINX, max-fails and fail-timeout configure passive health checking (mark upstream unhealthy after N failures within timeout). In Envoy, this maps to outlier detection in BackendTrafficPolicy. The behavior model differs — Envoy ejects hosts from the load balancing pool rather than marking them down. Current values: ${Y.join(", ")}. Recommended workaround (verify manually): Test thoroughly in non-production first.`,severity:"warning"});let _={};if(z)_.consecutive5XxErrors=Number.parseInt(z,10)||1;if(W){let F=W.endsWith("s")?W:`${W}s`;_.interval=F,_.baseEjectionTime=F}Q.push({apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-backend-connection`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},healthCheck:{passive:_}}})}if(V)U.push({annotation:`${$.prefix}max-conns`,value:V,reason:"In NGINX, max-conns limits concurrent connections per upstream server. In Envoy, configure max_connections via the cluster circuit breaker thresholds.",guidance:"Use EnvoyPatchPolicy to set circuit_breakers.thresholds[].max_connections on the cluster. Recommended workaround (verify manually): Test thoroughly in non-production first."});if(H)U.push({annotation:`${$.prefix}keepalive`,value:H,reason:"In NGINX, keepalive sets the number of idle keepalive connections to upstream servers cached per worker. In Envoy, upstream connection pooling is automatic.",guidance:"To tune connection pooling, configure connection pool settings via EnvoyPatchPolicy on the cluster. Recommended workaround (verify manually): Test thoroughly in non-production first."});return{policies:Q.length>0?Q:void 0,warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}},zZ={annotations:["ssl-services","grpc-services","websocket-services"],category:"backend-services",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=X[`${$.prefix}ssl-services`],z=X[`${$.prefix}grpc-services`],W=X[`${$.prefix}websocket-services`];if(Q){let V=Q.split(",").map((H)=>H.trim()).filter(Boolean);for(let H of V)U.push({apiVersion:"gateway.networking.k8s.io/v1alpha3",kind:"BackendTLSPolicy",metadata:{name:`${H}-backend-tls`,namespace:Z},spec:{targetRefs:[{group:"",kind:"Service",name:H}],validation:{caCertificateRefs:[],hostname:H}}});J.push({annotation:`${$.prefix}ssl-services`,message:`BackendTLSPolicy created for SSL service(s): ${V.join(", ")}. You may need to add a CA certificate reference for backend TLS validation.`,severity:"info"})}if(z)J.push({annotation:`${$.prefix}grpc-services`,message:`gRPC service(s) detected: ${z}. Consider using GRPCRoute instead of HTTPRoute for gRPC services. Gateway API provides native gRPC support via the GRPCRoute resource.`,severity:"warning"});if(W)J.push({annotation:`${$.prefix}websocket-services`,message:`WebSocket service(s) detected: ${W}. WebSocket upgrade is supported by default in Envoy Gateway — no additional configuration is required.`,severity:"info"});return{policies:U.length>0?U:void 0,warnings:J.length>0?J:void 0}}},VZ={annotations:["client-max-body-size","client-body-buffer-size"],category:"body-size",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=X[`${$.prefix}client-max-body-size`],z=X[`${$.prefix}client-body-buffer-size`];if(z)U.push({annotation:`${$.prefix}client-body-buffer-size`,value:z,reason:"In NGINX, client-body-buffer-size controls the buffer for reading the client request body. In Envoy, request buffering is configured via BackendTrafficPolicy requestBuffer. There's no separate buffer-size-only setting."});if(!Q)return{unsupported:U.length>0?U:void 0};if(Q==="0")return J.push({annotation:`${$.prefix}client-max-body-size`,message:'client-max-body-size is "0" (unlimited). No BackendTrafficPolicy limit will be set.',severity:"info"}),{warnings:J,unsupported:U.length>0?U:void 0};let W=YZ(Q);if(W===null)return J.push({annotation:`${$.prefix}client-max-body-size`,message:`Could not parse client-max-body-size value "${Q}". Expected format: "10m", "1g", "1024k", or raw bytes.`,severity:"warning"}),{warnings:J,unsupported:U.length>0?U:void 0};let V=WZ(W),H={apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-body-size`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},requestBuffer:{limit:V}}};return J.push({annotation:`${$.prefix}client-max-body-size`,message:`client-max-body-size mapped to BackendTrafficPolicy requestBuffer.limit: ${V}. The requestBuffer enforces a per-request size limit (HTTP 413 if exceeded). Consider also creating a ClientTrafficPolicy with connection.bufferLimit set to at least the same value to avoid premature rejection.`,severity:"warning"}),{policies:[H],warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}};function WZ($){if($>=1073741824&&$%1073741824===0)return`${$/1073741824}Gi`;if($>=1048576&&$%1048576===0)return`${$/1048576}Mi`;if($>=1024&&$%1024===0)return`${$/1024}Ki`;return`${$}`}function YZ($){let Z=$.trim().toLowerCase().match(/^(\d+(?:\.\d+)?)\s*([kmg]?)$/);if(!Z)return null;let G=Number.parseFloat(Z[1]);switch(Z[2]){case"k":return G*1024;case"m":return G*1024*1024;case"g":return G*1024*1024*1024;default:return G}}var HZ={annotations:["proxy-set-headers","proxy-hide-headers","proxy-pass-headers"],category:"headers",convert($){let{annotations:X,name:Z}=$,G=[],J=[],U=[],Q=X[`${$.prefix}proxy-set-headers`],z=X[`${$.prefix}proxy-hide-headers`],W=X[`${$.prefix}proxy-pass-headers`];if(Q)if(Q.includes(":")){let V=Q.split(",").map((Y)=>Y.trim()),H=[];for(let Y of V){let _=Y.indexOf(":");if(_>0)H.push({name:Y.substring(0,_).trim(),value:Y.substring(_+1).trim()})}if(H.length>0){G.push({type:"RequestHeaderModifier",filter:{type:"RequestHeaderModifier",requestHeaderModifier:{set:H}},targetRouteName:Z});let Y=H.filter((_)=>/\$\w+/.test(_.value));if(Y.length>0){let _=Y.map((F)=>`${F.name}: ${F.value}`).join(", ");U.push({annotation:`${$.prefix}proxy-set-headers`,message:`Header value(s) use NGINX variable syntax (${_}). Envoy does not support NGINX variables like $host or $remote_addr. Use Envoy header value syntax (e.g., %REQ(:authority)%, %DOWNSTREAM_REMOTE_ADDRESS%) or static values instead.`,severity:"warning"})}}}else J.push({annotation:`${$.prefix}proxy-set-headers`,value:Q,reason:"proxy-set-headers references a ConfigMap which cannot be automatically translated to Gateway API filters.",guidance:"Manually translate the ConfigMap headers into an HTTPRoute RequestHeaderModifier filter with set entries."});if(z){let V=z.split(",").map((H)=>H.trim()).filter(Boolean);if(V.length>0)G.push({type:"ResponseHeaderModifier",filter:{type:"ResponseHeaderModifier",responseHeaderModifier:{remove:V}},targetRouteName:Z})}if(W)U.push({annotation:`${$.prefix}proxy-pass-headers`,message:`proxy-pass-headers ("${W}") noted. Envoy Gateway passes most upstream headers by default. No conversion is needed unless specific header filtering is required.`,severity:"info"});return{routeFilters:G.length>0?G:void 0,unsupported:J.length>0?J:void 0,warnings:U.length>0?U:void 0}}},_Z={annotations:["health-checks","health-checks-mandatory","health-checks-mandatory-queue"],category:"health-checks",convert($){let{annotations:X}=$,Z=[],G=[],J=$.prefixes.find((W)=>W==="nginx.com/")??"nginx.com/",U=X[`${J}health-checks`],Q=X[`${J}health-checks-mandatory`],z=X[`${J}health-checks-mandatory-queue`];if(!U&&!Q&&!z)return{};if(Z.push({annotation:`${J}health-checks`,message:"Health check annotations require NGINX Plus. Verify your license supports this feature.",severity:"warning"}),U)G.push({annotation:`${J}health-checks`,value:U,reason:"Active health checks have no direct annotation-based equivalent in Gateway API.",guidance:"Active health checks can be configured via Envoy Gateway's BackendTrafficPolicy healthCheck field. See: https://gateway.envoyproxy.io/docs/api/extension_types/#healthcheck"});if(Q)G.push({annotation:`${J}health-checks-mandatory`,value:Q,reason:"Mandatory health checks have no direct Gateway API equivalent.",guidance:"Configure health check requirements in Envoy Gateway's BackendTrafficPolicy healthCheck field."});if(z)G.push({annotation:`${J}health-checks-mandatory-queue`,value:z,reason:"Mandatory health check queue size has no direct Gateway API equivalent.",guidance:"Envoy Gateway does not support queuing requests pending health checks. Consider circuit breaker configuration as an alternative."});return{warnings:Z.length>0?Z:void 0,unsupported:G.length>0?G:void 0}}},FZ={annotations:["hsts","hsts-max-age","hsts-include-subdomains","hsts-behind-proxy"],category:"hsts",convert($){let{annotations:X,name:Z}=$,G=[],J=X[`${$.prefix}hsts`],U=X[`${$.prefix}hsts-max-age`],Q=X[`${$.prefix}hsts-include-subdomains`],z=X[`${$.prefix}hsts-behind-proxy`];if(J!=="true")return{};let W=U?Number.parseInt(U,10):2592000,V=`max-age=${Number.isNaN(W)?2592000:W}`;if(Q==="true")V+="; includeSubDomains";if(z==="true")G.push({annotation:`${$.prefix}hsts-behind-proxy`,message:"hsts-behind-proxy is enabled. This setting adjusts HSTS behavior when NGINX is behind a load balancer. Envoy Gateway handles this via its own proxy configuration — no direct equivalent is needed.",severity:"info"});return{routeFilters:[{type:"ResponseHeaderModifier",filter:{type:"ResponseHeaderModifier",responseHeaderModifier:{set:[{name:"Strict-Transport-Security",value:V}]}},targetRouteName:Z}],warnings:G.length>0?G:void 0}}},IZ={annotations:["jwt-key","jwt-realm","jwt-token","jwt-login-url"],category:"jwt-auth",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=$.prefixes.find((Y)=>Y==="nginx.com/")??"nginx.com/",z=X[`${Q}jwt-key`],W=X[`${Q}jwt-realm`],V=X[`${Q}jwt-token`],H=X[`${Q}jwt-login-url`];if(!z&&!W&&!V&&!H)return{};if(J.push({annotation:`${Q}jwt-key`,message:"JWT annotations require NGINX Plus. Verify your license supports this feature.",severity:"warning"}),W)U.push({annotation:`${Q}jwt-realm`,value:W,reason:"JWT realm has no direct equivalent in Envoy Gateway JWT filter.",guidance:"Envoy Gateway SecurityPolicy JWT configuration does not support custom realms."});if(V)J.push({annotation:`${Q}jwt-token`,message:`jwt-token ("${V}") specifies where to extract the JWT. Envoy Gateway extracts JWTs from the Authorization header by default or can be configured via the JWT provider's extractFrom field.`,severity:"info"});if(H)U.push({annotation:`${Q}jwt-login-url`,value:H,reason:"JWT login URL redirect has no direct equivalent in Envoy Gateway.",guidance:"Consider implementing login redirect logic in your application or using Envoy Gateway's ExtAuth for custom authentication flows."});if(z){let Y={apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"SecurityPolicy",metadata:{name:`${G}-jwt-auth`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},jwt:{providers:[{name:"nginx-plus-jwt",remoteJWKS:{uri:"https://REPLACE_ME/.well-known/jwks.json"}}]}}};return J.push({annotation:`${Q}jwt-key`,message:`jwt-key ("${z}") references a Kubernetes Secret containing the JWT key. Envoy Gateway JWT uses remoteJWKS instead. Update the JWKS URI in the generated SecurityPolicy to point to your JWKS endpoint.`,severity:"warning"}),{policies:[Y],warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}return{warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}},DZ={annotations:["lb-method"],category:"lb-method",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}lb-method`];if(!U)return{};let Q;switch(U){case"round_robin":Q={type:"RoundRobin"};break;case"least_conn":Q={type:"LeastRequest"};break;case"ip_hash":Q={type:"ConsistentHash",consistentHash:{type:"SourceIP"}};break;case"random":Q={type:"Random"};break;default:if(U.startsWith("hash ")){let W=U.slice(5).trim();Q={type:"ConsistentHash",consistentHash:{type:"Header"}},J.push({annotation:`${$.prefix}lb-method`,message:`lb-method uses hash with NGINX variable "${W}". Envoy ConsistentHash is configured but the NGINX variable must be manually mapped to an Envoy hash policy (Header, Cookie, or SourceIP). Review and adjust the consistentHash configuration.`,severity:"warning"})}else return J.push({annotation:`${$.prefix}lb-method`,message:`Unsupported lb-method "${U}". Envoy Gateway supports RoundRobin, LeastRequest, Random, and ConsistentHash.`,severity:"warning"}),{warnings:J};break}return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-lb`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},loadBalancer:Q}}],warnings:J.length>0?J:void 0}}},qZ={annotations:["listen-ports","listen-ports-ssl"],category:"listen-ports",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}listen-ports`],J=X[`${$.prefix}listen-ports-ssl`];if(G){let U=X6(G);if(U.length>0)Z.push({annotation:`${$.prefix}listen-ports`,message:`Custom listener ports detected. In NGINX, listen-ports defines which ports the Ingress listens on. In Gateway API, ports are defined on the Gateway listeners directly. Add additional listeners to your Gateway resource for ports: ${U.join(", ")}.`,severity:"warning"});else Z.push({annotation:`${$.prefix}listen-ports`,message:`Could not parse listen-ports value: ${G}. Expected a JSON array of port numbers.`,severity:"warning"})}if(J){let U=X6(J);if(U.length>0)Z.push({annotation:`${$.prefix}listen-ports-ssl`,message:`Custom SSL listener ports detected. In NGINX, listen-ports-ssl defines which TLS ports the Ingress listens on. In Gateway API, ports are defined on the Gateway listeners directly. Add additional HTTPS listeners to your Gateway resource for ports: ${U.join(", ")}. Each SSL listener needs TLS mode: Terminate (or Passthrough) configured on the Gateway listener.`,severity:"warning"});else Z.push({annotation:`${$.prefix}listen-ports-ssl`,message:`Could not parse listen-ports-ssl value: ${J}. Expected a JSON array of port numbers.`,severity:"warning"})}return{warnings:Z.length>0?Z:void 0}}};function X6($){try{let X=JSON.parse($);if(Array.isArray(X))return X.filter((Z)=>typeof Z==="number"&&Z>0&&Z<=65535)}catch{}return[]}var LZ={annotations:["path-regex"],category:"path-regex",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}path-regex`];if(!G)return{};if(G==="case_insensitive")Z.push({annotation:`${$.prefix}path-regex`,message:'path-regex is set to "case_insensitive". Gateway API RegularExpression match type is case-sensitive by default. To achieve case-insensitive regex matching in Envoy Gateway, configure an EnvoyPatchPolicy.',severity:"warning"});else Z.push({annotation:`${$.prefix}path-regex`,message:`path-regex is set to "${G}". Path regex is enabled — Gateway API RegularExpression match type will be used for regex paths.`,severity:"info"});return{warnings:Z}}},OZ={annotations:["proxy-buffering","proxy-buffers","proxy-buffer-size","proxy-busy-buffers-size","proxy-max-temp-file-size"],category:"proxy-buffering",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}proxy-buffering`],J=X[`${$.prefix}proxy-buffers`],U=X[`${$.prefix}proxy-buffer-size`],Q=X[`${$.prefix}proxy-busy-buffers-size`],z=X[`${$.prefix}proxy-max-temp-file-size`];if(G)Z.push({annotation:`${$.prefix}proxy-buffering`,value:G,reason:"In NGINX, proxy-buffering controls whether responses from upstream are buffered before sending to the client. "+"In Envoy, response buffering is handled differently — Envoy streams responses by default.",guidance:"To enable response buffering, use EnvoyPatchPolicy to configure the buffer filter on the route. Alternatively, use ClientTrafficPolicy connection.bufferLimit for connection-level buffering. Recommended workaround (verify manually): Test thoroughly in non-production first."});if(U)Z.push({annotation:`${$.prefix}proxy-buffer-size`,value:U,reason:"In NGINX, proxy-buffer-size controls the memory allocation for the first part of the upstream response (headers). In Envoy, the per_connection_buffer_limit_bytes on the listener and cluster control connection-level buffering.",guidance:"Configure via EnvoyPatchPolicy or ClientTrafficPolicy connection.bufferLimit. Recommended workaround (verify manually): Test thoroughly in non-production first."});if(J)Z.push({annotation:`${$.prefix}proxy-buffers`,value:J,reason:"In NGINX, proxy-buffers controls the number and size of buffers for response buffering. In Envoy, the per_connection_buffer_limit_bytes on the listener and cluster control connection-level buffering.",guidance:"Configure via EnvoyPatchPolicy or ClientTrafficPolicy connection.bufferLimit. Recommended workaround (verify manually): Test thoroughly in non-production first."});if(Q)Z.push({annotation:`${$.prefix}proxy-busy-buffers-size`,value:Q,reason:"In NGINX, proxy-busy-buffers-size limits how much buffer space can be busy sending to the client while still reading from upstream. In Envoy, the per_connection_buffer_limit_bytes on the listener and cluster control connection-level buffering.",guidance:"Configure via EnvoyPatchPolicy or ClientTrafficPolicy connection.bufferLimit. Recommended workaround (verify manually): Test thoroughly in non-production first."});if(z)Z.push({annotation:`${$.prefix}proxy-max-temp-file-size`,value:z,reason:"In NGINX, when response buffers are full, data spills to a temp file up to this size. "+"Envoy does not buffer to disk — all buffering is in memory.",guidance:"Plan your Envoy pod memory allocation to account for this. If responses are very large, consider streaming rather than buffering. "+"No EnvoyPatchPolicy workaround available — this is a fundamental architectural difference."});return{unsupported:Z.length>0?Z:void 0}}},BZ={error:"connect-failure",timeout:"gateway-error",invalid_header:"retriable-headers",http_500:"5xx",http_502:"5xx",http_503:"5xx",http_504:"5xx",http_403:"retriable-status-codes",http_404:"retriable-status-codes",http_429:"retriable-status-codes",non_idempotent:"retriable-status-codes"},KZ={annotations:["proxy-next-upstream","proxy-next-upstream-timeout","proxy-next-upstream-tries"],category:"proxy-upstream",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}proxy-next-upstream`],Q=X[`${$.prefix}proxy-next-upstream-timeout`],z=X[`${$.prefix}proxy-next-upstream-tries`];if(!U&&!Q&&!z)return{};let W={};if(U){let H=U.split(/\s+/).filter(Boolean),Y=new Set,_=[];for(let F of H){if(F==="off")return J.push({annotation:`${$.prefix}proxy-next-upstream`,message:'proxy-next-upstream is set to "off", which disables retries. '+'Envoy Gateway does not have a direct "disable retries" flag — omit the retry config or set numRetries to 0.',severity:"warning"}),{warnings:J};let q=BZ[F];if(q)Y.add(q);else _.push(F)}if(_.length>0)J.push({annotation:`${$.prefix}proxy-next-upstream`,message:`Unknown NGINX upstream condition(s): ${_.join(", ")}. These could not be mapped to Envoy retryOn values.`,severity:"warning"});if(Y.size>0)W.retryOn={triggers:Array.from(Y).sort()};J.push({annotation:`${$.prefix}proxy-next-upstream`,message:"In NGINX, retry conditions are specific (error, timeout, invalid_header, http_5xx). In Envoy, retryOn uses different condition names (5xx, gateway-error, connect-failure). Verify the mapping matches your intent.",severity:"info"})}if(Q){let H=Q.endsWith("s")?Q:`${Q}s`;W.perRetry={timeout:H}}if(z){let H=Number.parseInt(z,10);if(!Number.isNaN(H))W.numRetries=H}return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-retry`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},retry:W}}],warnings:J.length>0?J:void 0}}},jZ={annotations:["limit-req-rate","limit-req-burst","limit-req-delay","limit-req-no-delay","limit-req-reject-code","limit-req-key","limit-req-zone-size","limit-req-dry-run","limit-req-log-level","limit-req-scale"],category:"rate-limit",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=[],Q=X[`${$.prefix}limit-req-rate`],z=X[`${$.prefix}limit-req-burst`],W=X[`${$.prefix}limit-req-delay`],V=X[`${$.prefix}limit-req-no-delay`],H=X[`${$.prefix}limit-req-reject-code`],Y=X[`${$.prefix}limit-req-key`],_=X[`${$.prefix}limit-req-zone-size`],F=X[`${$.prefix}limit-req-dry-run`],q=X[`${$.prefix}limit-req-log-level`],I=X[`${$.prefix}limit-req-scale`];if(!Q)return{};let D=MZ(Q);if(!D)return J.push({annotation:`${$.prefix}limit-req-rate`,message:`Could not parse limit-req-rate value "${Q}". Expected format: "10r/s" or "60r/m".`,severity:"warning"}),{warnings:J};if(z)J.push({annotation:`${$.prefix}limit-req-burst`,message:`limit-req-burst (${z}) has no direct Envoy Gateway equivalent. Envoy rate limiting does not support burst allowance in the same way as NGINX.`,severity:"warning"});if(W)U.push({annotation:`${$.prefix}limit-req-delay`,value:W,reason:"In NGINX, limit-req-delay controls how many excess requests are delayed before being rejected. "+"In Envoy, rate limiting uses a token bucket model without a delay phase — excess requests are immediately rate-limited."});if(V)J.push({annotation:`${$.prefix}limit-req-no-delay`,message:"limit-req-no-delay has no direct Envoy Gateway equivalent. Envoy processes rate-limited requests immediately without delay queuing.",severity:"info"});if(H)J.push({annotation:`${$.prefix}limit-req-reject-code`,message:`limit-req-reject-code (${H}) has no direct Envoy Gateway equivalent. Envoy returns 429 (Too Many Requests) by default.`,severity:"warning"});if(Y)J.push({annotation:`${$.prefix}limit-req-key`,message:`limit-req-key ("${Y}") noted. Envoy Gateway rate limiting uses different key descriptors. Manual configuration of rateLimit.local.rules[].clientSelectors may be needed.`,severity:"info"});if(_)J.push({annotation:`${$.prefix}limit-req-zone-size`,message:`limit-req-zone-size (${_}) has no direct Envoy Gateway equivalent. Zone sizing is managed internally by Envoy.`,severity:"info"});if(F)J.push({annotation:`${$.prefix}limit-req-dry-run`,message:"limit-req-dry-run has no direct Envoy Gateway equivalent. Consider using Envoy access logs to monitor rate limiting behavior.",severity:"info"});if(q)J.push({annotation:`${$.prefix}limit-req-log-level`,message:`limit-req-log-level (${q}) has no direct Envoy Gateway equivalent. Envoy uses its own logging configuration.`,severity:"info"});if(I)J.push({annotation:`${$.prefix}limit-req-scale`,message:"limit-req-scale has no direct Envoy Gateway equivalent. Rate limit scaling is handled differently in Envoy.",severity:"info"});return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-rate-limit`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},rateLimit:{type:"Local",local:{rules:[{limit:{requests:D.requests,unit:D.unit}}]}}}}],warnings:J.length>0?J:void 0,unsupported:U.length>0?U:void 0}}};function MZ($){let X=$.trim().match(/^(\d+)r\/(s|m)$/);if(!X)return null;let Z=Number.parseInt(X[1],10),G=X[2]==="s"?"Second":"Minute";if(Number.isNaN(Z)||Z<=0)return null;return{requests:Z,unit:G}}var TZ={annotations:["rewrites","rewrite-target","use-regex"],category:"rewrites",convert($){let{annotations:X,name:Z}=$,G=[],J=[],U=X[`${$.prefix}rewrites`],Q=X[`${$.prefix}rewrite-target`],z=X[`${$.prefix}use-regex`]==="true";if(Q){let V=/\$\d/.test(Q);if(z&&V)G.push({annotation:`${$.prefix}rewrite-target`,message:"In NGINX, rewrite-target supports capture groups ($1, $2) from regex path matches. "+"In Gateway API, URLRewrite does not support capture groups — the replacement is a static path. "+"For dynamic rewrites, consider using EnvoyPatchPolicy with Envoy's header/path manipulation filters.",severity:"warning"});let H=z?"ReplaceFullPath":"ReplacePrefixMatch",Y={type:H};if(H==="ReplaceFullPath")Y.replaceFullPath=Q;else Y.replacePrefixMatch=Q;J.push({type:"URLRewrite",filter:{type:"URLRewrite",urlRewrite:{path:Y}},targetRouteName:Z})}if(!U&&!Q)return{};if(!U)return{routeFilters:J.length>0?J:void 0,warnings:G.length>0?G:void 0};if(z)G.push({annotation:`${$.prefix}use-regex`,message:"use-regex is enabled. Gateway API supports RegularExpression path matching but regex syntax may differ from NGINX. Verify regex patterns after migration.",severity:"warning"});let W=U.split(";").map((V)=>V.trim()).filter(Boolean);for(let V of W){let H=RZ(V);if(!H){G.push({annotation:`${$.prefix}rewrites`,message:`Could not parse rewrite entry "${V}". Expected format: "serviceName= rewrite=".`,severity:"warning"});continue}let Y=z?"ReplaceFullPath":"ReplacePrefixMatch",_={type:Y};if(Y==="ReplaceFullPath")_.replaceFullPath=H.rewrite;else _.replacePrefixMatch=H.rewrite;J.push({type:"URLRewrite",filter:{type:"URLRewrite",urlRewrite:{path:_}},targetRouteName:Z})}return{routeFilters:J.length>0?J:void 0,warnings:G.length>0?G:void 0}}};function RZ($){let X=$.split(/\s+/),Z,G;for(let J of X){let[U,...Q]=J.split("="),z=Q.join("=");if(U==="serviceName"&&z)Z=z;else if(U==="rewrite"&&z)G=z}if(!Z||!G)return null;return{serviceName:Z,rewrite:G}}var AZ={annotations:["server-tokens"],category:"server-tokens",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}server-tokens`];if(G){let J=G.toLowerCase()==="off"||G==="false";Z.push({annotation:`${$.prefix}server-tokens`,value:G,reason:J?"In NGINX, server-tokens: off hides the NGINX version from the Server response header. In Envoy, the Server header can be controlled via server_header_transformation.":"In NGINX, server-tokens: on includes the NGINX version in the Server response header. In Envoy, the Server header can be controlled via server_header_transformation.",guidance:J?"Use EnvoyPatchPolicy to set server_header_transformation to PASS_THROUGH (preserve upstream Server header) or OVERWRITE to set a custom value. To suppress the Envoy server header entirely, set it to PASS_THROUGH. Recommended workaround (verify manually): Test thoroughly in non-production first.":"Envoy includes its own Server header by default. If you want to keep the default behavior, no action is needed. To customize the Server header, use EnvoyPatchPolicy with server_header_transformation set to OVERWRITE, APPEND_IF_ABSENT, or PASS_THROUGH."})}return{unsupported:Z.length>0?Z:void 0}}},PZ={annotations:["sticky-cookie-services"],category:"session-affinity",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}sticky-cookie-services`];if(!U)return{};let Q=bZ(U);if(!Q)return J.push({annotation:`${$.prefix}sticky-cookie-services`,message:`Could not parse sticky-cookie-services value "${U}". Expected format: "serviceName= expires= path=".`,severity:"warning"}),{warnings:J};let z={name:Q.cookieName,attributes:{path:Q.path||"/"}};if(Q.expires)z.ttl=Q.expires;return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-session-affinity`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},loadBalancer:{type:"ConsistentHash",consistentHash:{type:"Cookie",cookie:z}}}}],warnings:J.length>0?J:void 0}}};function bZ($){let X=$.trim().split(/\s+/),Z,G,J,U;for(let Q of X)if(Q.startsWith("serviceName="))Z=Q.slice(12);else if(Q.startsWith("expires="))J=Q.slice(8);else if(Q.startsWith("path="))U=Q.slice(5);else if(!Q.includes("="))G=Q;if(!Z||!G)return null;return{serviceName:Z,cookieName:G,expires:J,path:U}}var NZ={annotations:["slow-start"],category:"slow-start",convert($){let{annotations:X}=$,Z=[],G,J="";for(let U of $.prefixes){let Q=X[`${U}slow-start`];if(Q){G=Q,J=U;break}}if(G)Z.push({annotation:`${J}slow-start`,value:G,reason:`In NGINX Plus, slow-start gradually ramps traffic to newly added upstream servers over a configurable period (${G}). In Envoy, slow start is available as slow_start_config at the cluster level.`,guidance:"Configure via EnvoyPatchPolicy to set slow_start_config on the cluster's load balancing policy. "+"Note: This is a Plus-only feature — verify your NGINX Plus license supports it. "+"Recommended workaround (verify manually): Test thoroughly in non-production first."});return{unsupported:Z.length>0?Z:void 0}}},CZ={annotations:["location-snippets","server-snippets"],category:"snippets",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}location-snippets`],J=X[`${$.prefix}server-snippets`];if(G)Z.push({annotation:`${$.prefix}location-snippets`,value:G,reason:"Raw NGINX location configuration snippets cannot be automatically converted to Gateway API resources.",guidance:"Manually translate NGINX location directives to EnvoyPatchPolicy or equivalent Envoy Gateway configuration. See: https://gateway.envoyproxy.io/docs/tasks/extensibility/envoy-patch-policy/"});if(J)Z.push({annotation:`${$.prefix}server-snippets`,value:J,reason:"Raw NGINX server configuration snippets cannot be automatically converted to Gateway API resources.",guidance:"Manually translate NGINX server directives to EnvoyPatchPolicy or equivalent Envoy Gateway configuration. See: https://gateway.envoyproxy.io/docs/tasks/extensibility/envoy-patch-policy/"});return{unsupported:Z.length>0?Z:void 0}}},SZ={annotations:["redirect-to-https","ssl-redirect","http-redirect-code"],category:"ssl-redirect",convert($){let{annotations:X,name:Z}=$,G=[],J=X[`${$.prefix}redirect-to-https`],U=X[`${$.prefix}ssl-redirect`],Q=X[`${$.prefix}http-redirect-code`];if(J==="true"||U==="true"){let z=Q?Number.parseInt(Q,10):301;G.push({type:"RequestRedirect",filter:{type:"RequestRedirect",requestRedirect:{scheme:"https",statusCode:Number.isNaN(z)?301:z}},targetRouteName:Z})}return{routeFilters:G.length>0?G:void 0}}},EZ={annotations:["proxy-connect-timeout","proxy-send-timeout","proxy-read-timeout"],category:"timeouts",convert($){let{annotations:X,namespace:Z,name:G}=$,J=[],U=X[`${$.prefix}proxy-connect-timeout`],Q=X[`${$.prefix}proxy-send-timeout`],z=X[`${$.prefix}proxy-read-timeout`],W={},V={};if(U)V.connectTimeout=Z6(U),J.push({annotation:`${$.prefix}proxy-connect-timeout`,message:"In NGINX, proxy-connect-timeout controls the TCP handshake timeout to the upstream server. In Envoy, this maps to timeout.tcp.connectTimeout (time to establish the upstream connection). "+"Note: Envoy's connectionIdleTimeout is a different concept — it controls how long idle connections are kept alive.",severity:"info"});if(z)W.requestTimeout=Z6(z);if(Q)J.push({annotation:`${$.prefix}proxy-send-timeout`,message:`proxy-send-timeout (${Q}s) has no direct Envoy Gateway equivalent. Consider using BackendTrafficPolicy timeout.http.requestTimeout as an approximation, or configure Envoy proxy directly via EnvoyProxy custom resource.`,severity:"warning"});if(Object.keys(W).length===0&&Object.keys(V).length===0)return{warnings:J.length>0?J:void 0};return{policies:[{apiVersion:"gateway.envoyproxy.io/v1alpha1",kind:"BackendTrafficPolicy",metadata:{name:`${G}-timeouts`,namespace:Z},spec:{targetRef:{group:"gateway.networking.k8s.io",kind:"HTTPRoute",name:G},timeout:{...Object.keys(W).length>0?{http:W}:{},...Object.keys(V).length>0?{tcp:V}:{}}}}],warnings:J.length>0?J:void 0}}};function Z6($){return $.endsWith("s")?$:`${$}s`}var wZ={annotations:["ssl-ciphers","ssl-prefer-server-ciphers"],category:"tls-settings",convert($){let{annotations:X}=$,Z=[],G=X[`${$.prefix}ssl-ciphers`],J=X[`${$.prefix}ssl-prefer-server-ciphers`];if(G)Z.push({annotation:`${$.prefix}ssl-ciphers`,value:G,reason:"In NGINX, ssl-ciphers specifies the allowed TLS cipher suites per Ingress. In Envoy Gateway, cipher suite configuration is not exposed via SecurityPolicy.",guidance:"Use EnvoyPatchPolicy to configure cipher_suites on the DownstreamTlsContext, or set them globally via the EnvoyProxy resource's TLS configuration. Recommended workaround (verify manually): Test thoroughly in non-production first."});if(J)Z.push({annotation:`${$.prefix}ssl-prefer-server-ciphers`,value:J,reason:"In NGINX, ssl-prefer-server-ciphers forces the server's cipher preference order. In Envoy, server cipher preference is the default behavior.",guidance:"No configuration needed — Envoy uses server cipher preference by default."});return{unsupported:Z.length>0?Z:void 0}}};function kZ(){let $=new f8(["nginx.org/","nginx.com/"]);return $.register(EZ),$.register(VZ),$.register(SZ),$.register(TZ),$.register(jZ),$.register(UZ),$.register(IZ),$.register(PZ),$.register(zZ),$.register(FZ),$.register(HZ),$.register(_Z),$.register(CZ),$.register(JZ),$.register(LZ),$.register(DZ),$.register(KZ),$.register(qZ),$.register(QZ),$.register(OZ),$.register(wZ),$.register(AZ),$.register(NZ),$}var fZ={name:"nginxinc",displayName:"NGINX Inc (F5)",annotationPrefixes:["nginx.org/","nginx.com/"],docsUrl:"https://docs.nginx.com/nginx-ingress-controller/configuration/ingress-resources/advanced-configuration-with-annotations/",createEngine:kZ},G6=new Map([["ingress-nginx",GZ],["nginxinc",fZ]]);function J6($){let X=G6.get($);if(!X){let Z=[...G6.keys()].join(", ");throw Error(`Unknown provider "${$}". Available: ${Z}`)}return X}function yZ($){for(let X of $){let Z=Object.keys(X.metadata.annotations??{});if(Z.some((G)=>G.startsWith("nginx.org/")||G.startsWith("nginx.com/")))return"nginxinc";if(Z.some((G)=>G.startsWith("nginx.ingress.kubernetes.io/")))return"ingress-nginx"}return"ingress-nginx"}function hZ($){return{source:{namespace:$.namespace,name:$.name,ingressClassName:$.ingressClassName},gateways:[],httpRoutes:[],tlsRoutes:[],referenceGrants:[],policies:[],routeFilters:[],convertedAnnotations:[],warnings:[],unsupported:[],unknown:[]}}function gZ($,X){let Z=hZ({namespace:$[0]?.metadata.namespace??"default",name:"structural-conversion"}),G=X.gatewayClassName??"tigera-gateway-class",J=new Map;for(let Q of $){let z=Q.spec.ingressClassName??"",W=J.get(z)??[];W.push(Q),J.set(z,W)}let U=new Map;for(let[Q,z]of J){let W=X.gatewayNamespace??z[0].metadata.namespace??"default",V=Q||"default-gateway",{listeners:H,warnings:Y,crossNamespaceTLSSecrets:_}=xZ(z,W);Z.warnings.push(...Y);let F={apiVersion:"gateway.networking.k8s.io/v1",kind:"Gateway",metadata:{name:V,namespace:W},spec:{gatewayClassName:G,listeners:H}};U.set(Q,F),Z.gateways.push(F);for(let q of _)cZ(Z,W,q)}for(let Q of $){let z=Q.spec.ingressClassName??"",W=U.get(z);if(!W)continue;let V=mZ(Q,W,Z.warnings);Z.httpRoutes.push(V);let H=Q.metadata.namespace??"default",Y=W.metadata.namespace??"default";if(H!==Y)lZ(Z,H,Y)}return Z}function vZ($){return $.replace(/\*/g,"wildcard").replace(/[^a-z0-9-]/gi,"-").replace(/^-+|-+$/g,"").toLowerCase()}function xZ($,X){let Z=[],G=[],J=new Map;for(let V of $)if(V.spec.tls){let H=V.metadata.namespace??"default";for(let Y of V.spec.tls)if(Y.hosts)for(let _ of Y.hosts){let F=J.get(_)??[];if(Y.secretName&&!F.some((q)=>q.secretName===Y.secretName))F.push({secretName:Y.secretName,secretNamespace:H});J.set(_,F)}}for(let[V,H]of J)if(H.length>1)Z.push({annotation:"spec.tls",message:`Host "${V}" has conflicting TLS secrets: ${H.map((Y)=>Y.secretName).join(", ")}. Using the first one.`,severity:"warning"});let U=$.some((V)=>(V.metadata.namespace??"default")!==X),Q=new Set;for(let V of $)if(V.spec.rules)for(let H of V.spec.rules){let Y=H.host??"*";if(!Q.has(Y)){Q.add(Y);let _=J.get(Y),F=!!_&&_.length>0;G.push({host:Y,tls:F,secretName:F?_[0].secretName:void 0,secretNamespace:F?_[0].secretNamespace:void 0})}}if(G.length===0){let V={name:"http",port:80,protocol:"HTTP"};if(U)V.allowedRoutes={namespaces:{from:"All"}};return{listeners:[V],warnings:Z,crossNamespaceTLSSecrets:new Set}}let z=[],W=new Set;for(let V of G){let H=vZ(V.host);if(V.host.includes("<")||V.host.includes(">"))Z.push({annotation:"spec.rules[].host",message:`Host "${V.host}" contains placeholder characters (<, >). Replace with actual hostname before applying.`,severity:"warning"});if(V.tls){let _;if(V.secretName){let q={name:V.secretName};if(V.secretNamespace&&V.secretNamespace!==X)q.namespace=V.secretNamespace,W.add(V.secretNamespace);_=[q]}let F={name:`${H}-https`,hostname:V.host==="*"?void 0:V.host,port:443,protocol:"HTTPS",tls:{mode:"Terminate",certificateRefs:_}};if(U)F.allowedRoutes={namespaces:{from:"All"}};z.push(F)}let Y={name:V.tls?`${H}-http`:H,hostname:V.host==="*"?void 0:V.host,port:80,protocol:"HTTP"};if(U)Y.allowedRoutes={namespaces:{from:"All"}};z.push(Y)}return{listeners:z,warnings:Z,crossNamespaceTLSSecrets:W}}function mZ($,X,Z){let G=$.metadata.namespace??"default",J=X.metadata.namespace??"default",U={group:"gateway.networking.k8s.io",kind:"Gateway",name:X.metadata.name};if(J!==G)U.namespace=J;let Q=[],z=[];if($.spec.rules)for(let V of $.spec.rules){if(V.host){if(!Q.includes(V.host))Q.push(V.host)}if(V.http?.paths)for(let H of V.http.paths){let Y=U6(H.pathType,H.path,H.backend,Z);z.push(Y)}}if($.spec.defaultBackend){let V=U6("Prefix","/",$.spec.defaultBackend,Z);z.push(V)}let W={apiVersion:"gateway.networking.k8s.io/v1",kind:"HTTPRoute",metadata:{name:$.metadata.name,namespace:G},spec:{parentRefs:[U],rules:z}};if(Q.length>0)W.spec.hostnames=Q;return W}function uZ($){return/[(){}*+?\\|^$\[\]]/.test($)}function dZ($,X,Z){switch($){case"Prefix":return"PathPrefix";case"Exact":return"Exact";case"ImplementationSpecific":if(X&&uZ(X))return Z.push({annotation:"pathType",message:`ImplementationSpecific pathType with regex path "${X}" converted to RegularExpression — Gateway API regex may have behavioral differences from NGINX regex. Verify the pattern works as expected.`,severity:"warning"}),"RegularExpression";return Z.push({annotation:"pathType",message:"ImplementationSpecific pathType converted to PathPrefix — verify this matches your NGINX controller behavior.",severity:"warning"}),"PathPrefix"}}function U6($,X,Z,G){let Q={matches:[{path:{type:dZ($,X,G),value:X??"/"}}]},z=pZ(Z);if(z.length>0)Q.backendRefs=z;return Q}function pZ($){let X=[];if($.service){let Z={name:$.service.name};if($.service.port.number!=null)Z.port=$.service.port.number;X.push(Z)}return X}function lZ($,X,Z){let G=`allow-${X}-to-${Z}`,J=$.referenceGrants.find((Q)=>Q.metadata.name===G&&Q.metadata.namespace===Z);if(J){if(!J.spec.from.some((z)=>z.group==="gateway.networking.k8s.io"&&z.kind==="HTTPRoute"&&z.namespace===X))J.spec.from.push({group:"gateway.networking.k8s.io",kind:"HTTPRoute",namespace:X});return}let U={apiVersion:"gateway.networking.k8s.io/v1beta1",kind:"ReferenceGrant",metadata:{name:G,namespace:Z},spec:{from:[{group:"gateway.networking.k8s.io",kind:"HTTPRoute",namespace:X}],to:[{group:"gateway.networking.k8s.io",kind:"Gateway"}]}};$.referenceGrants.push(U)}function cZ($,X,Z){let G=`allow-gateway-tls-${Z}`;if($.referenceGrants.find((Q)=>Q.metadata.name===G&&Q.metadata.namespace===Z))return;let U={apiVersion:"gateway.networking.k8s.io/v1beta1",kind:"ReferenceGrant",metadata:{name:G,namespace:Z},spec:{from:[{group:"gateway.networking.k8s.io",kind:"Gateway",namespace:X}],to:[{group:"",kind:"Secret"}]}};$.referenceGrants.push(U)}function e6($){let X=$.options??{},{ingresses:Z,errors:G}=IX($.yaml);if(Z.length===0)return{resources:[],report:{summary:{inputCount:0,namespaces:[],outputCounts:{},annotationsConverted:0,annotationsTotal:0,needsManualReview:0,hosts:[],ingressClassNames:[],ingressesByNamespace:{}},convertedAnnotations:[],convertedByCategory:{},warnings:[],unsupported:[],unknown:[],detectedProvider:void 0},errors:G};let J=X.provider==="auto"||!X.provider?yZ(Z):X.provider,U;try{U=J6(J)}catch{U=J6("ingress-nginx")}let Q=gZ(Z,X),z=U.createEngine();for(let L of Z)z.process(L,Q);Q.policies=iZ(Q.policies);for(let L of Q.routeFilters){let B=L.targetRouteName?Q.httpRoutes.find((K)=>K.metadata.name===L.targetRouteName):Q.httpRoutes[0];if(B)if(L.type==="RequestRedirect")B.spec.rules.push({matches:[{path:{type:"PathPrefix",value:"/"}}],filters:[{type:L.type,...L.filter}]});else for(let K of B.spec.rules){if(K.filters?.some((j)=>j.type==="RequestRedirect")&&!K.backendRefs?.length)continue;if(!K.filters)K.filters=[];if(L.type==="URLRewrite"){let j=K.filters.find((M)=>M.type==="URLRewrite");if(j){j.urlRewrite={...j.urlRewrite,...L.filter.urlRewrite};continue}}if(L.type==="ResponseHeaderModifier"){let j=K.filters.find((M)=>M.type==="ResponseHeaderModifier");if(j){let M=j.responseHeaderModifier,T=L.filter.responseHeaderModifier;if(T?.set){let A=[...M.set??[]];for(let O of T.set)if(!A.some((b)=>b.name===O.name))A.push(O);M.set=A}continue}}if(L.type==="RequestHeaderModifier"){let j=K.filters.find((M)=>M.type==="RequestHeaderModifier");if(j){let M=j.requestHeaderModifier,T=L.filter.requestHeaderModifier;if(T?.set){let A=[...M.set??[]];for(let O of T.set)if(!A.some((b)=>b.name===O.name))A.push(O);M.set=A}if(T?.add){let A=[...M.add??[]];for(let O of T.add)if(!A.some((b)=>b.name===O.name))A.push(O);M.add=A}continue}}K.filters.push({type:L.type,...L.filter})}}let W=[...Q.gateways,...Q.httpRoutes,...Q.tlsRoutes,...Q.referenceGrants,...Q.policies],V=[...new Set(Z.map((L)=>L.metadata.namespace??"default"))],H={};for(let L of W)H[L.kind]=(H[L.kind]??0)+1;let Y=Q.convertedAnnotations.length+Q.unsupported.length+Q.unknown.length,_=[...new Set(Z.flatMap((L)=>(L.spec.rules??[]).map((B)=>B.host).filter(Boolean)))],F=[...new Set(Z.map((L)=>L.spec.ingressClassName).filter(Boolean))],q={};for(let L of Z){let B=L.metadata.namespace??"default";if(!q[B])q[B]=[];q[B].push(L.metadata.name)}let I={};for(let L of Q.convertedAnnotations){if(!I[L.handler])I[L.handler]=[];I[L.handler].push(L.annotation)}let D={summary:{inputCount:Z.length,namespaces:V,outputCounts:H,annotationsConverted:Q.convertedAnnotations.length,annotationsTotal:Y,needsManualReview:Q.unsupported.length,hosts:_,ingressClassNames:F,ingressesByNamespace:q},convertedAnnotations:Q.convertedAnnotations,convertedByCategory:I,warnings:Q.warnings,unsupported:Q.unsupported,unknown:Q.unknown,detectedProvider:U.displayName};return{resources:W,report:D,errors:G}}function iZ($){let X=new Map;for(let G of $){let J=G.spec,U=J.targetRef,Q=J.targetRefs,z=U?.name??Q?.[0]?.name??"unknown",W=U?.namespace??Q?.[0]?.namespace??"default",V=`${G.kind}:${W}/${z}`,H=X.get(V)??[];H.push(G),X.set(V,H)}let Z=[];for(let[,G]of X)if(G.length===1)Z.push(G[0]);else{let J={...G[0]};J.metadata={...J.metadata,name:J.metadata.name.replace(/-[^-]+$/,"")};let U=J.spec,Q=U.targetRef??U.targetRefs?.[0],z={...J.spec};for(let W of G.slice(1))Object.assign(z,W.spec);z.targetRef=Q,J.spec=z,Z.push(J)}return Z}var Q6={Gateway:0,HTTPRoute:1,TLSRoute:2,ReferenceGrant:3,BackendTrafficPolicy:4,SecurityPolicy:5,ClientTrafficPolicy:6,BackendTLSPolicy:7};function $$($){if($.length===0)return"";let X=[...$].sort((G,J)=>{let U=Q6[G.kind]??99,Q=Q6[J.kind]??99;return U-Q}),Z=[];for(let G of X){let J=JSON.parse(JSON.stringify(G)),U=FX(J,{lineWidth:0,defaultKeyType:"PLAIN",defaultStringType:"PLAIN"});Z.push(U.trimEnd())}return`--- +${Z.join(` +--- +`)} +`}var sZ={cors:"Verify preflight OPTIONS requests return correct headers","rate-limit":"Confirm rate limits trigger at expected thresholds",redirects:"HTTP requests redirect to HTTPS",rewrites:"URL rewrite rules produce expected paths",timeouts:"Long-running requests respect timeout values","body-size":"Test large file uploads at or near the configured limit. "+"A ClientTrafficPolicy with matching connection.bufferLimit is included — "+"if uploads still return 413, verify both policies are applied. If your service streams uploads (not buffering), remove the BackendTrafficPolicy requestBuffer and keep only the ClientTrafficPolicy connection.bufferLimit","ip-access":"Requests from allowed/denied IPs behave correctly","auth-basic":"Basic auth prompts appear and credentials are validated","backend-protocol":"Backend connections use the correct protocol (HTTP/HTTPS/gRPC)","ssl-passthrough":"TLS passthrough terminates at the backend, not the gateway","app-root":"Root path redirects to the configured app-root",headers:"Custom headers are present on upstream requests"};function X$($){let X=[];if(X.push("# Ingress-to-Gateway Migration Playbook"),X.push(aZ($)),$.clusterContext)X.push(rZ($));if(X.push(oZ($)),X.push(Z7($)),$.convertedAnnotations.length>0||$.summary.inputCount>0)X.push(z7($));if($.summary.inputCount>0)X.push(H7($));if($.summary.inputCount>0)X.push(_7($));if($.summary.inputCount>0)X.push(L7());if($.summary.inputCount>0)X.push(O7($));return X.push(`--- +Need help with your migration? [Talk to a Tigera engineer](https://tigera.io/contact)`),`${X.join(` + +`)} +`}function t($,X){return $===1?`${$} ${X}`:`${$} ${X}s`}function nZ($){let X=[];for(let[Z,G]of Object.entries($))X.push(t(G,Z));return X.join(", ")}function aZ($){let{summary:X}=$;return["> Generated by [Tigera Ingress-to-Gateway Converter](https://tigera.io/ing2gw)",`> Input: ${t(X.inputCount,"Ingress resource")} across ${t(X.namespaces.length,"namespace")}`].join(` +`)}function rZ($){let X=$.clusterContext;if(!X)return"";let{backupManifest:Z,backupPath:G}=X,J=["---","## 0. Recovery Plan",`A backup of your Ingress resources and related Secrets/ConfigMaps +was saved before conversion.`],U=["### Backup Contents",`- ${Z.ingresses.length} Ingress resource${Z.ingresses.length!==1?"s":""}`,`- ${Z.secrets.length} TLS Secret${Z.secrets.length!==1?"s":""}`,`- ${Z.configmaps.length} ConfigMap${Z.configmaps.length!==1?"s":""}`,`- Cluster: ${X.contextName}`];J.push(U.join(` +`));let Q=["### How to Roll Back","If anything goes wrong at any stage, restore your original resources:","```bash",`kubectl apply -f ${G}/ingresses/`,`kubectl apply -f ${G}/secrets/`,`kubectl apply -f ${G}/configmaps/`,"```","Your NGINX Ingress controller will resume serving traffic immediately."];return J.push(Q.join(` +`)),J.join(` + +`)}function oZ($){let X=["---","## 1. Pre-Migration Assessment"];if(X.push(tZ($)),X.push(eZ($)),$.unsupported.length>0)X.push($7($));if($.unknown.length>0)X.push(X7($));return X.join(` + +`)}function tZ($){let{summary:X}=$,Z=["### What You Have",`- **${t(X.inputCount,"Ingress resource")}** across ${t(X.namespaces.length,"namespace")}`];if(X.namespaces.length>0)Z.push(`- **Namespaces:** ${X.namespaces.join(", ")}`);if(X.hosts.length>0)Z.push(`- **Hosts:** ${X.hosts.join(", ")}`);if(X.ingressClassNames.length>0)Z.push(`- **Ingress class(es):** ${X.ingressClassNames.join(", ")}`);return Z.join(` +`)}function eZ($){let{summary:X}=$,Z=X.annotationsTotal>0?Math.round(X.annotationsConverted/X.annotationsTotal*100):0,G=["### What You'll Get"];if(Object.keys(X.outputCounts).length>0)G.push(`- ${nZ(X.outputCounts)}`);return G.push(`- **Annotations converted:** ${X.annotationsConverted}/${X.annotationsTotal} (${Z}%)`),G.join(` +`)}function $7($){let X=["### What Needs Manual Attention","| Annotation | Value | Reason | Guidance |","|-----------|-------|--------|----------|"];for(let Z of $.unsupported){let G=Z.guidance??"";X.push(`| \`${Z.annotation}\` | \`${Z.value}\` | ${Z.reason} | ${G} |`)}return X.join(` +`)}function X7($){let X=["### Unrecognized Annotations","The following NGINX annotations were not recognized by the converter:"];for(let Z of $.unknown)X.push(`- \`${Z}\``);return X.join(` +`)}function Z7($){let X=["---","## 2. Prepare"];if($.clusterContext){if(X.push(G7($)),!$.clusterContext.envoyGatewayInstalled||!$.clusterContext.gatewayClassExists)X.push(z6())}else X.push(z6());if($.convertedAnnotations.length>0)X.push(J7($));return X.push(U7()),X.push(Q7($)),X.join(` + +`)}function G7($){let X=$.clusterContext;if(!X)return"";let{envoyGatewayInstalled:Z,gatewayClassExists:G}=X,J="tigera-gateway-class";return["### 2.0 Prerequisites Check",`- [x] kubectl connected to \`${X.contextName}\` ✓`,`- [${Z?"x":" "}] Envoy Gateway CRDs installed ${Z?"✓":"✗ — install first"}`,`- [${G?"x":" "}] GatewayClass \`${J}\` exists ${G?"✓":"✗ — will be created"}`].join(` +`)}function z6(){return["### 2.1 Install Envoy Gateway","If you're a Calico/Tigera user, Envoy Gateway is bundled with your operator:","```bash","kubectl apply -f - < https://gateway.envoyproxy.io/docs/install/"].join(` +`)}function J7($){let X=["### 2.2 Review Converted Resources","The converter generated the following resources. Review them before applying:","","| Source Annotation | Handler | Output Resource |","|-------------------|---------|-----------------|"];for(let Z of $.convertedAnnotations)X.push(`| \`${Z.annotation}\` | ${Z.handler} | ${Z.outputKind} |`);return X.join(` +`)}function U7(){return["### 2.3 Validate Before Applying","```bash","# Dry-run against the cluster to catch version-specific CRD issues","kubectl apply --dry-run=server -f gateway.yaml","```","","If the dry-run reports errors (unknown fields, missing required fields), fix them before applying.","Common issues: field names differ between Envoy Gateway versions, or a policy targets the wrong resource kind.","","### 2.4 Apply Gateway API Resources","```bash","# Apply the generated resources (does NOT affect existing NGINX traffic)","kubectl apply -f gateway.yaml","```"].join(` +`)}function Q7($){let X=$.summary.namespaces[0]??"default";return["### 2.5 Verify Resources Are Accepted","```bash","# Check Gateway status",`kubectl get gateway -n ${X}`,"","# Check HTTPRoute status",`kubectl get httproute -n ${X}`,"","# Check policies",`kubectl get backendtrafficpolicy,securitypolicy -n ${X}`,"```","All resources should show `Accepted: True` in their status conditions."].join(` +`)}function z7($){let X=["---","## 3. Verify (Parallel Running)",`At this point, both NGINX and Envoy Gateway are running. Your existing +traffic is unaffected — NGINX still serves all requests via DNS.`];if(X.push(V7($)),$.summary.hosts.length>0)X.push(W7($));if(Object.keys($.convertedByCategory).length>0)X.push(Y7($));return X.join(` + +`)}function V7($){let X=["### 3.1 Find Envoy Gateway's External IP","```bash","kubectl get svc -n envoy-gateway-system","# Note the EXTERNAL-IP of the envoy gateway LoadBalancer service","```"];if($.clusterContext?.envoyGatewayIP)X.push(`Envoy Gateway IP: \`${$.clusterContext.envoyGatewayIP}\``);return X.join(` +`)}function W7($){let X=["### 3.2 Test Routes Directly (Without Changing DNS)"],Z=$.clusterContext?.envoyGatewayIP??"";for(let G of $.summary.hosts)X.push("```bash"),X.push(`# Test ${G} via Envoy Gateway (bypassing DNS)`),X.push(`curl -v --connect-to ${G}:443:${Z}:443 https://${G}/`),X.push(`curl -v --connect-to ${G}:80:${Z}:80 http://${G}/`),X.push("```");return X.join(` +`)}function Y7($){let X=["### 3.3 Annotation Behavior Checklist","Verify each converted annotation behaves equivalently:"];for(let Z of Object.keys($.convertedByCategory)){let G=sZ[Z]??`Verify ${Z} behavior matches NGINX`;X.push(`- [ ] **${Z}** — ${G}`)}return X.join(` +`)}function H7($){return["---","## 4. Traffic Shift","Choose one of the following strategies:",`### Option A: DNS-Based Migration (simplest) + +1. Add Envoy Gateway's LoadBalancer IP to your DNS records for each host +2. Both controllers now receive traffic via DNS round-robin +3. Monitor error rates and latency for both controllers +4. Once confident, remove NGINX's IP from DNS +5. Wait 24-48 hours for DNS TTL propagation before cleanup`,`### Option B: Weighted Routing (gradual) + +If you have an external load balancer (AWS ALB, GCP GCLB, etc.): + +1. Configure weighted routing: 90% NGINX / 10% Envoy Gateway +2. Monitor, then shift: 50/50 → 10/90 → 0/100 +3. Each shift: verify error rates, latency, and logs`,`### Option C: Gateway API Canary (native) + +Use HTTPRoute weights to canary specific paths: +\`\`\`yaml +# Example: send 10% of /api traffic to new backend +rules: + - matches: + - path: + type: PathPrefix + value: /api + backendRefs: + - name: api-service + port: 8080 + weight: 90 + - name: api-service-v2 + port: 8080 + weight: 10 +\`\`\``,`### Rollback + +At any point during traffic shifting, if issues arise: +- Revert DNS to NGINX-only (Option A) +- Shift weights back to 100% NGINX (Option B/C) +- Both controllers remain running — no data loss, no downtime`].join(` + +`)}function _7($){let X=["---","## 5. Cleanup","Once all traffic is flowing through Envoy Gateway:"];if(X.push(F7($)),X.push(I7()),X.push(D7()),$.clusterContext)X.push(q7($));return X.join(` + +`)}function F7($){let{ingressesByNamespace:X}=$.summary,Z=["### 5.1 Remove Ingress Resources","```bash"];for(let[G,J]of Object.entries(X)){Z.push(`# Remove Ingress resources in ${G}`);for(let U of J)Z.push(`kubectl delete ingress ${U} -n ${G}`)}return Z.push("```"),Z.join(` +`)}function I7(){return["### 5.2 Remove NGINX Ingress Controller","```bash","# If installed via Helm","helm uninstall ingress-nginx -n ingress-nginx","","# Remove the namespace","kubectl delete namespace ingress-nginx","```"].join(` +`)}function D7(){return["### 5.3 Verify Clean State","```bash","# No Ingress resources should remain","kubectl get ingress --all-namespaces","","# NGINX pods should be gone","kubectl get pods -n ingress-nginx","","# Gateway API resources should be healthy","kubectl get gateway,httproute --all-namespaces","```"].join(` +`)}function q7($){let X=$.clusterContext;if(!X)return"";return["### Rollback","Your original resources are backed up. To undo the cleanup:","```bash",`kubectl apply -f ${X.backupPath}/ingresses/`,"```"].join(` +`)}function L7(){return["---",`## 6. Post-Migration + +### Explore Gateway API Features + +Now that you're on Gateway API, you have access to features that weren't +possible with Ingress: + +- **Traffic splitting** — A/B testing, canary deployments via HTTPRoute weights +- **Header-based routing** — Route by header values, not just path/host +- **Request/response manipulation** — Transform headers, rewrite paths with full regex +- **Cross-namespace routing** — Shared Gateways with per-team HTTPRoutes +- **gRPC native routing** — First-class GRPCRoute support +- **TCP/UDP routing** — TCPRoute and UDPRoute for non-HTTP workloads + +### Observability + +> If using Calico/Tigera: L7 visibility is built in via ApplicationLayer +> Envoy Gateway metrics: https://gateway.envoyproxy.io/docs/tasks/observability/`].join(` + +`)}function O7($){let{summary:X}=$,Z=$.unsupported.length>0,G=$.warnings.length>0,J=["---",`## 7. For Coding Agents + +> This section is designed for AI coding agents (Claude, Gemini, Codex, etc.) assisting with this migration. + +### Context + +This playbook was generated by ing2gw from ${t(X.inputCount,"Ingress resource")} across ${t(X.namespaces.length,"namespace")}. + +- **Annotations converted:** ${X.annotationsConverted}/${X.annotationsTotal}${X.annotationsTotal>0?` (${Math.round(X.annotationsConverted/X.annotationsTotal*100)}%)`:""} +- **Target:** Envoy Gateway via Calico Ingress Gateway (tigera-gateway-class) +- **Output resources:** ${Object.entries(X.outputCounts).map(([U,Q])=>`${Q} ${U}`).join(", ")}`];if(Z||G){if(J.push(`### Items Requiring Human Judgment + +The following items were flagged during conversion and may need manual review:`),Z){let U=new Set,z=$.unsupported.filter((W)=>{if(U.has(W.annotation))return!1;return U.add(W.annotation),!0}).map((W)=>`- \`${W.annotation}\`: ${W.reason}${W.guidance?` — ${W.guidance}`:""}`);J.push(`**Unsupported annotations** (need manual conversion or EnvoyPatchPolicy): +${z.join(` +`)}`)}if(G){let U=$.warnings.filter((Q)=>Q.severity==="warning");if(U.length>0){let Q=new Set,W=U.filter((V)=>{let H=`${V.annotation}:${V.message.substring(0,80)}`;if(Q.has(H))return!1;return Q.add(H),!0}).map((V)=>`- \`${V.annotation}\`: ${V.message}`);J.push(`**Warnings** (behavioral differences to verify): +${W.join(` +`)}`)}}}return J.push(`### Instructions for Agents + +1. **Do not modify the generated YAML** unless you understand the specific Envoy Gateway CRD schema. The output uses gateway.envoyproxy.io/v1alpha1 for policies and gateway.networking.k8s.io/v1 for core resources. +2. **Workarounds marked "verify manually"** are automated suggestions — always recommend testing in non-production first. +3. **NGINX variables** ($host, $uri, $request_uri, etc.) are not evaluated by Envoy. If you see them in the generated output, flag them for replacement with actual values. +4. **Cross-namespace references** require ReferenceGrant resources — check that they are included in the output. +5. **The playbook sections above are sequential** — do not skip steps or reorder. Parallel running (Section 3) must be verified before traffic shifting (Section 4). +6. If the user asks you to apply these resources, remind them to **verify on a staging cluster first**.`),J.join(` + +`)}import{useCallback as Z$,useState as e0}from"react";function y8(){let[$,X]=e0(""),[Z,G]=e0(null),[J,U]=e0([]),[Q,z]=e0("auto"),W=Z$(()=>{if(U([]),G(null),!$.trim())return U(["Please paste or upload an Ingress YAML first."]),null;let H=e6({yaml:$,options:{provider:Q}}),Y=H.errors.map((I)=>I.message);if(H.resources.length===0&&Y.length===0)Y.push("No Ingress resources found. Make sure your YAML contains a valid networking.k8s.io/v1 Ingress resource.");if(Y.length>0&&H.resources.length===0)return U(Y),G(null),null;let _=$$(H.resources),F=X$(H.report),q={yaml:_,report:F};return G(q),U(Y),q},[$,Q]),V=Z$(()=>{X(""),G(null),U([]),z("auto")},[]);return{input:$,setInput:X,provider:Q,setProvider:z,output:Z,errors:J,handleConvert:W,reset:V}}import{useCallback as G$,useState as B7}from"react";import{jsx as S0,jsxs as j7}from"react/jsx-runtime";function K7({markdownRenderer:$}={}){let X=y8(),[Z,G]=B7("input"),J=G$(()=>{if(X.handleConvert())G("playbook")},[X.handleConvert]),U=G$(()=>{X.reset(),G("input")},[X.reset]);if(Z==="playbook"&&X.output)return j7("div",{className:"space-y-6",children:[S0("div",{className:"flex items-center justify-between",children:S0("button",{type:"button",onClick:U,className:"px-3 py-1.5 text-xs font-medium text-tigera-token-fg-subtle bg-transparent border border-tigera-token-border-default rounded cursor-pointer transition-colors hover:text-tigera-token-fg-danger hover:border-tigera-token-border-danger",children:"Start over"})}),S0(Z8,{markdown:X.output.report,markdownRenderer:$}),S0(G8,{yaml:X.output.yaml})]});return S0(X8,{value:X.input,onChange:X.setInput,onConvert:J,provider:X.provider,onProviderChange:X.setProvider,errors:X.errors})}export{y8 as useConversion,G8 as YamlOutput,Z8 as PlaybookOutput,X8 as IngressInput,K7 as IngressConverter,$8 as EXAMPLES}; diff --git a/src/___new___/components/index.ts b/src/___new___/components/index.ts index c657570e92..8fa18aed84 100644 --- a/src/___new___/components/index.ts +++ b/src/___new___/components/index.ts @@ -2,6 +2,7 @@ import CalicoProducts from './CalicoProducts'; import AskAIButton from './AskAIButton'; import DocCardLink, { PaidProductDocCardLink } from './DocCardLink'; import DocCardLinkLayout from './DocCardLinkLayout'; +import IngressConverter from './IngressConverter'; import SearchModalAskAI from './SearchModalAskAI'; -export { DocCardLink, DocCardLinkLayout, PaidProductDocCardLink, CalicoProducts, AskAIButton, SearchModalAskAI }; +export { DocCardLink, DocCardLinkLayout, PaidProductDocCardLink, CalicoProducts, AskAIButton, IngressConverter, SearchModalAskAI }; diff --git a/yarn.lock b/yarn.lock index 78e17c5995..f876abb426 100644 --- a/yarn.lock +++ b/yarn.lock @@ -14066,6 +14066,15 @@ __metadata: languageName: node linkType: hard +"marked@npm:^17.0.5": + version: 17.0.5 + resolution: "marked@npm:17.0.5" + bin: + marked: bin/marked.js + checksum: 10c0/a044c59cfa14662fefa37bc840200c1f0899214fb677da779112284d49cd5b277806f9a28398195b2031b1e2c80d8daef218e8ad588d7bfa9bc8041cac777ee6 + languageName: node + linkType: hard + "math-intrinsics@npm:^1.1.0": version: 1.1.0 resolution: "math-intrinsics@npm:1.1.0" @@ -19680,6 +19689,7 @@ __metadata: jest-environment-jsdom: "npm:^30.0.5" jest-junit: "npm:^15.0.0" limiter: "npm:^2.1.0" + marked: "npm:^17.0.5" needle: "npm:^3.2.0" playwright: "npm:1.47.2" prism-react-renderer: "npm:^2.1.0"