@at-flux/astro-feature-flags 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -275,7 +275,7 @@ Remote percentage rollouts, per-user experiment assignment, analytics, or a host
275
275
 
276
276
  ## How-to
277
277
 
278
- - `docs/how-to/hide-from-sitemaps.md`
278
+ - [Hide feature locked pages from sitemaps in production builds](docs/how-to/hide-from-sitemaps.md)
279
279
 
280
280
  ## Example Pages
281
281
 
package/dist/index.mjs CHANGED
@@ -102,10 +102,15 @@ function affHeadInlineRuntime(payload) {
102
102
  };
103
103
  try {
104
104
  const { featureFlagStyles, routeFlags: RF, flagNameToToken: M } = payload;
105
- const s = document.createElement("style");
106
- s.setAttribute("data-astro-feature-flags", "");
107
- s.textContent = featureFlagStyles;
108
- (document.head || document.documentElement).appendChild(s);
105
+ const ensureFeatureFlagStyles = () => {
106
+ if (document.querySelector("style[data-astro-feature-flags]")) return;
107
+ const s = document.createElement("style");
108
+ s.setAttribute("data-astro-feature-flags", "");
109
+ s.setAttribute("data-astro-transition-persist", "astro-feature-flags");
110
+ s.textContent = featureFlagStyles;
111
+ (document.head || document.documentElement).appendChild(s);
112
+ };
113
+ ensureFeatureFlagStyles();
109
114
  const affPath = () => {
110
115
  let p = typeof location !== "undefined" && location.pathname ? location.pathname : "/";
111
116
  if (!p.endsWith("/")) p += "/";
@@ -163,6 +168,7 @@ function affHeadInlineRuntime(payload) {
163
168
  pEl.textContent = `${disabled.length === 1 ? `Page disabled: ${flagsText} flag is disabled.` : `Page disabled: ${flagsText} flags are disabled.`}\nIn production builds, pages with disabled flags like this will not be emitted`;
164
169
  };
165
170
  const affApply = () => {
171
+ ensureFeatureFlagStyles();
166
172
  document.documentElement.setAttribute("data-ff-route", affTokens(affPath()).join(" "));
167
173
  affSyncDisabledOverlay();
168
174
  };
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../src/badge-layout.ts","../src/inline-script.ts","../src/dev-inline-runtimes.ts","../src/dev-outline-css.ts","../src/production-html-cull.ts","../src/dev-head-inject.ts","../src/route-prefix-js.ts","../src/index.ts"],"sourcesContent":["export type ElementBadgeVerticalAnchor = \"top\" | \"bottom\";\n\n/** Horizontal placement of the element pill: `end` = top-right (LTR), `start` = top-left, `center` = centred. */\nexport type ElementBadgeHorizontalAlign = \"start\" | \"center\" | \"end\";\n\n/** Subset of dev CSS options used for element-badge geometry (also on `DevOutlineCssOptions`). */\nexport interface ElementBadgeLayoutOptions {\n /**\n * Horizontal alignment along the element’s top or bottom edge. Default `end` (top-right in LTR).\n * Ignored when `elementBadgeHorizontalPercent` is set.\n */\n elementBadgeHorizontalAlign?: ElementBadgeHorizontalAlign;\n /**\n * Optional: anchor at a percentage along the element width (0–100), with the pill centred on that point (`translateX(-50%)`).\n * When set, overrides `elementBadgeHorizontalAlign`.\n */\n elementBadgeHorizontalPercent?: number;\n /**\n * Vertical shift as a percentage of the **badge’s own height** (CSS transform %).\n * With anchor `top`, moves the badge upward. Default `80` (~20% of the pill overlaps the element top edge).\n */\n elementBadgeVerticalShiftPercent?: number;\n /** Anchor the badge to the top or bottom edge of the element. Default `top`. */\n elementBadgeVerticalAnchor?: ElementBadgeVerticalAnchor;\n}\n\nexport type NormalizedElementBadgeLayout =\n | {\n mode: \"align\";\n horizontalAlign: ElementBadgeHorizontalAlign;\n verticalShiftPercent: number;\n verticalAnchor: ElementBadgeVerticalAnchor;\n }\n | {\n mode: \"percent\";\n horizontalPercent: number;\n verticalShiftPercent: number;\n verticalAnchor: ElementBadgeVerticalAnchor;\n };\n\nfunction clampPercent(n: number, fallback: number): number {\n if (typeof n !== \"number\" || Number.isNaN(n)) return fallback;\n return Math.min(100, Math.max(0, n));\n}\n\nexport function normalizeElementBadgeLayout(\n input: ElementBadgeLayoutOptions | undefined,\n): NormalizedElementBadgeLayout {\n const v = clampPercent(input?.elementBadgeVerticalShiftPercent ?? 80, 80);\n const anchor =\n input?.elementBadgeVerticalAnchor === \"bottom\" ? \"bottom\" : \"top\";\n const hp = input?.elementBadgeHorizontalPercent;\n if (hp !== undefined && hp !== null && typeof hp === \"number\") {\n return {\n mode: \"percent\",\n horizontalPercent: clampPercent(hp, 50),\n verticalShiftPercent: v,\n verticalAnchor: anchor,\n };\n }\n const align: ElementBadgeHorizontalAlign =\n input?.elementBadgeHorizontalAlign === \"start\" ||\n input?.elementBadgeHorizontalAlign === \"center\"\n ? input.elementBadgeHorizontalAlign\n : \"end\";\n return {\n mode: \"align\",\n horizontalAlign: align,\n verticalShiftPercent: v,\n verticalAnchor: anchor,\n };\n}\n\nconst INSET = \"0.35rem\";\n\n/**\n * `left` / `right` / `top` | `bottom` / `transform` for the element `::before` badge (no trailing newline).\n */\nexport function elementBadgePositionBlock(\n layout: NormalizedElementBadgeLayout,\n): string {\n const { verticalShiftPercent: v, verticalAnchor } = layout;\n const y =\n verticalAnchor === \"top\"\n ? `translateY(calc(-1 * ${v}%))`\n : `translateY(${v}%)`;\n\n if (layout.mode === \"percent\") {\n const h = layout.horizontalPercent;\n if (verticalAnchor === \"top\") {\n return ` left: ${h}%;\n right: auto;\n top: 0;\n bottom: auto;\n transform: translateX(-50%) ${y};`;\n }\n return ` left: ${h}%;\n right: auto;\n bottom: 0;\n top: auto;\n transform: translateX(-50%) ${y};`;\n }\n\n const { horizontalAlign } = layout;\n if (horizontalAlign === \"center\") {\n if (verticalAnchor === \"top\") {\n return ` left: 50%;\n right: auto;\n top: 0;\n bottom: auto;\n transform: translateX(-50%) ${y};`;\n }\n return ` left: 50%;\n right: auto;\n bottom: 0;\n top: auto;\n transform: translateX(-50%) ${y};`;\n }\n if (horizontalAlign === \"start\") {\n if (verticalAnchor === \"top\") {\n return ` left: ${INSET};\n right: auto;\n top: 0;\n bottom: auto;\n transform: ${y};`;\n }\n return ` left: ${INSET};\n right: auto;\n bottom: 0;\n top: auto;\n transform: ${y};`;\n }\n /* end — top-right / bottom-right */\n if (verticalAnchor === \"top\") {\n return ` left: auto;\n right: ${INSET};\n top: 0;\n bottom: auto;\n transform: ${y};`;\n }\n return ` left: auto;\n right: ${INSET};\n bottom: 0;\n top: auto;\n transform: ${y};`;\n}\n","export function compactInlineScript(source: string): string {\n const lines = source\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line, idx, arr) => line.length > 0 || (idx > 0 && idx < arr.length - 1));\n return lines.join(\"\\n\").trim();\n}\n\nexport function inlineInvoke<Payload>(\n fn: (payload: Payload) => void,\n payload: Payload,\n): string {\n return compactInlineScript(`(${fn.toString()})(${JSON.stringify(payload)});`);\n}\n","type HeadInlinePayload = {\n featureFlagStyles: string;\n routeFlags: Record<string, string[]>;\n flagNameToToken: Record<string, string>;\n};\n\ntype BootstrapPayload = {\n flagTokens: string[];\n colors: Record<string, string>;\n outlineDefaults: Record<string, boolean>;\n badgeDefaults: Record<string, boolean>;\n routeFlags: Record<string, string[]>;\n flagNameToToken: Record<string, string>;\n namespace: string;\n};\n\nexport function affHeadInlineRuntime(payload: HeadInlinePayload): void {\n const toPrefix = (pattern: string): string => {\n let p = String(pattern || \"\").trim();\n if (p.endsWith(\"/**\")) p = p.slice(0, -3);\n else if (p.endsWith(\"/*\")) p = p.slice(0, -2);\n else if (p.length > 1 && p.endsWith(\"*\")) p = p.slice(0, -1);\n return p.endsWith(\"/\") ? p : `${p}/`;\n };\n try {\n const { featureFlagStyles, routeFlags: RF, flagNameToToken: M } = payload;\n const s = document.createElement(\"style\");\n s.setAttribute(\"data-astro-feature-flags\", \"\");\n s.textContent = featureFlagStyles;\n (document.head || document.documentElement).appendChild(s);\n\n const affPath = () => {\n let p = typeof location !== \"undefined\" && location.pathname ? location.pathname : \"/\";\n if (!p.endsWith(\"/\")) p += \"/\";\n return p;\n };\n\n const affFlagNames = (pathname: string) => {\n let p = pathname || \"/\";\n if (!p.endsWith(\"/\")) p += \"/\";\n const out: string[] = [];\n const seen = new Set<string>();\n for (const rp in RF) {\n if (!Object.prototype.hasOwnProperty.call(RF, rp)) continue;\n const route = toPrefix(rp);\n if (!(p === route || p.indexOf(route) === 0)) continue;\n const fns = RF[rp] || [];\n for (let i = 0; i < fns.length; i++) {\n const fn = fns[i];\n if (fn && !seen.has(fn)) {\n seen.add(fn);\n out.push(fn);\n }\n }\n }\n return out;\n };\n\n const affTokens = (pathname: string) =>\n affFlagNames(pathname)\n .map((name) => M[name])\n .filter(Boolean);\n\n const affSyncDisabledOverlay = () => {\n const root = document.documentElement;\n if (!document.body) return;\n const names = affFlagNames(affPath());\n const disabled: string[] = [];\n for (let i = 0; i < names.length; i++) {\n const tk = M[names[i]];\n if (!tk) continue;\n if (root.getAttribute(`data-ff-enabled-${tk}`) === \"off\") disabled.push(tk);\n }\n const id = \"aff-route-disabled-overlay\";\n let el = document.getElementById(id);\n if (!disabled.length) {\n if (el) el.remove();\n root.removeAttribute(\"data-aff-route-disabled\");\n return;\n }\n root.setAttribute(\"data-aff-route-disabled\", \"\");\n if (!el) {\n el = document.createElement(\"div\");\n el.id = id;\n el.className = \"aff-route-disabled-overlay\";\n const p = document.createElement(\"p\");\n el.appendChild(p);\n document.body.appendChild(el);\n }\n const pEl = el.querySelector(\"p\") || el;\n const flagsText = disabled.join(\", \");\n const line1 =\n disabled.length === 1\n ? `Page disabled: ${flagsText} flag is disabled.`\n : `Page disabled: ${flagsText} flags are disabled.`;\n const line2 =\n \"In production builds, pages with disabled flags like this will not be emitted\";\n pEl.textContent = `${line1}\\n${line2}`;\n };\n\n const affApply = () => {\n document.documentElement.setAttribute(\"data-ff-route\", affTokens(affPath()).join(\" \"));\n affSyncDisabledOverlay();\n };\n\n affApply();\n if (typeof MutationObserver === \"function\") {\n const obs = new MutationObserver((muts) => {\n for (let i = 0; i < muts.length; i++) {\n const a = muts[i] && muts[i].attributeName;\n if (a === \"data-ff-route\" || (a && a.indexOf(\"data-ff-enabled-\") === 0)) {\n affSyncDisabledOverlay();\n break;\n }\n }\n });\n obs.observe(document.documentElement, { attributes: true });\n }\n document.addEventListener(\"astro:page-load\", affApply);\n document.addEventListener(\"astro:after-swap\", affApply);\n } catch {\n // no-op in injected runtime\n }\n}\n\nexport function affDevBootstrapRuntime(payload: BootstrapPayload): void {\n const toPrefix = (pattern: string): string => {\n let p = String(pattern || \"\").trim();\n if (p.endsWith(\"/**\")) p = p.slice(0, -3);\n else if (p.endsWith(\"/*\")) p = p.slice(0, -2);\n else if (p.length > 1 && p.endsWith(\"*\")) p = p.slice(0, -1);\n return p.endsWith(\"/\") ? p : `${p}/`;\n };\n const T = payload.flagTokens;\n const C = payload.colors;\n const OD = payload.outlineDefaults;\n const BD = payload.badgeDefaults;\n const RF = payload.routeFlags;\n const F2T = payload.flagNameToToken;\n const N = payload.namespace;\n const K = `${N}.dev.v1`;\n\n const root = document.documentElement;\n const isEnabled = (tk: string) => root.getAttribute(`data-ff-enabled-${tk}`) !== \"off\";\n const currentTokenColor = (\n tk: string,\n cols: Record<string, string>,\n ): string => {\n const cssVar = root.style.getPropertyValue(`--${N}-c-${tk}`).trim();\n return cssVar || cols[tk] || C[tk] || \"#94a3b8\";\n };\n const unique = (a: string[]) => [...new Set(a.filter(Boolean))];\n\n const comboTokens = (el: Element): string[] => {\n let out: string[] = [];\n const raw = (el.getAttribute(`data-${N}`) || \"\").trim();\n if (raw) out = out.concat(raw.split(/\\s+/).filter(Boolean));\n for (let i = 0; i < T.length; i++) {\n const tk = T[i];\n if (el.hasAttribute(`data-${N}-${tk}`)) out.push(tk);\n }\n return unique(out);\n };\n\n const stripBooleanFlagAttrs = (el: Element) => {\n for (let i = 0; i < T.length; i++) {\n el.removeAttribute(`data-${N}-${T[i]}`);\n }\n };\n const clearComboVisualState = (el: HTMLElement) => {\n el.removeAttribute(\"data-ff-label\");\n el.style.removeProperty(\"--ff-combo-gradient\");\n el.style.removeProperty(\"--ff-combo-gradient-soft\");\n el.style.removeProperty(\"--ff-combo-outline\");\n el.style.removeProperty(\"--ff-combo-text\");\n el.style.removeProperty(\"--ff-combo-badge-border\");\n el.style.removeProperty(\"--ff-combo-badge-gradient\");\n };\n\n const applyCombo = (el: HTMLElement, cols: Record<string, string>) => {\n const allTokens = comboTokens(el);\n const hasCombo = allTokens.length >= 2;\n const hasDisabledInCombo = hasCombo && allTokens.some((tk) => !isEnabled(tk));\n const visual = allTokens.filter((tk) => isEnabled(tk));\n if (hasDisabledInCombo) {\n // Fail-closed for combos: if any member token is disabled, hide the whole element.\n // Keep all combo tokens mirrored in data-<ns> so enabled-off CSS selectors can hide it.\n stripBooleanFlagAttrs(el);\n el.setAttribute(`data-${N}`, allTokens.join(\" \"));\n clearComboVisualState(el);\n return;\n }\n if (visual.length < 2) {\n clearComboVisualState(el);\n if (hasCombo) {\n // Only normalize attrs for true combos. Single-flag shorthand markers\n // (e.g. data-ff-wip) must stay intact so re-enabling can recover styling.\n stripBooleanFlagAttrs(el);\n if (visual.length === 1) {\n el.setAttribute(`data-${N}`, visual[0]!);\n } else {\n el.removeAttribute(`data-${N}`);\n }\n }\n return;\n }\n stripBooleanFlagAttrs(el);\n // Mirror all enabled tokens into the primary namespace attribute so `[data-<ns>*=\" \"]`\n // matches (required for boolean-only combos like data-ff-a + data-ff-b).\n el.setAttribute(`data-${N}`, visual.join(\" \"));\n el.setAttribute(\"data-ff-label\", visual.join(\" | \"));\n const strong = visual.map((tk) => currentTokenColor(tk, cols));\n const soft = strong.map((c) => `color-mix(in oklab, white 86%, ${c} 14%)`);\n el.style.setProperty(\"--ff-combo-gradient\", `linear-gradient(90deg, ${strong.join(\", \")})`);\n el.style.setProperty(\"--ff-combo-gradient-soft\", `linear-gradient(90deg, ${soft.join(\", \")})`);\n el.style.setProperty(\"--ff-combo-outline\", strong[0] || \"#64748b\");\n el.style.setProperty(\"--ff-combo-text\", strong[0] || \"#111827\");\n el.style.setProperty(\n \"--ff-combo-badge-border\",\n `color-mix(in oklab, ${strong[0] || \"#64748b\"} 35%, transparent)`,\n );\n el.style.setProperty(\"--ff-combo-badge-gradient\", `linear-gradient(90deg, ${soft.join(\", \")})`);\n };\n\n const routeTokensForPath = (path: string): string[] => {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const rp in RF) {\n if (!Object.prototype.hasOwnProperty.call(RF, rp)) continue;\n const cnd = toPrefix(rp);\n if (!(path === cnd || path.indexOf(cnd) === 0)) continue;\n const fns = RF[rp] || [];\n for (let i = 0; i < fns.length; i++) {\n const tk = F2T[fns[i]];\n if (tk && !seen.has(tk)) {\n seen.add(tk);\n out.push(tk);\n }\n }\n }\n return out;\n };\n\n const routeLabelFor = (path: string, toks: string[]): string => {\n if (!toks.length) return \"\";\n const byFlag: Record<string, string[]> = {};\n for (const rp in RF) {\n if (!Object.prototype.hasOwnProperty.call(RF, rp)) continue;\n const cnd = toPrefix(rp);\n if (!(path === cnd || path.indexOf(cnd) === 0)) continue;\n const fns = RF[rp] || [];\n for (let i = 0; i < fns.length; i++) {\n const tk = F2T[fns[i]];\n if (!tk) continue;\n if (!byFlag[tk]) byFlag[tk] = [];\n byFlag[tk].push(rp);\n }\n }\n return `Page: ${toks\n .map((tk) => `${tk} - ${(byFlag[tk] || []).join(\", \") || \"matched\"}`)\n .join(\" | \")}`;\n };\n\n const affSyncRouteChrome = (path: string) => {\n if (!document.body) return;\n // Keep every matched route token on <html> so per-token toolbar rules\n // (`[data-ff-route~=\"tk\"]`) still apply; gradients use enabled-only below.\n const routeAll = routeTokensForPath(path);\n if (!routeAll.length) {\n document.getElementById(\"aff-route-frame\")?.remove();\n document.getElementById(\"aff-route-badge\")?.remove();\n root.removeAttribute(\"data-ff-route\");\n root.removeAttribute(\"data-ff-route-label\");\n return;\n }\n root.setAttribute(\"data-ff-route\", routeAll.join(\" \"));\n root.setAttribute(\"data-ff-route-label\", routeLabelFor(path, routeAll));\n if (!document.getElementById(\"aff-route-frame\")) {\n const frame = document.createElement(\"div\");\n frame.id = \"aff-route-frame\";\n frame.className = \"aff-route-frame\";\n frame.setAttribute(\"aria-hidden\", \"true\");\n document.body.appendChild(frame);\n }\n let badge = document.getElementById(\"aff-route-badge\");\n if (!badge) {\n badge = document.createElement(\"div\");\n badge.id = \"aff-route-badge\";\n badge.className = \"aff-route-badge\";\n badge.setAttribute(\"role\", \"status\");\n document.body.appendChild(badge);\n }\n badge.textContent = root.getAttribute(\"data-ff-route-label\") || \"\";\n badge.setAttribute(\"aria-label\", badge.textContent || \"\");\n };\n\n try {\n (window as Window & { __AFF__?: unknown }).__AFF__ = {\n tokens: T,\n colors: C,\n namespace: N,\n };\n const raw = localStorage.getItem(K) || localStorage.getItem(\"aff.dev.v1\");\n const st = raw ? JSON.parse(raw) : {};\n const o = st.outline || st.chrome || {};\n const e = st.enabled || st.render || {};\n const b = st.badge || {};\n const cols = st.colors || {};\n\n for (let i = 0; i < T.length; i++) {\n const t = T[i];\n const od = OD[t] !== false;\n const bd = BD[t] !== false;\n if (o[t] === false || (!od && o[t] !== true))\n root.setAttribute(`data-ff-outline-${t}`, \"off\");\n else root.removeAttribute(`data-ff-outline-${t}`);\n if (e[t] === false) root.setAttribute(`data-ff-enabled-${t}`, \"off\");\n else root.removeAttribute(`data-ff-enabled-${t}`);\n if (b[t] === false || (!bd && b[t] !== true))\n root.setAttribute(`data-ff-badge-${t}`, \"off\");\n else root.removeAttribute(`data-ff-badge-${t}`);\n const col = cols[t] || C[t];\n const vn = `--${N}-c-${t}`;\n if (col) root.style.setProperty(vn, col);\n else root.style.removeProperty(vn);\n }\n\n const applyAll = () => {\n const map = new Set<HTMLElement>();\n document\n .querySelectorAll<HTMLElement>(`[data-${N}]`)\n .forEach((el) => map.add(el));\n for (let i = 0; i < T.length; i++) {\n document\n .querySelectorAll<HTMLElement>(`[data-${N}-${T[i]}]`)\n .forEach((el) => map.add(el));\n }\n map.forEach((el) => applyCombo(el, cols));\n };\n\n const affAfterNav = () => {\n if (!document.body) return;\n let p = window.location?.pathname || \"/\";\n if (!p.endsWith(\"/\")) p += \"/\";\n const toks = routeTokensForPath(p).filter((tk) => isEnabled(tk));\n if (toks.length) {\n const strong = toks.map((tk) => currentTokenColor(tk, cols));\n const soft = strong.map((c) => `color-mix(in oklab, white 86%, ${c} 14%)`);\n root.style.setProperty(`--${N}-route-gradient`, `linear-gradient(90deg, ${soft.join(\", \")})`);\n root.style.setProperty(\"--ff-route-outline-gradient\", `linear-gradient(90deg, ${strong.join(\", \")})`);\n root.style.setProperty(\"--ff-route-badge-gradient\", `linear-gradient(90deg, ${soft.join(\", \")})`);\n root.style.setProperty(\n \"--ff-route-badge-border\",\n `color-mix(in oklab, ${strong[0] || \"#64748b\"} 35%, transparent)`,\n );\n root.style.setProperty(\"--ff-route-outline\", strong[0] || \"#64748b\");\n root.style.setProperty(\"--ff-route-text\", strong[0] || \"#111827\");\n } else {\n root.style.removeProperty(`--${N}-route-gradient`);\n root.style.removeProperty(\"--ff-route-outline-gradient\");\n root.style.removeProperty(\"--ff-route-badge-gradient\");\n root.style.removeProperty(\"--ff-route-badge-border\");\n root.style.removeProperty(\"--ff-route-outline\");\n root.style.removeProperty(\"--ff-route-text\");\n }\n applyAll();\n affSyncRouteChrome(p);\n };\n\n affAfterNav();\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", affAfterNav, { once: true });\n }\n document.addEventListener(\"astro:page-load\", affAfterNav);\n document.addEventListener(\"astro:after-swap\", affAfterNav);\n if (typeof MutationObserver === \"function\") {\n const obs = new MutationObserver((muts) => {\n for (let i = 0; i < muts.length; i++) {\n const a = muts[i]?.attributeName;\n if (!a) continue;\n if (\n a === \"style\" ||\n a.indexOf(\"data-ff-enabled-\") === 0 ||\n a.indexOf(\"data-ff-outline-\") === 0 ||\n a.indexOf(\"data-ff-badge-\") === 0\n ) {\n affAfterNav();\n break;\n }\n }\n });\n obs.observe(root, { attributes: true });\n }\n } catch {\n // no-op in injected runtime\n }\n}\n","import {\n elementBadgePositionBlock,\n normalizeElementBadgeLayout,\n} from \"./badge-layout\";\nimport type { ElementBadgeLayoutOptions } from \"./badge-layout\";\nimport { inlineInvoke } from \"./inline-script\";\nimport { affDevBootstrapRuntime } from \"./dev-inline-runtimes\";\nimport type { ResolvedFeatureRuntime } from \"./runtime\";\nimport { toToken } from \"./runtime\";\n\nexport type DevOutlineHiddenStrategy = \"visibility\" | \"display\";\n\nexport interface DevOutlineCssOptions extends ElementBadgeLayoutOptions {\n outlineWidth?: string;\n /** Fallback when no per-token colour is set. */\n outlineColor?: string;\n /** Per-flag-token outline / badge / route-badge colour (overrides JSON `colors`). */\n outlineColorByToken?: Record<string, string>;\n outlineOffset?: string;\n borderRadius?: string;\n\n /** Default pill text for the `wip` flag token in the dev toolbar. */\n badgeLabelWip?: string;\n /** @deprecated Use {@link badgeLabelWip}. */\n badgeLabelDev?: string;\n badgeLabelByToken?: Record<string, string>;\n\n hiddenStrategy?: DevOutlineHiddenStrategy;\n\n /**\n * @deprecated Unused: badge does not use container queries.\n */\n badgeMinInlineSize?: string;\n}\ntype NormalizedDevOutlineCssOptions = {\n outlineWidth: string;\n outlineColor: string;\n outlineColorByToken: Record<string, string>;\n outlineOffset: string;\n borderRadius: string;\n badgeLabelWip: string;\n badgeLabelByToken: Record<string, string>;\n hiddenStrategy: DevOutlineHiddenStrategy;\n badgeMinInlineSize: string;\n};\n\nconst defaultDevOutlineCssOptions: Omit<\n NormalizedDevOutlineCssOptions,\n \"outlineColorByToken\"\n> = {\n outlineWidth: \"2px\",\n outlineColor: \"rgb(220 38 38)\",\n outlineOffset: \"-2px\",\n borderRadius: \"0.5rem\",\n badgeLabelWip: \"wip\",\n badgeLabelByToken: {},\n hiddenStrategy: \"visibility\",\n badgeMinInlineSize: \"8rem\",\n};\n\nfunction normalizeCssOptions(\n css: DevOutlineCssOptions | undefined,\n runtime: ResolvedFeatureRuntime,\n): NormalizedDevOutlineCssOptions {\n const byToken = {\n ...runtime.flagColorsByToken,\n ...(css?.outlineColorByToken ?? {}),\n };\n const badgeLabelWip =\n css?.badgeLabelWip ?? css?.badgeLabelDev ?? defaultDevOutlineCssOptions.badgeLabelWip;\n return {\n ...defaultDevOutlineCssOptions,\n ...(css ?? {}),\n outlineColorByToken: byToken,\n badgeLabelWip,\n badgeLabelByToken: {\n ...defaultDevOutlineCssOptions.badgeLabelByToken,\n ...(css?.badgeLabelByToken ?? {}),\n },\n };\n}\n\nfunction colorForToken(\n token: string,\n opts: NormalizedDevOutlineCssOptions,\n): string {\n return opts.outlineColorByToken[token] ?? opts.outlineColor;\n}\n\n/** CSS variable per token, set on &lt;html&gt; from ff.json + dev-toolbar overrides. */\nfunction featureColorVar(token: string, namespace: string): string {\n const ns = toToken(namespace) || \"ff\";\n return `--${ns}-c-${token}`;\n}\n\n/**\n * Dev-only styles: `data-ff=\"token\"` (or space-separated tokens) gates outlines/badges.\n * `html[data-ff-route=\"<token>\"]` (dev) adds a fixed top-right route badge (label only).\n * Toolbar toggles: `data-ff-enabled-*`, `data-ff-outline-*`, `data-ff-badge-*`, `--<namespace>-c-*`.\n */\nexport function createFeatureFlagStyles(\n runtime: ResolvedFeatureRuntime,\n css?: DevOutlineCssOptions,\n): string {\n const nsAttr = `data-${toToken(runtime.namespace) || \"ff\"}`;\n const selectorsForFlag = (flag: string) => {\n const token = toToken(flag);\n const valueSelectors = [`[${nsAttr}~=\"${token}\"]`];\n if (flag !== token) valueSelectors.push(`[${nsAttr}~=\"${flag}\"]`);\n const baseSelectors = [...valueSelectors, `[${nsAttr}-${token}]`];\n return {\n token,\n baseSelectors,\n selOutline: baseSelectors.map((s) => `${s}:not([${nsAttr}*=\" \"])`).join(\", \"),\n selIs: `:is(${baseSelectors.join(\", \")})`,\n };\n };\n const opts = normalizeCssOptions(css, runtime);\n const badgeLayout = normalizeElementBadgeLayout(css);\n const badgePos = elementBadgePositionBlock(badgeLayout);\n const chunks: string[] = [];\n\n for (const flag of Object.keys(runtime.flags)) {\n const { token, selOutline, selIs } = selectorsForFlag(flag);\n /** Combo hosts use `[${nsAttr}*=\" \"]` + `data-ff-label`; skip per-token ::before so combo rules win. */\n const selIsSingleBadge = `${selIs}:not([${nsAttr}*=\" \"])`;\n const col = colorForToken(token, opts);\n const v = featureColorVar(token, runtime.namespace);\n const label =\n opts.badgeLabelByToken[token] ??\n (token === \"wip\" ? opts.badgeLabelWip : token);\n const labelEscaped = String(label).replace(/'/g, \"\\\\'\");\n\n chunks.push(`\nhtml {\n --aff-outline-c-${token}: transparent;\n}\nhtml:not([data-ff-outline-${token}=\"off\"]) {\n --aff-outline-c-${token}: var(${v}, ${col});\n}\n${selOutline} {\n position: relative;\n outline: ${opts.outlineWidth} solid var(--aff-outline-c-${token});\n outline-offset: ${opts.outlineOffset};\n border-radius: ${opts.borderRadius};\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}::before {\n box-sizing: border-box;\n position: absolute;\n inset: auto;\n margin: 0;\n width: max-content;\n max-width: min(18rem, calc(100% - 0.75rem));\n pointer-events: auto;\n z-index: 20;\n${badgePos}\n display: block;\n content: '${labelEscaped}';\n border: 1px solid color-mix(in oklab, var(${v}, ${col}) 60%, transparent);\n border-radius: 9999px;\n padding: 0.125rem 0.45rem;\n font-size: 0.625rem;\n line-height: 1.2;\n letter-spacing: 0.02em;\n font-weight: 600;\n color: var(${v}, ${col});\n background: color-mix(in oklab, white 85%, var(${v}, ${col}) 15%);\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n transition: opacity 0.14s ease, background 0.14s ease, border-color 0.14s ease;\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}:hover::before {\n opacity: 0.16;\n background: color-mix(in oklab, white 94%, var(${v}, ${col}) 6%);\n border-color: color-mix(in oklab, var(${v}, ${col}) 35%, transparent);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-align=\"start\"]::before {\n left: 0.35rem;\n right: auto;\n transform: translateY(calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-align=\"center\"]::before {\n left: 50%;\n right: auto;\n transform: translate(-50%, calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-align=\"end\"]::before {\n left: auto;\n right: 0.35rem;\n transform: translateY(calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-horizontal]::before {\n left: calc(attr(data-ff-horizontal number, 50) * 1%);\n right: auto;\n transform: translate(-50%, calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-horizontal][data-ff-anchor=\"bottom\"]::before {\n transform: translate(-50%, 80%);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"]::before {\n top: auto;\n bottom: 0;\n transform: translateY(80%);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"][data-ff-align=\"center\"]::before {\n transform: translate(-50%, 80%);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-vertical]::before {\n transform: translateY(calc(attr(data-ff-vertical number, 80) * -1%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-horizontal][data-ff-vertical]::before {\n transform: translate(-50%, calc(attr(data-ff-vertical number, 80) * -1%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"][data-ff-vertical]::before {\n transform: translateY(calc(attr(data-ff-vertical number, 80) * 1%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"][data-ff-horizontal][data-ff-vertical]::before {\n transform: translate(-50%, calc(attr(data-ff-vertical number, 80) * 1%));\n}\nhtml[data-ff-enabled-${token}=\"off\"] ${selIs} {\n display: none !important;\n}\n`);\n }\n\n // Multi-token value on a single element: show combined badge text and a gradient outline.\n chunks.push(`\n[${nsAttr}*=\" \"] {\n position: relative;\n outline: none !important;\n outline-offset: 0 !important;\n border-radius: ${opts.borderRadius};\n}\nhtml [${nsAttr}*=\" \"]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n z-index: 10;\n box-sizing: border-box;\n padding: ${opts.outlineWidth};\n background: var(--ff-combo-gradient, linear-gradient(90deg, #fecaca, #bfdbfe, #bbf7d0));\n -webkit-mask:\n linear-gradient(#000 0 0) content-box,\n linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n}\nhtml [${nsAttr}*=\" \"]::before {\n box-sizing: border-box;\n position: absolute;\n inset: auto;\n margin: 0;\n width: max-content;\n max-width: min(18rem, calc(100% - 0.75rem));\n pointer-events: auto;\n z-index: 20;\n${badgePos}\n display: block;\n content: attr(${nsAttr});\n background: var(--ff-combo-badge-gradient, var(--ff-combo-gradient-soft, linear-gradient(90deg, #fff1f2, #eff6ff, #f0fdf4))) !important;\n color: var(--ff-combo-text, #111827) !important;\n border: 1px solid var(--ff-combo-badge-border, color-mix(in oklab, #94a3b8 50%, transparent)) !important;\n border-radius: 9999px;\n padding: 0.125rem 0.45rem;\n font-size: 0.625rem;\n line-height: 1.2;\n letter-spacing: 0.02em;\n font-weight: 600;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n transition: opacity 0.14s ease, background 0.14s ease, border-color 0.14s ease;\n}\nhtml [${nsAttr}*=\" \"][data-ff-label]::before {\n content: attr(data-ff-label) !important;\n}\nhtml [${nsAttr}*=\" \"]:hover::before {\n opacity: 0.2;\n background: linear-gradient(90deg, #fff8f9, #f8fbff, #f8fff9);\n border-color: color-mix(in oklab, #94a3b8 35%, transparent);\n}\n`);\n\n // Combo chrome must be suppressed *after* generic combo rules so `!important` / order cannot be\n // overridden by `html [...][data-ff-label]::before { content: ... !important }`.\n for (const flag of Object.keys(runtime.flags)) {\n const { token, selIs } = selectorsForFlag(flag);\n chunks.push(`\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"]::before,\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"][data-ff-label]::before {\n display: none !important;\n content: none !important;\n}\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"]:hover::before,\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"][data-ff-label]:hover::before {\n display: none !important;\n opacity: 1 !important;\n background: none !important;\n border-color: transparent !important;\n}\nhtml[data-ff-outline-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"] {\n outline: none !important;\n outline-offset: 0 !important;\n}\nhtml[data-ff-outline-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"]::after {\n display: none !important;\n}\n`);\n }\n\n chunks.push(`\n.aff-route-frame {\n position: fixed;\n inset: 0.6rem;\n pointer-events: none;\n z-index: 10040;\n box-sizing: border-box;\n border-radius: 0.5rem;\n overflow: hidden;\n}\n.aff-route-frame::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n padding: 2px;\n background: var(--ff-route-outline-gradient, linear-gradient(90deg, #ef4444, #3b82f6, #22c55e));\n -webkit-mask:\n linear-gradient(#000 0 0) content-box,\n linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n}\n.aff-route-badge {\n position: fixed;\n top: 0.75rem;\n right: 0.75rem;\n z-index: 10060;\n pointer-events: auto;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n max-width: min(34rem, calc(100vw - 1.5rem));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border: 1px solid var(--ff-route-badge-border, color-mix(in oklab, #94a3b8 55%, transparent));\n border-radius: 9999px;\n padding: 0.15rem 0.5rem;\n font-size: 0.625rem;\n line-height: 1.2;\n letter-spacing: 0.02em;\n font-weight: 600;\n color: var(--ff-route-text, #111827);\n background: var(--ff-route-badge-gradient, linear-gradient(90deg, #fff1f2));\n opacity: 1;\n transition: opacity 0.14s ease, border-color 0.14s ease, background 0.14s ease;\n}\n.aff-route-badge:hover {\n opacity: 0.2;\n border-color: color-mix(in oklab, #94a3b8 35%, transparent);\n}\n.aff-route-disabled-overlay {\n position: fixed;\n inset: 0;\n z-index: 10050;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: clamp(1rem, 4vw, 2.5rem);\n text-align: center;\n font: 600 0.95rem/1.45 system-ui, -apple-system, Segoe UI, sans-serif;\n color: #f8fafc;\n background: rgba(15, 23, 42, 0.52);\n backdrop-filter: blur(2px);\n pointer-events: auto;\n}\n.aff-route-disabled-overlay > p {\n margin: 0;\n max-width: min(44rem, calc(100vw - 2rem));\n white-space: pre-line;\n}\n`);\n\n for (const flag of Object.keys(runtime.flags)) {\n const token = toToken(flag);\n chunks.push(`\n[data-ff-route~=\"${token}\"][data-ff-outline-${token}=\"off\"] #aff-route-frame {\n display: none !important;\n}\n[data-ff-route~=\"${token}\"][data-ff-badge-${token}=\"off\"] #aff-route-badge {\n display: none !important;\n}\n[data-ff-route~=\"${token}\"][data-ff-enabled-${token}=\"off\"] #aff-route-frame {\n display: none !important;\n}\n[data-ff-route~=\"${token}\"][data-ff-enabled-${token}=\"off\"] #aff-route-badge {\n display: none !important;\n}\n`);\n }\n\n return \"\\n\" + chunks.join(\"\\n\") + \"\\n\";\n}\n\n/**\n * CSS-only hiding for disabled flags. The Astro integration no longer injects this into\n * production builds — static HTML is culled instead. Kept for custom tooling or tests.\n */\nexport function createProductionGateStyles(\n runtime: ResolvedFeatureRuntime,\n): string {\n const nsAttr = `data-${toToken(runtime.namespace) || \"ff\"}`;\n const chunks: string[] = [];\n for (const [flag, enabled] of Object.entries(runtime.flags)) {\n if (enabled) continue;\n const token = toToken(flag);\n const valueSelectors = [`[${nsAttr}~=\"${token}\"]`];\n if (flag !== token) valueSelectors.push(`[${nsAttr}~=\"${flag}\"]`);\n chunks.push(\n `${[...valueSelectors, `[${nsAttr}-${token}]`].join(\", \")} { display: none !important; }`,\n );\n }\n return chunks.length ? `\\n${chunks.join(\"\\n\")}\\n` : \"\";\n}\n\nexport function buildAffDevBootstrapScript(\n flagTokens: string[],\n colors: Record<string, string>,\n outlineDefaults: Record<string, boolean>,\n badgeDefaults: Record<string, boolean>,\n routeFlags: Record<string, string[]>,\n flagNameToToken: Record<string, string>,\n namespace: string,\n): string {\n return inlineInvoke(affDevBootstrapRuntime, {\n flagTokens,\n colors,\n outlineDefaults,\n badgeDefaults,\n routeFlags,\n flagNameToToken,\n namespace: toToken(namespace) || \"ff\",\n });\n}\n","import { readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse, type HTMLElement } from \"node-html-parser\";\nimport type { ResolvedFeatureRuntime } from \"./runtime\";\nimport { toToken } from \"./runtime\";\n\nfunction walkHtmlFiles(dir: string): string[] {\n const out: string[] = [];\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const ent of entries) {\n const p = join(dir, ent.name);\n if (ent.isDirectory()) out.push(...walkHtmlFiles(p));\n else if (ent.isFile() && ent.name.endsWith(\".html\")) out.push(p);\n }\n return out;\n}\n\nfunction namespaceAttrName(namespace: string): string {\n return `data-${toToken(namespace) || \"ff\"}`;\n}\n\nfunction elementFlagSlugs(\n el: HTMLElement,\n nsAttr: string,\n knownTokens: readonly string[],\n): string[] {\n const slugSet = new Set<string>();\n const combined = el.getAttribute(nsAttr)?.trim();\n if (combined) {\n for (const part of combined.split(/\\s+/).filter(Boolean)) {\n slugSet.add(part);\n }\n }\n for (const tk of knownTokens) {\n if (el.hasAttribute(`${nsAttr}-${tk}`)) slugSet.add(tk);\n }\n return [...slugSet];\n}\n\nfunction slugToFlagNames(slug: string, flagNames: readonly string[]): string[] {\n const out: string[] = [];\n for (const name of flagNames) {\n if (slug === name || slug === toToken(name)) out.push(name);\n }\n return out;\n}\n\nfunction shouldCullElement(\n el: HTMLElement,\n runtime: ResolvedFeatureRuntime,\n nsAttr: string,\n knownTokens: readonly string[],\n flagNames: readonly string[],\n): boolean {\n const slugs = elementFlagSlugs(el, nsAttr, knownTokens);\n if (slugs.length === 0) return false;\n for (const slug of slugs) {\n const names = slugToFlagNames(slug, flagNames);\n if (names.length === 0) return false;\n }\n for (const slug of slugs) {\n const names = slugToFlagNames(slug, flagNames);\n for (const name of names) {\n if (!runtime.flags[name]) return true;\n }\n }\n return false;\n}\n\nfunction elementDepth(el: HTMLElement): number {\n let d = 0;\n let n: HTMLElement | null = el.parentNode as HTMLElement | null;\n while (n) {\n d++;\n n = n.parentNode as HTMLElement | null;\n }\n return d;\n}\n\n/**\n * Production static HTML: remove nodes gated by disabled flags (same rules as dev\n * `data-*` / `data-*-<token>`), strip dev-only route attributes on `<html>`, and drop\n * empty `<style>` tags left after `featureFlagStyles` is empty.\n */\nexport function cullProductionHtml(\n html: string,\n runtime: ResolvedFeatureRuntime,\n): string {\n const root = parse(html) as HTMLElement;\n const nsAttr = namespaceAttrName(runtime.namespace);\n const flagNames = Object.keys(runtime.flags);\n const knownTokens = flagNames.map((n) => toToken(n));\n\n const candidates = root.querySelectorAll(\"*\") as unknown as HTMLElement[];\n const toRemove: HTMLElement[] = [];\n for (const el of candidates) {\n if (shouldCullElement(el, runtime, nsAttr, knownTokens, flagNames)) {\n toRemove.push(el);\n }\n }\n toRemove.sort((a, b) => elementDepth(b) - elementDepth(a));\n for (const el of toRemove) {\n el.remove();\n }\n\n const htmlEl = root.querySelector(\"html\") as HTMLElement | null;\n if (htmlEl) {\n htmlEl.removeAttribute(\"data-ff-route\");\n htmlEl.removeAttribute(\"data-ff-route-label\");\n }\n\n const styles = root.querySelectorAll(\"style\") as unknown as HTMLElement[];\n for (const st of styles) {\n const text = st.textContent ?? \"\";\n if (!text.trim()) st.remove();\n }\n\n return root.outerHTML;\n}\n\n/**\n * Walk `outDir` (e.g. Astro `dist/`) and apply {@link cullProductionHtml} to every `.html` file.\n */\nexport function applyProductionHtmlCullToDist(\n outDir: string,\n runtime: ResolvedFeatureRuntime,\n): void {\n for (const file of walkHtmlFiles(outDir)) {\n const before = readFileSync(file, \"utf8\");\n const after = cullProductionHtml(before, runtime);\n if (after !== before) writeFileSync(file, after, \"utf8\");\n }\n}\n","import type { ResolvedFeatureRuntime } from \"./runtime\";\nimport { toToken } from \"./runtime\";\nimport { inlineInvoke } from \"./inline-script\";\nimport { affHeadInlineRuntime } from \"./dev-inline-runtimes\";\n\n/**\n * Single `injectScript('head-inline', …)` payload for `astro dev`: dev-only outline CSS,\n * `data-ff-route` on `<html>` (pathname + {@link ResolvedFeatureRuntime.routeFlags}), then\n * the feature-flag dev bootstrap (toolbar state, combo badges, route chrome).\n */\nexport function buildAffDevHeadInline(args: {\n runtime: ResolvedFeatureRuntime;\n featureFlagStyles: string;\n affDevBootstrap: string;\n}): string {\n const { runtime, featureFlagStyles, affDevBootstrap } = args;\n const flagNameToToken = Object.fromEntries(\n Object.keys(runtime.flags).map((name) => [name, toToken(name)]),\n );\n const setup = inlineInvoke(affHeadInlineRuntime, {\n featureFlagStyles,\n routeFlags: runtime.routeFlags,\n flagNameToToken,\n });\n return `${setup}\\n${affDevBootstrap}`;\n}\n","/**\n * Shared JS helper source used by generated browser scripts.\n * Keep this in sync with runtime.routePatternToPrefix semantics.\n */\nexport function routePrefixJsHelper(functionName: string): string {\n return `function ${functionName}(pattern){var p=String(pattern||'').trim();if(p.endsWith('/**'))p=p.slice(0,-3);else if(p.endsWith('/*'))p=p.slice(0,-2);else if(p.length>1&&p.endsWith('*'))p=p.slice(0,-1);return p.endsWith('/')?p:p+'/';}`;\n}\n","import { rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n elementBadgePositionBlock,\n normalizeElementBadgeLayout,\n} from \"./badge-layout\";\nimport { DEV_TOOLBAR_FLAG_ICON_SVG } from \"./dev-toolbar-flag-icon\";\nimport type {\n ElementBadgeHorizontalAlign,\n ElementBadgeLayoutOptions,\n ElementBadgeVerticalAnchor,\n NormalizedElementBadgeLayout,\n} from \"./badge-layout\";\nimport {\n type FeatureFlagMap,\n type ResolveFeatureRuntimeOptions,\n type ResolvedFeatureRuntime,\n resolveFeatureFlagsByEnvironment,\n resolveFeatureRuntime,\n routePathsToPrune,\n shouldIncludeRoute,\n toEnumKey,\n toToken,\n} from \"./runtime\";\n\nexport type {\n ElementBadgeHorizontalAlign,\n ElementBadgeLayoutOptions,\n ElementBadgeVerticalAnchor,\n NormalizedElementBadgeLayout,\n};\nexport { elementBadgePositionBlock, normalizeElementBadgeLayout };\n\nexport type {\n DevOutlineCssOptions,\n DevOutlineHiddenStrategy,\n} from \"./dev-outline-css\";\n\nimport type { DevOutlineCssOptions } from \"./dev-outline-css\";\nimport {\n buildAffDevBootstrapScript,\n createFeatureFlagStyles,\n createProductionGateStyles,\n} from \"./dev-outline-css\";\nimport { applyProductionHtmlCullToDist } from \"./production-html-cull\";\nimport { buildAffDevHeadInline } from \"./dev-head-inject\";\nimport { routePrefixJsHelper } from \"./route-prefix-js\";\n\nexport { createFeatureFlagStyles, createProductionGateStyles };\nexport {\n cullProductionHtml,\n applyProductionHtmlCullToDist,\n} from \"./production-html-cull\";\n\nexport interface AstroFeatureFlagsOptions extends ResolveFeatureRuntimeOptions {\n css?: DevOutlineCssOptions;\n /** When false, keeps static build output untouched (no route pruning / HTML cull). */\n staticMinify?: boolean;\n}\n\n/** Prefer `prod` if present; else first non-`dev` key (sorted); else `\"prod\"`. */\nexport function primaryNonDevEnvironmentKey(\n flagsByEnvironment: Record<string, FeatureFlagMap>,\n): string {\n const keys = Object.keys(flagsByEnvironment);\n if (keys.includes(\"prod\")) return \"prod\";\n const nonDev = keys.filter((k) => k !== \"dev\").sort();\n return nonDev[0] ?? \"prod\";\n}\n\nfunction withDefaultEnvironments(\n options: ResolveFeatureRuntimeOptions = {},\n): ResolveFeatureRuntimeOptions {\n const mode = options.mode ?? process.env.NODE_ENV ?? \"development\";\n const raw =\n options.environments &&\n typeof options.environments === \"object\" &&\n !Array.isArray(options.environments)\n ? { ...options.environments }\n : {};\n delete raw.dev;\n const environments: NonNullable<ResolveFeatureRuntimeOptions[\"environments\"]> =\n {\n dev: {\n when: mode !== \"production\",\n },\n ...raw,\n };\n if (Object.keys(environments).length < 2) {\n environments.prod = environments.prod ?? {\n when: mode === \"production\",\n flags: {},\n };\n }\n return { ...options, environments };\n}\n\nexport function createVirtualModuleSource(\n runtime: ResolvedFeatureRuntime,\n css?: DevOutlineCssOptions,\n flagsByEnvironment: Record<string, FeatureFlagMap> = {},\n devBootstrap?: string,\n): string {\n const flagNames = Object.keys(runtime.flags);\n const flagTokens = flagNames.map((name) => toToken(name));\n const enumEntries = flagNames\n .map((name) => ` ${toEnumKey(name)}: ${JSON.stringify(name)}`)\n .join(\",\\n\");\n const tokenEntries = flagNames\n .map((name) => ` ${toEnumKey(name)}: ${JSON.stringify(toToken(name))}`)\n .join(\",\\n\");\n const styles = createFeatureFlagStyles(runtime, css);\n const colorsJson = JSON.stringify(runtime.flagColorsByToken, null, 2);\n const flagNameToToken = JSON.stringify(\n Object.fromEntries(flagNames.map((name) => [name, toToken(name)])),\n null,\n 2,\n );\n const defaultNonDevEnvironment =\n primaryNonDevEnvironmentKey(flagsByEnvironment);\n const bootstrap =\n devBootstrap ??\n buildAffDevBootstrapScript(\n flagTokens,\n { ...runtime.flagColorsByToken },\n { ...runtime.flagOutlineDefaultsByToken },\n { ...runtime.flagBadgeDefaultsByToken },\n { ...runtime.routeFlags },\n Object.fromEntries(flagNames.map((name) => [name, toToken(name)])),\n runtime.namespace,\n );\n const affRoutePatternToPrefix = routePrefixJsHelper(\"affRoutePatternToPrefix\");\n\n return `${affRoutePatternToPrefix}\n\nexport const FeatureFlag = Object.freeze({\n${enumEntries}\n});\n\nexport const FeatureToken = Object.freeze({\n${tokenEntries}\n});\n\nexport const featureFlagTokens = Object.freeze(${JSON.stringify(flagTokens)});\nexport const featureFlagColors = Object.freeze(${colorsJson});\nexport const featureFlags = Object.freeze(${JSON.stringify(runtime.flags, null, 2)});\nexport const featureFlagsByEnvironment = Object.freeze(${JSON.stringify(flagsByEnvironment, null, 2)});\nexport const defaultNonDevEnvironment = ${JSON.stringify(defaultNonDevEnvironment)};\nexport const featureRouteFlags = Object.freeze(${JSON.stringify(runtime.routeFlags, null, 2)});\nexport const featureNamespace = ${JSON.stringify(runtime.namespace)};\n/** True when Astro is running the dev server or dev build (import.meta.env.DEV). Not the same as the reserved \\`dev\\` environment layer — see \\`activeEnvironmentKey\\`. */\nexport const isAstroDev = import.meta.env.DEV;\nexport const activeEnvironmentKey = ${JSON.stringify(runtime.activeEnvironment)};\n\nexport const affDevBootstrap = import.meta.env.DEV ? ${JSON.stringify(bootstrap)} : '';\n\nexport function isFeatureEnabled(flag) {\n return Boolean(featureFlags[flag]);\n}\n\nexport function shouldRenderFeature(flag) {\n return isFeatureEnabled(flag);\n}\n\nexport function isFeatureRoute(pathname) {\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n return Object.keys(featureRouteFlags).some((route) => {\n const candidate = affRoutePatternToPrefix(route);\n return normalized === candidate || normalized.startsWith(candidate);\n });\n}\n\nexport function shouldIncludePath(pathname) {\n if (import.meta.env.DEV) return true;\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n for (const [route, flags] of Object.entries(featureRouteFlags)) {\n const candidate = affRoutePatternToPrefix(route);\n if (normalized === candidate || normalized.startsWith(candidate)) {\n return flags.every((flag) => isFeatureEnabled(flag));\n }\n }\n return true;\n}\n\nexport function flagsForEnvironment(envName) {\n var F = featureFlagsByEnvironment[envName];\n return F != null ? F : null;\n}\n\nexport function isFeatureEnabledForEnvironment(flag, envName) {\n var F = featureFlagsByEnvironment[envName];\n return !!(F && F[flag]);\n}\n\nexport function shouldIncludePathForEnvironment(pathname, envName) {\n var F = featureFlagsByEnvironment[envName];\n if (!F) return true;\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n for (const [route, flagList] of Object.entries(featureRouteFlags)) {\n const candidate = affRoutePatternToPrefix(route);\n if (normalized === candidate || normalized.startsWith(candidate)) {\n return flagList.every((flag) => Boolean(F[flag]));\n }\n }\n return true;\n}\n\nconst flagNameToToken = Object.freeze(${flagNameToToken});\n\nexport function matchedFeatureRoutePrefix(pathname) {\n if (!pathname) return null;\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n let best = null;\n let bestLen = -1;\n for (const routePrefix of Object.keys(featureRouteFlags)) {\n const route = affRoutePatternToPrefix(routePrefix);\n if (normalized === route || normalized.startsWith(route)) {\n if (route.length > bestLen) {\n bestLen = route.length;\n best = routePrefix;\n }\n }\n }\n return best;\n}\n\nexport function routeFeatureTokenForPath(pathname) {\n const prefix = matchedFeatureRoutePrefix(pathname);\n if (!prefix) return undefined;\n const flagNames = featureRouteFlags[prefix] || [];\n const first = flagNames[0];\n return first != null ? flagNameToToken[first] : undefined;\n}\n\nexport function routeFeatureTokensForPath(pathname) {\n if (!pathname) return [];\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n const out = [];\n const seen = new Set();\n for (const [routePrefix, flagNames] of Object.entries(featureRouteFlags)) {\n const route = affRoutePatternToPrefix(routePrefix);\n if (!(normalized === route || normalized.startsWith(route))) continue;\n for (const name of flagNames || []) {\n const token = flagNameToToken[name];\n if (!token || seen.has(token)) continue;\n seen.add(token);\n out.push(token);\n }\n }\n return out;\n}\n\nexport function routeFeatureMatchesForPath(pathname) {\n if (!pathname) return [];\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n var byFlag = {};\n for (const [routePattern, flagNames] of Object.entries(featureRouteFlags)) {\n const candidate = affRoutePatternToPrefix(routePattern);\n if (!(normalized === candidate || normalized.startsWith(candidate))) continue;\n for (const flagName of flagNames || []) {\n const token = flagNameToToken[flagName];\n if (!token) continue;\n if (!byFlag[flagName]) byFlag[flagName] = { flag: flagName, token, routes: [] };\n byFlag[flagName].routes.push(routePattern);\n }\n }\n return Object.values(byFlag);\n}\n\nexport const featureFlagStyles = import.meta.env.DEV ? ${JSON.stringify(styles)} : \"\";\n`;\n}\n\nexport function getResolvedFeatures(\n options: ResolveFeatureRuntimeOptions = {},\n): ResolvedFeatureRuntime {\n return resolveFeatureRuntime(withDefaultEnvironments(options));\n}\n\nexport function featureRouteIncluded(\n pathname: string,\n runtime: ResolvedFeatureRuntime,\n): boolean {\n return shouldIncludeRoute({\n pathname,\n routeFlags: runtime.routeFlags,\n flags: runtime.flags,\n isDev: runtime.isDev,\n });\n}\n\nexport type { FeatureFlagMap } from \"./runtime\";\nexport {\n longestMatchingRoutePrefix,\n mergeFlagsWithProcessEnvOverrides,\n resolveFeatureFlagsByEnvironment,\n routePatternToPrefix,\n} from \"./runtime\";\n\nexport default function astroFeatureFlags(\n options: AstroFeatureFlagsOptions = {},\n): any {\n const { css, staticMinify = true, ...flagOpts } = options;\n const opts = withDefaultEnvironments(flagOpts);\n const mode = opts.mode ?? process.env.NODE_ENV ?? \"development\";\n\n const runtime = resolveFeatureRuntime({\n ...opts,\n mode,\n });\n\n return {\n name: \"astro-feature-flags\",\n hooks: {\n \"astro:config:setup\": ({\n updateConfig,\n addDevToolbarApp,\n command,\n injectScript,\n }: {\n updateConfig: (config: unknown) => void;\n addDevToolbarApp?: (opts: {\n id: string;\n name: string;\n icon: string;\n entrypoint: string;\n }) => void;\n command?: string;\n injectScript?: (stage: string, content: string) => void;\n }) => {\n const flagNames = Object.keys(runtime.flags);\n const flagTokens = flagNames.map((name) => toToken(name));\n const flagsByEnvironment = resolveFeatureFlagsByEnvironment(opts);\n const flagNameToToken = Object.fromEntries(\n flagNames.map((name) => [name, toToken(name)]),\n );\n const bootstrap = buildAffDevBootstrapScript(\n flagTokens,\n { ...runtime.flagColorsByToken },\n { ...runtime.flagOutlineDefaultsByToken },\n { ...runtime.flagBadgeDefaultsByToken },\n { ...runtime.routeFlags },\n flagNameToToken,\n runtime.namespace,\n );\n\n if (\n command === \"dev\" &&\n runtime.isDev &&\n typeof addDevToolbarApp === \"function\"\n ) {\n addDevToolbarApp({\n id: \"astro-feature-flags\",\n name: \"Feature flags\",\n icon: DEV_TOOLBAR_FLAG_ICON_SVG,\n entrypoint: fileURLToPath(\n new URL(\"./dev-toolbar-app.mjs\", import.meta.url),\n ),\n });\n }\n if (\n command === \"dev\" &&\n runtime.isDev &&\n typeof injectScript === \"function\"\n ) {\n const styles = createFeatureFlagStyles(runtime, css);\n injectScript(\n \"head-inline\",\n buildAffDevHeadInline({\n runtime,\n featureFlagStyles: styles,\n affDevBootstrap: bootstrap,\n }),\n );\n }\n updateConfig({\n vite: {\n plugins: [\n {\n name: \"astro-feature-flags:virtual-module\",\n resolveId(id: string) {\n if (id === \"virtual:astro-feature-flags\")\n return \"\\0virtual:astro-feature-flags\";\n return null;\n },\n load(id: string) {\n if (id === \"\\0virtual:astro-feature-flags\") {\n return createVirtualModuleSource(\n runtime,\n css,\n flagsByEnvironment,\n bootstrap,\n );\n }\n return null;\n },\n },\n ],\n },\n });\n },\n \"astro:build:done\": ({ dir }: { dir: URL }) => {\n if (runtime.isDev || !staticMinify) return;\n const outDir = fileURLToPath(dir);\n const prunePaths = routePathsToPrune({\n routeFlags: runtime.routeFlags,\n flags: runtime.flags,\n });\n for (const routePath of prunePaths) {\n rmSync(join(outDir, routePath), { recursive: true, force: true });\n }\n applyProductionHtmlCullToDist(outDir, runtime);\n },\n },\n };\n}\n"],"mappings":";;;;;;;AAwCA,SAAS,aAAa,GAAW,UAA0B;AACzD,KAAI,OAAO,MAAM,YAAY,OAAO,MAAM,EAAE,CAAE,QAAO;AACrD,QAAO,KAAK,IAAI,KAAK,KAAK,IAAI,GAAG,EAAE,CAAC;;AAGtC,SAAgB,4BACd,OAC8B;CAC9B,MAAM,IAAI,aAAa,OAAO,oCAAoC,IAAI,GAAG;CACzE,MAAM,SACJ,OAAO,+BAA+B,WAAW,WAAW;CAC9D,MAAM,KAAK,OAAO;AAClB,KAAI,OAAO,KAAA,KAAa,OAAO,QAAQ,OAAO,OAAO,SACnD,QAAO;EACL,MAAM;EACN,mBAAmB,aAAa,IAAI,GAAG;EACvC,sBAAsB;EACtB,gBAAgB;EACjB;AAOH,QAAO;EACL,MAAM;EACN,iBANA,OAAO,gCAAgC,WACvC,OAAO,gCAAgC,WACnC,MAAM,8BACN;EAIJ,sBAAsB;EACtB,gBAAgB;EACjB;;AAGH,MAAM,QAAQ;;;;AAKd,SAAgB,0BACd,QACQ;CACR,MAAM,EAAE,sBAAsB,GAAG,mBAAmB;CACpD,MAAM,IACJ,mBAAmB,QACf,wBAAwB,EAAE,OAC1B,cAAc,EAAE;AAEtB,KAAI,OAAO,SAAS,WAAW;EAC7B,MAAM,IAAI,OAAO;AACjB,MAAI,mBAAmB,MACrB,QAAO,WAAW,EAAE;;;;gCAIM,EAAE;AAE9B,SAAO,WAAW,EAAE;;;;gCAIQ,EAAE;;CAGhC,MAAM,EAAE,oBAAoB;AAC5B,KAAI,oBAAoB,UAAU;AAChC,MAAI,mBAAmB,MACrB,QAAO;;;;gCAImB,EAAE;AAE9B,SAAO;;;;gCAIqB,EAAE;;AAEhC,KAAI,oBAAoB,SAAS;AAC/B,MAAI,mBAAmB,MACrB,QAAO,WAAW,MAAM;;;;eAIf,EAAE;AAEb,SAAO,WAAW,MAAM;;;;eAIb,EAAE;;AAGf,KAAI,mBAAmB,MACrB,QAAO;WACA,MAAM;;;eAGF,EAAE;AAEf,QAAO;WACE,MAAM;;;eAGF,EAAE;;;;AChJjB,SAAgB,oBAAoB,QAAwB;AAK1D,QAJc,OACX,MAAM,KAAK,CACX,KAAK,SAAS,KAAK,MAAM,CAAC,CAC1B,QAAQ,MAAM,KAAK,QAAQ,KAAK,SAAS,KAAM,MAAM,KAAK,MAAM,IAAI,SAAS,EAAG,CACtE,KAAK,KAAK,CAAC,MAAM;;AAGhC,SAAgB,aACd,IACA,SACQ;AACR,QAAO,oBAAoB,IAAI,GAAG,UAAU,CAAC,IAAI,KAAK,UAAU,QAAQ,CAAC,IAAI;;;;ACI/E,SAAgB,qBAAqB,SAAkC;CACrE,MAAM,YAAY,YAA4B;EAC5C,IAAI,IAAI,OAAO,WAAW,GAAG,CAAC,MAAM;AACpC,MAAI,EAAE,SAAS,MAAM,CAAE,KAAI,EAAE,MAAM,GAAG,GAAG;WAChC,EAAE,SAAS,KAAK,CAAE,KAAI,EAAE,MAAM,GAAG,GAAG;WACpC,EAAE,SAAS,KAAK,EAAE,SAAS,IAAI,CAAE,KAAI,EAAE,MAAM,GAAG,GAAG;AAC5D,SAAO,EAAE,SAAS,IAAI,GAAG,IAAI,GAAG,EAAE;;AAEpC,KAAI;EACF,MAAM,EAAE,mBAAmB,YAAY,IAAI,iBAAiB,MAAM;EAClE,MAAM,IAAI,SAAS,cAAc,QAAQ;AACzC,IAAE,aAAa,4BAA4B,GAAG;AAC9C,IAAE,cAAc;AAChB,GAAC,SAAS,QAAQ,SAAS,iBAAiB,YAAY,EAAE;EAE1D,MAAM,gBAAgB;GACpB,IAAI,IAAI,OAAO,aAAa,eAAe,SAAS,WAAW,SAAS,WAAW;AACnF,OAAI,CAAC,EAAE,SAAS,IAAI,CAAE,MAAK;AAC3B,UAAO;;EAGT,MAAM,gBAAgB,aAAqB;GACzC,IAAI,IAAI,YAAY;AACpB,OAAI,CAAC,EAAE,SAAS,IAAI,CAAE,MAAK;GAC3B,MAAM,MAAgB,EAAE;GACxB,MAAM,uBAAO,IAAI,KAAa;AAC9B,QAAK,MAAM,MAAM,IAAI;AACnB,QAAI,CAAC,OAAO,UAAU,eAAe,KAAK,IAAI,GAAG,CAAE;IACnD,MAAM,QAAQ,SAAS,GAAG;AAC1B,QAAI,EAAE,MAAM,SAAS,EAAE,QAAQ,MAAM,KAAK,GAAI;IAC9C,MAAM,MAAM,GAAG,OAAO,EAAE;AACxB,SAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;KACnC,MAAM,KAAK,IAAI;AACf,SAAI,MAAM,CAAC,KAAK,IAAI,GAAG,EAAE;AACvB,WAAK,IAAI,GAAG;AACZ,UAAI,KAAK,GAAG;;;;AAIlB,UAAO;;EAGT,MAAM,aAAa,aACjB,aAAa,SAAS,CACnB,KAAK,SAAS,EAAE,MAAM,CACtB,OAAO,QAAQ;EAEpB,MAAM,+BAA+B;GACnC,MAAM,OAAO,SAAS;AACtB,OAAI,CAAC,SAAS,KAAM;GACpB,MAAM,QAAQ,aAAa,SAAS,CAAC;GACrC,MAAM,WAAqB,EAAE;AAC7B,QAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK;IACrC,MAAM,KAAK,EAAE,MAAM;AACnB,QAAI,CAAC,GAAI;AACT,QAAI,KAAK,aAAa,mBAAmB,KAAK,KAAK,MAAO,UAAS,KAAK,GAAG;;GAE7E,MAAM,KAAK;GACX,IAAI,KAAK,SAAS,eAAe,GAAG;AACpC,OAAI,CAAC,SAAS,QAAQ;AACpB,QAAI,GAAI,IAAG,QAAQ;AACnB,SAAK,gBAAgB,0BAA0B;AAC/C;;AAEF,QAAK,aAAa,2BAA2B,GAAG;AAChD,OAAI,CAAC,IAAI;AACP,SAAK,SAAS,cAAc,MAAM;AAClC,OAAG,KAAK;AACR,OAAG,YAAY;IACf,MAAM,IAAI,SAAS,cAAc,IAAI;AACrC,OAAG,YAAY,EAAE;AACjB,aAAS,KAAK,YAAY,GAAG;;GAE/B,MAAM,MAAM,GAAG,cAAc,IAAI,IAAI;GACrC,MAAM,YAAY,SAAS,KAAK,KAAK;AAOrC,OAAI,cAAc,GALhB,SAAS,WAAW,IAChB,kBAAkB,UAAU,sBAC5B,kBAAkB,UAAU,sBAGP;;EAG7B,MAAM,iBAAiB;AACrB,YAAS,gBAAgB,aAAa,iBAAiB,UAAU,SAAS,CAAC,CAAC,KAAK,IAAI,CAAC;AACtF,2BAAwB;;AAG1B,YAAU;AACV,MAAI,OAAO,qBAAqB,WAClB,KAAI,kBAAkB,SAAS;AACzC,QAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;IACpC,MAAM,IAAI,KAAK,MAAM,KAAK,GAAG;AAC7B,QAAI,MAAM,mBAAoB,KAAK,EAAE,QAAQ,mBAAmB,KAAK,GAAI;AACvE,6BAAwB;AACxB;;;IAGJ,CACE,QAAQ,SAAS,iBAAiB,EAAE,YAAY,MAAM,CAAC;AAE7D,WAAS,iBAAiB,mBAAmB,SAAS;AACtD,WAAS,iBAAiB,oBAAoB,SAAS;SACjD;;AAKV,SAAgB,uBAAuB,SAAiC;CACtE,MAAM,YAAY,YAA4B;EAC5C,IAAI,IAAI,OAAO,WAAW,GAAG,CAAC,MAAM;AACpC,MAAI,EAAE,SAAS,MAAM,CAAE,KAAI,EAAE,MAAM,GAAG,GAAG;WAChC,EAAE,SAAS,KAAK,CAAE,KAAI,EAAE,MAAM,GAAG,GAAG;WACpC,EAAE,SAAS,KAAK,EAAE,SAAS,IAAI,CAAE,KAAI,EAAE,MAAM,GAAG,GAAG;AAC5D,SAAO,EAAE,SAAS,IAAI,GAAG,IAAI,GAAG,EAAE;;CAEpC,MAAM,IAAI,QAAQ;CAClB,MAAM,IAAI,QAAQ;CAClB,MAAM,KAAK,QAAQ;CACnB,MAAM,KAAK,QAAQ;CACnB,MAAM,KAAK,QAAQ;CACnB,MAAM,MAAM,QAAQ;CACpB,MAAM,IAAI,QAAQ;CAClB,MAAM,IAAI,GAAG,EAAE;CAEf,MAAM,OAAO,SAAS;CACtB,MAAM,aAAa,OAAe,KAAK,aAAa,mBAAmB,KAAK,KAAK;CACjF,MAAM,qBACJ,IACA,SACW;AAEX,SADe,KAAK,MAAM,iBAAiB,KAAK,EAAE,KAAK,KAAK,CAAC,MAAM,IAClD,KAAK,OAAO,EAAE,OAAO;;CAExC,MAAM,UAAU,MAAgB,CAAC,GAAG,IAAI,IAAI,EAAE,OAAO,QAAQ,CAAC,CAAC;CAE/D,MAAM,eAAe,OAA0B;EAC7C,IAAI,MAAgB,EAAE;EACtB,MAAM,OAAO,GAAG,aAAa,QAAQ,IAAI,IAAI,IAAI,MAAM;AACvD,MAAI,IAAK,OAAM,IAAI,OAAO,IAAI,MAAM,MAAM,CAAC,OAAO,QAAQ,CAAC;AAC3D,OAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;GACjC,MAAM,KAAK,EAAE;AACb,OAAI,GAAG,aAAa,QAAQ,EAAE,GAAG,KAAK,CAAE,KAAI,KAAK,GAAG;;AAEtD,SAAO,OAAO,IAAI;;CAGpB,MAAM,yBAAyB,OAAgB;AAC7C,OAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,IAC5B,IAAG,gBAAgB,QAAQ,EAAE,GAAG,EAAE,KAAK;;CAG3C,MAAM,yBAAyB,OAAoB;AACjD,KAAG,gBAAgB,gBAAgB;AACnC,KAAG,MAAM,eAAe,sBAAsB;AAC9C,KAAG,MAAM,eAAe,2BAA2B;AACnD,KAAG,MAAM,eAAe,qBAAqB;AAC7C,KAAG,MAAM,eAAe,kBAAkB;AAC1C,KAAG,MAAM,eAAe,0BAA0B;AAClD,KAAG,MAAM,eAAe,4BAA4B;;CAGtD,MAAM,cAAc,IAAiB,SAAiC;EACpE,MAAM,YAAY,YAAY,GAAG;EACjC,MAAM,WAAW,UAAU,UAAU;EACrC,MAAM,qBAAqB,YAAY,UAAU,MAAM,OAAO,CAAC,UAAU,GAAG,CAAC;EAC7E,MAAM,SAAS,UAAU,QAAQ,OAAO,UAAU,GAAG,CAAC;AACtD,MAAI,oBAAoB;AAGtB,yBAAsB,GAAG;AACzB,MAAG,aAAa,QAAQ,KAAK,UAAU,KAAK,IAAI,CAAC;AACjD,yBAAsB,GAAG;AACzB;;AAEF,MAAI,OAAO,SAAS,GAAG;AACrB,yBAAsB,GAAG;AACzB,OAAI,UAAU;AAGZ,0BAAsB,GAAG;AACzB,QAAI,OAAO,WAAW,EACpB,IAAG,aAAa,QAAQ,KAAK,OAAO,GAAI;QAExC,IAAG,gBAAgB,QAAQ,IAAI;;AAGnC;;AAEF,wBAAsB,GAAG;AAGzB,KAAG,aAAa,QAAQ,KAAK,OAAO,KAAK,IAAI,CAAC;AAC9C,KAAG,aAAa,iBAAiB,OAAO,KAAK,MAAM,CAAC;EACpD,MAAM,SAAS,OAAO,KAAK,OAAO,kBAAkB,IAAI,KAAK,CAAC;EAC9D,MAAM,OAAO,OAAO,KAAK,MAAM,kCAAkC,EAAE,OAAO;AAC1E,KAAG,MAAM,YAAY,uBAAuB,0BAA0B,OAAO,KAAK,KAAK,CAAC,GAAG;AAC3F,KAAG,MAAM,YAAY,4BAA4B,0BAA0B,KAAK,KAAK,KAAK,CAAC,GAAG;AAC9F,KAAG,MAAM,YAAY,sBAAsB,OAAO,MAAM,UAAU;AAClE,KAAG,MAAM,YAAY,mBAAmB,OAAO,MAAM,UAAU;AAC/D,KAAG,MAAM,YACP,2BACA,uBAAuB,OAAO,MAAM,UAAU,oBAC/C;AACD,KAAG,MAAM,YAAY,6BAA6B,0BAA0B,KAAK,KAAK,KAAK,CAAC,GAAG;;CAGjG,MAAM,sBAAsB,SAA2B;EACrD,MAAM,MAAgB,EAAE;EACxB,MAAM,uBAAO,IAAI,KAAa;AAC9B,OAAK,MAAM,MAAM,IAAI;AACnB,OAAI,CAAC,OAAO,UAAU,eAAe,KAAK,IAAI,GAAG,CAAE;GACnD,MAAM,MAAM,SAAS,GAAG;AACxB,OAAI,EAAE,SAAS,OAAO,KAAK,QAAQ,IAAI,KAAK,GAAI;GAChD,MAAM,MAAM,GAAG,OAAO,EAAE;AACxB,QAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;IACnC,MAAM,KAAK,IAAI,IAAI;AACnB,QAAI,MAAM,CAAC,KAAK,IAAI,GAAG,EAAE;AACvB,UAAK,IAAI,GAAG;AACZ,SAAI,KAAK,GAAG;;;;AAIlB,SAAO;;CAGT,MAAM,iBAAiB,MAAc,SAA2B;AAC9D,MAAI,CAAC,KAAK,OAAQ,QAAO;EACzB,MAAM,SAAmC,EAAE;AAC3C,OAAK,MAAM,MAAM,IAAI;AACnB,OAAI,CAAC,OAAO,UAAU,eAAe,KAAK,IAAI,GAAG,CAAE;GACnD,MAAM,MAAM,SAAS,GAAG;AACxB,OAAI,EAAE,SAAS,OAAO,KAAK,QAAQ,IAAI,KAAK,GAAI;GAChD,MAAM,MAAM,GAAG,OAAO,EAAE;AACxB,QAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;IACnC,MAAM,KAAK,IAAI,IAAI;AACnB,QAAI,CAAC,GAAI;AACT,QAAI,CAAC,OAAO,IAAK,QAAO,MAAM,EAAE;AAChC,WAAO,IAAI,KAAK,GAAG;;;AAGvB,SAAO,SAAS,KACb,KAAK,OAAO,GAAG,GAAG,MAAM,OAAO,OAAO,EAAE,EAAE,KAAK,KAAK,IAAI,YAAY,CACpE,KAAK,MAAM;;CAGhB,MAAM,sBAAsB,SAAiB;AAC3C,MAAI,CAAC,SAAS,KAAM;EAGpB,MAAM,WAAW,mBAAmB,KAAK;AACzC,MAAI,CAAC,SAAS,QAAQ;AACpB,YAAS,eAAe,kBAAkB,EAAE,QAAQ;AACpD,YAAS,eAAe,kBAAkB,EAAE,QAAQ;AACpD,QAAK,gBAAgB,gBAAgB;AACrC,QAAK,gBAAgB,sBAAsB;AAC3C;;AAEF,OAAK,aAAa,iBAAiB,SAAS,KAAK,IAAI,CAAC;AACtD,OAAK,aAAa,uBAAuB,cAAc,MAAM,SAAS,CAAC;AACvE,MAAI,CAAC,SAAS,eAAe,kBAAkB,EAAE;GAC/C,MAAM,QAAQ,SAAS,cAAc,MAAM;AAC3C,SAAM,KAAK;AACX,SAAM,YAAY;AAClB,SAAM,aAAa,eAAe,OAAO;AACzC,YAAS,KAAK,YAAY,MAAM;;EAElC,IAAI,QAAQ,SAAS,eAAe,kBAAkB;AACtD,MAAI,CAAC,OAAO;AACV,WAAQ,SAAS,cAAc,MAAM;AACrC,SAAM,KAAK;AACX,SAAM,YAAY;AAClB,SAAM,aAAa,QAAQ,SAAS;AACpC,YAAS,KAAK,YAAY,MAAM;;AAElC,QAAM,cAAc,KAAK,aAAa,sBAAsB,IAAI;AAChE,QAAM,aAAa,cAAc,MAAM,eAAe,GAAG;;AAG3D,KAAI;AACD,SAA0C,UAAU;GACnD,QAAQ;GACR,QAAQ;GACR,WAAW;GACZ;EACD,MAAM,MAAM,aAAa,QAAQ,EAAE,IAAI,aAAa,QAAQ,aAAa;EACzE,MAAM,KAAK,MAAM,KAAK,MAAM,IAAI,GAAG,EAAE;EACrC,MAAM,IAAI,GAAG,WAAW,GAAG,UAAU,EAAE;EACvC,MAAM,IAAI,GAAG,WAAW,GAAG,UAAU,EAAE;EACvC,MAAM,IAAI,GAAG,SAAS,EAAE;EACxB,MAAM,OAAO,GAAG,UAAU,EAAE;AAE5B,OAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,KAAK;GACjC,MAAM,IAAI,EAAE;GACZ,MAAM,KAAK,GAAG,OAAO;GACrB,MAAM,KAAK,GAAG,OAAO;AACrB,OAAI,EAAE,OAAO,SAAU,CAAC,MAAM,EAAE,OAAO,KACrC,MAAK,aAAa,mBAAmB,KAAK,MAAM;OAC7C,MAAK,gBAAgB,mBAAmB,IAAI;AACjD,OAAI,EAAE,OAAO,MAAO,MAAK,aAAa,mBAAmB,KAAK,MAAM;OAC/D,MAAK,gBAAgB,mBAAmB,IAAI;AACjD,OAAI,EAAE,OAAO,SAAU,CAAC,MAAM,EAAE,OAAO,KACrC,MAAK,aAAa,iBAAiB,KAAK,MAAM;OAC3C,MAAK,gBAAgB,iBAAiB,IAAI;GAC/C,MAAM,MAAM,KAAK,MAAM,EAAE;GACzB,MAAM,KAAK,KAAK,EAAE,KAAK;AACvB,OAAI,IAAK,MAAK,MAAM,YAAY,IAAI,IAAI;OACnC,MAAK,MAAM,eAAe,GAAG;;EAGpC,MAAM,iBAAiB;GACrB,MAAM,sBAAM,IAAI,KAAkB;AAClC,YACG,iBAA8B,SAAS,EAAE,GAAG,CAC5C,SAAS,OAAO,IAAI,IAAI,GAAG,CAAC;AAC/B,QAAK,IAAI,IAAI,GAAG,IAAI,EAAE,QAAQ,IAC5B,UACG,iBAA8B,SAAS,EAAE,GAAG,EAAE,GAAG,GAAG,CACpD,SAAS,OAAO,IAAI,IAAI,GAAG,CAAC;AAEjC,OAAI,SAAS,OAAO,WAAW,IAAI,KAAK,CAAC;;EAG3C,MAAM,oBAAoB;AACxB,OAAI,CAAC,SAAS,KAAM;GACpB,IAAI,IAAI,OAAO,UAAU,YAAY;AACrC,OAAI,CAAC,EAAE,SAAS,IAAI,CAAE,MAAK;GAC3B,MAAM,OAAO,mBAAmB,EAAE,CAAC,QAAQ,OAAO,UAAU,GAAG,CAAC;AAChE,OAAI,KAAK,QAAQ;IACf,MAAM,SAAS,KAAK,KAAK,OAAO,kBAAkB,IAAI,KAAK,CAAC;IAC5D,MAAM,OAAO,OAAO,KAAK,MAAM,kCAAkC,EAAE,OAAO;AAC1E,SAAK,MAAM,YAAY,KAAK,EAAE,kBAAkB,0BAA0B,KAAK,KAAK,KAAK,CAAC,GAAG;AAC7F,SAAK,MAAM,YAAY,+BAA+B,0BAA0B,OAAO,KAAK,KAAK,CAAC,GAAG;AACrG,SAAK,MAAM,YAAY,6BAA6B,0BAA0B,KAAK,KAAK,KAAK,CAAC,GAAG;AACjG,SAAK,MAAM,YACT,2BACA,uBAAuB,OAAO,MAAM,UAAU,oBAC/C;AACD,SAAK,MAAM,YAAY,sBAAsB,OAAO,MAAM,UAAU;AACpE,SAAK,MAAM,YAAY,mBAAmB,OAAO,MAAM,UAAU;UAC5D;AACL,SAAK,MAAM,eAAe,KAAK,EAAE,iBAAiB;AAClD,SAAK,MAAM,eAAe,8BAA8B;AACxD,SAAK,MAAM,eAAe,4BAA4B;AACtD,SAAK,MAAM,eAAe,0BAA0B;AACpD,SAAK,MAAM,eAAe,qBAAqB;AAC/C,SAAK,MAAM,eAAe,kBAAkB;;AAE9C,aAAU;AACV,sBAAmB,EAAE;;AAGvB,eAAa;AACb,MAAI,SAAS,eAAe,UAC1B,UAAS,iBAAiB,oBAAoB,aAAa,EAAE,MAAM,MAAM,CAAC;AAE5E,WAAS,iBAAiB,mBAAmB,YAAY;AACzD,WAAS,iBAAiB,oBAAoB,YAAY;AAC1D,MAAI,OAAO,qBAAqB,WAClB,KAAI,kBAAkB,SAAS;AACzC,QAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK;IACpC,MAAM,IAAI,KAAK,IAAI;AACnB,QAAI,CAAC,EAAG;AACR,QACE,MAAM,WACN,EAAE,QAAQ,mBAAmB,KAAK,KAClC,EAAE,QAAQ,mBAAmB,KAAK,KAClC,EAAE,QAAQ,iBAAiB,KAAK,GAChC;AACA,kBAAa;AACb;;;IAGJ,CACE,QAAQ,MAAM,EAAE,YAAY,MAAM,CAAC;SAEnC;;;;AC3VV,MAAM,8BAGF;CACF,cAAc;CACd,cAAc;CACd,eAAe;CACf,cAAc;CACd,eAAe;CACf,mBAAmB,EAAE;CACrB,gBAAgB;CAChB,oBAAoB;CACrB;AAED,SAAS,oBACP,KACA,SACgC;CAChC,MAAM,UAAU;EACd,GAAG,QAAQ;EACX,GAAI,KAAK,uBAAuB,EAAE;EACnC;CACD,MAAM,gBACJ,KAAK,iBAAiB,KAAK,iBAAiB,4BAA4B;AAC1E,QAAO;EACL,GAAG;EACH,GAAI,OAAO,EAAE;EACb,qBAAqB;EACrB;EACA,mBAAmB;GACjB,GAAG,4BAA4B;GAC/B,GAAI,KAAK,qBAAqB,EAAE;GACjC;EACF;;AAGH,SAAS,cACP,OACA,MACQ;AACR,QAAO,KAAK,oBAAoB,UAAU,KAAK;;;AAIjD,SAAS,gBAAgB,OAAe,WAA2B;AAEjE,QAAO,KADI,QAAQ,UAAU,IAAI,KAClB,KAAK;;;;;;;AAQtB,SAAgB,wBACd,SACA,KACQ;CACR,MAAM,SAAS,QAAQ,QAAQ,QAAQ,UAAU,IAAI;CACrD,MAAM,oBAAoB,SAAiB;EACzC,MAAM,QAAQ,QAAQ,KAAK;EAC3B,MAAM,iBAAiB,CAAC,IAAI,OAAO,KAAK,MAAM,IAAI;AAClD,MAAI,SAAS,MAAO,gBAAe,KAAK,IAAI,OAAO,KAAK,KAAK,IAAI;EACjE,MAAM,gBAAgB,CAAC,GAAG,gBAAgB,IAAI,OAAO,GAAG,MAAM,GAAG;AACjE,SAAO;GACL;GACA;GACA,YAAY,cAAc,KAAK,MAAM,GAAG,EAAE,QAAQ,OAAO,SAAS,CAAC,KAAK,KAAK;GAC7E,OAAO,OAAO,cAAc,KAAK,KAAK,CAAC;GACxC;;CAEH,MAAM,OAAO,oBAAoB,KAAK,QAAQ;CAE9C,MAAM,WAAW,0BADG,4BAA4B,IAAI,CACG;CACvD,MAAM,SAAmB,EAAE;AAE3B,MAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,MAAM,EAAE;EAC7C,MAAM,EAAE,OAAO,YAAY,UAAU,iBAAiB,KAAK;;EAE3D,MAAM,mBAAmB,GAAG,MAAM,QAAQ,OAAO;EACjD,MAAM,MAAM,cAAc,OAAO,KAAK;EACtC,MAAM,IAAI,gBAAgB,OAAO,QAAQ,UAAU;EACnD,MAAM,QACJ,KAAK,kBAAkB,WACtB,UAAU,QAAQ,KAAK,gBAAgB;EAC1C,MAAM,eAAe,OAAO,MAAM,CAAC,QAAQ,MAAM,MAAM;AAEvD,SAAO,KAAK;;oBAEI,MAAM;;4BAEE,MAAM;oBACd,MAAM,QAAQ,EAAE,IAAI,IAAI;;EAE1C,WAAW;;aAEA,KAAK,aAAa,6BAA6B,MAAM;oBAC9C,KAAK,cAAc;mBACpB,KAAK,aAAa;;0BAEX,MAAM,WAAW,iBAAiB;;;;;;;;;EAS1D,SAAS;;cAEG,aAAa;8CACmB,EAAE,IAAI,IAAI;;;;;;;eAOzC,EAAE,IAAI,IAAI;mDAC0B,EAAE,IAAI,IAAI;;;;;;0BAMnC,MAAM,WAAW,iBAAiB;;mDAET,EAAE,IAAI,IAAI;0CACnB,EAAE,IAAI,IAAI;;0BAE1B,MAAM,WAAW,iBAAiB;;;;;0BAKlC,MAAM,WAAW,iBAAiB;;;;;0BAKlC,MAAM,WAAW,iBAAiB;;;;;0BAKlC,MAAM,WAAW,iBAAiB;;;;;0BAKlC,MAAM,WAAW,iBAAiB;;;0BAGlC,MAAM,WAAW,iBAAiB;;;;;0BAKlC,MAAM,WAAW,iBAAiB;;;0BAGlC,MAAM,WAAW,iBAAiB;;;0BAGlC,MAAM,WAAW,iBAAiB;;;0BAGlC,MAAM,WAAW,iBAAiB;;;0BAGlC,MAAM,WAAW,iBAAiB;;;uBAGrC,MAAM,UAAU,MAAM;;;EAG3C;;AAIA,QAAO,KAAK;GACX,OAAO;;;;mBAIS,KAAK,aAAa;;QAE7B,OAAO;;;;;;;;aAQF,KAAK,aAAa;;;;;;;;QAQvB,OAAO;;;;;;;;;EASb,SAAS;;kBAEO,OAAO;;;;;;;;;;;;;;;QAejB,OAAO;;;QAGP,OAAO;;;;;EAKb;AAIA,MAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,MAAM,EAAE;EAC7C,MAAM,EAAE,OAAO,UAAU,iBAAiB,KAAK;AAC/C,SAAO,KAAK;qBACK,MAAM,UAAU,MAAM,GAAG,OAAO;qBAChC,MAAM,UAAU,MAAM,GAAG,OAAO;;;;qBAIhC,MAAM,UAAU,MAAM,GAAG,OAAO;qBAChC,MAAM,UAAU,MAAM,GAAG,OAAO;;;;;;uBAM9B,MAAM,UAAU,MAAM,GAAG,OAAO;;;;uBAIhC,MAAM,UAAU,MAAM,GAAG,OAAO;;;EAGrD;;AAGA,QAAO,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyEZ;AAEA,MAAK,MAAM,QAAQ,OAAO,KAAK,QAAQ,MAAM,EAAE;EAC7C,MAAM,QAAQ,QAAQ,KAAK;AAC3B,SAAO,KAAK;mBACG,MAAM,qBAAqB,MAAM;;;mBAGjC,MAAM,mBAAmB,MAAM;;;mBAG/B,MAAM,qBAAqB,MAAM;;;mBAGjC,MAAM,qBAAqB,MAAM;;;EAGlD;;AAGA,QAAO,OAAO,OAAO,KAAK,KAAK,GAAG;;;;;;AAOpC,SAAgB,2BACd,SACQ;CACR,MAAM,SAAS,QAAQ,QAAQ,QAAQ,UAAU,IAAI;CACrD,MAAM,SAAmB,EAAE;AAC3B,MAAK,MAAM,CAAC,MAAM,YAAY,OAAO,QAAQ,QAAQ,MAAM,EAAE;AAC3D,MAAI,QAAS;EACb,MAAM,QAAQ,QAAQ,KAAK;EAC3B,MAAM,iBAAiB,CAAC,IAAI,OAAO,KAAK,MAAM,IAAI;AAClD,MAAI,SAAS,MAAO,gBAAe,KAAK,IAAI,OAAO,KAAK,KAAK,IAAI;AACjE,SAAO,KACL,GAAG,CAAC,GAAG,gBAAgB,IAAI,OAAO,GAAG,MAAM,GAAG,CAAC,KAAK,KAAK,CAAC,gCAC3D;;AAEH,QAAO,OAAO,SAAS,KAAK,OAAO,KAAK,KAAK,CAAC,MAAM;;AAGtD,SAAgB,2BACd,YACA,QACA,iBACA,eACA,YACA,iBACA,WACQ;AACR,QAAO,aAAa,wBAAwB;EAC1C;EACA;EACA;EACA;EACA;EACA;EACA,WAAW,QAAQ,UAAU,IAAI;EAClC,CAAC;;;;ACzbJ,SAAS,cAAc,KAAuB;CAC5C,MAAM,MAAgB,EAAE;CACxB,MAAM,UAAU,YAAY,KAAK,EAAE,eAAe,MAAM,CAAC;AACzD,MAAK,MAAM,OAAO,SAAS;EACzB,MAAM,IAAI,KAAK,KAAK,IAAI,KAAK;AAC7B,MAAI,IAAI,aAAa,CAAE,KAAI,KAAK,GAAG,cAAc,EAAE,CAAC;WAC3C,IAAI,QAAQ,IAAI,IAAI,KAAK,SAAS,QAAQ,CAAE,KAAI,KAAK,EAAE;;AAElE,QAAO;;AAGT,SAAS,kBAAkB,WAA2B;AACpD,QAAO,QAAQ,QAAQ,UAAU,IAAI;;AAGvC,SAAS,iBACP,IACA,QACA,aACU;CACV,MAAM,0BAAU,IAAI,KAAa;CACjC,MAAM,WAAW,GAAG,aAAa,OAAO,EAAE,MAAM;AAChD,KAAI,SACF,MAAK,MAAM,QAAQ,SAAS,MAAM,MAAM,CAAC,OAAO,QAAQ,CACtD,SAAQ,IAAI,KAAK;AAGrB,MAAK,MAAM,MAAM,YACf,KAAI,GAAG,aAAa,GAAG,OAAO,GAAG,KAAK,CAAE,SAAQ,IAAI,GAAG;AAEzD,QAAO,CAAC,GAAG,QAAQ;;AAGrB,SAAS,gBAAgB,MAAc,WAAwC;CAC7E,MAAM,MAAgB,EAAE;AACxB,MAAK,MAAM,QAAQ,UACjB,KAAI,SAAS,QAAQ,SAAS,QAAQ,KAAK,CAAE,KAAI,KAAK,KAAK;AAE7D,QAAO;;AAGT,SAAS,kBACP,IACA,SACA,QACA,aACA,WACS;CACT,MAAM,QAAQ,iBAAiB,IAAI,QAAQ,YAAY;AACvD,KAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,MAAK,MAAM,QAAQ,MAEjB,KADc,gBAAgB,MAAM,UAAU,CACpC,WAAW,EAAG,QAAO;AAEjC,MAAK,MAAM,QAAQ,OAAO;EACxB,MAAM,QAAQ,gBAAgB,MAAM,UAAU;AAC9C,OAAK,MAAM,QAAQ,MACjB,KAAI,CAAC,QAAQ,MAAM,MAAO,QAAO;;AAGrC,QAAO;;AAGT,SAAS,aAAa,IAAyB;CAC7C,IAAI,IAAI;CACR,IAAI,IAAwB,GAAG;AAC/B,QAAO,GAAG;AACR;AACA,MAAI,EAAE;;AAER,QAAO;;;;;;;AAQT,SAAgB,mBACd,MACA,SACQ;CACR,MAAM,OAAO,MAAM,KAAK;CACxB,MAAM,SAAS,kBAAkB,QAAQ,UAAU;CACnD,MAAM,YAAY,OAAO,KAAK,QAAQ,MAAM;CAC5C,MAAM,cAAc,UAAU,KAAK,MAAM,QAAQ,EAAE,CAAC;CAEpD,MAAM,aAAa,KAAK,iBAAiB,IAAI;CAC7C,MAAM,WAA0B,EAAE;AAClC,MAAK,MAAM,MAAM,WACf,KAAI,kBAAkB,IAAI,SAAS,QAAQ,aAAa,UAAU,CAChE,UAAS,KAAK,GAAG;AAGrB,UAAS,MAAM,GAAG,MAAM,aAAa,EAAE,GAAG,aAAa,EAAE,CAAC;AAC1D,MAAK,MAAM,MAAM,SACf,IAAG,QAAQ;CAGb,MAAM,SAAS,KAAK,cAAc,OAAO;AACzC,KAAI,QAAQ;AACV,SAAO,gBAAgB,gBAAgB;AACvC,SAAO,gBAAgB,sBAAsB;;CAG/C,MAAM,SAAS,KAAK,iBAAiB,QAAQ;AAC7C,MAAK,MAAM,MAAM,OAEf,KAAI,EADS,GAAG,eAAe,IACrB,MAAM,CAAE,IAAG,QAAQ;AAG/B,QAAO,KAAK;;;;;AAMd,SAAgB,8BACd,QACA,SACM;AACN,MAAK,MAAM,QAAQ,cAAc,OAAO,EAAE;EACxC,MAAM,SAAS,aAAa,MAAM,OAAO;EACzC,MAAM,QAAQ,mBAAmB,QAAQ,QAAQ;AACjD,MAAI,UAAU,OAAQ,eAAc,MAAM,OAAO,OAAO;;;;;;;;;;ACxH5D,SAAgB,sBAAsB,MAI3B;CACT,MAAM,EAAE,SAAS,mBAAmB,oBAAoB;CACxD,MAAM,kBAAkB,OAAO,YAC7B,OAAO,KAAK,QAAQ,MAAM,CAAC,KAAK,SAAS,CAAC,MAAM,QAAQ,KAAK,CAAC,CAAC,CAChE;AAMD,QAAO,GALO,aAAa,sBAAsB;EAC/C;EACA,YAAY,QAAQ;EACpB;EACD,CAAC,CACc,IAAI;;;;;;;;ACpBtB,SAAgB,oBAAoB,cAA8B;AAChE,QAAO,YAAY,aAAa;;;;;ACyDlC,SAAgB,4BACd,oBACQ;CACR,MAAM,OAAO,OAAO,KAAK,mBAAmB;AAC5C,KAAI,KAAK,SAAS,OAAO,CAAE,QAAO;AAElC,QADe,KAAK,QAAQ,MAAM,MAAM,MAAM,CAAC,MAAM,CACvC,MAAM;;AAGtB,SAAS,wBACP,UAAwC,EAAE,EACZ;CAC9B,MAAM,OAAO,QAAQ,QAAQ,QAAQ,IAAI,YAAY;CACrD,MAAM,MACJ,QAAQ,gBACR,OAAO,QAAQ,iBAAiB,YAChC,CAAC,MAAM,QAAQ,QAAQ,aAAa,GAChC,EAAE,GAAG,QAAQ,cAAc,GAC3B,EAAE;AACR,QAAO,IAAI;CACX,MAAM,eACJ;EACE,KAAK,EACH,MAAM,SAAS,cAChB;EACD,GAAG;EACJ;AACH,KAAI,OAAO,KAAK,aAAa,CAAC,SAAS,EACrC,cAAa,OAAO,aAAa,QAAQ;EACvC,MAAM,SAAS;EACf,OAAO,EAAE;EACV;AAEH,QAAO;EAAE,GAAG;EAAS;EAAc;;AAGrC,SAAgB,0BACd,SACA,KACA,qBAAqD,EAAE,EACvD,cACQ;CACR,MAAM,YAAY,OAAO,KAAK,QAAQ,MAAM;CAC5C,MAAM,aAAa,UAAU,KAAK,SAAS,QAAQ,KAAK,CAAC;CACzD,MAAM,cAAc,UACjB,KAAK,SAAS,KAAK,UAAU,KAAK,CAAC,IAAI,KAAK,UAAU,KAAK,GAAG,CAC9D,KAAK,MAAM;CACd,MAAM,eAAe,UAClB,KAAK,SAAS,KAAK,UAAU,KAAK,CAAC,IAAI,KAAK,UAAU,QAAQ,KAAK,CAAC,GAAG,CACvE,KAAK,MAAM;CACd,MAAM,SAAS,wBAAwB,SAAS,IAAI;CACpD,MAAM,aAAa,KAAK,UAAU,QAAQ,mBAAmB,MAAM,EAAE;CACrE,MAAM,kBAAkB,KAAK,UAC3B,OAAO,YAAY,UAAU,KAAK,SAAS,CAAC,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC,EAClE,MACA,EACD;CACD,MAAM,2BACJ,4BAA4B,mBAAmB;CACjD,MAAM,YACJ,gBACA,2BACE,YACA,EAAE,GAAG,QAAQ,mBAAmB,EAChC,EAAE,GAAG,QAAQ,4BAA4B,EACzC,EAAE,GAAG,QAAQ,0BAA0B,EACvC,EAAE,GAAG,QAAQ,YAAY,EACzB,OAAO,YAAY,UAAU,KAAK,SAAS,CAAC,MAAM,QAAQ,KAAK,CAAC,CAAC,CAAC,EAClE,QAAQ,UACT;AAGH,QAAO,GAFyB,oBAAoB,0BAA0B,CAE5C;;;EAGlC,YAAY;;;;EAIZ,aAAa;;;iDAGkC,KAAK,UAAU,WAAW,CAAC;iDAC3B,WAAW;4CAChB,KAAK,UAAU,QAAQ,OAAO,MAAM,EAAE,CAAC;yDAC1B,KAAK,UAAU,oBAAoB,MAAM,EAAE,CAAC;0CAC3D,KAAK,UAAU,yBAAyB,CAAC;iDAClC,KAAK,UAAU,QAAQ,YAAY,MAAM,EAAE,CAAC;kCAC3D,KAAK,UAAU,QAAQ,UAAU,CAAC;;;sCAG9B,KAAK,UAAU,QAAQ,kBAAkB,CAAC;;uDAEzB,KAAK,UAAU,UAAU,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wCAqDzC,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;yDA8DC,KAAK,UAAU,OAAO,CAAC;;;AAIhF,SAAgB,oBACd,UAAwC,EAAE,EAClB;AACxB,QAAO,sBAAsB,wBAAwB,QAAQ,CAAC;;AAGhE,SAAgB,qBACd,UACA,SACS;AACT,QAAO,mBAAmB;EACxB;EACA,YAAY,QAAQ;EACpB,OAAO,QAAQ;EACf,OAAO,QAAQ;EAChB,CAAC;;AAWJ,SAAwB,kBACtB,UAAoC,EAAE,EACjC;CACL,MAAM,EAAE,KAAK,eAAe,MAAM,GAAG,aAAa;CAClD,MAAM,OAAO,wBAAwB,SAAS;CAC9C,MAAM,OAAO,KAAK,QAAQ,QAAQ,IAAI,YAAY;CAElD,MAAM,UAAU,sBAAsB;EACpC,GAAG;EACH;EACD,CAAC;AAEF,QAAO;EACL,MAAM;EACN,OAAO;GACL,uBAAuB,EACrB,cACA,kBACA,SACA,mBAWI;IACJ,MAAM,YAAY,OAAO,KAAK,QAAQ,MAAM;IAC5C,MAAM,aAAa,UAAU,KAAK,SAAS,QAAQ,KAAK,CAAC;IACzD,MAAM,qBAAqB,iCAAiC,KAAK;IACjE,MAAM,kBAAkB,OAAO,YAC7B,UAAU,KAAK,SAAS,CAAC,MAAM,QAAQ,KAAK,CAAC,CAAC,CAC/C;IACD,MAAM,YAAY,2BAChB,YACA,EAAE,GAAG,QAAQ,mBAAmB,EAChC,EAAE,GAAG,QAAQ,4BAA4B,EACzC,EAAE,GAAG,QAAQ,0BAA0B,EACvC,EAAE,GAAG,QAAQ,YAAY,EACzB,iBACA,QAAQ,UACT;AAED,QACE,YAAY,SACZ,QAAQ,SACR,OAAO,qBAAqB,WAE5B,kBAAiB;KACf,IAAI;KACJ,MAAM;KACN,MAAM;KACN,YAAY,cACV,IAAI,IAAI,yBAAyB,OAAO,KAAK,IAAI,CAClD;KACF,CAAC;AAEJ,QACE,YAAY,SACZ,QAAQ,SACR,OAAO,iBAAiB,WAGxB,cACE,eACA,sBAAsB;KACpB;KACA,mBALW,wBAAwB,SAAS,IAAI;KAMhD,iBAAiB;KAClB,CAAC,CACH;AAEH,iBAAa,EACX,MAAM,EACJ,SAAS,CACP;KACE,MAAM;KACN,UAAU,IAAY;AACpB,UAAI,OAAO,8BACT,QAAO;AACT,aAAO;;KAET,KAAK,IAAY;AACf,UAAI,OAAO,gCACT,QAAO,0BACL,SACA,KACA,oBACA,UACD;AAEH,aAAO;;KAEV,CACF,EACF,EACF,CAAC;;GAEJ,qBAAqB,EAAE,UAAwB;AAC7C,QAAI,QAAQ,SAAS,CAAC,aAAc;IACpC,MAAM,SAAS,cAAc,IAAI;IACjC,MAAM,aAAa,kBAAkB;KACnC,YAAY,QAAQ;KACpB,OAAO,QAAQ;KAChB,CAAC;AACF,SAAK,MAAM,aAAa,WACtB,QAAO,KAAK,QAAQ,UAAU,EAAE;KAAE,WAAW;KAAM,OAAO;KAAM,CAAC;AAEnE,kCAA8B,QAAQ,QAAQ;;GAEjD;EACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../src/badge-layout.ts","../src/inline-script.ts","../src/dev-inline-runtimes.ts","../src/dev-outline-css.ts","../src/production-html-cull.ts","../src/dev-head-inject.ts","../src/route-prefix-js.ts","../src/index.ts"],"sourcesContent":["export type ElementBadgeVerticalAnchor = \"top\" | \"bottom\";\n\n/** Horizontal placement of the element pill: `end` = top-right (LTR), `start` = top-left, `center` = centred. */\nexport type ElementBadgeHorizontalAlign = \"start\" | \"center\" | \"end\";\n\n/** Subset of dev CSS options used for element-badge geometry (also on `DevOutlineCssOptions`). */\nexport interface ElementBadgeLayoutOptions {\n /**\n * Horizontal alignment along the element’s top or bottom edge. Default `end` (top-right in LTR).\n * Ignored when `elementBadgeHorizontalPercent` is set.\n */\n elementBadgeHorizontalAlign?: ElementBadgeHorizontalAlign;\n /**\n * Optional: anchor at a percentage along the element width (0–100), with the pill centred on that point (`translateX(-50%)`).\n * When set, overrides `elementBadgeHorizontalAlign`.\n */\n elementBadgeHorizontalPercent?: number;\n /**\n * Vertical shift as a percentage of the **badge’s own height** (CSS transform %).\n * With anchor `top`, moves the badge upward. Default `80` (~20% of the pill overlaps the element top edge).\n */\n elementBadgeVerticalShiftPercent?: number;\n /** Anchor the badge to the top or bottom edge of the element. Default `top`. */\n elementBadgeVerticalAnchor?: ElementBadgeVerticalAnchor;\n}\n\nexport type NormalizedElementBadgeLayout =\n | {\n mode: \"align\";\n horizontalAlign: ElementBadgeHorizontalAlign;\n verticalShiftPercent: number;\n verticalAnchor: ElementBadgeVerticalAnchor;\n }\n | {\n mode: \"percent\";\n horizontalPercent: number;\n verticalShiftPercent: number;\n verticalAnchor: ElementBadgeVerticalAnchor;\n };\n\nfunction clampPercent(n: number, fallback: number): number {\n if (typeof n !== \"number\" || Number.isNaN(n)) return fallback;\n return Math.min(100, Math.max(0, n));\n}\n\nexport function normalizeElementBadgeLayout(\n input: ElementBadgeLayoutOptions | undefined,\n): NormalizedElementBadgeLayout {\n const v = clampPercent(input?.elementBadgeVerticalShiftPercent ?? 80, 80);\n const anchor =\n input?.elementBadgeVerticalAnchor === \"bottom\" ? \"bottom\" : \"top\";\n const hp = input?.elementBadgeHorizontalPercent;\n if (hp !== undefined && hp !== null && typeof hp === \"number\") {\n return {\n mode: \"percent\",\n horizontalPercent: clampPercent(hp, 50),\n verticalShiftPercent: v,\n verticalAnchor: anchor,\n };\n }\n const align: ElementBadgeHorizontalAlign =\n input?.elementBadgeHorizontalAlign === \"start\" ||\n input?.elementBadgeHorizontalAlign === \"center\"\n ? input.elementBadgeHorizontalAlign\n : \"end\";\n return {\n mode: \"align\",\n horizontalAlign: align,\n verticalShiftPercent: v,\n verticalAnchor: anchor,\n };\n}\n\nconst INSET = \"0.35rem\";\n\n/**\n * `left` / `right` / `top` | `bottom` / `transform` for the element `::before` badge (no trailing newline).\n */\nexport function elementBadgePositionBlock(\n layout: NormalizedElementBadgeLayout,\n): string {\n const { verticalShiftPercent: v, verticalAnchor } = layout;\n const y =\n verticalAnchor === \"top\"\n ? `translateY(calc(-1 * ${v}%))`\n : `translateY(${v}%)`;\n\n if (layout.mode === \"percent\") {\n const h = layout.horizontalPercent;\n if (verticalAnchor === \"top\") {\n return ` left: ${h}%;\n right: auto;\n top: 0;\n bottom: auto;\n transform: translateX(-50%) ${y};`;\n }\n return ` left: ${h}%;\n right: auto;\n bottom: 0;\n top: auto;\n transform: translateX(-50%) ${y};`;\n }\n\n const { horizontalAlign } = layout;\n if (horizontalAlign === \"center\") {\n if (verticalAnchor === \"top\") {\n return ` left: 50%;\n right: auto;\n top: 0;\n bottom: auto;\n transform: translateX(-50%) ${y};`;\n }\n return ` left: 50%;\n right: auto;\n bottom: 0;\n top: auto;\n transform: translateX(-50%) ${y};`;\n }\n if (horizontalAlign === \"start\") {\n if (verticalAnchor === \"top\") {\n return ` left: ${INSET};\n right: auto;\n top: 0;\n bottom: auto;\n transform: ${y};`;\n }\n return ` left: ${INSET};\n right: auto;\n bottom: 0;\n top: auto;\n transform: ${y};`;\n }\n /* end — top-right / bottom-right */\n if (verticalAnchor === \"top\") {\n return ` left: auto;\n right: ${INSET};\n top: 0;\n bottom: auto;\n transform: ${y};`;\n }\n return ` left: auto;\n right: ${INSET};\n bottom: 0;\n top: auto;\n transform: ${y};`;\n}\n","export function compactInlineScript(source: string): string {\n const lines = source\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line, idx, arr) => line.length > 0 || (idx > 0 && idx < arr.length - 1));\n return lines.join(\"\\n\").trim();\n}\n\nexport function inlineInvoke<Payload>(\n fn: (payload: Payload) => void,\n payload: Payload,\n): string {\n return compactInlineScript(`(${fn.toString()})(${JSON.stringify(payload)});`);\n}\n","type HeadInlinePayload = {\n featureFlagStyles: string;\n routeFlags: Record<string, string[]>;\n flagNameToToken: Record<string, string>;\n};\n\ntype BootstrapPayload = {\n flagTokens: string[];\n colors: Record<string, string>;\n outlineDefaults: Record<string, boolean>;\n badgeDefaults: Record<string, boolean>;\n routeFlags: Record<string, string[]>;\n flagNameToToken: Record<string, string>;\n namespace: string;\n};\n\nexport function affHeadInlineRuntime(payload: HeadInlinePayload): void {\n const toPrefix = (pattern: string): string => {\n let p = String(pattern || \"\").trim();\n if (p.endsWith(\"/**\")) p = p.slice(0, -3);\n else if (p.endsWith(\"/*\")) p = p.slice(0, -2);\n else if (p.length > 1 && p.endsWith(\"*\")) p = p.slice(0, -1);\n return p.endsWith(\"/\") ? p : `${p}/`;\n };\n try {\n const { featureFlagStyles, routeFlags: RF, flagNameToToken: M } = payload;\n\n // Inject the dev-chrome stylesheet. Astro's ClientRouter swaps <head> on a\n // view-transition navigation, and a runtime-injected <style> is dropped on\n // the first swap unless marked persistent — which is why the element\n // outline/badge chrome vanished after navigating. Mark it\n // data-astro-transition-persist so ClientRouter keeps a single copy, and\n // keep the injector idempotent + re-run it on every nav so the styles stay\n // constant across pages even where a swap removes them.\n const ensureFeatureFlagStyles = () => {\n if (document.querySelector(\"style[data-astro-feature-flags]\")) return;\n const s = document.createElement(\"style\");\n s.setAttribute(\"data-astro-feature-flags\", \"\");\n s.setAttribute(\"data-astro-transition-persist\", \"astro-feature-flags\");\n s.textContent = featureFlagStyles;\n (document.head || document.documentElement).appendChild(s);\n };\n ensureFeatureFlagStyles();\n\n const affPath = () => {\n let p = typeof location !== \"undefined\" && location.pathname ? location.pathname : \"/\";\n if (!p.endsWith(\"/\")) p += \"/\";\n return p;\n };\n\n const affFlagNames = (pathname: string) => {\n let p = pathname || \"/\";\n if (!p.endsWith(\"/\")) p += \"/\";\n const out: string[] = [];\n const seen = new Set<string>();\n for (const rp in RF) {\n if (!Object.prototype.hasOwnProperty.call(RF, rp)) continue;\n const route = toPrefix(rp);\n if (!(p === route || p.indexOf(route) === 0)) continue;\n const fns = RF[rp] || [];\n for (let i = 0; i < fns.length; i++) {\n const fn = fns[i];\n if (fn && !seen.has(fn)) {\n seen.add(fn);\n out.push(fn);\n }\n }\n }\n return out;\n };\n\n const affTokens = (pathname: string) =>\n affFlagNames(pathname)\n .map((name) => M[name])\n .filter(Boolean);\n\n const affSyncDisabledOverlay = () => {\n const root = document.documentElement;\n if (!document.body) return;\n const names = affFlagNames(affPath());\n const disabled: string[] = [];\n for (let i = 0; i < names.length; i++) {\n const tk = M[names[i]];\n if (!tk) continue;\n if (root.getAttribute(`data-ff-enabled-${tk}`) === \"off\") disabled.push(tk);\n }\n const id = \"aff-route-disabled-overlay\";\n let el = document.getElementById(id);\n if (!disabled.length) {\n if (el) el.remove();\n root.removeAttribute(\"data-aff-route-disabled\");\n return;\n }\n root.setAttribute(\"data-aff-route-disabled\", \"\");\n if (!el) {\n el = document.createElement(\"div\");\n el.id = id;\n el.className = \"aff-route-disabled-overlay\";\n const p = document.createElement(\"p\");\n el.appendChild(p);\n document.body.appendChild(el);\n }\n const pEl = el.querySelector(\"p\") || el;\n const flagsText = disabled.join(\", \");\n const line1 =\n disabled.length === 1\n ? `Page disabled: ${flagsText} flag is disabled.`\n : `Page disabled: ${flagsText} flags are disabled.`;\n const line2 =\n \"In production builds, pages with disabled flags like this will not be emitted\";\n pEl.textContent = `${line1}\\n${line2}`;\n };\n\n const affApply = () => {\n ensureFeatureFlagStyles();\n document.documentElement.setAttribute(\"data-ff-route\", affTokens(affPath()).join(\" \"));\n affSyncDisabledOverlay();\n };\n\n affApply();\n if (typeof MutationObserver === \"function\") {\n const obs = new MutationObserver((muts) => {\n for (let i = 0; i < muts.length; i++) {\n const a = muts[i] && muts[i].attributeName;\n if (a === \"data-ff-route\" || (a && a.indexOf(\"data-ff-enabled-\") === 0)) {\n affSyncDisabledOverlay();\n break;\n }\n }\n });\n obs.observe(document.documentElement, { attributes: true });\n }\n document.addEventListener(\"astro:page-load\", affApply);\n document.addEventListener(\"astro:after-swap\", affApply);\n } catch {\n // no-op in injected runtime\n }\n}\n\nexport function affDevBootstrapRuntime(payload: BootstrapPayload): void {\n const toPrefix = (pattern: string): string => {\n let p = String(pattern || \"\").trim();\n if (p.endsWith(\"/**\")) p = p.slice(0, -3);\n else if (p.endsWith(\"/*\")) p = p.slice(0, -2);\n else if (p.length > 1 && p.endsWith(\"*\")) p = p.slice(0, -1);\n return p.endsWith(\"/\") ? p : `${p}/`;\n };\n const T = payload.flagTokens;\n const C = payload.colors;\n const OD = payload.outlineDefaults;\n const BD = payload.badgeDefaults;\n const RF = payload.routeFlags;\n const F2T = payload.flagNameToToken;\n const N = payload.namespace;\n const K = `${N}.dev.v1`;\n\n const root = document.documentElement;\n const isEnabled = (tk: string) => root.getAttribute(`data-ff-enabled-${tk}`) !== \"off\";\n const currentTokenColor = (\n tk: string,\n cols: Record<string, string>,\n ): string => {\n const cssVar = root.style.getPropertyValue(`--${N}-c-${tk}`).trim();\n return cssVar || cols[tk] || C[tk] || \"#94a3b8\";\n };\n const unique = (a: string[]) => [...new Set(a.filter(Boolean))];\n\n const comboTokens = (el: Element): string[] => {\n let out: string[] = [];\n const raw = (el.getAttribute(`data-${N}`) || \"\").trim();\n if (raw) out = out.concat(raw.split(/\\s+/).filter(Boolean));\n for (let i = 0; i < T.length; i++) {\n const tk = T[i];\n if (el.hasAttribute(`data-${N}-${tk}`)) out.push(tk);\n }\n return unique(out);\n };\n\n const stripBooleanFlagAttrs = (el: Element) => {\n for (let i = 0; i < T.length; i++) {\n el.removeAttribute(`data-${N}-${T[i]}`);\n }\n };\n const clearComboVisualState = (el: HTMLElement) => {\n el.removeAttribute(\"data-ff-label\");\n el.style.removeProperty(\"--ff-combo-gradient\");\n el.style.removeProperty(\"--ff-combo-gradient-soft\");\n el.style.removeProperty(\"--ff-combo-outline\");\n el.style.removeProperty(\"--ff-combo-text\");\n el.style.removeProperty(\"--ff-combo-badge-border\");\n el.style.removeProperty(\"--ff-combo-badge-gradient\");\n };\n\n const applyCombo = (el: HTMLElement, cols: Record<string, string>) => {\n const allTokens = comboTokens(el);\n const hasCombo = allTokens.length >= 2;\n const hasDisabledInCombo = hasCombo && allTokens.some((tk) => !isEnabled(tk));\n const visual = allTokens.filter((tk) => isEnabled(tk));\n if (hasDisabledInCombo) {\n // Fail-closed for combos: if any member token is disabled, hide the whole element.\n // Keep all combo tokens mirrored in data-<ns> so enabled-off CSS selectors can hide it.\n stripBooleanFlagAttrs(el);\n el.setAttribute(`data-${N}`, allTokens.join(\" \"));\n clearComboVisualState(el);\n return;\n }\n if (visual.length < 2) {\n clearComboVisualState(el);\n if (hasCombo) {\n // Only normalize attrs for true combos. Single-flag shorthand markers\n // (e.g. data-ff-wip) must stay intact so re-enabling can recover styling.\n stripBooleanFlagAttrs(el);\n if (visual.length === 1) {\n el.setAttribute(`data-${N}`, visual[0]!);\n } else {\n el.removeAttribute(`data-${N}`);\n }\n }\n return;\n }\n stripBooleanFlagAttrs(el);\n // Mirror all enabled tokens into the primary namespace attribute so `[data-<ns>*=\" \"]`\n // matches (required for boolean-only combos like data-ff-a + data-ff-b).\n el.setAttribute(`data-${N}`, visual.join(\" \"));\n el.setAttribute(\"data-ff-label\", visual.join(\" | \"));\n const strong = visual.map((tk) => currentTokenColor(tk, cols));\n const soft = strong.map((c) => `color-mix(in oklab, white 86%, ${c} 14%)`);\n el.style.setProperty(\"--ff-combo-gradient\", `linear-gradient(90deg, ${strong.join(\", \")})`);\n el.style.setProperty(\"--ff-combo-gradient-soft\", `linear-gradient(90deg, ${soft.join(\", \")})`);\n el.style.setProperty(\"--ff-combo-outline\", strong[0] || \"#64748b\");\n el.style.setProperty(\"--ff-combo-text\", strong[0] || \"#111827\");\n el.style.setProperty(\n \"--ff-combo-badge-border\",\n `color-mix(in oklab, ${strong[0] || \"#64748b\"} 35%, transparent)`,\n );\n el.style.setProperty(\"--ff-combo-badge-gradient\", `linear-gradient(90deg, ${soft.join(\", \")})`);\n };\n\n const routeTokensForPath = (path: string): string[] => {\n const out: string[] = [];\n const seen = new Set<string>();\n for (const rp in RF) {\n if (!Object.prototype.hasOwnProperty.call(RF, rp)) continue;\n const cnd = toPrefix(rp);\n if (!(path === cnd || path.indexOf(cnd) === 0)) continue;\n const fns = RF[rp] || [];\n for (let i = 0; i < fns.length; i++) {\n const tk = F2T[fns[i]];\n if (tk && !seen.has(tk)) {\n seen.add(tk);\n out.push(tk);\n }\n }\n }\n return out;\n };\n\n const routeLabelFor = (path: string, toks: string[]): string => {\n if (!toks.length) return \"\";\n const byFlag: Record<string, string[]> = {};\n for (const rp in RF) {\n if (!Object.prototype.hasOwnProperty.call(RF, rp)) continue;\n const cnd = toPrefix(rp);\n if (!(path === cnd || path.indexOf(cnd) === 0)) continue;\n const fns = RF[rp] || [];\n for (let i = 0; i < fns.length; i++) {\n const tk = F2T[fns[i]];\n if (!tk) continue;\n if (!byFlag[tk]) byFlag[tk] = [];\n byFlag[tk].push(rp);\n }\n }\n return `Page: ${toks\n .map((tk) => `${tk} - ${(byFlag[tk] || []).join(\", \") || \"matched\"}`)\n .join(\" | \")}`;\n };\n\n const affSyncRouteChrome = (path: string) => {\n if (!document.body) return;\n // Keep every matched route token on <html> so per-token toolbar rules\n // (`[data-ff-route~=\"tk\"]`) still apply; gradients use enabled-only below.\n const routeAll = routeTokensForPath(path);\n if (!routeAll.length) {\n document.getElementById(\"aff-route-frame\")?.remove();\n document.getElementById(\"aff-route-badge\")?.remove();\n root.removeAttribute(\"data-ff-route\");\n root.removeAttribute(\"data-ff-route-label\");\n return;\n }\n root.setAttribute(\"data-ff-route\", routeAll.join(\" \"));\n root.setAttribute(\"data-ff-route-label\", routeLabelFor(path, routeAll));\n if (!document.getElementById(\"aff-route-frame\")) {\n const frame = document.createElement(\"div\");\n frame.id = \"aff-route-frame\";\n frame.className = \"aff-route-frame\";\n frame.setAttribute(\"aria-hidden\", \"true\");\n document.body.appendChild(frame);\n }\n let badge = document.getElementById(\"aff-route-badge\");\n if (!badge) {\n badge = document.createElement(\"div\");\n badge.id = \"aff-route-badge\";\n badge.className = \"aff-route-badge\";\n badge.setAttribute(\"role\", \"status\");\n document.body.appendChild(badge);\n }\n badge.textContent = root.getAttribute(\"data-ff-route-label\") || \"\";\n badge.setAttribute(\"aria-label\", badge.textContent || \"\");\n };\n\n try {\n (window as Window & { __AFF__?: unknown }).__AFF__ = {\n tokens: T,\n colors: C,\n namespace: N,\n };\n const raw = localStorage.getItem(K) || localStorage.getItem(\"aff.dev.v1\");\n const st = raw ? JSON.parse(raw) : {};\n const o = st.outline || st.chrome || {};\n const e = st.enabled || st.render || {};\n const b = st.badge || {};\n const cols = st.colors || {};\n\n for (let i = 0; i < T.length; i++) {\n const t = T[i];\n const od = OD[t] !== false;\n const bd = BD[t] !== false;\n if (o[t] === false || (!od && o[t] !== true))\n root.setAttribute(`data-ff-outline-${t}`, \"off\");\n else root.removeAttribute(`data-ff-outline-${t}`);\n if (e[t] === false) root.setAttribute(`data-ff-enabled-${t}`, \"off\");\n else root.removeAttribute(`data-ff-enabled-${t}`);\n if (b[t] === false || (!bd && b[t] !== true))\n root.setAttribute(`data-ff-badge-${t}`, \"off\");\n else root.removeAttribute(`data-ff-badge-${t}`);\n const col = cols[t] || C[t];\n const vn = `--${N}-c-${t}`;\n if (col) root.style.setProperty(vn, col);\n else root.style.removeProperty(vn);\n }\n\n const applyAll = () => {\n const map = new Set<HTMLElement>();\n document\n .querySelectorAll<HTMLElement>(`[data-${N}]`)\n .forEach((el) => map.add(el));\n for (let i = 0; i < T.length; i++) {\n document\n .querySelectorAll<HTMLElement>(`[data-${N}-${T[i]}]`)\n .forEach((el) => map.add(el));\n }\n map.forEach((el) => applyCombo(el, cols));\n };\n\n const affAfterNav = () => {\n if (!document.body) return;\n let p = window.location?.pathname || \"/\";\n if (!p.endsWith(\"/\")) p += \"/\";\n const toks = routeTokensForPath(p).filter((tk) => isEnabled(tk));\n if (toks.length) {\n const strong = toks.map((tk) => currentTokenColor(tk, cols));\n const soft = strong.map((c) => `color-mix(in oklab, white 86%, ${c} 14%)`);\n root.style.setProperty(`--${N}-route-gradient`, `linear-gradient(90deg, ${soft.join(\", \")})`);\n root.style.setProperty(\"--ff-route-outline-gradient\", `linear-gradient(90deg, ${strong.join(\", \")})`);\n root.style.setProperty(\"--ff-route-badge-gradient\", `linear-gradient(90deg, ${soft.join(\", \")})`);\n root.style.setProperty(\n \"--ff-route-badge-border\",\n `color-mix(in oklab, ${strong[0] || \"#64748b\"} 35%, transparent)`,\n );\n root.style.setProperty(\"--ff-route-outline\", strong[0] || \"#64748b\");\n root.style.setProperty(\"--ff-route-text\", strong[0] || \"#111827\");\n } else {\n root.style.removeProperty(`--${N}-route-gradient`);\n root.style.removeProperty(\"--ff-route-outline-gradient\");\n root.style.removeProperty(\"--ff-route-badge-gradient\");\n root.style.removeProperty(\"--ff-route-badge-border\");\n root.style.removeProperty(\"--ff-route-outline\");\n root.style.removeProperty(\"--ff-route-text\");\n }\n applyAll();\n affSyncRouteChrome(p);\n };\n\n affAfterNav();\n if (document.readyState === \"loading\") {\n document.addEventListener(\"DOMContentLoaded\", affAfterNav, { once: true });\n }\n document.addEventListener(\"astro:page-load\", affAfterNav);\n document.addEventListener(\"astro:after-swap\", affAfterNav);\n if (typeof MutationObserver === \"function\") {\n const obs = new MutationObserver((muts) => {\n for (let i = 0; i < muts.length; i++) {\n const a = muts[i]?.attributeName;\n if (!a) continue;\n if (\n a === \"style\" ||\n a.indexOf(\"data-ff-enabled-\") === 0 ||\n a.indexOf(\"data-ff-outline-\") === 0 ||\n a.indexOf(\"data-ff-badge-\") === 0\n ) {\n affAfterNav();\n break;\n }\n }\n });\n obs.observe(root, { attributes: true });\n }\n } catch {\n // no-op in injected runtime\n }\n}\n","import {\n elementBadgePositionBlock,\n normalizeElementBadgeLayout,\n} from \"./badge-layout\";\nimport type { ElementBadgeLayoutOptions } from \"./badge-layout\";\nimport { inlineInvoke } from \"./inline-script\";\nimport { affDevBootstrapRuntime } from \"./dev-inline-runtimes\";\nimport type { ResolvedFeatureRuntime } from \"./runtime\";\nimport { toToken } from \"./runtime\";\n\nexport type DevOutlineHiddenStrategy = \"visibility\" | \"display\";\n\nexport interface DevOutlineCssOptions extends ElementBadgeLayoutOptions {\n outlineWidth?: string;\n /** Fallback when no per-token colour is set. */\n outlineColor?: string;\n /** Per-flag-token outline / badge / route-badge colour (overrides JSON `colors`). */\n outlineColorByToken?: Record<string, string>;\n outlineOffset?: string;\n borderRadius?: string;\n\n /** Default pill text for the `wip` flag token in the dev toolbar. */\n badgeLabelWip?: string;\n /** @deprecated Use {@link badgeLabelWip}. */\n badgeLabelDev?: string;\n badgeLabelByToken?: Record<string, string>;\n\n hiddenStrategy?: DevOutlineHiddenStrategy;\n\n /**\n * @deprecated Unused: badge does not use container queries.\n */\n badgeMinInlineSize?: string;\n}\ntype NormalizedDevOutlineCssOptions = {\n outlineWidth: string;\n outlineColor: string;\n outlineColorByToken: Record<string, string>;\n outlineOffset: string;\n borderRadius: string;\n badgeLabelWip: string;\n badgeLabelByToken: Record<string, string>;\n hiddenStrategy: DevOutlineHiddenStrategy;\n badgeMinInlineSize: string;\n};\n\nconst defaultDevOutlineCssOptions: Omit<\n NormalizedDevOutlineCssOptions,\n \"outlineColorByToken\"\n> = {\n outlineWidth: \"2px\",\n outlineColor: \"rgb(220 38 38)\",\n outlineOffset: \"-2px\",\n borderRadius: \"0.5rem\",\n badgeLabelWip: \"wip\",\n badgeLabelByToken: {},\n hiddenStrategy: \"visibility\",\n badgeMinInlineSize: \"8rem\",\n};\n\nfunction normalizeCssOptions(\n css: DevOutlineCssOptions | undefined,\n runtime: ResolvedFeatureRuntime,\n): NormalizedDevOutlineCssOptions {\n const byToken = {\n ...runtime.flagColorsByToken,\n ...(css?.outlineColorByToken ?? {}),\n };\n const badgeLabelWip =\n css?.badgeLabelWip ?? css?.badgeLabelDev ?? defaultDevOutlineCssOptions.badgeLabelWip;\n return {\n ...defaultDevOutlineCssOptions,\n ...(css ?? {}),\n outlineColorByToken: byToken,\n badgeLabelWip,\n badgeLabelByToken: {\n ...defaultDevOutlineCssOptions.badgeLabelByToken,\n ...(css?.badgeLabelByToken ?? {}),\n },\n };\n}\n\nfunction colorForToken(\n token: string,\n opts: NormalizedDevOutlineCssOptions,\n): string {\n return opts.outlineColorByToken[token] ?? opts.outlineColor;\n}\n\n/** CSS variable per token, set on &lt;html&gt; from ff.json + dev-toolbar overrides. */\nfunction featureColorVar(token: string, namespace: string): string {\n const ns = toToken(namespace) || \"ff\";\n return `--${ns}-c-${token}`;\n}\n\n/**\n * Dev-only styles: `data-ff=\"token\"` (or space-separated tokens) gates outlines/badges.\n * `html[data-ff-route=\"<token>\"]` (dev) adds a fixed top-right route badge (label only).\n * Toolbar toggles: `data-ff-enabled-*`, `data-ff-outline-*`, `data-ff-badge-*`, `--<namespace>-c-*`.\n */\nexport function createFeatureFlagStyles(\n runtime: ResolvedFeatureRuntime,\n css?: DevOutlineCssOptions,\n): string {\n const nsAttr = `data-${toToken(runtime.namespace) || \"ff\"}`;\n const selectorsForFlag = (flag: string) => {\n const token = toToken(flag);\n const valueSelectors = [`[${nsAttr}~=\"${token}\"]`];\n if (flag !== token) valueSelectors.push(`[${nsAttr}~=\"${flag}\"]`);\n const baseSelectors = [...valueSelectors, `[${nsAttr}-${token}]`];\n return {\n token,\n baseSelectors,\n selOutline: baseSelectors.map((s) => `${s}:not([${nsAttr}*=\" \"])`).join(\", \"),\n selIs: `:is(${baseSelectors.join(\", \")})`,\n };\n };\n const opts = normalizeCssOptions(css, runtime);\n const badgeLayout = normalizeElementBadgeLayout(css);\n const badgePos = elementBadgePositionBlock(badgeLayout);\n const chunks: string[] = [];\n\n for (const flag of Object.keys(runtime.flags)) {\n const { token, selOutline, selIs } = selectorsForFlag(flag);\n /** Combo hosts use `[${nsAttr}*=\" \"]` + `data-ff-label`; skip per-token ::before so combo rules win. */\n const selIsSingleBadge = `${selIs}:not([${nsAttr}*=\" \"])`;\n const col = colorForToken(token, opts);\n const v = featureColorVar(token, runtime.namespace);\n const label =\n opts.badgeLabelByToken[token] ??\n (token === \"wip\" ? opts.badgeLabelWip : token);\n const labelEscaped = String(label).replace(/'/g, \"\\\\'\");\n\n chunks.push(`\nhtml {\n --aff-outline-c-${token}: transparent;\n}\nhtml:not([data-ff-outline-${token}=\"off\"]) {\n --aff-outline-c-${token}: var(${v}, ${col});\n}\n${selOutline} {\n position: relative;\n outline: ${opts.outlineWidth} solid var(--aff-outline-c-${token});\n outline-offset: ${opts.outlineOffset};\n border-radius: ${opts.borderRadius};\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}::before {\n box-sizing: border-box;\n position: absolute;\n inset: auto;\n margin: 0;\n width: max-content;\n max-width: min(18rem, calc(100% - 0.75rem));\n pointer-events: auto;\n z-index: 20;\n${badgePos}\n display: block;\n content: '${labelEscaped}';\n border: 1px solid color-mix(in oklab, var(${v}, ${col}) 60%, transparent);\n border-radius: 9999px;\n padding: 0.125rem 0.45rem;\n font-size: 0.625rem;\n line-height: 1.2;\n letter-spacing: 0.02em;\n font-weight: 600;\n color: var(${v}, ${col});\n background: color-mix(in oklab, white 85%, var(${v}, ${col}) 15%);\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n transition: opacity 0.14s ease, background 0.14s ease, border-color 0.14s ease;\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}:hover::before {\n opacity: 0.16;\n background: color-mix(in oklab, white 94%, var(${v}, ${col}) 6%);\n border-color: color-mix(in oklab, var(${v}, ${col}) 35%, transparent);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-align=\"start\"]::before {\n left: 0.35rem;\n right: auto;\n transform: translateY(calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-align=\"center\"]::before {\n left: 50%;\n right: auto;\n transform: translate(-50%, calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-align=\"end\"]::before {\n left: auto;\n right: 0.35rem;\n transform: translateY(calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-horizontal]::before {\n left: calc(attr(data-ff-horizontal number, 50) * 1%);\n right: auto;\n transform: translate(-50%, calc(-1 * 80%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-horizontal][data-ff-anchor=\"bottom\"]::before {\n transform: translate(-50%, 80%);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"]::before {\n top: auto;\n bottom: 0;\n transform: translateY(80%);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"][data-ff-align=\"center\"]::before {\n transform: translate(-50%, 80%);\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-vertical]::before {\n transform: translateY(calc(attr(data-ff-vertical number, 80) * -1%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-horizontal][data-ff-vertical]::before {\n transform: translate(-50%, calc(attr(data-ff-vertical number, 80) * -1%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"][data-ff-vertical]::before {\n transform: translateY(calc(attr(data-ff-vertical number, 80) * 1%));\n}\nhtml:not([data-ff-badge-${token}=\"off\"]) ${selIsSingleBadge}[data-ff-anchor=\"bottom\"][data-ff-horizontal][data-ff-vertical]::before {\n transform: translate(-50%, calc(attr(data-ff-vertical number, 80) * 1%));\n}\nhtml[data-ff-enabled-${token}=\"off\"] ${selIs} {\n display: none !important;\n}\n`);\n }\n\n // Multi-token value on a single element: show combined badge text and a gradient outline.\n chunks.push(`\n[${nsAttr}*=\" \"] {\n position: relative;\n outline: none !important;\n outline-offset: 0 !important;\n border-radius: ${opts.borderRadius};\n}\nhtml [${nsAttr}*=\" \"]::after {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n pointer-events: none;\n z-index: 10;\n box-sizing: border-box;\n padding: ${opts.outlineWidth};\n background: var(--ff-combo-gradient, linear-gradient(90deg, #fecaca, #bfdbfe, #bbf7d0));\n -webkit-mask:\n linear-gradient(#000 0 0) content-box,\n linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n}\nhtml [${nsAttr}*=\" \"]::before {\n box-sizing: border-box;\n position: absolute;\n inset: auto;\n margin: 0;\n width: max-content;\n max-width: min(18rem, calc(100% - 0.75rem));\n pointer-events: auto;\n z-index: 20;\n${badgePos}\n display: block;\n content: attr(${nsAttr});\n background: var(--ff-combo-badge-gradient, var(--ff-combo-gradient-soft, linear-gradient(90deg, #fff1f2, #eff6ff, #f0fdf4))) !important;\n color: var(--ff-combo-text, #111827) !important;\n border: 1px solid var(--ff-combo-badge-border, color-mix(in oklab, #94a3b8 50%, transparent)) !important;\n border-radius: 9999px;\n padding: 0.125rem 0.45rem;\n font-size: 0.625rem;\n line-height: 1.2;\n letter-spacing: 0.02em;\n font-weight: 600;\n white-space: nowrap;\n text-overflow: ellipsis;\n overflow: hidden;\n transition: opacity 0.14s ease, background 0.14s ease, border-color 0.14s ease;\n}\nhtml [${nsAttr}*=\" \"][data-ff-label]::before {\n content: attr(data-ff-label) !important;\n}\nhtml [${nsAttr}*=\" \"]:hover::before {\n opacity: 0.2;\n background: linear-gradient(90deg, #fff8f9, #f8fbff, #f8fff9);\n border-color: color-mix(in oklab, #94a3b8 35%, transparent);\n}\n`);\n\n // Combo chrome must be suppressed *after* generic combo rules so `!important` / order cannot be\n // overridden by `html [...][data-ff-label]::before { content: ... !important }`.\n for (const flag of Object.keys(runtime.flags)) {\n const { token, selIs } = selectorsForFlag(flag);\n chunks.push(`\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"]::before,\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"][data-ff-label]::before {\n display: none !important;\n content: none !important;\n}\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"]:hover::before,\nhtml[data-ff-badge-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"][data-ff-label]:hover::before {\n display: none !important;\n opacity: 1 !important;\n background: none !important;\n border-color: transparent !important;\n}\nhtml[data-ff-outline-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"] {\n outline: none !important;\n outline-offset: 0 !important;\n}\nhtml[data-ff-outline-${token}=\"off\"] ${selIs}[${nsAttr}*=\" \"]::after {\n display: none !important;\n}\n`);\n }\n\n chunks.push(`\n.aff-route-frame {\n position: fixed;\n inset: 0.6rem;\n pointer-events: none;\n z-index: 10040;\n box-sizing: border-box;\n border-radius: 0.5rem;\n overflow: hidden;\n}\n.aff-route-frame::before {\n content: \"\";\n position: absolute;\n inset: 0;\n border-radius: inherit;\n padding: 2px;\n background: var(--ff-route-outline-gradient, linear-gradient(90deg, #ef4444, #3b82f6, #22c55e));\n -webkit-mask:\n linear-gradient(#000 0 0) content-box,\n linear-gradient(#000 0 0);\n -webkit-mask-composite: xor;\n mask-composite: exclude;\n}\n.aff-route-badge {\n position: fixed;\n top: 0.75rem;\n right: 0.75rem;\n z-index: 10060;\n pointer-events: auto;\n box-sizing: border-box;\n display: inline-flex;\n align-items: center;\n max-width: min(34rem, calc(100vw - 1.5rem));\n white-space: nowrap;\n overflow: hidden;\n text-overflow: ellipsis;\n border: 1px solid var(--ff-route-badge-border, color-mix(in oklab, #94a3b8 55%, transparent));\n border-radius: 9999px;\n padding: 0.15rem 0.5rem;\n font-size: 0.625rem;\n line-height: 1.2;\n letter-spacing: 0.02em;\n font-weight: 600;\n color: var(--ff-route-text, #111827);\n background: var(--ff-route-badge-gradient, linear-gradient(90deg, #fff1f2));\n opacity: 1;\n transition: opacity 0.14s ease, border-color 0.14s ease, background 0.14s ease;\n}\n.aff-route-badge:hover {\n opacity: 0.2;\n border-color: color-mix(in oklab, #94a3b8 35%, transparent);\n}\n.aff-route-disabled-overlay {\n position: fixed;\n inset: 0;\n z-index: 10050;\n box-sizing: border-box;\n display: flex;\n align-items: center;\n justify-content: center;\n padding: clamp(1rem, 4vw, 2.5rem);\n text-align: center;\n font: 600 0.95rem/1.45 system-ui, -apple-system, Segoe UI, sans-serif;\n color: #f8fafc;\n background: rgba(15, 23, 42, 0.52);\n backdrop-filter: blur(2px);\n pointer-events: auto;\n}\n.aff-route-disabled-overlay > p {\n margin: 0;\n max-width: min(44rem, calc(100vw - 2rem));\n white-space: pre-line;\n}\n`);\n\n for (const flag of Object.keys(runtime.flags)) {\n const token = toToken(flag);\n chunks.push(`\n[data-ff-route~=\"${token}\"][data-ff-outline-${token}=\"off\"] #aff-route-frame {\n display: none !important;\n}\n[data-ff-route~=\"${token}\"][data-ff-badge-${token}=\"off\"] #aff-route-badge {\n display: none !important;\n}\n[data-ff-route~=\"${token}\"][data-ff-enabled-${token}=\"off\"] #aff-route-frame {\n display: none !important;\n}\n[data-ff-route~=\"${token}\"][data-ff-enabled-${token}=\"off\"] #aff-route-badge {\n display: none !important;\n}\n`);\n }\n\n return \"\\n\" + chunks.join(\"\\n\") + \"\\n\";\n}\n\n/**\n * CSS-only hiding for disabled flags. The Astro integration no longer injects this into\n * production builds — static HTML is culled instead. Kept for custom tooling or tests.\n */\nexport function createProductionGateStyles(\n runtime: ResolvedFeatureRuntime,\n): string {\n const nsAttr = `data-${toToken(runtime.namespace) || \"ff\"}`;\n const chunks: string[] = [];\n for (const [flag, enabled] of Object.entries(runtime.flags)) {\n if (enabled) continue;\n const token = toToken(flag);\n const valueSelectors = [`[${nsAttr}~=\"${token}\"]`];\n if (flag !== token) valueSelectors.push(`[${nsAttr}~=\"${flag}\"]`);\n chunks.push(\n `${[...valueSelectors, `[${nsAttr}-${token}]`].join(\", \")} { display: none !important; }`,\n );\n }\n return chunks.length ? `\\n${chunks.join(\"\\n\")}\\n` : \"\";\n}\n\nexport function buildAffDevBootstrapScript(\n flagTokens: string[],\n colors: Record<string, string>,\n outlineDefaults: Record<string, boolean>,\n badgeDefaults: Record<string, boolean>,\n routeFlags: Record<string, string[]>,\n flagNameToToken: Record<string, string>,\n namespace: string,\n): string {\n return inlineInvoke(affDevBootstrapRuntime, {\n flagTokens,\n colors,\n outlineDefaults,\n badgeDefaults,\n routeFlags,\n flagNameToToken,\n namespace: toToken(namespace) || \"ff\",\n });\n}\n","import { readdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { parse, type HTMLElement } from \"node-html-parser\";\nimport type { ResolvedFeatureRuntime } from \"./runtime\";\nimport { toToken } from \"./runtime\";\n\nfunction walkHtmlFiles(dir: string): string[] {\n const out: string[] = [];\n const entries = readdirSync(dir, { withFileTypes: true });\n for (const ent of entries) {\n const p = join(dir, ent.name);\n if (ent.isDirectory()) out.push(...walkHtmlFiles(p));\n else if (ent.isFile() && ent.name.endsWith(\".html\")) out.push(p);\n }\n return out;\n}\n\nfunction namespaceAttrName(namespace: string): string {\n return `data-${toToken(namespace) || \"ff\"}`;\n}\n\nfunction elementFlagSlugs(\n el: HTMLElement,\n nsAttr: string,\n knownTokens: readonly string[],\n): string[] {\n const slugSet = new Set<string>();\n const combined = el.getAttribute(nsAttr)?.trim();\n if (combined) {\n for (const part of combined.split(/\\s+/).filter(Boolean)) {\n slugSet.add(part);\n }\n }\n for (const tk of knownTokens) {\n if (el.hasAttribute(`${nsAttr}-${tk}`)) slugSet.add(tk);\n }\n return [...slugSet];\n}\n\nfunction slugToFlagNames(slug: string, flagNames: readonly string[]): string[] {\n const out: string[] = [];\n for (const name of flagNames) {\n if (slug === name || slug === toToken(name)) out.push(name);\n }\n return out;\n}\n\nfunction shouldCullElement(\n el: HTMLElement,\n runtime: ResolvedFeatureRuntime,\n nsAttr: string,\n knownTokens: readonly string[],\n flagNames: readonly string[],\n): boolean {\n const slugs = elementFlagSlugs(el, nsAttr, knownTokens);\n if (slugs.length === 0) return false;\n for (const slug of slugs) {\n const names = slugToFlagNames(slug, flagNames);\n if (names.length === 0) return false;\n }\n for (const slug of slugs) {\n const names = slugToFlagNames(slug, flagNames);\n for (const name of names) {\n if (!runtime.flags[name]) return true;\n }\n }\n return false;\n}\n\nfunction elementDepth(el: HTMLElement): number {\n let d = 0;\n let n: HTMLElement | null = el.parentNode as HTMLElement | null;\n while (n) {\n d++;\n n = n.parentNode as HTMLElement | null;\n }\n return d;\n}\n\n/**\n * Production static HTML: remove nodes gated by disabled flags (same rules as dev\n * `data-*` / `data-*-<token>`), strip dev-only route attributes on `<html>`, and drop\n * empty `<style>` tags left after `featureFlagStyles` is empty.\n */\nexport function cullProductionHtml(\n html: string,\n runtime: ResolvedFeatureRuntime,\n): string {\n const root = parse(html) as HTMLElement;\n const nsAttr = namespaceAttrName(runtime.namespace);\n const flagNames = Object.keys(runtime.flags);\n const knownTokens = flagNames.map((n) => toToken(n));\n\n const candidates = root.querySelectorAll(\"*\") as unknown as HTMLElement[];\n const toRemove: HTMLElement[] = [];\n for (const el of candidates) {\n if (shouldCullElement(el, runtime, nsAttr, knownTokens, flagNames)) {\n toRemove.push(el);\n }\n }\n toRemove.sort((a, b) => elementDepth(b) - elementDepth(a));\n for (const el of toRemove) {\n el.remove();\n }\n\n const htmlEl = root.querySelector(\"html\") as HTMLElement | null;\n if (htmlEl) {\n htmlEl.removeAttribute(\"data-ff-route\");\n htmlEl.removeAttribute(\"data-ff-route-label\");\n }\n\n const styles = root.querySelectorAll(\"style\") as unknown as HTMLElement[];\n for (const st of styles) {\n const text = st.textContent ?? \"\";\n if (!text.trim()) st.remove();\n }\n\n return root.outerHTML;\n}\n\n/**\n * Walk `outDir` (e.g. Astro `dist/`) and apply {@link cullProductionHtml} to every `.html` file.\n */\nexport function applyProductionHtmlCullToDist(\n outDir: string,\n runtime: ResolvedFeatureRuntime,\n): void {\n for (const file of walkHtmlFiles(outDir)) {\n const before = readFileSync(file, \"utf8\");\n const after = cullProductionHtml(before, runtime);\n if (after !== before) writeFileSync(file, after, \"utf8\");\n }\n}\n","import type { ResolvedFeatureRuntime } from \"./runtime\";\nimport { toToken } from \"./runtime\";\nimport { inlineInvoke } from \"./inline-script\";\nimport { affHeadInlineRuntime } from \"./dev-inline-runtimes\";\n\n/**\n * Single `injectScript('head-inline', …)` payload for `astro dev`: dev-only outline CSS,\n * `data-ff-route` on `<html>` (pathname + {@link ResolvedFeatureRuntime.routeFlags}), then\n * the feature-flag dev bootstrap (toolbar state, combo badges, route chrome).\n */\nexport function buildAffDevHeadInline(args: {\n runtime: ResolvedFeatureRuntime;\n featureFlagStyles: string;\n affDevBootstrap: string;\n}): string {\n const { runtime, featureFlagStyles, affDevBootstrap } = args;\n const flagNameToToken = Object.fromEntries(\n Object.keys(runtime.flags).map((name) => [name, toToken(name)]),\n );\n const setup = inlineInvoke(affHeadInlineRuntime, {\n featureFlagStyles,\n routeFlags: runtime.routeFlags,\n flagNameToToken,\n });\n return `${setup}\\n${affDevBootstrap}`;\n}\n","/**\n * Shared JS helper source used by generated browser scripts.\n * Keep this in sync with runtime.routePatternToPrefix semantics.\n */\nexport function routePrefixJsHelper(functionName: string): string {\n return `function ${functionName}(pattern){var p=String(pattern||'').trim();if(p.endsWith('/**'))p=p.slice(0,-3);else if(p.endsWith('/*'))p=p.slice(0,-2);else if(p.length>1&&p.endsWith('*'))p=p.slice(0,-1);return p.endsWith('/')?p:p+'/';}`;\n}\n","import { rmSync } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport {\n elementBadgePositionBlock,\n normalizeElementBadgeLayout,\n} from \"./badge-layout\";\nimport { DEV_TOOLBAR_FLAG_ICON_SVG } from \"./dev-toolbar-flag-icon\";\nimport type {\n ElementBadgeHorizontalAlign,\n ElementBadgeLayoutOptions,\n ElementBadgeVerticalAnchor,\n NormalizedElementBadgeLayout,\n} from \"./badge-layout\";\nimport {\n type FeatureFlagMap,\n type ResolveFeatureRuntimeOptions,\n type ResolvedFeatureRuntime,\n resolveFeatureFlagsByEnvironment,\n resolveFeatureRuntime,\n routePathsToPrune,\n shouldIncludeRoute,\n toEnumKey,\n toToken,\n} from \"./runtime\";\n\nexport type {\n ElementBadgeHorizontalAlign,\n ElementBadgeLayoutOptions,\n ElementBadgeVerticalAnchor,\n NormalizedElementBadgeLayout,\n};\nexport { elementBadgePositionBlock, normalizeElementBadgeLayout };\n\nexport type {\n DevOutlineCssOptions,\n DevOutlineHiddenStrategy,\n} from \"./dev-outline-css\";\n\nimport type { DevOutlineCssOptions } from \"./dev-outline-css\";\nimport {\n buildAffDevBootstrapScript,\n createFeatureFlagStyles,\n createProductionGateStyles,\n} from \"./dev-outline-css\";\nimport { applyProductionHtmlCullToDist } from \"./production-html-cull\";\nimport { buildAffDevHeadInline } from \"./dev-head-inject\";\nimport { routePrefixJsHelper } from \"./route-prefix-js\";\n\nexport { createFeatureFlagStyles, createProductionGateStyles };\nexport {\n cullProductionHtml,\n applyProductionHtmlCullToDist,\n} from \"./production-html-cull\";\n\nexport interface AstroFeatureFlagsOptions extends ResolveFeatureRuntimeOptions {\n css?: DevOutlineCssOptions;\n /** When false, keeps static build output untouched (no route pruning / HTML cull). */\n staticMinify?: boolean;\n}\n\n/** Prefer `prod` if present; else first non-`dev` key (sorted); else `\"prod\"`. */\nexport function primaryNonDevEnvironmentKey(\n flagsByEnvironment: Record<string, FeatureFlagMap>,\n): string {\n const keys = Object.keys(flagsByEnvironment);\n if (keys.includes(\"prod\")) return \"prod\";\n const nonDev = keys.filter((k) => k !== \"dev\").sort();\n return nonDev[0] ?? \"prod\";\n}\n\nfunction withDefaultEnvironments(\n options: ResolveFeatureRuntimeOptions = {},\n): ResolveFeatureRuntimeOptions {\n const mode = options.mode ?? process.env.NODE_ENV ?? \"development\";\n const raw =\n options.environments &&\n typeof options.environments === \"object\" &&\n !Array.isArray(options.environments)\n ? { ...options.environments }\n : {};\n delete raw.dev;\n const environments: NonNullable<ResolveFeatureRuntimeOptions[\"environments\"]> =\n {\n dev: {\n when: mode !== \"production\",\n },\n ...raw,\n };\n if (Object.keys(environments).length < 2) {\n environments.prod = environments.prod ?? {\n when: mode === \"production\",\n flags: {},\n };\n }\n return { ...options, environments };\n}\n\nexport function createVirtualModuleSource(\n runtime: ResolvedFeatureRuntime,\n css?: DevOutlineCssOptions,\n flagsByEnvironment: Record<string, FeatureFlagMap> = {},\n devBootstrap?: string,\n): string {\n const flagNames = Object.keys(runtime.flags);\n const flagTokens = flagNames.map((name) => toToken(name));\n const enumEntries = flagNames\n .map((name) => ` ${toEnumKey(name)}: ${JSON.stringify(name)}`)\n .join(\",\\n\");\n const tokenEntries = flagNames\n .map((name) => ` ${toEnumKey(name)}: ${JSON.stringify(toToken(name))}`)\n .join(\",\\n\");\n const styles = createFeatureFlagStyles(runtime, css);\n const colorsJson = JSON.stringify(runtime.flagColorsByToken, null, 2);\n const flagNameToToken = JSON.stringify(\n Object.fromEntries(flagNames.map((name) => [name, toToken(name)])),\n null,\n 2,\n );\n const defaultNonDevEnvironment =\n primaryNonDevEnvironmentKey(flagsByEnvironment);\n const bootstrap =\n devBootstrap ??\n buildAffDevBootstrapScript(\n flagTokens,\n { ...runtime.flagColorsByToken },\n { ...runtime.flagOutlineDefaultsByToken },\n { ...runtime.flagBadgeDefaultsByToken },\n { ...runtime.routeFlags },\n Object.fromEntries(flagNames.map((name) => [name, toToken(name)])),\n runtime.namespace,\n );\n const affRoutePatternToPrefix = routePrefixJsHelper(\"affRoutePatternToPrefix\");\n\n return `${affRoutePatternToPrefix}\n\nexport const FeatureFlag = Object.freeze({\n${enumEntries}\n});\n\nexport const FeatureToken = Object.freeze({\n${tokenEntries}\n});\n\nexport const featureFlagTokens = Object.freeze(${JSON.stringify(flagTokens)});\nexport const featureFlagColors = Object.freeze(${colorsJson});\nexport const featureFlags = Object.freeze(${JSON.stringify(runtime.flags, null, 2)});\nexport const featureFlagsByEnvironment = Object.freeze(${JSON.stringify(flagsByEnvironment, null, 2)});\nexport const defaultNonDevEnvironment = ${JSON.stringify(defaultNonDevEnvironment)};\nexport const featureRouteFlags = Object.freeze(${JSON.stringify(runtime.routeFlags, null, 2)});\nexport const featureNamespace = ${JSON.stringify(runtime.namespace)};\n/** True when Astro is running the dev server or dev build (import.meta.env.DEV). Not the same as the reserved \\`dev\\` environment layer — see \\`activeEnvironmentKey\\`. */\nexport const isAstroDev = import.meta.env.DEV;\nexport const activeEnvironmentKey = ${JSON.stringify(runtime.activeEnvironment)};\n\nexport const affDevBootstrap = import.meta.env.DEV ? ${JSON.stringify(bootstrap)} : '';\n\nexport function isFeatureEnabled(flag) {\n return Boolean(featureFlags[flag]);\n}\n\nexport function shouldRenderFeature(flag) {\n return isFeatureEnabled(flag);\n}\n\nexport function isFeatureRoute(pathname) {\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n return Object.keys(featureRouteFlags).some((route) => {\n const candidate = affRoutePatternToPrefix(route);\n return normalized === candidate || normalized.startsWith(candidate);\n });\n}\n\nexport function shouldIncludePath(pathname) {\n if (import.meta.env.DEV) return true;\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n for (const [route, flags] of Object.entries(featureRouteFlags)) {\n const candidate = affRoutePatternToPrefix(route);\n if (normalized === candidate || normalized.startsWith(candidate)) {\n return flags.every((flag) => isFeatureEnabled(flag));\n }\n }\n return true;\n}\n\nexport function flagsForEnvironment(envName) {\n var F = featureFlagsByEnvironment[envName];\n return F != null ? F : null;\n}\n\nexport function isFeatureEnabledForEnvironment(flag, envName) {\n var F = featureFlagsByEnvironment[envName];\n return !!(F && F[flag]);\n}\n\nexport function shouldIncludePathForEnvironment(pathname, envName) {\n var F = featureFlagsByEnvironment[envName];\n if (!F) return true;\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n for (const [route, flagList] of Object.entries(featureRouteFlags)) {\n const candidate = affRoutePatternToPrefix(route);\n if (normalized === candidate || normalized.startsWith(candidate)) {\n return flagList.every((flag) => Boolean(F[flag]));\n }\n }\n return true;\n}\n\nconst flagNameToToken = Object.freeze(${flagNameToToken});\n\nexport function matchedFeatureRoutePrefix(pathname) {\n if (!pathname) return null;\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n let best = null;\n let bestLen = -1;\n for (const routePrefix of Object.keys(featureRouteFlags)) {\n const route = affRoutePatternToPrefix(routePrefix);\n if (normalized === route || normalized.startsWith(route)) {\n if (route.length > bestLen) {\n bestLen = route.length;\n best = routePrefix;\n }\n }\n }\n return best;\n}\n\nexport function routeFeatureTokenForPath(pathname) {\n const prefix = matchedFeatureRoutePrefix(pathname);\n if (!prefix) return undefined;\n const flagNames = featureRouteFlags[prefix] || [];\n const first = flagNames[0];\n return first != null ? flagNameToToken[first] : undefined;\n}\n\nexport function routeFeatureTokensForPath(pathname) {\n if (!pathname) return [];\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n const out = [];\n const seen = new Set();\n for (const [routePrefix, flagNames] of Object.entries(featureRouteFlags)) {\n const route = affRoutePatternToPrefix(routePrefix);\n if (!(normalized === route || normalized.startsWith(route))) continue;\n for (const name of flagNames || []) {\n const token = flagNameToToken[name];\n if (!token || seen.has(token)) continue;\n seen.add(token);\n out.push(token);\n }\n }\n return out;\n}\n\nexport function routeFeatureMatchesForPath(pathname) {\n if (!pathname) return [];\n const normalized = pathname.endsWith('/') ? pathname : pathname + '/';\n var byFlag = {};\n for (const [routePattern, flagNames] of Object.entries(featureRouteFlags)) {\n const candidate = affRoutePatternToPrefix(routePattern);\n if (!(normalized === candidate || normalized.startsWith(candidate))) continue;\n for (const flagName of flagNames || []) {\n const token = flagNameToToken[flagName];\n if (!token) continue;\n if (!byFlag[flagName]) byFlag[flagName] = { flag: flagName, token, routes: [] };\n byFlag[flagName].routes.push(routePattern);\n }\n }\n return Object.values(byFlag);\n}\n\nexport const featureFlagStyles = import.meta.env.DEV ? ${JSON.stringify(styles)} : \"\";\n`;\n}\n\nexport function getResolvedFeatures(\n options: ResolveFeatureRuntimeOptions = {},\n): ResolvedFeatureRuntime {\n return resolveFeatureRuntime(withDefaultEnvironments(options));\n}\n\nexport function featureRouteIncluded(\n pathname: string,\n runtime: ResolvedFeatureRuntime,\n): boolean {\n return shouldIncludeRoute({\n pathname,\n routeFlags: runtime.routeFlags,\n flags: runtime.flags,\n isDev: runtime.isDev,\n });\n}\n\nexport type { FeatureFlagMap } from \"./runtime\";\nexport {\n longestMatchingRoutePrefix,\n mergeFlagsWithProcessEnvOverrides,\n resolveFeatureFlagsByEnvironment,\n routePatternToPrefix,\n} from \"./runtime\";\n\nexport default function astroFeatureFlags(\n options: AstroFeatureFlagsOptions = {},\n): any {\n const { css, staticMinify = true, ...flagOpts } = options;\n const opts = withDefaultEnvironments(flagOpts);\n const mode = opts.mode ?? process.env.NODE_ENV ?? \"development\";\n\n const runtime = resolveFeatureRuntime({\n ...opts,\n mode,\n });\n\n return {\n name: \"astro-feature-flags\",\n hooks: {\n \"astro:config:setup\": ({\n updateConfig,\n addDevToolbarApp,\n command,\n injectScript,\n }: {\n updateConfig: (config: unknown) => void;\n addDevToolbarApp?: (opts: {\n id: string;\n name: string;\n icon: string;\n entrypoint: string;\n }) => void;\n command?: string;\n injectScript?: (stage: string, content: string) => void;\n }) => {\n const flagNames = Object.keys(runtime.flags);\n const flagTokens = flagNames.map((name) => toToken(name));\n const flagsByEnvironment = resolveFeatureFlagsByEnvironment(opts);\n const flagNameToToken = Object.fromEntries(\n flagNames.map((name) => [name, toToken(name)]),\n );\n const bootstrap = buildAffDevBootstrapScript(\n flagTokens,\n { ...runtime.flagColorsByToken },\n { ...runtime.flagOutlineDefaultsByToken },\n { ...runtime.flagBadgeDefaultsByToken },\n { ...runtime.routeFlags },\n flagNameToToken,\n runtime.namespace,\n );\n\n if (\n command === \"dev\" &&\n runtime.isDev &&\n typeof addDevToolbarApp === \"function\"\n ) {\n addDevToolbarApp({\n id: \"astro-feature-flags\",\n name: \"Feature flags\",\n icon: DEV_TOOLBAR_FLAG_ICON_SVG,\n entrypoint: fileURLToPath(\n new URL(\"./dev-toolbar-app.mjs\", import.meta.url),\n ),\n });\n }\n if (\n command === \"dev\" &&\n runtime.isDev &&\n typeof injectScript === \"function\"\n ) {\n const styles = createFeatureFlagStyles(runtime, css);\n injectScript(\n \"head-inline\",\n buildAffDevHeadInline({\n runtime,\n featureFlagStyles: styles,\n affDevBootstrap: bootstrap,\n }),\n );\n }\n updateConfig({\n vite: {\n plugins: [\n {\n name: \"astro-feature-flags:virtual-module\",\n resolveId(id: string) {\n if (id === \"virtual:astro-feature-flags\")\n return \"\\0virtual:astro-feature-flags\";\n return null;\n },\n load(id: string) {\n if (id === \"\\0virtual:astro-feature-flags\") {\n return createVirtualModuleSource(\n runtime,\n css,\n flagsByEnvironment,\n bootstrap,\n );\n }\n return null;\n },\n },\n ],\n },\n });\n },\n \"astro:build:done\": ({ dir }: { dir: URL }) => {\n if (runtime.isDev || !staticMinify) return;\n const outDir = fileURLToPath(dir);\n const prunePaths = routePathsToPrune({\n routeFlags: runtime.routeFlags,\n flags: runtime.flags,\n });\n for (const routePath of prunePaths) {\n rmSync(join(outDir, routePath), { recursive: true, force: true });\n }\n applyProductionHtmlCullToDist(outDir, runtime);\n },\n },\n };\n}\n"],"mappings":"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"}
@@ -11,12 +11,12 @@
11
11
  },
12
12
  "dependencies": {
13
13
  "@at-flux/astro-feature-flags": "link:..",
14
- "astro": "^5.18.0"
14
+ "astro": "5.18.0"
15
15
  },
16
16
  "devDependencies": {
17
- "@astrojs/check": "^0.9.8",
18
- "@tailwindcss/vite": "^4.2.2",
19
- "tailwindcss": "^4.2.2",
20
- "typescript": "^5.9.3"
17
+ "@astrojs/check": "0.9.8",
18
+ "@tailwindcss/vite": "4.2.2",
19
+ "tailwindcss": "4.2.2",
20
+ "typescript": "5.9.3"
21
21
  }
22
22
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@at-flux/astro-feature-flags",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "type": "module",
5
5
  "description": "Typed Astro feature flags from JSON config. Includes environment specific rendering and pruning from elements to entire routes",
6
6
  "author": "atflux <dev@atflux.uk>",
@@ -35,14 +35,14 @@
35
35
  }
36
36
  },
37
37
  "peerDependencies": {
38
- "astro": "^4.7.0 || ^5.0.0"
38
+ "astro": "4.7.0 || 5.0.0"
39
39
  },
40
40
  "devDependencies": {
41
- "@types/node": "^24.7.0",
42
- "astro": "^5.18.0",
43
- "tsdown": "^0.21.7",
44
- "typescript": "^5.9.3",
45
- "vitest": "^3.2.4"
41
+ "@types/node": "24.7.0",
42
+ "astro": "5.18.1",
43
+ "tsdown": "0.21.8",
44
+ "typescript": "5.9.3",
45
+ "vitest": "3.2.4"
46
46
  },
47
47
  "repository": {
48
48
  "type": "git",
@@ -57,7 +57,7 @@
57
57
  "access": "public"
58
58
  },
59
59
  "dependencies": {
60
- "node-html-parser": "^7.1.0"
60
+ "node-html-parser": "7.1.0"
61
61
  },
62
62
  "scripts": {
63
63
  "build": "tsdown",
@@ -24,10 +24,23 @@ export function affHeadInlineRuntime(payload: HeadInlinePayload): void {
24
24
  };
25
25
  try {
26
26
  const { featureFlagStyles, routeFlags: RF, flagNameToToken: M } = payload;
27
- const s = document.createElement("style");
28
- s.setAttribute("data-astro-feature-flags", "");
29
- s.textContent = featureFlagStyles;
30
- (document.head || document.documentElement).appendChild(s);
27
+
28
+ // Inject the dev-chrome stylesheet. Astro's ClientRouter swaps <head> on a
29
+ // view-transition navigation, and a runtime-injected <style> is dropped on
30
+ // the first swap unless marked persistent — which is why the element
31
+ // outline/badge chrome vanished after navigating. Mark it
32
+ // data-astro-transition-persist so ClientRouter keeps a single copy, and
33
+ // keep the injector idempotent + re-run it on every nav so the styles stay
34
+ // constant across pages even where a swap removes them.
35
+ const ensureFeatureFlagStyles = () => {
36
+ if (document.querySelector("style[data-astro-feature-flags]")) return;
37
+ const s = document.createElement("style");
38
+ s.setAttribute("data-astro-feature-flags", "");
39
+ s.setAttribute("data-astro-transition-persist", "astro-feature-flags");
40
+ s.textContent = featureFlagStyles;
41
+ (document.head || document.documentElement).appendChild(s);
42
+ };
43
+ ensureFeatureFlagStyles();
31
44
 
32
45
  const affPath = () => {
33
46
  let p = typeof location !== "undefined" && location.pathname ? location.pathname : "/";
@@ -99,6 +112,7 @@ export function affHeadInlineRuntime(payload: HeadInlinePayload): void {
99
112
  };
100
113
 
101
114
  const affApply = () => {
115
+ ensureFeatureFlagStyles();
102
116
  document.documentElement.setAttribute("data-ff-route", affTokens(affPath()).join(" "));
103
117
  affSyncDisabledOverlay();
104
118
  };